Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge Changes #14

Open
wants to merge 694 commits into
base: DEV
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
694 commits
Select commit Hold shift + click to select a range
0e0a472
fix(mobile): ensure notifier is mounted before updating state (#6308)
shenlong-tanwen Jan 10, 2024
4f942bc
fix(mobile): copy shared link (#6310)
shenlong-tanwen Jan 10, 2024
2974743
ci: Run e2e tests on self-hosted runner (#6296)
bo0tzz Jan 11, 2024
7532929
feat(ml): ARMNN acceleration (#5667)
fyfrey Jan 11, 2024
a1523a9
docs: Update libraries.md (#6333)
gtsteffaniak Jan 11, 2024
2439c5a
refactor: open api (#6334)
jrasm91 Jan 12, 2024
a4f49d1
refactor(web): admin settings (#6177)
danieldietzler Jan 12, 2024
d12a361
fix(web): OAuth settings nits (#6330)
bo0tzz Jan 12, 2024
df4af02
Edit and update of FAQ+new database GUI guide (#5958)
aviv926 Jan 12, 2024
19e9908
fix(web): show description in shared links (#4249)
sellnat77 Jan 12, 2024
f4edb6c
feat(server): track metadata extracted at (#6352)
jrasm91 Jan 13, 2024
deb1f97
feat(server, web): quotas (#4471)
cfitzw Jan 13, 2024
bd5ae9f
fix(web): wrap long word in description (#6351)
alextran1502 Jan 13, 2024
20be42c
chore(deps): update machine-learning (#6302)
renovate[bot] Jan 13, 2024
e2666f0
fix(ml): remove unused import (#6356)
mertalev Jan 13, 2024
5e2aec3
fix(web): quota usage view (#6358)
danieldietzler Jan 13, 2024
9a7f987
fix(docs) Fix relative paths leading to broken links (#6354)
thariq-shanavas Jan 13, 2024
f3d9196
fix(docs) Fix command name (#6368)
Funk66 Jan 13, 2024
6ee9c82
chore(dep): remove unused badges dep (#6384)
shenlong-tanwen Jan 15, 2024
a09fbe5
chore(renovate): enforce compatible flavor for guava (#6392)
shenlong-tanwen Jan 15, 2024
2a8cb70
fix(deps): update dependency geo-tz to v8 (#6388)
renovate[bot] Jan 15, 2024
d096cac
chore(web): quota enhancement (#6371)
alextran1502 Jan 15, 2024
aa8c54e
feat(mobile): Focus search on doubletap nav button (#6048)
justinvdk Jan 15, 2024
e6c0f0e
refactor(mobile): maplibre (#6087)
shenlong-tanwen Jan 15, 2024
4f021a7
chore(renovate): enforce compatible flavor for guava using versionSch…
shenlong-tanwen Jan 15, 2024
984feaf
fix(server): extract image description (#6344)
tlvince Jan 15, 2024
ba5cca9
chore(dep): update auto_route (#6390)
shenlong-tanwen Jan 15, 2024
fff3a52
deps: fix guava versioning (#6402)
jrasm91 Jan 15, 2024
3e793c5
docs: fix pgadmin links (#6403)
jrasm91 Jan 15, 2024
f160969
fix(deps): update dependency com.google.guava:guava to v33 (#5390)
renovate[bot] Jan 15, 2024
7fc1954
fix(server): add filename search (#6394)
sushain97 Jan 15, 2024
76bad76
fix(mobile): null check on null value on top app bar (#6406)
alextran1502 Jan 16, 2024
9976b2a
chore(deps): update @immich/cli (#6412)
renovate[bot] Jan 16, 2024
6457436
chore(deps): update web (#6413)
renovate[bot] Jan 16, 2024
9300946
fix(deps): update docs (#6420)
renovate[bot] Jan 16, 2024
8e2d790
deps: separate out reflect metadata (#6425)
jrasm91 Jan 16, 2024
7b0104f
chore(deps): update dependency @types/node to v20.11.0 (#6424)
renovate[bot] Jan 16, 2024
76b66e4
chore(deps): update base-image to v20240111 (major) (#6355)
renovate[bot] Jan 16, 2024
d3ff240
chore(deps): update mambaorg/micromamba:bookworm-slim docker digest t…
renovate[bot] Jan 16, 2024
abce82e
fix(server): enable/disable password login on truenas (#6433)
jrasm91 Jan 16, 2024
78de4f1
feat(mobile): quota (#6409)
alextran1502 Jan 17, 2024
4c2befc
feat(mobile): separate delete buttons (#4505)
shenlong-tanwen Jan 17, 2024
702e911
Move 'Add' button on album user invite to the same row as 'To' List …
A-wels Jan 17, 2024
a7768cc
docs: Update remove-offline-files.md (#6449)
hirenshah Jan 17, 2024
0350058
fix(deps): update server (#6415)
renovate[bot] Jan 17, 2024
c317fea
feat(web): force delete with shift key (#6239)
martabal Jan 17, 2024
574aecc
fix(mobile): add to album - list thumbnails (#6444)
waclaw66 Jan 17, 2024
6f29100
fix(server): handle 5 digit years (#6457)
jrasm91 Jan 17, 2024
c555034
fix(server): set log level of immich-admin process in boostrap functi…
alextran1502 Jan 17, 2024
f0b328f
feat(server, web): smart merge (#5796)
martabal Jan 18, 2024
d4146e3
feat(server): provide the ability to search archived photos (#6332)
stevenwcarter Jan 18, 2024
b98d1bf
fix(cli): uploadCounters increase only when files are uploaded (#6357)
AngelPone Jan 18, 2024
9a2fa21
fix(server): scale transcoded videos if dimensions are odd (#6461)
mertalev Jan 18, 2024
98cee88
docs: remove tsc check (#6464)
jrasm91 Jan 18, 2024
44873b4
deps: update docs (#6462)
jrasm91 Jan 18, 2024
68f5281
feat(server): separate face clustering job (#5598)
mertalev Jan 18, 2024
0239312
fix(web): trash or delete (#6475)
martabal Jan 18, 2024
91efe7f
chore: update bug template (#6465)
jrasm91 Jan 18, 2024
660b2e9
feat(format): hif format (#6477)
haossu Jan 18, 2024
04c783f
fix(mobile): asset state when delete from trash (#6476)
shenlong-tanwen Jan 18, 2024
f62678f
feat(mobile): long-press delete button to permanently delete asset (#…
shenlong-tanwen Jan 18, 2024
fa0b7c8
Version v1.93.0
alextran1502 Jan 19, 2024
7e1b1ea
chore: post release tasks
alextran1502 Jan 19, 2024
1af5fcf
chore: post release openapi update
alextran1502 Jan 19, 2024
20c2844
chore: post release openapi update
alextran1502 Jan 19, 2024
b4d1470
fix(web): prevent layout change from scrollbar in admin setings (#6482)
mertalev Jan 19, 2024
dacca4c
fix(web): slider (#6485)
jrasm91 Jan 19, 2024
d5af357
Version v1.93.1
alextran1502 Jan 19, 2024
df27460
fix: open api pump (#6502)
jrasm91 Jan 19, 2024
07b874e
fix(web): revert smart merge (#6504)
alextran1502 Jan 19, 2024
d15c443
fix(web): user list when sharing an album (#6500)
martabal Jan 19, 2024
f80f867
fix(mobile): stack button not in bottom app bar (#6497)
shenlong-tanwen Jan 19, 2024
17eaeb6
feat: smart merge (#6508)
martabal Jan 19, 2024
88ac3c2
fix(web): better invite shared user to album layout (#6511)
alextran1502 Jan 19, 2024
3dddc6b
Version v1.93.2
alextran1502 Jan 19, 2024
68d4f1b
fix(web): delete user (#6514)
martabal Jan 19, 2024
aa02ccb
fix(web): album description (#6512)
martabal Jan 19, 2024
732f289
fix(web): better button placement within the user management table (#…
Funk66 Jan 19, 2024
1efcb00
fix(web): Use correct unit for user quota (#6518)
Mortein Jan 20, 2024
144822d
chore: recap post date
alextran1502 Jan 20, 2024
4ebb997
Version v1.93.3
alextran1502 Jan 20, 2024
6e066aa
chore: svelte-kit-2 (#6103)
jrasm91 Jan 20, 2024
2c783b7
feat(web) dismiss user management modals on escape (#6530)
Funk66 Jan 20, 2024
a9dc16e
chore(deps): update dependency vite to v5.0.12 [security] (#6526)
renovate[bot] Jan 20, 2024
c8b33c0
fix(server): use crf-based two pass for vp9 if max bitrate is disable…
mertalev Jan 20, 2024
4d41701
chore(deps): update dependency @types/node to v20.11.3 (#6533)
renovate[bot] Jan 21, 2024
311261b
fix(server): disable sharp file caching (#6542)
mertalev Jan 21, 2024
607fd39
fix(server): only calculate quota usage for internal assets (#6556)
danieldietzler Jan 21, 2024
b34a808
feat(mobile): Add Slovenian (#6558)
alextran1502 Jan 21, 2024
6b419a9
doc: developer setup docs (#6557)
kaysond Jan 21, 2024
95cfe22
feat(ml)!: cuda and openvino acceleration (#5619)
mertalev Jan 21, 2024
3845fec
refactor(web): descriptions (#6517)
martabal Jan 22, 2024
e427712
chore: remove unused files and references (#6562)
jrasm91 Jan 22, 2024
42d2088
fix(web): auto generate open api build (#6561)
jrasm91 Jan 22, 2024
e6f260c
chore(deps): update base-image to v20240118 (major) (#6473)
renovate[bot] Jan 22, 2024
871d0c7
chore(deps): pin dependencies (#6436)
renovate[bot] Jan 22, 2024
7b6fb2b
chore(deps): update dependency @types/node to v20.11.5 (#6546)
renovate[bot] Jan 22, 2024
a8efbc6
deps: update renovate groups and schedule (#6579)
jrasm91 Jan 22, 2024
bd2dbb4
fix(web): always use websocket transport (#6564)
jrasm91 Jan 22, 2024
7b314f9
chore(server): sort open api params (#6484)
jrasm91 Jan 22, 2024
a972dd4
fix(server): extraction of Samsung Motionphoto videos (#6337)
kaysond Jan 22, 2024
1490e6c
chore(deps): update web (#6584)
renovate[bot] Jan 23, 2024
acf83bd
chore(deps): update @immich/cli (#6585)
renovate[bot] Jan 23, 2024
773d093
fix(deps): update server (#6587)
renovate[bot] Jan 23, 2024
234a959
fix(web): revert descriptions (#6582)
martabal Jan 23, 2024
a00768c
chore(server): remove old device id endpoint (#6578)
jrasm91 Jan 23, 2024
f97f23d
chore(deps): update dependency @sveltejs/kit to v2.3.5 (#6590)
renovate[bot] Jan 23, 2024
74f1000
fix(web): statusbox re-rendering and nav bar when trashing assets (#6…
martabal Jan 23, 2024
7e84cd6
fix(deps): update dependency svelte-maplibre to v0.7.6 (#6591)
renovate[bot] Jan 23, 2024
4fa7005
spelling of "recommend" - Update command-line-interface.md (#6603)
sybenx Jan 23, 2024
61bb52a
docs: diff highlighting (#6604)
jrasm91 Jan 23, 2024
d801131
fix(docs): search (#6605)
jrasm91 Jan 23, 2024
bf64e64
chore(server): remove unused dependency (#6606)
jrasm91 Jan 23, 2024
160366c
fix(deps): update server (#6588)
renovate[bot] Jan 23, 2024
1c35110
fix(deps): update dependency orjson to v3.9.12 (#6600)
renovate[bot] Jan 23, 2024
3ae10c7
fix(mobile): es-US pluralization (#6612)
shenlong-tanwen Jan 24, 2024
9652813
fix(web): merging people when renaming (#6608)
martabal Jan 24, 2024
5f051e3
docs: add reverse proxy configuration for Apache (#6625)
Lenart12 Jan 24, 2024
3e5448a
chore(deps): update dependency @sveltejs/kit to v2.4.3 [security] (#6…
renovate[bot] Jan 24, 2024
4424f3c
fix(deps): update exiftool (#6586)
renovate[bot] Jan 24, 2024
852effa
refactor(server): e2e (#6632)
jrasm91 Jan 24, 2024
bd87eb3
feat(server): optimize partial facial recognition (#6634)
mertalev Jan 25, 2024
7fc4abb
feat(server): sql access checks (#6635)
jrasm91 Jan 25, 2024
64e299b
fix(mobile): skip tests using mock http client to make actual network…
alextran1502 Jan 25, 2024
4eca2b0
feat(web): include timestamp in download filename (#5878)
MohamedFBoussaid Jan 25, 2024
19d4c5e
docs: Update documentation (#6430)
aviv926 Jan 25, 2024
b306cf5
refactor(server): move asset detail endpoint to new controller (#6636)
jrasm91 Jan 25, 2024
ca28e1e
fix(ml): error logging (#6646)
mertalev Jan 26, 2024
2fff907
chore: fix typo in `generate-open-api.sh` (#6648)
benmccann Jan 26, 2024
ae4229b
chore(deps): update base-image to v20240125 (major) (#6637)
renovate[bot] Jan 26, 2024
84f8a4a
chore(open-api): remove no-op patch (#6649)
benmccann Jan 26, 2024
6b011b9
chore: remove unused packages (#6654)
benmccann Jan 26, 2024
741ce04
Fix CVE–2023–45133 (#6661)
debricked[bot] Jan 26, 2024
de47a6a
fix(web): feature photo not changing in the edit name component (#6663)
martabal Jan 26, 2024
7ea55c7
refactor(server): download endpoints (#6653)
jrasm91 Jan 26, 2024
48fd64a
fix(deps) CVE–2021–43138 (#6662)
debricked[bot] Jan 26, 2024
33b3b80
fix(deps) CVE–2022–25887 (#6664)
debricked[bot] Jan 26, 2024
2d0db5d
fix(deps) CVE-2022-25883 (#6667)
etnoy Jan 26, 2024
0d3929f
fix(deps) bump marked to 11.1.1 (#6670)
etnoy Jan 26, 2024
78a2fa8
fix(deps): bump lodash (#6672)
etnoy Jan 26, 2024
2e39243
deps: open-api generator (#6655)
jrasm91 Jan 26, 2024
3375768
fix(deps) bump msgpackr (#6673)
etnoy Jan 26, 2024
96b7885
refactor(server): trash endpoints (#6652)
jrasm91 Jan 26, 2024
8aef92a
feat(server, web): accepted codecs (#6460)
Hely0n Jan 26, 2024
77f11e3
feat(web): places page (#6669)
l0nax Jan 26, 2024
70aeb82
fix(deps) CVE–2023–45857 (bump axios) (#6665)
debricked[bot] Jan 27, 2024
0522058
chore(deps): update redis:6.2-alpine docker digest to 201502e (#6684)
renovate[bot] Jan 27, 2024
27488ce
deps(mobile): flutter 3.16 (#6677)
shenlong-tanwen Jan 27, 2024
7404688
chore(deps): update redis:6.2-alpine docker digest to 60727c1 (#6688)
renovate[bot] Jan 27, 2024
5cec8d1
chore: remove unused test util methods (#6689)
benmccann Jan 27, 2024
64ab09b
Update fr-FR.json (#6633)
axgdcode Jan 27, 2024
c4b8c85
feat(wip): add Combobox component for timezone picker (#6154)
m1yon Jan 27, 2024
25cad79
refactor: move asset stacks to their own entity (#6353)
zackpollard Jan 27, 2024
9422359
fix(mobile): Fix single element archiving (#6668)
l0nax Jan 27, 2024
e933383
chore(server): use fs to read package.json (#6692)
danieldietzler Jan 27, 2024
e2ac019
Fixed errors regarding the installation script (#6644)
Peppe289 Jan 28, 2024
2249f7d
fix(ml): handle missing `context_length` field (#6695)
mertalev Jan 28, 2024
fa09131
feat(web,server): search people (#5703)
martabal Jan 28, 2024
a84b6f5
feat(ml): conditionally download .armnn models (#6650)
mertalev Jan 28, 2024
be55396
chore(deps): update redis:6.2-alpine docker digest to e37f165 (#6693)
renovate[bot] Jan 28, 2024
f52994e
fix(ml): model paths not working (#6705)
mertalev Jan 28, 2024
28806d0
fix(server): fix person pagination when deleting (#6707)
mertalev Jan 28, 2024
0770ad1
fix(server): do not process faces of deleted assets (#6710)
mertalev Jan 29, 2024
e5a7032
fix(server): HEVC videos not playing on Apple devices (#6712)
mertalev Jan 29, 2024
ae7f174
refactor: rename clip -> smart search (#6713)
mertalev Jan 29, 2024
0af8398
docs: use organization support link in README (#6722)
danieldietzler Jan 29, 2024
6dca47c
refactor: no experimental vm modules (#6719)
jrasm91 Jan 29, 2024
a162af0
docs: fix broken link (#6735)
jrasm91 Jan 29, 2024
68f8525
chore(deps): update dependency @types/node to v20.11.6 (#6742)
renovate[bot] Jan 30, 2024
76f8d03
added a configuration option to select the dri node in transcoding (#…
t4keda Jan 30, 2024
2bcba52
fix(deps): update machine-learning (#6745)
renovate[bot] Jan 30, 2024
a7e01bc
docs: fix typo (#6739)
benmccann Jan 30, 2024
d1ae46e
chore(deps): update @immich/cli (#6741)
renovate[bot] Jan 30, 2024
1231d62
chore(deps): update server (#6744)
renovate[bot] Jan 30, 2024
64fad67
chore(deps): update web (#6746)
renovate[bot] Jan 30, 2024
64da2c1
refactor(cli): organize files, simplify types, use @immich/sdk (#6747)
jrasm91 Jan 30, 2024
f6afb23
docs(server): Fix glob pattern in library exclusions (#6752)
etnoy Jan 30, 2024
fdf4ee1
chore(deps): update redis:6.2-alpine docker digest to afb290a (#6708)
renovate[bot] Jan 30, 2024
40e14fc
fix(deps): update dependency geo-tz to v8.0.1 (#6750)
renovate[bot] Jan 30, 2024
dd9b08d
chore(deps): update dependency @faker-js/faker to v8.4.0 (#6758)
renovate[bot] Jan 30, 2024
9e1d358
fix(mobile): blurry memory photos (#6734)
alextran1502 Jan 30, 2024
4290a29
chore(deps): update base-image to v20240130 (major) (#6756)
renovate[bot] Jan 30, 2024
7a1f25b
feat(web): add warning when setting a quota superior to the disk size…
martabal Jan 30, 2024
97cd917
fix(mobile): FR translation (#6771)
alextran1502 Jan 30, 2024
417a351
docs: add FAQ for duplicate primary key errors (#6763)
mmomjian Jan 30, 2024
8864d07
chore(mobile): Corrects FVM gitignore and sets Flutter version to 3.1…
martyfuhry Jan 30, 2024
33bf7d5
Localizely: Translations update (#6772)
alextran1502 Jan 30, 2024
e5bfe22
chore(devops): use new MacOS runner (#6774)
alextran1502 Jan 30, 2024
1461656
chore(deps): update dependency @sveltejs/kit to v2.5.0 (#6776)
renovate[bot] Jan 30, 2024
b4d5f14
Document how to prevent HTML entity escaping. (#6773)
mmomjian Jan 30, 2024
102b5be
fix(docs): formatting
alextran1502 Jan 30, 2024
e90d3a1
fix(docs): formatting
alextran1502 Jan 30, 2024
1bfef20
fix(server): avoid leaking people data on shared links (#6779)
martabal Jan 30, 2024
9c7dee8
chore: migrate CLI to ESM and vitest (#6777)
benmccann Jan 30, 2024
149bc71
feat(mobile): Add end page to the end to memories (#6780)
martyfuhry Jan 30, 2024
87c38d1
feat(server, web): Added TranscodePolicy "Bitrate higher than max bit…
Hely0n Jan 31, 2024
4079e92
chore: drop CJS build from API (#6783)
benmccann Jan 31, 2024
068e703
feat(server): Automatic watching of library folders (#6192)
etnoy Jan 31, 2024
c083636
docs: update milestones (#6787)
etnoy Jan 31, 2024
a7ed2b7
Version v1.94.0
alextran1502 Jan 31, 2024
b0d7434
fix: library watching (#6802)
jrasm91 Jan 31, 2024
d4c000c
Version v1.94.0
alextran1502 Jan 31, 2024
b7a372c
chore: release group labels (#6805)
jrasm91 Jan 31, 2024
0b5e138
Merge branch 'main' of github.com:immich-app/immich
alextran1502 Jan 31, 2024
d2c2db2
chore: post release tasks
alextran1502 Jan 31, 2024
7a075d7
fix(web): oauth login (#6813)
jrasm91 Jan 31, 2024
07466fa
Version v1.94.1
alextran1502 Jan 31, 2024
7a6ec8b
fix(mobile): video player shows black screen (#6819)
alextran1502 Jan 31, 2024
ada3eeb
chore: post release tasks
alextran1502 Jan 31, 2024
efdbe79
docs(ml): hardware acceleration (#6821)
mertalev Feb 1, 2024
81cf653
docs: provide details on storage engine album naming (#6812)
mmomjian Feb 1, 2024
606147b
fix(web): Admin Settings banner/scrolling issue (#6839)
azroberts8 Feb 1, 2024
a8dcfe4
chore: simplify API build (#6841)
benmccann Feb 1, 2024
e4d0560
feat(web): improved album view UI & album card UI (#6822)
azroberts8 Feb 2, 2024
f44fa45
chore(server,cli,web): housekeeping and stricter code style (#6751)
etnoy Feb 2, 2024
b87a6ab
fix(deps): update server (#6769)
renovate[bot] Feb 2, 2024
8a643e5
chore(deps): update redis:6.2-alpine docker digest to 51d6c56 (#6782)
renovate[bot] Feb 2, 2024
d3404f9
feat(server)!: oauth encryption algorithm setting (#6818)
danieldietzler Feb 2, 2024
2d278d9
feat(web): search filter form (#6651)
alextran1502 Feb 2, 2024
b768eef
fix(server): extract duration from video as ISO time (#6863)
alextran1502 Feb 2, 2024
79d3342
fix(ml): openvino not working with dynamic axes (#6871)
mertalev Feb 3, 2024
a4cfb51
chore: remove `form-data` dependency (#6876)
benmccann Feb 3, 2024
329659b
docs(ml,server): updated hwaccel docs (#6878)
mertalev Feb 3, 2024
b4c211c
fix: bundle CLI with Vite (#6893)
benmccann Feb 4, 2024
5061c35
feat(mobile): Add support for Basic Authentication (#6840)
rovo89 Feb 4, 2024
f4ab5d3
feat(mobile): Add haptic feedback to asset grid (#5344)
l0nax Feb 5, 2024
1d93889
fix(mobile): debounce map layer update (#6861)
shenlong-tanwen Feb 5, 2024
8e4bf30
chore(docs): Small FAQ tweaks and nits (#6880)
bo0tzz Feb 5, 2024
f6b4024
feat(mobile): Adds show password field to login (#6918)
martyfuhry Feb 5, 2024
c29976c
feat(mobile): Memories activity is now full screen, better image fit,…
martyfuhry Feb 5, 2024
6ed33da
chore: remove axios dependency from CLI (#6888)
benmccann Feb 5, 2024
ce6dc3b
fix(cli): auth file should be chmod 600 (#6925)
etnoy Feb 5, 2024
755444e
chore(cli): use upload api and update documentation (#6927)
etnoy Feb 6, 2024
9b3764d
chore(deps): update @immich/cli (#6928)
renovate[bot] Feb 6, 2024
31eb479
feat(cli): dockerize (#6858)
etnoy Feb 6, 2024
f7d0a8e
fix(deps): update dependency fastapi to v0.109.1 [security] (#6923)
renovate[bot] Feb 6, 2024
bb3f872
chore(deps): update dependency @types/node to v20.11.15 (#6929)
renovate[bot] Feb 6, 2024
0169707
chore: build API with esnext target (#6926)
benmccann Feb 6, 2024
16e85af
Update command-line-interface.md (#6944)
jsixface Feb 6, 2024
2d9b9aa
chore(deps): update dependency @types/node to v20.11.16 (#6946)
renovate[bot] Feb 6, 2024
1a644db
chore(deps): update dependency @types/node to v20.11.16 (#6947)
renovate[bot] Feb 6, 2024
3092a72
chore(web): remove maplibre dependency (#6948)
danieldietzler Feb 6, 2024
61768ce
chore(deps): update base-image to v20240206 (major) (#6942)
renovate[bot] Feb 6, 2024
4164bcf
chore(server): Use ChunkedSet in Access repository (#6943)
adamantike Feb 6, 2024
8c60c21
chore(deps): update web (#6933)
renovate[bot] Feb 6, 2024
d88ca5f
chore(deps): update server (#6930)
renovate[bot] Feb 6, 2024
f80af06
chore(deps): update dependency eslint-plugin-unicorn to v51 (#6952)
renovate[bot] Feb 6, 2024
b2775c4
chore(deps): update @immich/cli (#6951)
renovate[bot] Feb 6, 2024
56b0643
feat(server)!: pgvecto.rs 0.2 and pgvector compatibility (#6785)
mertalev Feb 7, 2024
479fca8
chore(deps): pin tensorchord/pgvecto-rs docker tag to 9072418 (#6960)
renovate[bot] Feb 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat(server, web): smart merge (immich-app#5796)
* pr feedback

* fix: tests

* update assets statistics

* pr feedback

* pr feedback

* fix: linter

* pr feedback

* fix: don't limit the smart merge

* pr feedback

* fix: server code

---------

Co-authored-by: Jason Rasmussen <[email protected]>
  • Loading branch information
martabal and jrasm91 authored Jan 18, 2024
commit f0b328fb6b52feb1a00e7d7fb59ebd67c28f00b3
37 changes: 34 additions & 3 deletions server/src/domain/person/person.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -834,7 +834,7 @@ describe(PersonService.name, () => {
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
});

it('should merge two people', async () => {
it('should merge two people without smart merge', async () => {
personMock.getById.mockResolvedValueOnce(personStub.primaryPerson);
personMock.getById.mockResolvedValueOnce(personStub.mergePerson);
personMock.delete.mockResolvedValue(personStub.mergePerson);
Expand All @@ -850,13 +850,44 @@ describe(PersonService.name, () => {
oldPersonId: personStub.mergePerson.id,
});

expect(personMock.update).not.toHaveBeenCalled();

expect(jobMock.queue).toHaveBeenCalledWith({
name: JobName.PERSON_DELETE,
data: { id: personStub.mergePerson.id },
});
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
});

it('should merge two people with smart merge', async () => {
personMock.getById.mockResolvedValueOnce(personStub.randomPerson);
personMock.getById.mockResolvedValueOnce(personStub.primaryPerson);
personMock.delete.mockResolvedValue(personStub.primaryPerson);
personMock.update.mockResolvedValue({ ...personStub.randomPerson, name: personStub.primaryPerson.name });
accessMock.person.checkOwnerAccess.mockResolvedValueOnce(new Set(['person-3']));
accessMock.person.checkOwnerAccess.mockResolvedValueOnce(new Set(['person-1']));

await expect(sut.mergePerson(authStub.admin, 'person-3', { ids: ['person-1'] })).resolves.toEqual([
{ id: 'person-1', success: true },
]);

expect(personMock.reassignFaces).toHaveBeenCalledWith({
newPersonId: personStub.randomPerson.id,
oldPersonId: personStub.primaryPerson.id,
});

expect(personMock.update).toHaveBeenCalledWith({
id: personStub.randomPerson.id,
name: personStub.primaryPerson.name,
});

expect(jobMock.queue).toHaveBeenCalledWith({
name: JobName.PERSON_DELETE,
data: { id: personStub.primaryPerson.id },
});
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
});

it('should throw an error when the primary person is not found', async () => {
personMock.getById.mockResolvedValue(null);
accessMock.person.checkOwnerAccess.mockResolvedValue(new Set(['person-1']));
Expand Down Expand Up @@ -885,8 +916,8 @@ describe(PersonService.name, () => {
});

it('should handle an error reassigning faces', async () => {
personMock.getById.mockResolvedValue(personStub.primaryPerson);
personMock.getById.mockResolvedValue(personStub.mergePerson);
personMock.getById.mockResolvedValueOnce(personStub.primaryPerson);
personMock.getById.mockResolvedValueOnce(personStub.mergePerson);
personMock.reassignFaces.mockRejectedValue(new Error('update failed'));
accessMock.person.checkOwnerAccess.mockResolvedValueOnce(new Set(['person-1']));
accessMock.person.checkOwnerAccess.mockResolvedValueOnce(new Set(['person-2']));
Expand Down
16 changes: 14 additions & 2 deletions server/src/domain/person/person.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ export class PersonService {
async mergePerson(auth: AuthDto, id: string, dto: MergePersonDto): Promise<BulkIdResponseDto[]> {
const mergeIds = dto.ids;
await this.access.requirePermission(auth, Permission.PERSON_WRITE, id);
const primaryPerson = await this.findOrFail(id);
let primaryPerson = await this.findOrFail(id);
const primaryName = primaryPerson.name || primaryPerson.id;

const results: BulkIdResponseDto[] = [];
Expand All @@ -481,6 +481,19 @@ export class PersonService {
continue;
}

const update: Partial<PersonEntity> = {};
if (!primaryPerson.name && mergePerson.name) {
update.name = mergePerson.name;
}

if (!primaryPerson.birthDate && mergePerson.birthDate) {
update.birthDate = mergePerson.birthDate;
}

if (Object.keys(update).length > 0) {
primaryPerson = await this.repository.update({ id: primaryPerson.id, ...update });
}

const mergeName = mergePerson.name || mergePerson.id;
const mergeData: UpdateFacesData = { oldPersonId: mergeId, newPersonId: id };
this.logger.log(`Merging ${mergeName} into ${primaryName}`);
Expand All @@ -495,7 +508,6 @@ export class PersonService {
results.push({ id: mergeId, success: false, error: BulkIdErrorReason.UNKNOWN });
}
}

return results;
}

Expand Down
14 changes: 14 additions & 0 deletions server/test/fixtures/person.stub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,18 @@ export const personStub = {
faceAsset: null,
isHidden: false,
}),
randomPerson: Object.freeze<PersonEntity>({
id: 'person-3',
createdAt: new Date('2021-01-01'),
updatedAt: new Date('2021-01-01'),
ownerId: userStub.admin.id,
owner: userStub.admin,
name: '',
birthDate: null,
thumbnailPath: '/path/to/thumbnail',
faces: [],
faceAssetId: null,
faceAsset: null,
isHidden: false,
}),
};
97 changes: 16 additions & 81 deletions web/src/lib/components/admin-page/settings/setting-switch.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { quintOut } from 'svelte/easing';
import { fly } from 'svelte/transition';
import { createEventDispatcher } from 'svelte';
import Slider from '$lib/components/elements/slider.svelte';

export let title: string;
export let subtitle = '';
Expand All @@ -10,88 +11,22 @@
export let isEdited = false;

const dispatch = createEventDispatcher<{ toggle: boolean }>();
const onToggle = (event: Event) => dispatch('toggle', (event.target as HTMLInputElement).checked);
</script>

<div class="flex place-items-center justify-between">
<div>
<div class="flex h-[26px] place-items-center gap-1">
<label class="font-medium text-immich-primary dark:text-immich-dark-primary text-sm" for={title}>
{title}
</label>
{#if isEdited}
<div
transition:fly={{ x: 10, duration: 200, easing: quintOut }}
class="rounded-full bg-orange-100 px-2 text-[10px] text-orange-900"
>
Unsaved change
</div>
{/if}
</div>

<p class="text-sm dark:text-immich-dark-fg">{subtitle}</p>
</div>

<label class="relative inline-block h-[10px] w-[36px] flex-none">
<input
class="disabled::cursor-not-allowed h-0 w-0 opacity-0"
type="checkbox"
bind:checked
on:click={onToggle}
{disabled}
/>

{#if disabled}
<span class="slider slider-disabled cursor-not-allowed" />
{:else}
<span class="slider slider-enabled cursor-pointer" />
<Slider bind:checked {disabled} on:toggle={() => dispatch('toggle', checked)}>
<div class="flex h-[26px] place-items-center gap-1">
<label class="font-medium text-immich-primary dark:text-immich-dark-primary text-sm" for={title}>
{title}
</label>
{#if isEdited}
<div
transition:fly={{ x: 10, duration: 200, easing: quintOut }}
class="rounded-full bg-orange-100 px-2 text-[10px] text-orange-900"
>
Unsaved change
</div>
{/if}
</label>
</div>

<style>
.slider {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: 0.4s;
transition: 0.4s;
border-radius: 34px;
}

input:disabled {
cursor: not-allowed;
}

.slider:before {
position: absolute;
content: '';
height: 20px;
width: 20px;
left: 0px;
right: 0px;
bottom: -4px;
background-color: gray;
-webkit-transition: 0.4s;
transition: 0.4s;
border-radius: 50%;
}

input:checked + .slider:before {
-webkit-transform: translateX(18px);
-ms-transform: translateX(18px);
transform: translateX(18px);
background-color: #4250af;
}

input:checked + .slider-disabled {
background-color: gray;
}
</div>

input:checked + .slider-enabled {
background-color: #adcbfa;
}
</style>
<p class="text-sm dark:text-immich-dark-fg">{subtitle}</p>
</Slider>
75 changes: 75 additions & 0 deletions web/src/lib/components/elements/slider.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';

export let checked = false;
export let disabled = false;

const dispatch = createEventDispatcher<{ toggle: boolean }>();
const onToggle = (event: Event) => dispatch('toggle', (event.target as HTMLInputElement).checked);
</script>

<div class="flex place-items-center justify-between">
<slot name="leading" />
<label class="relative inline-block h-[10px] w-[36px] flex-none">
<input
class="disabled::cursor-not-allowed h-0 w-0 opacity-0"
type="checkbox"
bind:checked
on:click={onToggle}
{disabled}
/>

{#if disabled}
<span class="slider slider-disabled cursor-not-allowed" />
{:else}
<span class="slider slider-enabled cursor-pointer" />
{/if}
</label>
</div>

<style>
.slider {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: 0.4s;
transition: 0.4s;
border-radius: 34px;
}

input:disabled {
cursor: not-allowed;
}

.slider:before {
position: absolute;
content: '';
height: 20px;
width: 20px;
left: 0px;
right: 0px;
bottom: -4px;
background-color: gray;
-webkit-transition: 0.4s;
transition: 0.4s;
border-radius: 50%;
}

input:checked + .slider:before {
-webkit-transform: translateX(18px);
-ms-transform: translateX(18px);
transform: translateX(18px);
background-color: #4250af;
}

input:checked + .slider-disabled {
background-color: gray;
}

input:checked + .slider-enabled {
background-color: #adcbfa;
}
</style>
7 changes: 4 additions & 3 deletions web/src/lib/components/faces-page/merge-face-selector.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

let dispatch = createEventDispatcher<{
back: void;
merge: void;
merge: PersonResponseDto;
}>();

$: hasSelection = selectedPeople.length > 0;
Expand Down Expand Up @@ -68,16 +68,17 @@

const handleMerge = async () => {
try {
const { data: results } = await api.personApi.mergePerson({
let { data: results } = await api.personApi.mergePerson({
id: person.id,
mergePersonDto: { ids: selectedPeople.map(({ id }) => id) },
});
const { data: mergedPerson } = await api.personApi.getPerson({ id: person.id });
const count = results.filter(({ success }) => success).length;
notificationController.show({
message: `Merged ${count} ${count === 1 ? 'person' : 'people'}`,
type: NotificationType.Info,
});
dispatch('merge');
dispatch('merge', mergedPerson);
} catch (error) {
handleError(error, 'Cannot merge people');
} finally {
Expand Down
4 changes: 4 additions & 0 deletions web/src/routes/(user)/people/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,12 @@
id: personMerge2.id,
mergePersonDto: { ids: [personToMerge.id] },
});

const { data: mergedPerson } = await api.personApi.getPerson({ id: personToMerge.id });

countVisiblePeople--;
people = people.filter((person: PersonResponseDto) => person.id !== personToMerge.id);
people = people.map((person: PersonResponseDto) => (person.id === personMerge2.id ? mergedPerson : person));

notificationController.show({
message: 'Merge people succesfully',
Expand Down
9 changes: 7 additions & 2 deletions web/src/routes/(user)/people/[personId]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,13 @@
}
};

const handleMerge = () => {
const handleMerge = async (person: PersonResponseDto) => {
const { data: statistics } = await api.personApi.getPersonStatistics({ id: person.id });
numberOfAssets = statistics.assets;
handleGoBack();

data.person = person;

refreshAssetGrid = !refreshAssetGrid;
};

Expand Down Expand Up @@ -374,7 +379,7 @@
{/if}

{#if viewMode === ViewMode.MERGE_PEOPLE}
<MergeFaceSelector person={data.person} on:back={handleGoBack} on:merge={handleMerge} />
<MergeFaceSelector person={data.person} on:back={handleGoBack} on:merge={({ detail }) => handleMerge(detail)} />
{/if}

<header>
Expand Down