Compare commits

...

360 Commits

Author SHA1 Message Date
Alex The Bot
cd184cf366 Version v1.67.0 2023-07-14 00:39:54 +00:00
Alex
6387e38e27 fix(server): Asset controller does not load all endpoint (#3245) 2023-07-13 18:42:27 -05:00
Mert
2fb85f4a16 chore: rebase main (#3103)
Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
2023-07-13 16:02:49 -05:00
dependabot[bot]
34d1f74b77 chore(deps): bump docker/setup-buildx-action from 2.9.0 to 2.9.1 (#3236)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2.9.0 to 2.9.1.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2.9.0...v2.9.1)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-13 10:44:37 -05:00
Harry Tran
48c9cfb432 fix(web): remove processing key events in photo viewer window (#3238) 2023-07-13 10:43:11 -05:00
Fynn Petersen-Frey
f9739c9730 feat(mobile): stop asset grid rebuilds (#3226)
* feat(mobile): stop asset grid rebuilds

* undo unnecessary changes

---------

Co-authored-by: Fynn Petersen-Frey <zoodyy@users.noreply.github.com>
2023-07-13 10:42:06 -05:00
Alex
863e983726 fix(web): download livephotos video part correctly (#3230) 2023-07-13 09:00:46 -05:00
Jason Rasmussen
b71d7e33bb refactor(server): calculate asset type server side (#3200)
* refactor(server): calculate asset type server-side

* chore: open api

* chore: remove comments

* fix: linting

* update

* Revert "update"

This reverts commit dc58702923.

* fix: upload LivePhotos

* chore: remove unused request fields for upload

* remove unused method

* mobile-fix: livePhoto filename

* fix: revert check for livephotos filename and extension

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-07-11 22:56:30 -05:00
Sergey Kondrikov
93462aafbc feat(web): improve range selection (#3193)
* Improve range selection

* Add comments

* Add PR feedback

* Remove focus outline from select asset button

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-07-11 21:12:19 -05:00
Jonathan Jogenfors
ea64fdd7b4 feat(server): Use Testcontainers for e2e tests (#3202)
* Add testcontainers to e2e

* Run e2e tests in github without docker

* Run lint on e2e

* Cleaner setup ordering

* Rename setup file
2023-07-11 16:54:44 -05:00
Alex
c86b2ae500 feat(web/server): merge faces (#3121)
* feat(server/web): Merge faces

* get parent id

* update

* query to get identical asset and change controller

* change delete asset signature

* delete identical assets

* gaming time

* delete merge person

* query

* query

* generate api

* pr feedback

* generate api

* naming

* remove unused method

* Update server/src/domain/person/person.service.ts

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>

* Update server/src/domain/person/person.service.ts

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>

* better method signature

* cleaning up

* fix bug

* added interfaces

* added tests

* merge main

* api

* build merge face interface

* api

* selector interface

* style

* more style

* clean up import

* styling

* styling

* better

* styling

* styling

* add merge face diablog

* finished

* refactor: merge person endpoint

* refactor: merge person component

* chore: open api

* fix: tests

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
2023-07-11 16:52:41 -05:00
Mert
848ba685eb fix(ml): race condition when loading models (#3207)
* sync model loading, disabled model ttl by default

* disable revalidation if model unloading disabled

* moved lock
2023-07-11 12:01:21 -05:00
Jesaja Everling
9ad024c189 Fix typo (#3216) 2023-07-11 16:48:13 +00:00
Jason Rasmussen
0b15f6035b docs: add mobile links (#3214) 2023-07-11 10:10:28 -05:00
Jason Rasmussen
1e7b657156 docs: upgrade deps (#3215) 2023-07-11 10:09:19 -05:00
Jason Rasmussen
6180828ed2 refactor(server): mime types (#3197)
* refactor(server): mime type check

* chore: open api

* chore: remove duplicate test
2023-07-10 12:56:45 -05:00
Johan Stenehall
785f61ba70 Update read-only-gallery.md (#3191)
Prevent Immich from having write access to volumes that should only be read.
2023-07-09 20:19:48 -05:00
kasgel
50f26374e3 fix(server): enable transcoding of audioless videos (#3147)
* Fix: enable transcoding of audioless videos

* Fix: enable transcoding of audioless videos & typing

* Fix: enable transcoding of audioless videos & formatting

* fix: do not always transcode if there is no audio stream
2023-07-09 15:15:34 -05:00
Jason Rasmussen
398bd04ffd refactor(server): upload config (#3148) 2023-07-08 23:37:40 -05:00
Mert
8349a28ed8 refactor(server): modularize getFfmpegOptions (#3138)
* refactored `getFfmpegOptions`

refactor transcoding, make separate service

* fixed enum casing

* use `Logger` instead of `console.log`

* review suggestions

* use enum for `getHandler`

* fixed formatting

* Update server/src/domain/media/media.util.ts

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>

* Update server/src/domain/media/media.util.ts

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>

* More specific imports, renamed codec classes

* simplified code

* removed unused import

* added tests

* added base implementation for bitrate and threads

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
2023-07-08 21:43:11 -05:00
faupau
27018e4ab6 feat(web): add emptyplaceholder when no assets (#3155)
* add emptyplace holder when no assets

* remove unecessary number type

* wording

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-07-08 21:32:34 -05:00
Alex
73e82303e7 [Localizely] Translations update (#3162) 2023-07-08 15:27:24 -05:00
Alex
64697235d6 feat(mobile): adding additional languages (#3161)
* update locale

* localizely

* Update info.plist

---------

Co-authored-by: Alex Tran <Alex.Tran@conductix.com>
2023-07-08 15:26:26 -05:00
Mert
a5cc408469 fix(server): thumbnail content type not being passed to stream handle (#3137)
* asset mimetype instead of application/octet-stream

* use thumbnail mimetype instead

* narrowed openapi spec

* thumbnail format validation

* JPEG fallback, `getThumbnailPath` returns format

* return content type in `getThumbnailPath`

* moved `format` validation to dto

* removed unused import

* moved fallback warning

* added `ApiOkResponse`
2023-07-08 15:07:56 -05:00
Fynn Petersen-Frey
d590dec159 Revert "feat(mobile): reduce UI rebuilds (#3129)" (#3159)
This reverts commit fe2330ebf6.
2023-07-08 15:03:54 -05:00
Alex
b262bcec03 [Localizely] Translations update (#3158) 2023-07-08 10:09:34 -05:00
Fynn Petersen-Frey
fe2330ebf6 feat(mobile): reduce UI rebuilds (#3129) 2023-07-08 08:46:22 -05:00
Mert
50c7b35291 clarified openapi use (#3149) 2023-07-07 20:49:18 -05:00
dependabot[bot]
927d6ab1c6 chore(deps): bump docker/setup-buildx-action from 2.8.0 to 2.9.0 (#3145)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2.8.0 to 2.9.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2.8.0...v2.9.0)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-07 08:57:30 -05:00
Alex
d064477a45 fix(web): Places and Things thumbnail cannot be clicked (#3133)
* fix(web): Places and Things thumbnail cannot be clicked

* fix openapi

* fix test
2023-07-06 17:34:15 -05:00
Jordi Altayó
6588bb3d79 Add catalan translation of readme (#3130)
* Create README_ca_ES.md

* Update README_ca_ES.md

* Update README.md

* Update README_ca_ES.md

* Update README_ca_ES.md

* Update README_ca_ES.md
2023-07-06 17:28:59 -05:00
Alex
812cb3d940 Revert "fix(server): use thumbnail content type instead of application/octet-stream (#3075)" (#3134)
* Revert "fix(server): use thumbnail content type instead of application/octet-stream (#3075)"

This reverts commit 3cc77d945b.

* generate api
2023-07-06 17:25:56 -05:00
Alex Tran
852ef3cd1b chore(server): update openapi 2023-07-06 16:56:02 -05:00
Mert
3cc77d945b fix(server): use thumbnail content type instead of application/octet-stream (#3075)
* asset mimetype instead of application/octet-stream

* use thumbnail mimetype instead

* narrowed openapi spec

* thumbnail format validation

* JPEG fallback, `getThumbnailPath` returns format

* return content type in `getThumbnailPath`

* moved `format` validation to dto

* removed unused import

* moved fallback warning
2023-07-06 10:27:21 -05:00
Jonathan Jogenfors
6f4449d5e9 feat(cli) Add new CLI (#3066)
* Add new cli

* Remove old readme

* Add documentation to readme file

* Add github workflow tests for cli

* Fix typo in docs

* Add usage info to readme

* Add package-lock.json

* Fix tsconfig

* Cleanup

* Fix lint

* Cleanup package.json

* Fix accidental server change

* Remove rootdir from cli

* Remove tsbuildinfo

* Add prettierignore

* Make CLI use internal openapi specs

* Add ignore and dry-run features

* Sort paths alphabetically

* Don't remove substring

* Remove shorthand for delete

* Remove unused import

* Remove chokidar

* Set correct openapi cli generator script

* Add progress bar

* Rename target to asset

* Add deletion progress bar

* Ignore require statement

* Use read streams instead of readfile

* Fix github feedback

* Fix upload requires

* More github comments

* Cleanup messages

* Cleaner pattern concats

* Github comments

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-07-06 09:37:47 -05:00
Sergey Kondrikov
37edef834e fix(web): previous/next asset navigation (#3107)
* fix(web): previous/next asset navigation

* Apply suggestions from code review

Co-authored-by: Thomas <9749173+uhthomas@users.noreply.github.com>

* Call setViewingAsset once

* Make code more readable

* Avoid recursive call

* Simplify return statement

* Set position of the bucket to Unknown

---------

Co-authored-by: Thomas <9749173+uhthomas@users.noreply.github.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-07-05 15:24:23 -05:00
Rohitt Vashishtha
814030be77 fix(server): Set dev restart policy to unless-stopped. (#3123)
Previously, if you'd shut down the `make-dev` command and restart
the docker daemon (say, on a system reboot), there would be 3 immich
containers already running.
2023-07-05 15:23:23 -05:00
Mert
71a2914f3e fix(server): transcodes failing due to storage migration happening simultaneously (#3071) 2023-07-05 00:36:16 -05:00
Alex The Bot
0d30ceb284 Version v1.66.1 2023-07-05 02:50:53 +00:00
Alex
4add6cb26e fix(web): Thumbnail not disappear after performing actions on the timeline (#3116)
* fix(web): Thumbnail not dissapear after action

* actual cause

* actual cause
2023-07-04 21:48:21 -05:00
Alex The Bot
8a3ab5be3e Version v1.66.0 2023-07-04 15:51:53 +00:00
Sergey Kondrikov
8e18acff85 feat(web): enhance date group title (#3094)
Co-authored-by: Alex <alex.tran1502@gmail.com>
2023-07-03 12:04:46 +00:00
Thomas
8fd4edb206 feat(web): select a range of assets (#3086)
The shift key can be held to select a range of assets.

Fixes: #2862
2023-07-03 09:56:58 +00:00
Mert
2099b04057 fix(server): Premature stream close error when viewing videos in web (#3093)
* suppress 'ERR_STREAM_PREMATURE_CLOSE'

* refactor stream range logic
2023-07-02 21:37:12 -05:00
Thomas
1a0a3aa2c1 fix(web): use natural asset order for navigation (#3092) 2023-07-02 21:02:38 -05:00
Alex
7947f4db4c feat(web/server): Face thumbnail selection (#3081)
* add migration

* verify running migration populate new value

* implemented service

* generate api

* FE works

* FR Works

* fix test

* fix test fixture

* fix test

* fix test

* consolidate api

* fix test

* added test

* pr feedback

* refactor

* click ont humbnail to show feature selection as well
2023-07-02 17:46:20 -05:00
Alex
1df068bac9 chore(server): add limit to people return (#3069) 2023-07-02 15:28:53 -05:00
Dhrumil Shah
d9e084706f chore(docs): clarifications to --import flag on CLI docs (#2996)
* Clarifications to `--import` flag on CLI docs

* WIP: Fixed heading, added some more info

* PR: fixing format issues
2023-07-01 13:33:04 -05:00
Alex Tran
55e7893bad docs: type 2023-07-01 13:30:59 -05:00
Friso Smit
604b10778c Add more detail to reverse proxy docs (#2841)
* Add more detail to reverse proxy docs

Document fix for issue #2564

* Add port to the proxy_pass example
2023-07-01 13:30:19 -05:00
Jason Rasmussen
d69fa3ceae refactor(server): guards, decorators, and utils (#3060) 2023-07-01 13:27:34 -05:00
Jason Rasmussen
f55b3add80 chore(web): prettier (#2821)
Co-authored-by: Thomas Way <thomas@6f.io>
2023-06-30 23:50:47 -05:00
Jason Rasmussen
7c2f7d6c51 chore: remove refactored controllers from unit test coverage (#3063) 2023-06-30 23:47:28 -05:00
Jason Rasmussen
19cc94e594 refactor(server): use better algorithm for calculating the duplicate filename (#3061) 2023-06-30 23:44:55 -05:00
Jason Rasmussen
b93bbc9f5d refactor(server): storage template core (#3059) 2023-06-30 23:43:24 -05:00
Jason Rasmussen
2feac54382 refactro(server): job dto (#3057) 2023-06-30 23:41:12 -05:00
Jason Rasmussen
49f1f6cad7 refactor(server): person dto (#3058) 2023-06-30 20:52:40 -05:00
Jason Rasmussen
399312ead3 refactor(server): api key auth (#3054) 2023-06-30 20:49:30 -05:00
Mert
f9671dfbf7 fix(server): h264 videos failing to transcode in two-pass mode (#3053)
* set `-fps_mode` to passthrough

* updated tests
2023-06-30 20:48:40 -05:00
Mert
b1fcf02d13 fix(server): h264 and hevc not respecting max bitrate (#3052)
* added `-bufsize` flag

* updated test
2023-06-30 20:48:05 -05:00
Fynn Petersen-Frey
615893be38 fix(mobile): setting to always display remote assets (#3044) 2023-06-30 20:47:44 -05:00
Ethan Margaillan
5869648f19 chore(web): replace window.confirm by ConfirmDialogues and cleanup existing ones (#3039)
* chore(web): replace window.confirm by ConfirmDialogues and cleanup existing ones

* fix(web): linter and svelte-check issues

* fix(web): rephrase some confirm dialogs

* fix(web): run prettier

* fix(web): merge with last version and run prettier again

* fix(web): run prettier
2023-06-30 14:53:16 -05:00
martyfuhry
734f8e02b5 fix(mobile): Uses ImageFiltered for performance (#3051)
* Uses ImageFiltered for performance

* values

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-30 13:49:17 -05:00
Mert
e477f99c7d fix(server): fix more vector search results being returned than intended (#3042) 2023-06-30 11:33:54 -05:00
Jason Rasmussen
455a36b0fc fix(server): read file permission checks (#3046) 2023-06-30 11:25:08 -05:00
Jason Rasmussen
ad343b7b32 refactor(server): download assets (#3032)
* refactor: download assets

* chore: open api

* chore: finish tests, make size configurable

* chore: defualt to 4GiB

* chore: open api

* fix: optional archive size

* fix: bugs

* chore: cleanup
2023-06-30 11:24:28 -05:00
Alex The Bot
df9c05bef3 Version v1.65.0 2023-06-30 03:01:48 +00:00
Alex Tran
6c8c16c85f chore: update release note notes 2023-06-29 21:48:57 -05:00
Alex
b05f3fd266 fix(mobile): avatar without last name (#3038) 2023-06-29 17:05:12 -05:00
Alex
ca98d73d86 chore(mobile): update flutter to 3.10.5 (#3036) 2023-06-29 16:28:18 -05:00
Fynn Petersen-Frey
b7ae3be394 fix(mobile): rework album detail page header (#3035) 2023-06-29 16:11:56 -05:00
Alex Tran
621fa5ba54 update readme 2023-06-29 15:23:55 -05:00
Alex Tran
ca1b9bf7b3 fix(doc): format 2023-06-29 14:49:23 -05:00
Dhrumil Shah
6fa685d9d8 chore: add CLI tool to the server image (#2999)
* WIP: Added immich cli tool to `immich-server` image

* WIP: Added doc entry to show it is preinstalled

* WIP: Moved immich upload cli to `immich` and default to `immich-admin`

* WIP: undid previous commit

* WIP: Updated server docs with new `immich-admin` command
2023-06-29 14:48:16 -05:00
Fynn Petersen-Frey
ff26d3666e fix(mobile): set scrolling state only if changed (#3034)
* fix(mobile): set scrolling state only if changed

* fix: generate api

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-29 14:35:29 -05:00
faupau
e3557fd80e Fix(web): drag n drop shared link (#3030)
* add event to trigger uploadhandler

* add dragndrop store
to handle upload in album-viewer and individuel-shared-viewer
(only on shares)

* fix handleUploadAssets no parameter

* fix format
2023-06-29 10:26:25 -05:00
faupau
c065705608 fix(web): Share link multi-select download icon showing when not available #3006 (#3027)
* only show download button if allowDownload
add SelectAll to individual share

* fix allow download if not share
2023-06-29 10:11:37 -05:00
dependabot[bot]
3948247055 chore(deps): bump docker/setup-buildx-action from 2.7.0 to 2.8.0 (#3028)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2.7.0 to 2.8.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2.7.0...v2.8.0)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-29 08:11:17 -05:00
Sergey Kondrikov
dca48d7722 fix(web): aspect ratio for videos (#3023) 2023-06-29 08:11:00 -05:00
Alex
8e6c90e294 chore(mobile): minor UI tweak (#3021)
* chore(mobile): minor UI tweak

* fix test

* refactor
2023-06-28 22:33:57 -05:00
Thomas
e5908f2508 fix(server): use private cache (#3017)
The omission of additional cache-control directives implied the resource could
be stored in shared/public caches, which is not desirable.

In addition, the no-transform directive will ensure content is not
unintentionally mangled.

Fixes: #3014
2023-06-28 21:26:16 -05:00
martin
fbd98ec0f9 feat(web): persist info panel (#3013)
Signed-off-by: martabal <74269598+martabal@users.noreply.github.com>
2023-06-28 21:14:16 -05:00
Fynn Petersen-Frey
1ab05e8de0 fix(mobile): fix endless rendering of asset grid when scrolling (#3010) 2023-06-28 21:13:18 -05:00
Sergey Kondrikov
86562f256f fix(web): aspect ratio for photos with Rotate 270 CW orientation (#3003)
* fix(web): aspect ratio for photos with Rotate 270 CW orientation

* Remove checks assuming we can have only numeric values

* Remove the -90 value check for the orientation

* Add comment to numeric values of the orientation tag
2023-06-28 13:04:32 -05:00
Jason Rasmussen
add5219d34 fix: live photos not playing in shared links/albums (#3008) 2023-06-28 12:58:38 -05:00
Keszei Balázs
6ae5d11ec0 chore(server): Image description disappears after toggle favorite (#3009)
* Fixed asset rewrite description on toggle favorite

* Fixed description removing error

* Rewrite description condition for asset update

---------

Co-authored-by: Balazs Keszei <balazs.keszei@clbr.hu>
2023-06-28 12:54:48 -05:00
Rohitt Vashishtha
b4e641548c fix(web): Add m: to search query upon loading results. (#2954)
Previously, we'd drop the m: from non-clip searches entirely. This
behavior incorrectly represents the page's status (results from
non-clip search but query implies a clip search). Also, any follow-up
searches change to clip searches, which feels like a jarring UX if you
have to add m: every time in a 'search-session'.
2023-06-28 17:48:53 +00:00
Mert
017214fd56 fix(server): empty tag responses should be considered valid (#2993)
* accept empty tag array

* renamed test
2023-06-28 09:40:30 -05:00
Alex Phillips
22a73b67d3 chore(server): check file extension for XMP instead of mimetype (#2990)
* just check file extension for XMP instead of mimetype

* use path to get extension instead of regex

* single quotes

* remove unused import

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-28 14:40:21 +00:00
Thomas
792ecc6cac fix(server): add missing avi mime types and add tests (#3001)
See https://github.com/immich-app/immich/pull/2952#pullrequestreview-1497194041

Fixes: #2975
2023-06-28 09:21:42 -05:00
Jason Rasmussen
e98398cab8 refactor(server): access permissions (#2910)
* refactor: access repo interface

* feat: access core

* fix: allow shared links to add to a shared link

* chore: comment out unused code

* fix: pr feedback

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-28 08:56:24 -05:00
Mert
df1e8679d9 chore(ml): added testing and github workflow (#2969)
* added testing

* github action for python, made mypy happy

* formatted with black

* minor fixes and styling

* test model cache

* cache test dependencies

* narrowed model cache tests

* moved endpoint tests to their own class

* cleaned up fixtures

* formatting

* removed unused dep
2023-06-27 18:21:33 -05:00
Alex Tran
5e3bdc76b2 chore(mobile): auto dispose future provider 2023-06-27 16:02:54 -05:00
Mert
47982641b2 fix(ml): clear model cache on load error (#2951)
* clear model cache on load error

* updated caught exceptions
2023-06-27 16:01:24 -05:00
Alex
39a885a37c feat(mobile): memories (#2988)
* Add page view

* Nice page view

* refactor file structure

* Added card

* invalidating data

* transition

* styling

* correct styleing

* refactor

* click to navigate

* styling

* TODO

* clean up

* clean up

* pr feedback

* pr feedback

* better loading indicator
2023-06-27 16:00:20 -05:00
Alex Tran
0e8d235148 fix(mobile): format 2023-06-27 12:28:15 -05:00
Alex Elkins
053a5235be chore(mobile): Capitalize Places cities in app (#2985) 2023-06-27 17:26:23 +00:00
Fynn Petersen-Frey
de42ebf3d8 feat(Android): find & delete corrupt asset backups (#2963)
* feat(mobile): find & delete corrupt asset backups

* show backup fix only for advanced troubleshooting
2023-06-27 12:25:00 -05:00
Mert
4d3ce0a65e fixed setting different clip, removed unused stubs (#2987) 2023-06-27 12:21:50 -05:00
Alex
b3e97a1a0c chore(web): Only show Copy button in HTTPS context (#2983) 2023-06-27 08:49:20 -05:00
Sergey Kondrikov
f5d9826b12 Fix download asset loading indicator position (#2974) 2023-06-27 08:48:20 -05:00
Alex
61e5e65173 feat(server): Add camera make and model to search criteria (#2982) 2023-06-27 06:53:07 -05:00
Alex The Bot
b258f3552a Version v1.64.0 2023-06-26 18:06:11 +00:00
Ethan Margaillan
e803bc909f feat(web): store albums sorting policy in local storage (#2966) 2023-06-26 11:54:20 -05:00
Sergey Kondrikov
d078aea32b Normalize progress bar value (#2967) 2023-06-26 11:54:08 -05:00
Sergey Kondrikov
fb2cfcb640 feat(mobile): custom video player controls (#2960)
* Remove toggle fullscreen button

* Implement custom video player controls

* Move Padding into Container
2023-06-26 10:27:47 -05:00
Fynn Petersen-Frey
99f85fb359 feat(Android): guard against missing EXIF info (#2965) 2023-06-26 10:27:32 -05:00
Keszei Balázs
454fb106d2 Updated OSM tile URLs immich-app/immich#2874 (#2961)
Co-authored-by: Balazs Keszei <balazs.keszei@clbr.hu>
2023-06-26 08:47:37 -05:00
Nurullah Türkoğlu
7d078a2f0e add Turkish README file (#2943)
* add Turkish README file

* fixed a typo
2023-06-25 21:29:41 -05:00
Alex
b015648bfe chore(mobile): Add more error log (#2949)
Co-authored-by: alex <alex@pop-os.localdomain>
2023-06-25 18:59:35 -05:00
Mert
a58482cb2b added locustfile (#2926) 2023-06-25 13:20:45 -05:00
Elliot Lee
9dd1d81536 Allow Windows' version of the MIME types for raw photos. (#2945)
* Allow Windows' version of the MIME types for raw photos.

* Fix prettier warning.

---------

Co-authored-by: Elliot Lee <sopwith@gmail.com>
2023-06-25 13:10:39 -05:00
Mert
a2f5674bbb refactor(ml): modularization and styling (#2835)
* basic refactor and styling

* removed batching

* module entrypoint

* removed unused imports

* model superclass,  model cache now in app state

* fixed cache dir and enforced abstract method

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-24 22:18:09 -05:00
Mert
837ad24f58 fix(deps): install poetry in pump workflow (#2938)
* install poetry in pump workflow

* formatting
2023-06-24 22:03:50 -05:00
bo0tzz
058c62b111 feat(blog): Make blog public, add June 2023 update post (#2935)
* feat(blog): Add blog link to website header

* feat(blog): June 2023 update post

* chore(blog): Reorder folder structure

* chore(blog): Add discord link

* chore(blog): Formatting

* chore(blog): Use youtube embeds

* fix format

---------

Co-authored-by: alex <alex@pop-os.localdomain>
2023-06-24 22:02:39 -05:00
Alex The Bot
bbb6bca605 Version v1.63.2 2023-06-25 02:53:18 +00:00
Alex
a8e5a1de15 chore(server): support DNG file (#2940) 2023-06-24 21:50:01 -05:00
Alex The Bot
bba4c44182 Version v1.63.1 2023-06-24 15:31:16 +00:00
Alex
7c76249e1f fix(server): Share link with expire creation time error (#2934)
* fix(server): Share link with expire creation time error

* better
2023-06-24 10:24:55 -05:00
Alex Tran
294955db17 wording 2023-06-23 23:32:39 -05:00
Alex
752ad2d2eb chore(docs): ReadOnly documentation (#2925)
* update

* update

* update'

* update

* update

* update

* update

* chore: typoes etc

* update

* prettier

---------

Co-authored-by: bo0tzz <git@bo0tzz.me>
2023-06-23 21:06:52 -05:00
Alex The Bot
02a268c7c6 Version v1.63.0 2023-06-24 01:41:12 +00:00
Alex
b2dc7adf3b fix(web): memory pause autoplay when scrolling down (#2923) 2023-06-23 11:13:05 -05:00
416c616e
6e62558d81 Fix Canon CR3 mime type (#2922) 2023-06-23 11:12:11 -05:00
Alex
0d0866d5d9 feat(mobile): Facial recognition (#2507)
* Add API service

* Added service, provider

* merge main

* update pubspec

* styling

* dev: add person search result page

* dev: display person asset on page

* dev: add rename form

* style form

* dev: mechanism to add name to faces

* styling

* fix bad merge

* update api

* test

* revert

* Add header widget

* change name

* show all people page

* fix test

* pr feedback

* Add name to app bar

* feedback

* styling
2023-06-23 10:44:02 -05:00
Alex
00f65a53dd fix(server): Fix person's assets retrival order (#2920) 2023-06-23 09:34:55 -05:00
Alex
751922990f chore/remove openapi assertion for dart 2 (#2916)
* chore(server): patch dart openapi assertion 2

* removed usused file
2023-06-22 13:00:07 -05:00
Alex
4311d385fc chore(server): patch dart openapi assertion (#2914)
* chore(server): patch dart openapi assertion

* remove unused file
2023-06-22 12:48:57 -05:00
Fynn Petersen-Frey
3e2f335a4c feat(mobile): optimize screen space usage (#2911)
* feat(mobile): optimize screen space usage

* undo nav bar changes
2023-06-22 09:50:27 -05:00
Alex
cf1eddb449 fix(server): transform isReadOnly DTO to boolean (#2912) 2023-06-22 09:46:21 -05:00
Alex Phillips
e171fec5aa feat(server): support for read-only assets and importing existing items in the filesystem (#2715)
* Added read-only flag for assets, endpoint to trigger file import vs upload

* updated fixtures with new property

* if upload is 'read-only', ensure there is no existing asset at the designated originalPath

* added test for file import as well as detecting existing image at read-only destination location

* Added storage service test for a case where it should not move read-only assets

* upload doesn't need the read-only flag available, just importing

* default isReadOnly on import endpoint to true

* formatting fixes

* create-asset dto needs isReadOnly, so set it to false by default on create, updated api generation

* updated code to reflect changes in MR

* fixed read stream promise return type

* new index for originalPath, check for existing path on import, reglardless of user, to prevent duplicates

* refactor: import asset

* chore: open api

* chore: tests

* Added externalPath support for individual users, updated UI to allow this to be set by admin

* added missing var for externalPath in ui

* chore: open api

* fix: compilation issues

* fix: server test

* built api, fixed user-response dto to include externalPath

* reverted accidental commit

* bad commit of duplicate externalPath in user response  dto

* fixed tests to include externalPath on expected result

* fix: unit tests

* centralized supported filetypes, perform file type checking of asset and sidecar during file import process

* centralized supported filetype check method to keep regex DRY

* fixed typo

* combined migrations into one

* update api

* Removed externalPath from shared-link code, added column to admin user page whether external paths / import is enabled or not

* update mimetype

* Fixed detect correct mimetype

* revert asset-upload config

* reverted domain.constant

* refactor

* fix mime-type issue

* fix format

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-21 21:33:20 -05:00
Fynn Petersen-Frey
7f44d508dc feat(mobile): pinch to zoom on asset grid (#2905) 2023-06-21 21:13:23 -05:00
Krisjanis Lejejs
2c924e4c1c feature (web): Add keyboard event support to memory view (#2890)
* Add keyboard event support to memory view in web

* Implement PR suggestions
2023-06-21 15:28:58 -05:00
Alex
0f0375a67e feat(web): add album to search result (#2900)
* Add album to search result page

* Update web/src/routes/(user)/search/+page.svelte

Co-authored-by: Thomas <9749173+uhthomas@users.noreply.github.com>

* Update web/src/routes/(user)/search/+page.svelte

Co-authored-by: Thomas <9749173+uhthomas@users.noreply.github.com>

* change font weight

* hide context menu in this view

---------

Co-authored-by: Thomas <9749173+uhthomas@users.noreply.github.com>
2023-06-21 15:18:00 -05:00
Thomas
069c68bfe4 feat: M2TS (#2896)
Support the Blu-ray disc Audio-Video (BDAV) MPEG-2 Transport Stream (M2TS) format.

https://en.wikipedia.org/wiki/.m2ts

Fixes: #2350
2023-06-21 13:50:12 -05:00
Fynn Petersen-Frey
c03d8e312a chore(readme): mention offline support feature (#2902) 2023-06-21 13:49:35 -05:00
faupau
de7f66f983 fix(web): keep video volume (#2897)
* save video volume in asset-interaction.store.ts

* move video-viewer-volume to preferences store
save in localstorage by using persisted
2023-06-21 09:59:13 -05:00
Alex
82b89aa20b feat(web): custom drop down button (#2887)
* feat(web): custom drop down button

* fix test

* fix test
2023-06-21 08:05:59 -05:00
Thomas
80d02e8a8d feat: JPEG XL (#2893)
Support the JPEG XL format (.jxl).

JPEG XL is reported as supported by `sharp.format`:

```
jxl: {
  id: 'jxl',
  input: { file: true, buffer: true, stream: true, fileSuffix: [Array] },
  output: { file: true, buffer: true, stream: true }
}
```

Fixes: #2743
2023-06-21 07:29:02 -05:00
Jason Rasmussen
868f629f32 refactor(server, web): create shared link (#2879)
* refactor: shared links

* chore: open api

* fix: tsc error
2023-06-20 20:08:43 -05:00
Krisjanis Lejejs
746ca5d5ed feat(web): Add album sorting to albums view (#2861)
* Add album sorting to web albums view

* generate api

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-20 20:00:59 -05:00
Skyler Mäntysaari
3c5fefde2e fix(web): Mov files should show up in file picker. (#2886) 2023-06-20 19:58:35 -05:00
martyfuhry
26f58d3335 Fixes local position late initialization (#2884) 2023-06-20 19:58:17 -05:00
Alex
6baeca654b chore(server): Improve moveAsset log (#2878)
* chore(server): Improve moveAsset log

* Update storage-template.service.ts
2023-06-20 16:24:47 -05:00
martyfuhry
1b15b5414c Adds photo thumbnail to videos (#2880)
* Motion photos use placeholder image for more seamless loading

* Fixes merge conflicts
2023-06-20 16:17:43 -05:00
Manuel Taberna
48e4ea5231 feat(web): add zoom toggle icon (#2873)
* feat(web): add zoom toggle icon

* update zoom-image dependency

* fix lint issues

* remove variable testing line

* Simplify code using ternary conditional

Co-authored-by: Thomas <9749173+uhthomas@users.noreply.github.com>

* fix typo

---------

Co-authored-by: Thomas <9749173+uhthomas@users.noreply.github.com>
2023-06-20 09:36:38 -05:00
Thomas
f9fbf1a2a5 fix(server): use HTTP status OK instead of CREATED for assets (#2876)
The NGINX gzip module does not compress responses with a status of 201, which is
a major issue specifically for the /api/asset/time-bucket endpoint where
responses can be upwards of 5Mi. The size of the response is dramatically
reduced with gzip to 500Ki in some cases.

https://trac.nginx.org/nginx/ticket/471
https://trac.nginx.org/nginx/ticket/394

The signature of these endpoints should be GET rather than POST anyway, but that
is a bigger discussion.
2023-06-20 08:49:36 -05:00
Jonathan Jogenfors
f003ff3c98 Add dependency on immich-web to immich-proxy (#2875) 2023-06-20 08:46:48 -05:00
Elliot Lee
81e2b18531 Add support for many missing raw formats (#2834)
* Allow upload of AVIF and x-canon-cr2 mime types

* Allow generic RAW file mime type image/x-dcraw

* Another place to uploading avif and cr2

* Determine mime type for .avif and .cr2 files correctly

* Update asset-upload.config.spec.ts for CR2 and AVIF files

* More changes for AVIF & CR2 files

Found some other places where avif and cr2 should be mentioned.

* Merge in upstream changes

* Allow uploading and using most of the formats that libraw supports

* Add raw files to allowable mobile uploads

* Update asset-upload.config.spec.ts

Fix errant commas.

* Update asset-utils.ts

Remove duplicate entry in hash table.

* Fix missing k25 mime type in server upload check.
Fix prettier formatting message in web file-uploader.

* fix test

---------

Co-authored-by: Elliot Lee <sopwith@gmail.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-19 21:10:29 -05:00
Sophie
c404ea20ee fix(web): redirect to parent folder if asset viewer was open before reload (#2863)
* fix(web): redirect to parent folder if asset viewer was open before reload

* chore(web): use route constants in all routes
2023-06-19 21:06:08 -05:00
Stavros Kois
cc45564d84 move public msg to general section (#2864) 2023-06-19 16:42:59 -05:00
Alex The Bot
8d560ec55f Version v1.62.1 2023-06-19 21:31:38 +00:00
Thomas
df74111427 chore(web): fade between thumbhash and thumbnail (#2856) 2023-06-19 16:21:06 -05:00
Stavros Kois
93c35efe67 [docs]: Document environment variables (#2814)
* draft env vars

* remove mapbox refs, fixes #2535

* formatting and add some notes

* add examples for redis and typesense url

* [skipci] add note for redis socket

* do some formatting

* update md

* fix url

* fix variable

* add web for NODE_ENV

* fix variable name

* Apply suggestions from code review

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>

* address review feedback

* Update docker/example.env

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>

* add section for docker compose envs

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
2023-06-19 15:55:12 -05:00
cycneuramus
296c77ac73 feat(server): support rclone as storage backend (#2832) 2023-06-19 11:58:10 -05:00
Alex The Bot
9c0f444e4d Version v1.62.0 2023-06-19 15:43:49 +00:00
Jason Rasmussen
6b0f91cafd fix(server): only show assets 'on-this-day' with thumbnails (#2851) 2023-06-19 09:12:18 -05:00
Dan Cowell
3f71d2d33d chore(deps): change compose service dependencies to use alpine variants (#2825)
* chore(deps): change compose service dependencies to use alpine variants

* chore(deps): pin manifest hashes for dependency containers
2023-06-18 20:51:46 -05:00
Sergey Kondrikov
f2942588f2 chore(mobile): Add debug build type suffix to the applicationId and version (#2826) 2023-06-17 23:10:57 -05:00
Alex
b47027efc2 fix(mobile): Sort newest first for asset selection in album (#2833) 2023-06-17 23:09:55 -05:00
Zeeshan Khan
34201be74c feat(ml) backend takes image over HTTP (#2783)
* using pydantic BaseSetting

* ML API takes image file as input

* keeping image in memory

* reducing duplicate code

* using bytes instead of UploadFile & other small code improvements

* removed form-multipart, using HTTP body

* format code

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-17 22:49:19 -05:00
Covalent
3e804f16df feat(web,server): add thumbhash support (#2649)
* add thumbhash: server generation and web impl

* move logic to infra & use byta in db

* remove unnecesary logs

* update generated API and simplify thumbhash gen

* fix check errors

* removed unnecessary library and css tag

* style edits

* syntax mistake

* update server test, change thumbhash job name

* fix tests

* Update server/src/domain/asset/response-dto/asset-response.dto.ts

Co-authored-by: Thomas <9749173+uhthomas@users.noreply.github.com>

* add unit test, change migration date

* change to official thumbhash impl

* update call method to not use eval

* "generate missing" looks for thumbhash

* improve queue & improve syntax

* update syntax again

* update tests

* fix thumbhash generation

* consolidate queueing to avoid duplication

* cover all types of incorrect thumbnail cases

* split out jest tasks

* put back thumbnail duration loading for images without thumbhash

* Remove stray package.json

---------

Co-authored-by: Luke McCarthy <mail@lukehmcc.com>
Co-authored-by: Thomas <9749173+uhthomas@users.noreply.github.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-17 22:22:31 -05:00
Thomas
3512140148 feat(web): add padding to memory asset navigation (#2822)
The bars are 2 pixels tall, which can be tricky to click. Additional padding
increases the height to 16 pixels, without changing how it looks, and makes for
much easier clicking.

In addition, remove the onDestroy lifecycle for the tween as it's not
necessary. It was a relic from using animation frames.
2023-06-16 23:37:11 +01:00
Jason Rasmussen
bff6914a73 chore(server): organize imports (#2779)
* feat: lint rule for organize imports

* chore: organize imports
2023-06-16 19:54:17 +00:00
Jason Rasmussen
652add635f refactor: rename get auth user decorator (#2778)
Co-authored-by: Alex <alex.tran1502@gmail.com>
2023-06-16 19:39:53 +00:00
Jason Rasmussen
fde410e2ac refactor(server): send job command (#2777)
* refactor: send job command

* chore: open api
2023-06-16 14:36:07 -05:00
Jason Rasmussen
f04e47803c refactor(server): access checks (#2776)
* refactor(server): access checks

* chore: simply asset module
2023-06-16 14:01:34 -05:00
Thomas
61d74263d9 fix(web): hide memory lane navigation properly on scaled resolutions (#2819)
Fixes: #2817
2023-06-16 13:55:11 -05:00
Alex Tran
66ee065c0c pause renovate 2023-06-16 13:52:29 -05:00
Thomas
09bcf6974e feat(web): show number of assets in memory progress bar (#2813)
Fixes: #2810
2023-06-16 13:17:39 -05:00
renovate[bot]
5d7d615433 chore(deps): update web (#2806)
* chore(deps): update web

* fixed svelte-check being a nuisance

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-16 12:45:05 -05:00
renovate[bot]
5387048dc3 fix(deps): update dependency tailwindcss to v3.3.2 (#2808)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-16 17:14:29 +00:00
renovate[bot]
6930df71cf fix(deps): update dependency docusaurus-preset-openapi to v0.6.4 (#2800)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-16 11:40:04 -05:00
renovate[bot]
52bbf6da5d fix(deps): update dependency url to v0.11.1 (#2802)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-16 11:39:54 -05:00
Alex
1cd5df7558 fix(web): not displaying assets in album after adding shared user (#2804) 2023-06-16 11:39:40 -05:00
renovate[bot]
74429798e2 fix(deps): update dependency autoprefixer to v10.4.14 (#2799)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-16 11:39:25 -05:00
renovate[bot]
651f3ea5eb chore(deps): update typesense/typesense docker tag to v0.24.1 (#2798)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-16 11:39:11 -05:00
renovate[bot]
0909335d02 chore(deps): update python:3.11.4-slim-bullseye docker digest to 91d194f (#2797)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-16 11:38:59 -05:00
renovate[bot]
827e4b5f75 chore(deps): update python:3.11.4-bullseye docker digest to 5b40167 (#2796)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-16 11:17:23 -05:00
renovate[bot]
c8ff07fff0 fix(deps): update dependency postcss to v8.4.24 (#2801)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-16 11:16:44 -05:00
Thomas
4a21cb2d00 chore(web): hide memory lane navigation when it's no longer possible to scroll (#2791)
Fixes: #2790
2023-06-16 11:06:38 -05:00
Jason Rasmussen
07f7fffae7 refactor(server): album count (#2746)
* refactor(server): album count

* chore: open api
2023-06-16 10:48:48 -05:00
renovate[bot]
441ee2ef90 chore(deps): update dependency typescript to v5 (#2795)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-16 10:43:40 -05:00
renovate[bot]
acad133e3a chore(deps): update dependency @tsconfig/docusaurus to v1.0.7 (#2793)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-16 15:30:14 +00:00
renovate[bot]
ef8714fda9 chore(deps): update dependency vite to v4.1.5 [security] (#2792)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-16 15:28:49 +00:00
Thomas
16171eee8d pin image digests (#2754)
Manifest list digests can be found with:

```sh
docker buildx imagetools inspect python:3.11.4-bullseye
docker buildx imagetools inspect python:3.11.4-slim-bullseye
docker buildx imagetools inspect ghcr.io/nginxinc/nginx-unprivileged:1.25.0-alpine3.17
```

The node images are pinned in #2736

Fixes #2751
Partially fixes #2752
2023-06-16 10:28:41 -05:00
renovate[bot]
d3c1781478 Configure Renovate (#2739)
* Add renovate.json

* Update renovate.json

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>
2023-06-16 10:22:52 -05:00
Thomas
329b52e670 use svelte motion tweening for animation (#2788)
It look like Svelte has a concept of 'tweening' for writing animations, which should reduce the complexity of the animation code.

Thanks to @probablykasper for finding this.

A lot of the logic has been rewritten for reactivity, which further reduces
complexity.
2023-06-16 10:09:28 -05:00
Alex
a1b9a1d244 fix(web): error when refreshing asset view in memory page (#2789) 2023-06-16 10:09:16 -05:00
phillibl
377cec9fb1 Update xmp-sidecars.md (#2785)
Fixed a spelling mistake
2023-06-16 09:42:49 -05:00
phillibl
48b9c63268 Update README.md (#2787)
Changed Partner Sharing to Yes for mobile
2023-06-16 09:39:06 -05:00
Alex The Bot
caccb1094d Version v1.61.0 2023-06-16 02:29:11 +00:00
Thomas
43ffcf7e8f use animation frames for memory autoplay (#2771)
The current implementation mixes intervals and animation frames, which is a
little convoluted. The use of intervals means that the animation is not going
to be smooth and may have strange behaviour when the window is moved to the
background. It's possible that the current animation frames could pile up and
run all at once which would be undesirable.

Moving everything into animation frames means the code is simpler and easier to
reason about. It should also be more performant and less buggy.

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-15 21:27:32 -05:00
Alex
77fe2e55be fix(web): show memory in main timeline (#2775) 2023-06-15 14:11:14 -05:00
Alex Tran
a59e9e1d9e fix(web): center name 2023-06-15 14:10:21 -05:00
Jason Rasmussen
896645130b fix(server): memory lane title (#2772)
* fix(server): memory lane title

* feat: parallel requests

* pr feedback

* fix test

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-15 13:05:30 -05:00
Zack Pollard
045bb855d2 fix: increase request timeout from 5 minutes to 30 minutes (#2769)
since node 18 the default request timeout was changed from unlimited to 5 minutes
2023-06-15 08:21:31 -05:00
Thomas
3b4f6edbdb don't fallback to exiftool for embedded image previews (#2747)
Given #2668 introduced support for imagemagick and libraw, this should no
longer be necessary which allow for reduced code footprint and complexity.

Fixes: #2744
2023-06-14 22:42:35 -05:00
Zack Pollard
1cbf9ff621 fix: increase request timeout from 5 minutes to 30 minutes (#2766)
since node 18 the default request timeout was changed from unlimited to 5 minutes
2023-06-14 21:35:54 -05:00
Thomas
41c2c8b82d use imagemagick and libraw for raw image support (#2668)
* use imagemagick and libraw for raw image support

imagemagick and libraw have generally good support for raw images, including
Sony's ARW format. These tools should also allow Immich to support many more
image formats in future without any major code changes.

https://www.libraw.org/supported-cameras

I've tested and verified this change with .ARW files and other standard formats.

Fixes: #2156

* Add additional type for awr

* pr feedback

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-14 21:34:03 -05:00
Alex
43ec0b77a0 feat(web): Memory (#2759)
* Add on this day

* add query for x year

* dev: add query

* dev: front end

* dev: styling

* styling

* more styling

* add new page

* navigating

* navigate back and forth

* styling

* show gallery

* fix test

* fix test

* show previous and next title

* fix test

* show up down scrolling button

* more styling

* styling

* fix app bar

* fix height of next/previous

* autoplay

* auto play

* refactor

* refactor

* refactor

* show date

* Navigate

* finish

* pr feedback
2023-06-14 20:47:18 -05:00
Thomas
408fa45c51 allow emails without a tld (#2762)
It's perfectly valid to have an email address without a TLD, for instance:

- test@localhost
- test@svc-in-same-k8s-namespace
- test@internal-corp

Fixes #2667
2023-06-14 16:26:17 -05:00
dependabot[bot]
eed1243263 chore(deps): bump docker/build-push-action from 4.1.0 to 4.1.1 (#2761)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-14 16:26:05 -05:00
Fynn Petersen-Frey
8f5214724c feat(mobile): sync remote assets without thumbs (#2705) 2023-06-14 16:35:32 +02:00
Thomas
55b6b28afb update node images (#2736)
This is required to support raw images as Alpine 3.18 included fixes to imagemagick.

Related: #2156

In addtion, the images have stricter tags and are pinned with a digest. The
manifest list digest can be found using:

```sh
❯ docker buildx imagetools inspect node:18.16.0-alpine3.18
```
2023-06-13 07:17:07 -05:00
dependabot[bot]
5a48034e33 chore(deps): bump docker/setup-buildx-action from 2.6.0 to 2.7.0 (#2755) 2023-06-13 06:36:51 -05:00
Jason Rasmussen
756f4e5986 fix(web): empty user initials (#2737) 2023-06-12 09:11:28 -05:00
TruongSinh Tran-Nguyen
48492b9f4e feat(web): support uploading Insta360 file format (#2725)
Insta360 "raw" formats `insv` and `insp` are actually
mp4 (video) and jpeg (picture) respectively.
However, we don't want user to rename the original files,
because they follow Insta360 convention, which is required
by Insta360 Studio.
2023-06-12 08:29:03 -05:00
Sergey Kondrikov
e101e40c47 fix(mobile): Disable hit testing for transparent bars (#2727) 2023-06-11 13:10:17 -05:00
Yonggan
9a80a2151c feat(web): Add select all button to all views (#2714)
* Add select all to photos

* Add selection of favorites

* Add select all button to albums

* Add select all to archive

* Add select all to search

* try to fix identation

* Revert "try to fix identation"

This reverts commit 40c727b74a.

* try to fix identation

* try to fix identation

* try to fix identation

* try to fix identation

* fix bucketposition

* Run prettier

---------

Co-authored-by: Yonggan <yonggan@obco.pro>
2023-06-10 14:06:13 -05:00
Fynn Petersen-Frey
73075c64d1 feature(mobile): hash assets & sync via checksum (#2592)
* compare different sha1 implementations

* remove openssl sha1

* sync via checksum

* hash assets in batches

* hash in background, show spinner in tab

* undo tmp changes

* migrate by clearing assets

* ignore duplicate assets

* error handling

* trigger sync/merge after download and update view

* review feedback improvements

* hash in background isolate on iOS

* rework linking assets with existing from DB

* fine-grained errors on unique index violation

* hash lenth validation

* revert compute in background on iOS

* ignore duplicate assets on device

* fix bug with batching based on accumulated size

---------

Co-authored-by: Fynn Petersen-Frey <zoodyy@users.noreply.github.com>
2023-06-10 13:13:59 -05:00
Sergey Kondrikov
053a0482b4 fix(web): Timeline narrow date groups style (#2713)
* Truncate date group title

* Precalculate justified layout width

* Add title to date group title to show when truncated

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-10 13:13:06 -05:00
Mert
9cdec62918 feat(server): option to transcode to original resolution (#2709)
* option to transcode to original resolution

* changed value for target res setting

* updated test, clarified scaling condition
2023-06-09 23:15:12 -05:00
Jason Rasmussen
e3694695ae chore: sort open api spec keys (#2710) 2023-06-09 23:14:18 -05:00
Jason Rasmussen
9a3a01ca78 chore: remove unused code (#2700) 2023-06-09 15:21:00 -05:00
Jason Rasmussen
f0bc318712 chore: fix test coverage (#2699) 2023-06-09 15:20:15 -05:00
dependabot[bot]
53adb0c515 chore(deps): bump docker/build-push-action from 4.0.0 to 4.1.0 (#2702)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4.0.0 to 4.1.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v4.0.0...v4.1.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-09 10:07:44 -05:00
Evan
747afa0cee Fix trailing slash in server address while using docker (#2704) 2023-06-09 09:24:13 -05:00
Sergey Kondrikov
104e489000 fix(server): Filter out deleted partners (#2697)
* Filter out deleted partners

* Add separate filter clause for soft deleted users

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-08 10:45:06 -05:00
Sergey Kondrikov
5764bf16f3 feat(web, server): Implement justified layout for AssetGrid (#2666)
* Implement justified layout for timeline

* Add withoutThumbs field to GetTimelineLayotDto

* Back to rough estimation of initial buckets height

* Remove getTimelineLayout endpoint

* Estimate rough viewport height better

* Fix shift/jump issues while scrolling up

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-08 10:22:45 -05:00
Jason Rasmussen
8ebac41318 refactor(server)*: tsconfigs (#2689)
* refactor(server): tsconfigs

* chore: dummy commit

* fix: start.sh

* chore: restore original entry scripts
2023-06-08 10:01:07 -05:00
dependabot[bot]
a2130aa6c5 chore(deps): bump docker/setup-buildx-action from 2.5.0 to 2.6.0 (#2696)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2.5.0 to 2.6.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2.5.0...v2.6.0)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-08 08:41:09 -05:00
dependabot[bot]
5dbf46ac3c chore(deps): bump docker/setup-qemu-action from 2.1.0 to 2.2.0 (#2695)
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 2.1.0 to 2.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v2.1.0...v2.2.0)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-08 08:40:27 -05:00
Jason Rasmussen
b7d42e7e8e feat(web): mobile job cards (#2688) 2023-06-07 11:10:31 -05:00
Jason Rasmussen
d08535e7f6 refactor(server): bootstrap code (#2682)
* refactor(server): bootstrap code

* Add service name

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-07 09:56:08 -05:00
Jason Rasmussen
eb1225a0a5 refactor(server,web): add/remove album users (#2681)
* refactor(server,web): add/remove album users

* fix(web): bug fixes for multiple users

* fix: linting
2023-06-07 09:37:25 -05:00
Jason Rasmussen
284edd97d6 refactor(server): shared link asset access check (#2680) 2023-06-06 23:34:42 -05:00
Jason Rasmussen
d1b0b64d59 refactor(server): album download check (#2679) 2023-06-06 23:27:28 -05:00
Mert
d0cc231782 feat(ml): model unloading (#2661)
* model cache

* fixed revalidation when using cache namespace

* fixed ttl not being set, added lock
2023-06-06 20:48:51 -05:00
Jason Rasmussen
6ce35d47f5 refactor(server): partner core (#2678)
* refactor(server): partner core

* refactor(server): partner access check
2023-06-06 15:18:38 -05:00
Jason Rasmussen
d1db479727 refactor(server): move asset checks to service (#2640) 2023-06-06 14:17:15 -05:00
Mert
1e748864c5 chore(ml): updated dockerfile, added typing, packaging (#2642)
* updated dockerfile, added typing, packaging

apply env change

* added arm64 support

* added ml version pump, second try for arm64

* added linting config to pyproject.toml

* renamed ml input field

* fixed linter config

* fixed dev docker compose
2023-06-05 09:40:48 -05:00
wittymap
c92c442356 Update restore example to address SQL errors on database import. (#2663) 2023-06-05 08:56:39 -05:00
Alex The Bot
1f4993350a Version v1.60.0 2023-06-04 15:45:06 +00:00
Michel Heusschen
f9b1d1edaf fix(server): better metadata extraction for images (#2653) 2023-06-03 21:55:30 -05:00
Michel Heusschen
cab5477656 fix(web+server): showing assets without thumbnail (#2652)
* fix(web+server): showing assets without thumbnail

* missed change
2023-06-03 21:41:27 -05:00
Mert
b8de668f5f feat(ml): env variables for tags, faces and eager startup (#2626)
* env variables for tags, faces and eager startup

* chore(server,ml): remove object detection job and endpoint (#2627)

* removed object detection job

* removed object detection endpoint

* env variables for tags, faces and eager startup

* download without caching models if not eager

* simplified `get_cached_model`

* re-added env for clip text model
2023-06-02 21:42:47 -05:00
Jason Rasmussen
c5234731d6 fix(server): add executable permission to start scripts (#2650) 2023-06-02 14:04:26 -05:00
Michel Heusschen
ef86a77946 refactor(server): remove invalid exif coordinates (#2651) 2023-06-02 14:04:07 -05:00
Michel Heusschen
1b301984dd fix(server): handle invalid coordinates (#2648) 2023-06-02 11:29:12 -05:00
Michel Heusschen
9807f76aff chore(web): improve type checking (#2644)
* fix(web): use id instead of assetId

* chore(web): improve type checking

* fix test jobs

* improve type checking and resolve errors
2023-06-02 08:55:08 -05:00
Michel Heusschen
47673dd773 fix(web): use id instead of assetId (#2643) 2023-06-02 08:50:35 -05:00
Jason Rasmussen
a9fb1d435a refactor(server): use UUID dto in asset controller (#2641)
* refactor: assetId => id

* chore: open api

* chore: remove unused dto

* fix(web): assetId => id

* fix: web test
2023-06-01 21:19:25 -05:00
Jason Rasmussen
422ad20641 refactor(server): use swagger (#2639) 2023-06-02 02:12:22 +00:00
Jason Rasmussen
3ea2fe1c48 refactor(server): shared links (#2632)
* refactor: rename share => shared-link

* refactor: shared link crud methods

* chore: open api
2023-06-01 21:09:57 -05:00
Jason Rasmussen
038e064e60 refactor(server): handle download (#2637) 2023-06-01 21:03:15 -05:00
Jason Rasmussen
800f010383 refactor(server): app init (#2638) 2023-06-01 20:54:16 -05:00
Jason Rasmussen
4350f9363d feat(server): use base64 shared links (#2633)
* feat(server): use base64 shared links

* fix: handle array values
2023-06-01 15:56:37 -05:00
Jason Rasmussen
76a1629e75 test(server): job service (#2634) 2023-06-01 16:07:45 -04:00
Jason Rasmussen
2493dfaba3 feat(server): dynamic job concurrency (#2622)
* feat(server): dynamic job concurrency

* styling and add setting info to top of the job list

* regenerate api

* remove DETECT_OBJECT job

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-01 05:32:51 -05:00
Jason Rasmussen
656dc08406 refactor(server): tags (#2589)
* refactor: tags

* chore: open api

* chore: unused import

* feat: add/remove/get tag assets

* chore: open api

* chore: finish tag tests for add/remove assets
2023-05-31 20:51:28 -05:00
Mert
631f13cf2f chore(server,ml): remove object detection job and endpoint (#2627)
* removed object detection job

* removed object detection endpoint
2023-05-31 20:49:51 -05:00
Jason Rasmussen
9730bf0acc fix(server): without queries (#2621) 2023-05-31 10:00:37 -05:00
Stormrover
9f2b5ea86e Allow docker image version pinning (#2617)
* Allow docker image version pinning

* Updating docker-compose documentation.

* Fixing formatting

* Added Optional to documentation.
2023-05-30 20:53:24 -05:00
Sergey Kondrikov
5702442783 fix: remove mbtree files (#2620) 2023-05-30 20:52:57 -05:00
Jason Rasmussen
74c2f446e9 fix: missing faces job (#2618) 2023-05-30 13:51:53 -05:00
Alex The Bot
da1710bcd2 Version v1.59.1 2023-05-30 17:56:47 +00:00
Jason Rasmussen
2dfd56b49b fix: reload assets from typesense results (#2615)
* fix: reload assets from typesense results

* chore: coverage
2023-05-30 12:55:06 -05:00
Alex The Bot
6538e599dd Version v1.59.0 2023-05-30 15:27:35 +00:00
Michel Heusschen
789e3e3924 refactor(server): use date type for entities (#2602) 2023-05-30 08:15:56 -05:00
Michel Heusschen
3d505e425d fix(web): show icons for empty album (#2604) 2023-05-29 13:58:09 -05:00
Manuel Taberna
e7122d7a72 feat(web): add zoom to photo viewer (#2577)
* feat(web): add zoom to photo viewer

* reduce asset viewer next/prev button div width

* add wrap to block statement
2023-05-29 09:12:58 -05:00
Michel Heusschen
94d0705607 refactor(server): change asset entity to date type (#2599)
* refactor(server): change asset entity to date type

* lower coverage threshold
2023-05-29 09:05:14 -05:00
Jason Rasmussen
caba462703 fix(server): library folder missing on new install (#2597) 2023-05-28 20:48:07 -05:00
Jason Rasmussen
ffe397247e refactor(server): auth decorator (#2588) 2023-05-28 11:30:01 -05:00
Michel Heusschen
e7ad622c02 refactor(web): user avatar (#2585)
* refactor(web): user avatar

* change user settings link

* update package lock json

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-05-28 08:10:55 -05:00
Jason Rasmussen
bca4626708 feat(server): return asset checksum (#2582)
* feat: return asset checksum

* chore: generate open api

* chore: coverage

* feat(server): support base64 hashes in bulk upload check:

* chore: generate open api
2023-05-27 20:56:17 -05:00
Michel Heusschen
7f0ad8e2d2 fix(web+mobile): consistent filename handling (#2534) 2023-05-27 20:53:29 -05:00
Sergey Kondrikov
6c6c5ef651 chore(web): generate API functions with a single argument (#2568) 2023-05-27 20:52:22 -05:00
Alex The Bot
a460940430 Version v1.58.0 2023-05-27 21:56:06 +00:00
Alex
fc2455be80 fix(server): missing metadata extraction job (#2586)
* fix(server): missing metadata extraction job

* format

* fix test

* fix test

* Added source to upload

* fix test

* OK! CODE COVERAGE
2023-05-27 16:49:57 -05:00
Michel Heusschen
fd4357cf23 fix(server): exif time extraction (#2583) 2023-05-27 16:24:07 -05:00
Jason Rasmussen
e41e0df27e fix(server): invalid exif date string (#2580) 2023-05-26 21:13:09 -05:00
Michel Heusschen
f370dc3929 fix(web): small style issues (#2578) 2023-05-26 14:44:06 -05:00
Jason Rasmussen
1c2d83e2c7 refactor(server): job handlers (#2572)
* refactor(server): job handlers

* chore: remove comment

* chore: add comments for
2023-05-26 14:43:24 -05:00
Jason Rasmussen
d6756f3d81 feat(web): improved action bar actions (#2553)
* feat(web): improved action bar actions

* Update web/src/lib/components/photos-page/actions/delete-assets.svelte

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

* update archive and favorite actions

* feat: add un archive/favorite on associated pages

* fix favorite action + use isAllArchived for photos

* remove unneeded unarchive check

---------

Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>
2023-05-26 08:11:10 -05:00
Fynn Petersen-Frey
71ef7685c5 chore(mobile): update isar (#2571) 2023-05-26 08:09:44 -05:00
Jason Rasmussen
b7516f31c6 refactor(server): delete album (#2570) 2023-05-26 08:04:09 -05:00
Jason Rasmussen
065fb166c2 refactor(server): bull jobs (#2569)
* refactor(server): bull jobs

* chore: add comment

* chore: metadata test coverage

* fix typo

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-05-26 07:52:52 -05:00
Jason Rasmussen
4cc6e3b966 refactor(server): update album (#2562)
* refactor: update album

* fix: remove unnecessary decorator
2023-05-25 14:37:19 -05:00
Michel Heusschen
1c293a2759 fix(web): back button on person page (#2566) 2023-05-25 11:48:36 -05:00
Michel Heusschen
062e2eca6f feat(web+server): map date filters + small changes (#2565) 2023-05-25 11:47:52 -05:00
Fynn Petersen-Frey
bcc2c34eef feat(mobile): partner sharing (#2541)
* feat(mobile): partner sharing

* getAllAssets for other users

* i18n

* fix tests

* try to fix web tests

* shared with/by confusion

* error logging

* guard against outdated server version
2023-05-24 22:52:43 -05:00
Jason Rasmussen
1613ae9185 feat(web): show assets without thumbs (#2561)
* feat(web): show assets without thumbnails

* chore: open api
2023-05-24 21:13:02 -05:00
Jason Rasmussen
d827a6182b refactor: create album (#2555) 2023-05-24 21:10:45 -05:00
Jason Rasmussen
83df14d379 feat: disk stats for library folder (#2560) 2023-05-24 21:05:31 -05:00
Alex Phillips
7c1dae918d feat(server): xmp sidecar metadata (#2466)
* initial commit for XMP sidecar support

* Added support for 'missing' metadata files to include those without sidecar files, now detects sidecar files in the filesystem for media already ingested but the sidecar was created afterwards

* didn't mean to commit default log level during testing

* new sidecar logic for video metadata as well

* Added xml mimetype for sidecars only

* don't need capture group for this regex

* wrong default value reverted

* simplified the move here - keep it in the same try catch since the outcome is to move the media back anyway

* simplified setter logic

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>

* simplified logic per suggestions

* sidecar is now its own queue with a discover and sync, updated UI for the new job queueing

* queue a sidecar job for every asset based on discovery or sync, though the logic is almost identical aside from linking the sidecar

* now queue sidecar jobs for each assset, though logic is mostly the same between discovery and sync

* simplified logic of filename extraction and asset instantiation

* not sure how that got deleted..

* updated code per suggestions and comments in the PR

* stat was not being used, removed the variable set

* better type checking, using in-scope variables for exif getter instead of passing in every time

* removed commented out test

* ran and resolved all lints, formats, checks, and tests

* resolved suggested change in PR

* made getExifProperty more dynamic with multiple possible args for fallbacks, fixed typo, used generic in function  for better type checking

* better error handling and moving files back to positions on move or save failure

* regenerated api

* format fixes

* Added XMP documentation

* documentation typo

* Merged in main

* missed merge conflict

* more changes due to a merge

* Resolving conflicts

* added icon for sidecar jobs

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-05-24 20:59:30 -05:00
Jonathan Jogenfors
1b54c4f8e7 feat(server): Add support for client-side hashing (#2072)
* Modify controller DTOs

* Can check duplicates on server side

* Remove deviceassetid and deviceid

* Remove device ids from file uploader

* Add db migration for removed device ids

* Don't sanitize checksum

* Convert asset checksum to string

* Make checksum not optional for asset

* Use enums when rejecting duplicates

* Cleanup

* Return of the device id, but optional

* Don't use deviceId for upload folder

* Use checksum in thumb path

* Only use asset id in thumb path

* Openapi generation

* Put deviceAssetId back in asset response dto

* Add missing checksum in test fixture

* Add another missing checksum in test fixture

* Cleanup asset repository

* Add back previous /exists endpoint

* Require checksum to not be null

* Correctly set deviceId in db

* Remove index

* Fix compilation errors

* Make device id nullabel in asset response dto

* Reduce PR scope

* Revert asset service

* Reorder imports

* Reorder imports

* Reduce PR scope

* Reduce PR scope

* Reduce PR scope

* Reduce PR scope

* Reduce PR scope

* Update openapi

* Reduce PR scope

* refactor: asset bulk upload check

* chore: regenreate open-api

* chore: fix tests

* chore: tests

* update migrations and regenerate api

* Feat: use checksum in web file uploader

* Change to wasm-crypto

* Use crypto api for checksumming in web uploader

* Minor cleanup of file upload

* feat(web): pause and resume jobs

* Make device asset id not nullable again

* Cleanup

* Device id not nullable in response dto

* Update API specs

* Bump api specs

* Remove old TODO comment

* Remove NOT NULL constraint on checksum index

* Fix requested pubspec changes

* Remove unneeded import

* Update server/apps/immich/src/api-v1/asset/asset.service.ts

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

* Update server/apps/immich/src/api-v1/asset/asset-repository.ts

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

* Remove unneeded check

* Update server/apps/immich/src/api-v1/asset/asset-repository.ts

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

* Remove hashing in the web uploader

* Cleanup file uploader

* Remove varchar from asset entity fields

* Return 200 from bulk upload check

* Put device asset id back into asset repository

* Merge migrations

* Revert pubspec lock

* Update openapi specs

* Merge upstream changes

* Fix failing asset service tests

* Fix formatting issue

* Cleanup migrations

* Remove newline from pubspec

* Revert newline

* Checkout main version

* Revert again

* Only return AssetCheck

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>
2023-05-24 16:08:21 -05:00
Jason Rasmussen
49b74e9091 refactor(server): album controller (#2539)
* refactor: album controller/service

* chore: open-api

* fix: tests
2023-05-24 09:30:13 -05:00
Jason Rasmussen
a1f1e5bc37 fix(server): reverse geocoding delete dump logic (#2551) 2023-05-23 20:36:36 -05:00
Jason Rasmussen
2dc8a93685 feat(web): use user layout on admin pages (#2550) 2023-05-23 19:02:12 -05:00
Jason Rasmussen
c2145cbe11 fix: hide album context menu (#2543) 2023-05-23 15:40:32 -05:00
Jason Rasmussen
50a792a81a refactor(server): use cascades for keys and tokens (#2544) 2023-05-23 15:40:04 -05:00
Jason Rasmussen
e2bd7e1e08 feat(web): job tile icons (#2546) 2023-05-23 15:04:24 -05:00
Thomas
11a5a990d0 docker: use default entrypoint and command where applicable (#2529)
A default entrypoint and command make it just a bit easier to use the images as
there is no longer a need for an explicit entrypoint. The exception is the
server image, which still requires the shell script to be specified.
2023-05-23 09:02:47 -05:00
Alex The Bot
ecc894ac82 Version v1.57.1 2023-05-23 09:21:22 +00:00
Michel Heusschen
50b649cd3e fix(web): small fixes for album selection modal (#2527) 2023-05-23 04:15:48 -05:00
Michel Heusschen
99b018cd49 fix(web): loading leaflet in production builds (#2526) 2023-05-23 04:14:00 -05:00
Alex
6aa2800275 chore: post release tasks 2023-05-22 22:43:06 -05:00
Alex The Bot
cd7fc7e026 Version v1.57.0 2023-05-23 02:03:49 +00:00
Alex
b4d312efb6 fix(web): revert justify layout - improve gallery view load time (#2522)
* fix(web): revert justify layout - improve gallery view load time

* Remove package
2023-05-22 21:01:32 -05:00
Mert
e9722710ac feat(server): transcode bitrate and thread settings (#2488)
* support for two-pass transcoding

* added max bitrate and thread to transcode api

* admin page setting desc+bitrate and thread options

* Update web/src/lib/components/admin-page/settings/setting-input-field.svelte

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

* Update web/src/lib/components/admin-page/settings/setting-input-field.svelte

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

* two-pass slider, `crf` and `threads` as numbers

* updated and added transcode tests

* refactored `getFfmpegOptions`

* default `threads`, `maxBitrate` now 0, more tests

* vp9 constant quality mode

* fixed nullable `crf` and `threads`

* fixed two-pass slider, added apiproperty

* optional `desc` for `SettingSelect`

* disable two-pass if settings are incompatible

* fixed test

* transcode interface

---------

Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>
2023-05-22 13:07:43 -05:00
Michel Heusschen
f1384fea58 feat(server): pagination for asset queries in jobs (#2516)
* feat(server): pagination for asset queries in jobs

* default mock value for getAll

* remove live photo name correction

* order paginated results by createdAt

* change log level

* move usePagination to domain
2023-05-22 13:05:06 -05:00
Alex
feadc45e75 chore(server): Remove dist directory in command script (#2518) 2023-05-22 10:27:08 -05:00
Jason Rasmussen
eefe5266a8 chore(server): remove unused filename (#2517) 2023-05-22 10:26:56 -05:00
Jason Rasmussen
74353193f8 feat(web,server): user storage label (#2418)
* feat: user storage label

* chore: open api

* fix: checks

* fix: api update validation and tests

* feat: default admin storage label

* fix: linting

* fix: user create/update dto

* fix: delete library with custom label
2023-05-21 23:18:10 -04:00
Jason Rasmussen
0ccb73cf2b feat(server): add missing thumbnail check to nightly jobs (#2510) 2023-05-21 21:24:21 -05:00
Mert
356f4424df chore(server): queue handlers shouldn't increase concurrency (#2508) 2023-05-21 21:11:26 -05:00
Michel Heusschen
85c6cf4309 fix(web): context menu overlap + outclick types (#2506) 2023-05-21 11:01:08 -05:00
Michel Heusschen
96fb68135e fix(nginx): enable gzip and show error logs (#2504) 2023-05-21 08:23:46 -05:00
Michel Heusschen
a7b9adc692 feat(web+server): map improvements (#2498)
* feat(web+server): map improvements

* add number format double to fix mobile
2023-05-21 01:26:06 -05:00
Jason Rasmussen
e028cf9002 fix(server): reverse geocoding crash loop (#2489) 2023-05-20 21:39:12 -05:00
Jason Rasmussen
f984be8ea0 docs: update contributing pages (#2503) 2023-05-20 20:46:09 -05:00
Jason Rasmussen
3d426b55d3 chore(server): auth request type (#2502) 2023-05-20 20:44:26 -05:00
Fynn Petersen-Frey
02b8b2c125 chore(mobile): remove hive (#2497) 2023-05-20 20:42:19 -05:00
Fynn Petersen-Frey
dc7b0f75bb chore(mobile): use Record instead of custom pair+triple (#2483) 2023-05-20 20:41:34 -05:00
Jason Rasmussen
a089d9891d feat: confirm before deleting all faces and people (#2496) 2023-05-20 20:40:53 -05:00
Alex The Bot
a1183f4b4b Version v1.56.2 2023-05-20 03:53:45 +00:00
Alex
84cfa38510 chore(ml): load models on start up (#2487)
* chore(ml): load models on start up

* Download correct model
2023-05-19 22:37:01 -05:00
Fynn Petersen-Frey
89edbcacfa chore(mobile): remove obsolete files (#2482) 2023-05-19 22:06:39 -05:00
Alex The Bot
c8e649f190 Version v1.56.1 2023-05-19 04:01:36 +00:00
Alex
790e43dd6e chore(server): Enhancement for query to get assets for each recognized person (#2475) 2023-05-18 22:59:57 -05:00
Alex
59f6b2ff2e [Localizely] Translations update (#2471) 2023-05-18 15:10:22 -05:00
Alex Tran
829defbf61 docs: update RAM requirement 2023-05-18 15:04:21 -05:00
martin
70a0f4ae48 chore: update to node 18 and alpine 3.17 (#2430)
* chore: update to node 18 and alpine 3.17

Signed-off-by: martin <martin.labat92@gmail.com>

* chore: fix sharp version

Signed-off-by: martin <martin.labat92@gmail.com>

* chore(server): use vips-dev

Signed-off-by: martin <martin.labat92@gmail.com>

* update checkDiskUsage

Signed-off-by: martin <martin.labat92@gmail.com>

* fix: use vips-heif instead of libheif

Signed-off-by: martin <martin.labat92@gmail.com>

* fix: use vips instead of vips-cpp

Signed-off-by: martin <martin.labat92@gmail.com>

* fix: ensure vips installation

Signed-off-by: martin <martin.labat92@gmail.com>

---------

Signed-off-by: martin <martin.labat92@gmail.com>
2023-05-18 10:56:33 -05:00
Michel Heusschen
c7c0ef6abc chore(web): switch to eslint-plugin-svelte package (#2467) 2023-05-18 10:43:09 -05:00
Jason Rasmussen
2fc8a0db92 docs: add new features (#2441)
* chore: add new features

* Add facial recognition to docs site

* Added partner sharing to the docs site

* update developer docs

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-05-18 09:48:43 -05:00
Alex
b50c621be8 chore: update changelog for android 2023-05-18 09:47:12 -05:00
Alex The Bot
126f5857c3 Version v1.56.0 2023-05-18 14:03:48 +00:00
Alex Tran
8b3e1764a8 fix(web): asset count z-index 2023-05-17 21:39:34 -05:00
Alex
b776461297 fix(web): unable to change person name (#2458)
* fix(web): unable to change person name

* name changed

* chore: strongly-typed dispatcher

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
2023-05-17 21:13:54 -05:00
Jason Rasmussen
4a0052026f feat(web): improve page header + scrolling (#2453)
* fix: line to edge of screen

* refactor: user layout page
2023-05-17 14:45:16 -05:00
Alex
35c4887e4a fix return update asset with new update time (#2457) 2023-05-17 14:28:58 -05:00
Mark Monteiro
f5b87833f8 Add comment about Docker secrets to example.env (#2454)
Add a comment to indicate the support for Docker secrets added in https://github.com/immich-app/immich/pull/1254
2023-05-17 17:36:44 +00:00
Fynn Petersen-Frey
0dde76bbbc feat(mobile): lazy loading of assets (#2413) 2023-05-17 12:36:02 -05:00
Jason Rasmussen
93863b0629 feat: facial recognition (#2180) 2023-05-17 12:07:17 -05:00
Michel Heusschen
115a47d4c6 fix(web): layout spacing when zooming (#2452) 2023-05-17 10:44:15 -05:00
martin
308c63df16 fix(web): use correct favicon sizes (#2446)
* fix(web): use correct favicon sizes

Signed-off-by: martin <martin.labat92@gmail.com>

* fix: format

Signed-off-by: martin <martin.labat92@gmail.com>

---------

Signed-off-by: martin <martin.labat92@gmail.com>
2023-05-17 09:20:32 -05:00
Michel Heusschen
ab86d0a18d refactor(web): asset select actions (#2444)
* refactor(web): asset select actions

* remaining pages/components + data flow changes

* fix check
2023-05-16 09:13:20 -05:00
Jason Rasmussen
3ec74444b0 docs: remove roadmap link (#2442) 2023-05-15 18:25:46 +00:00
Michel Heusschen
1979c84ea8 chore(web): update eslint and prettier packages (#2437)
Co-authored-by: Alex <alex.tran1502@gmail.com>
2023-05-15 17:58:35 +00:00
Trenton H
f4aefcb18b chore(ci): versions the Docker cleaning action to latest released (#2438) 2023-05-15 12:45:03 -05:00
Sergey Kondrikov
7f2fa23179 feat (server, web): Share with partner (#2388)
* feat(server, web): implement share with partner

* chore: regenerate api

* chore: regenerate api

* Pass userId to getAssetCountByTimeBucket and getAssetByTimeBucket

* chore: regenerate api

* Use AssetGrid to view partner's assets

* Remove disableNavBarActions flag

* Check access to buckets

* Apply suggestions from code review

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>

* Remove exception rethrowing

* Simplify partner access check

* Create new PartnerController

* chore api:generate

* Use partnerApi

* Remove id from PartnerResponseDto

* Refactor PartnerEntity

* Rename args

* Remove duplicate code in getAll

* Create composite primary keys for partners table

* Move asset access check into PartnerCore

* Remove redundant getUserAssets call

* Remove unused getUserAssets method

* chore: regenerate api

* Simplify getAll

* Replace ?? with ||

* Simplify PartnerRepository.create

* Introduce PartnerIds interface

* Replace two database migrations with one

* Simplify getAll

* Change PartnerResponseDto to include UserResponseDto

* Move partner sharing endpoints to PartnerController

* Rename ShareController to SharedLinkController

* chore: regenerate api after rebase

* refactor: shared link remove return type

* refactor: return user response dto

* chore: regenerate open api

* refactor: partner getAll

* refactor: partner settings event typing

* chore: remove unused code

* refactor: add partners modal trigger

* refactor: update url for viewing partner photos

* feat: update partner sharing title

* refactor: rename service method names

* refactor: http exception logic to service, PartnerIds interface

* chore: regenerate open api

* test: coverage for domain code

* fix: addPartner => createPartner

* fix: missed rename

* refactor: more code cleanup

* chore: alphabetize settings order

* feat: stop sharing confirmation modal

* Enhance contrast of the email in dark mode

* Replace button with CircleIconButton

* Fix linter warning

* Fix date types for PartnerEntity

* Fix PartnerEntity creation

* Reset assetStore state

* Change layout of the partner's assets page

* Add bulk download action for partner's assets

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
2023-05-15 12:30:53 -05:00
Michel Heusschen
4524aa0d06 refactor(web): use ImmichApi to create urls (#2435) 2023-05-13 21:52:29 -05:00
Michel Heusschen
15fa8250cb fix(web): profile image load (#2434) 2023-05-13 09:16:14 -05:00
Alex
4dff129949 feat(web): Replicate albums view for sharing view (#2433)
* replicate album view for sharing view

* Remove unused file

* fix test

* correct title
2023-05-13 09:05:30 -05:00
Alex
43951ec208 chore(mobile): Upgrade to Flutter 3.10 (#2429)
* update dependencies

* resolve dependency and update code for Flutter 3.10

* update github action flutter version

* update test version

* iOS deployment

* pump intl package

* list tile fix
2023-05-12 09:21:13 -05:00
Alex
f961acdf0c feat(web): album card hover styling (#2424)
* feat(web): album card hover styling

* feedback

* fix delete button not shown

* better color
2023-05-11 11:50:48 -05:00
Alex
2c7821e5e6 chore: update api (#2428) 2023-05-11 10:49:28 -05:00
Alex
d25ddfc46b chore: update screenshots for readme and docs (#2425)
* update screenshot

* better quality for docs
2023-05-10 23:33:32 -05:00
Alex
8cc9b08c06 chore(ml): use official pytorch channel (#2416) 2023-05-10 07:09:33 -05:00
Michel Heusschen
98b9d815a6 chore(web): update svelte related packages (#2419) 2023-05-10 04:58:53 -05:00
Jason Rasmussen
a808b9403e feat(web,server): logout all devices (#2415)
* feat: logout all devices

* chore: regenerate openapi

* chore: add test

* chore: logout vs log out
2023-05-09 14:34:17 -05:00
Jason Rasmussen
c956eee919 chore: fix backup dumper formatting (#2414) 2023-05-09 13:44:50 -05:00
Alex The Bot
aa97ca9ccf Version v1.55.1 2023-05-09 15:29:06 +00:00
faupau
98bb3de8da fix(web) small UI improvements (#2369)
* small changes in asset viewer navigation

* add conditional wrapper and scroll only content

* fix formatting

* update conditional wrapper

* remove emptz title attribute

* remove conditional-wrapper as it is not needed

* remove isTimeline

* fix map over sidebar

* fix overlap

* fix conflict

* revert z-index

* add relative z index

---------

Co-authored-by: faupau03 <paul.paffe@gmx.net>
2023-05-09 10:10:13 -05:00
Michel Heusschen
cd43edf074 feat(mobile): improve localization (#2405) 2023-05-09 08:58:27 -05:00
Michel Heusschen
dffd992304 fix(web): remove global style from map marker (#2408) 2023-05-09 08:57:17 -05:00
bo0tzz
f1b70e13a1 Revert "Bump local-reverse-geocoder to 0.15.2 / Fix Corrupted Reverse Geocoding CSV File (#2396)" (#2409)
This reverts commit b5a4aef829.
2023-05-09 08:56:31 -05:00
Alex Tran
d91247dc35 chore: post release 2023-05-08 22:27:55 -05:00
1381 changed files with 96022 additions and 43013 deletions

View File

@@ -45,7 +45,7 @@ jobs:
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: "3.7.3"
flutter-version: "3.10.5"
cache: true
- name: Create the Keystore

View File

@@ -38,11 +38,12 @@ jobs:
-
name: Clean temporary images
if: "${{ env.TOKEN != '' }}"
uses: stumpylog/image-cleaner-action/ephemeral@develop
uses: stumpylog/image-cleaner-action/ephemeral@v0.1.0
with:
token: "${{ env.TOKEN }}"
owner: "immich-app"
is_org: "true"
do_delete: "true"
package_name: "${{ matrix.primary-name }}"
scheme: "pull_request"
repo_name: "immich"
@@ -69,9 +70,10 @@ jobs:
-
name: Clean untagged images
if: "${{ env.TOKEN != '' }}"
uses: stumpylog/image-cleaner-action/untagged@develop
uses: stumpylog/image-cleaner-action/untagged@v0.1.0
with:
token: "${{ env.TOKEN }}"
owner: "immich-app"
do_delete: "true"
is_org: "true"
package_name: "${{ matrix.primary-name }}"

View File

@@ -42,10 +42,10 @@ jobs:
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2.1.0
uses: docker/setup-qemu-action@v2.2.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2.5.0
uses: docker/setup-buildx-action@v2.9.1
# Workaround to fix error:
# failed to push: failed to copy: io: read/write on closed pipe
# See https://github.com/docker/build-push-action/issues/761
@@ -100,7 +100,7 @@ jobs:
fi
- name: Build and push image
uses: docker/build-push-action@v4.0.0
uses: docker/build-push-action@v4.1.1
with:
context: ${{ matrix.context }}
platforms: ${{ matrix.platforms }}

View File

@@ -34,6 +34,9 @@ jobs:
with:
token: ${{ secrets.ORG_RELEASE_TOKEN }}
- name: Install Poetry
run: pipx install poetry
- name: Bump version
run: misc/release/pump-version.sh -s "${{ inputs.serverBump }}" -m "${{ inputs.mobileBump }}"

View File

@@ -22,8 +22,8 @@ jobs:
- name: Setup Flutter SDK
uses: subosito/flutter-action@v2
with:
channel: 'stable'
flutter-version: '3.7.3'
channel: "stable"
flutter-version: "3.10.5"
- name: Install dependencies
run: dart pub get
@@ -32,4 +32,3 @@ jobs:
- name: Run dart analyze
run: dart analyze --fatal-infos
working-directory: ./mobile

View File

@@ -13,13 +13,20 @@ jobs:
e2e-tests:
name: Run end-to-end test suites
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./server
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Run Immich Server E2E Test
run: docker-compose -f ./docker/docker-compose.test.yml --env-file ./docker/.env.test up --abort-on-container-exit --exit-code-from immich-server-test
- name: Run npm install
run: npm ci
- name: Run e2e tests
run: npm run test:e2e
if: ${{ !cancelled() }}
doc-tests:
name: Run documentation checks
@@ -73,6 +80,32 @@ jobs:
run: npm run test:cov
if: ${{ !cancelled() }}
cli-unit-tests:
name: Run cli test suites
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./cli
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Run npm install
run: npm ci
- name: Run linter
run: npm run lint
if: ${{ !cancelled() }}
- name: Run formatter
run: npm run format
if: ${{ !cancelled() }}
- name: Run unit tests & coverage
run: npm run test:cov
if: ${{ !cancelled() }}
web-unit-tests:
name: Run web unit test suites and checks
runs-on: ubuntu-latest
@@ -96,7 +129,11 @@ jobs:
if: ${{ !cancelled() }}
- name: Run svelte checks
run: npm run check
run: npm run check:svelte
if: ${{ !cancelled() }}
- name: Run tsc
run: npm run check:typescript
if: ${{ !cancelled() }}
- name: Run unit tests & coverage
@@ -112,10 +149,40 @@ jobs:
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: "3.7.3"
flutter-version: "3.10.5"
- name: Run tests
working-directory: ./mobile
run: flutter test
run: flutter test -j 1
ml-unit-tests:
name: Run ML unit tests and checks
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./machine-learning
steps:
- uses: actions/checkout@v3
- name: Install poetry
run: pipx install poetry
- uses: actions/setup-python@v4
with:
python-version: 3.11
cache: "poetry"
- name: Install dependencies
run: |
poetry install --with dev
- name: Lint with ruff
run: |
poetry run ruff check --format=github app
- name: Check black formatting
run: |
poetry run black --check app
- name: Run mypy type checking
run: |
poetry run mypy --install-types --non-interactive app/
- name: Run tests and coverage
run: |
poetry run pytest --cov app
generated-api-up-to-date:
name: Check generated files are up-to-date
@@ -163,13 +230,13 @@ jobs:
run: npm --prefix server run typeorm:migrations:run
- name: Generate new migrations
continue-on-error: true
run: npm --prefix server run typeorm:migrations:generate ./libs/infra/src/migrations/TestMigration
run: npm --prefix server run typeorm:migrations:generate ./src/infra/migrations/TestMigration
- name: Find file changes
uses: tj-actions/verify-changed-files@v13.1
id: verify-changed-files
with:
files: |
server/libs/infra/src/migrations/
server/src/infra/migrations/
- name: Verify files have not changed
if: steps.verify-changed-files.outputs.files_changed == 'true'
run: |

View File

@@ -1,17 +1,17 @@
dev:
rm -rf ./server/dist && docker-compose -f ./docker/docker-compose.dev.yml up --remove-orphans
docker-compose -f ./docker/docker-compose.dev.yml up --remove-orphans
dev-new:
rm -rf ./server/dist && docker compose -f ./docker/docker-compose.dev.yml up --remove-orphans
docker compose -f ./docker/docker-compose.dev.yml up --remove-orphans
dev-new-update:
rm -rf ./server/dist && docker compose -f ./docker/docker-compose.dev.yml up --build -V --remove-orphans
docker compose -f ./docker/docker-compose.dev.yml up --build -V --remove-orphans
dev-update:
rm -rf ./server/dist && docker-compose -f ./docker/docker-compose.dev.yml up --build -V --remove-orphans
docker-compose -f ./docker/docker-compose.dev.yml up --build -V --remove-orphans
dev-scale:
rm -rf ./server/dist && docker-compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans
docker-compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans
stage:
docker-compose -f ./docker/docker-compose.staging.yml up --build -V --remove-orphans

View File

@@ -19,6 +19,8 @@
<br/>
<p align="center">
<a href="README_zh_CN.md">中文</a>
<a href="README_tr_TR.md">Türkçe</a>
<a href="README_ca_ES.md">Català</a>
</p>
## Disclaimer
@@ -60,25 +62,33 @@ Spec: Free-tier Oracle VM - Amsterdam - 2.4Ghz quad-core ARM64 CPU, 24GB RAM
# Features
| Features | Mobile | Web |
| ------------------------------------------- | ------ | --- |
| Upload and view videos and photos | Yes | Yes |
| Auto backup when the app is opened | Yes | N/A |
| Selective album(s) for backup | Yes | N/A |
| Download photos and videos to local device | Yes | Yes |
| Multi-user support | Yes | Yes |
| Album and Shared albums | Yes | Yes |
| Scrubbable/draggable scrollbar | Yes | Yes |
| Support RAW (HEIC, HEIF, DNG, Apple ProRaw) | Yes | Yes |
| Metadata view (EXIF, map) | Yes | Yes |
| Search by metadata, objects and CLIP | Yes | Yes |
| Administrative functions (user management) | N/A | Yes |
| Background backup | Yes | N/A |
| Virtual scroll | Yes | Yes |
| OAuth support | Yes | Yes |
| LivePhoto backup and playback | iOS | Yes |
| User-defined storage structure | Yes | Yes |
| Public Sharing | N/A | Yes |
| Features | Mobile | Web |
| -------------------------------------------- | ------ | --- |
| Upload and view videos and photos | Yes | Yes |
| Auto backup when the app is opened | Yes | N/A |
| Selective album(s) for backup | Yes | N/A |
| Download photos and videos to local device | Yes | Yes |
| Multi-user support | Yes | Yes |
| Album and Shared albums | Yes | Yes |
| Scrubbable/draggable scrollbar | Yes | Yes |
| Support raw formats | Yes | Yes |
| Metadata view (EXIF, map) | Yes | Yes |
| Search by metadata, objects, faces, and CLIP | Yes | Yes |
| Administrative functions (user management) | No | Yes |
| Background backup | Yes | N/A |
| Virtual scroll | Yes | Yes |
| OAuth support | Yes | Yes |
| API Keys | N/A | Yes |
| LivePhoto backup and playback | iOS | Yes |
| User-defined storage structure | Yes | Yes |
| Public Sharing | No | Yes |
| Archive and Favorites | Yes | Yes |
| Global Map | No | Yes |
| Partner Sharing | Yes | Yes |
| Facial recognition and clustering | Yes | Yes |
| Memories (x years ago) | Yes | Yes |
| Offline support | Yes | No |
| Read-only gallery | Yes | Yes |
# Support the project

107
README_ca_ES.md Normal file
View File

@@ -0,0 +1,107 @@
<p align="center">
<br/>
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-green.svg?color=3F51B5&style=for-the-badge&label=Llicència&logoColor=000000&labelColor=ececec" alt="Llicència: MIT"></a>
<a href="https://discord.gg/D8JsnBEuKb">
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" atl="Discord"/>
</a>
<br/>
<br/>
</p>
<p align="center">
<img src="design/immich-logo.svg" width="150" title="Iniciar sessió amb URL personalitzada">
</p>
<h3 align="center">Immich - Solució de còpia de seguretat d'alta rendiment per a fotos i vídeos auto-allotjada</h3>
<br/>
<a href="https://immich.app">
<img src="design/immich-screenshots.png" title="Captura de pantalla principal">
</a>
<br/>
<p align="center">
<a href="README.md">English</a>
<a href="README_zh_CN.md">中文</a>
<a href="README_tr_TR.md">Türkçe</a>
</p>
## Avís legal
- ⚠️ El projecte està en desenvolupament **molt actiu**.
- ⚠️ Espereu errors i canvis que poden trencar coses.
- ⚠️ **No utilitzeu l'aplicació com a única manera de guardar les vostres fotos i vídeos!**
## Contingut
- [Documentació oficial](https://immich.app/docs)
- [Mapa de ruta](https://github.com/orgs/immich-app/projects/1)
- [Demo](#demo)
- [Funcionalitats](#funcionalitats)
- [Introducció](https://immich.app/docs/overview/introduction)
- [Instal·lació](https://immich.app/docs/install/requirements)
- [Directrius de contribució](https://immich.app/docs/overview/support-the-project)
- [Donar suport al projecte](#suportar-el-projecte)
## Documentació
Podeu trobar la documentació principal, incloent les guies d'instal·lació, a https://immich.app/.
## Demo
Podeu accedir a la demostració web a https://demo.immich.app
Per a l'aplicació mòbil, podeu utilitzar `https://demo.immich.app/api` com a "URL de punt final del servidor".
```bash title="Credencials de la demo"
Les credencials
email: demo@immich.app
contrasenya: demo
```
```
Spec: Free-tier Oracle VM - Amsterdam - 2.4Ghz quad-core ARM64 CPU, 24GB RAM
```
# Funcionalitats
| Característiques | Mòbil | Web |
| -------------------------------------------- | ------ | --- |
| Pujar i veure vídeos i fotos | Sí | Sí |
| Còpia de seguretat automàtica en obrir l'aplicació | Sí | N/A |
| Selecció d'àlbums per a la còpia de seguretat | Sí | N/A |
| Descarregar fotos i vídeos a l'aparell local | Sí | Sí |
| Suport per a múltiples usuaris | Sí | Sí |
| Àlbums i àlbums compartits | Sí | Sí |
| Barra de desplaçament amb funció de rasclet/arrossegament | Sí | Sí |
| Suport per a formats raw | Sí | Sí |
| Visualització de metadades (EXIF, mapa) | Sí | Sí |
| Cerca per metadades, objectes, cares i CLIP | Sí | Sí |
| Funcions administratives (gestió d'usuaris) | No | Sí |
| Còpia de seguretat en segon pla | Sí | N/A |
| Desplaçament virtual | Sí | Sí |
| Suport per a OAuth | Sí | Sí |
| Claus d'API | N/A | Sí |
| Còpia de seguretat i reproducció de LivePhoto | iOS | Sí |
| Estructura d'emmagatzematge definida per l'usuari | Sí | Sí |
| Compartició pública | No | Sí |
| Arxiu i preferits | Sí | Sí |
| Mapa global | No | Sí |
| Compartició amb associats | Sí | Sí |
| Reconeixement facial i agrupament | Sí | Sí |
| Records (fa x anys) | Sí | Sí |
| Suport fora de línia | Sí | No |
| Galeria de només lectura | Sí | Sí |
# Donar suport al projecte
M'he compromès amb aquest projecte i no em detindré. Continuaré actualitzant la documentació, afegint noves funcionalitats i solucionant errors. Però no ho puc fer sol. Per això, necessito la vostra ajuda per donar-me motivació addicional per seguir endavant.
Com van dir els nostres amfitrions a l'episodi [selfhosted.show - 'The-organization-must-not-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418), això és una tasca enorme del que l'equip i jo estem fent. I m'encantaria poder dedicar-m'hi a temps complet, per la qual cosa us demano la vostra ajuda per fer-ho possible.
Si creieu que aquesta és una causa justa i l'aplicació és alguna cosa que us veieu utilitzant durant molt de temps, considereu donar suport al projecte amb alguna de les opcions següents.
## Donació
- [Donació mensual](https://github.com/sponsors/alextran1502) a través de GitHub Sponsors
- [Donació única](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) a través de GitHub Sponsors
- [Librepay](https://liberapay.com/alex.tran1502/)
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX

104
README_tr_TR.md Normal file
View File

@@ -0,0 +1,104 @@
<p align="center">
<br/>
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-green.svg?color=3F51B5&style=for-the-badge&label=License&logoColor=000000&labelColor=ececec" alt="License: MIT"></a>
<a href="https://discord.gg/D8JsnBEuKb">
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" atl="Discord"/>
</a>
<br/>
<br/>
</p>
<p align="center">
<img src="design/immich-logo.svg" width="150" title="Login With Custom URL">
</p>
<h3 align="center">Immich - Yüksek performanslı, kendine ait barındırılan fotoğraf ve video yedekleme çözümü</h3>
<br/>
<a href="https://immich.app">
<img src="design/immich-screenshots.png" title="Main Screenshot">
</a>
<br/>
<p align="center">
<a href="README.md">English</a>
<a href="README_zh_CN.md">中文</a>
</p>
## Feragatname
- ⚠️ Proje **çok aktif** bir şekilde geliştirilmektedir.
- ⚠️ Hatalar ve uygulama yapısını bozan değişiklikler olabilir.
- ⚠️ **Uygulamayı, fotoğraflarınızı ve videolarınızı saklamanın tek yöntemi olarak kullanmayın!**
## Content
- [Resmi Belgeler](https://immich.app/docs)
- [Yol Haritası](https://github.com/orgs/immich-app/projects/1)
- [Demo](#demo)
- [Özellikler](#özellikler)
- [Giriş](https://immich.app/docs/overview/introduction)
- [Kurulum](https://immich.app/docs/install/requirements)
- [Katkı Sağlama Rehberi](https://immich.app/docs/overview/support-the-project)
- [Projeyi Destekle](#projeyi-destekle)
## Belgeler
Kurulum dahil olmak üzere resmi belgeleri https://immich.app/ adresinde bulabilirsiniz.
## Demo
Web demo adresi: https://demo.immich.app
Mobil uygulama için `Server Endpoint URL` olarak `https://demo.immich.app/api` adresini kullanabilirsiniz.
```bash title="Demo Bilgileri"
Giriş bilgileri:
email: demo@immich.app
password: demo
```
```
Server Özellikleri: Free-tier Oracle VM - Amsterdam - 2.4Ghz quad-core ARM64 CPU, 24GB RAM
```
# Özellikler
| Özellikler | Mobile | Web |
| ----------------------------------------------------| ------ | --- |
| Videoları ve fotoğrafları yükleme ve görüntüleme | Evet | Evet |
| Uygulama açıldığında otomatik yedekleme | Evet | N/A |
| Yedekleme için seçilebilir albüm(ler) | Evet | N/A |
| Fotoğrafları ve videoları yerel cihaza yükleme | Evet | Evet |
| Çoklu kullanıcı desteği | Evet | Evet |
| Albüm ve paylaşılan albümler | Evet | Evet |
| Silinebilir/sürüklenebilir kaydırma çubuğu | Evet | Evet |
| RAW (HEIC, HEIF, DNG, Apple ProRaw) format desteği | Evet | Evet |
| Metadata'ya uygun görüntüleme (EXIF, map) | Evet | Evet |
| Metadata, objects, faces ve CLIP'e göre arama | Evet | Evet |
| Yönetimsel işlevler (kullanıcı yönetimi) | Hayır | Evet |
| Arka planda yedekleme | Evet | N/A |
| Sanal kaydırma | Evet | Evet |
| OAuth desteği | Evet | Evet |
| API anahtarları | N/A | Evet |
| LivePhoto yedekleme ve oynatma | iOS | Evet |
| Kullanıcı tanımlı depolama yapısı | Evet | Evet |
| Herkese açık paylaşım | Hayır | Evet |
| Arşiv ve Favoriler | Evet | Evet |
| Dünya haritası | Hayır | Evet |
| Partner paylaşımı | Evet | Evet |
| Yüz tanıma ve kümeleme | Hayır | Evet |
| Çevrimdışı destek | Evet | Hayır|
# Projeyi Destekle
Bu projeye bağlı kaldım ve durmayacağım. Belgeleri güncellemeye, yeni özellikler eklemeye ve hataları düzeltmeye devam edeceğim. Ancak bunu tek başıma yapamam. Bu yüzden devam etme konusunda bana motivasyon sağlamanız için yardımınıza ihtiyacım var.
[selfhosted.show - In the episode 'The-organization-must-not-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418) bölümünde söylendiği üzere,bu projede takımımın ve benim projeye harcadağımız büyük bir çaba var. Bir gün bunu tam zamanlı olarak yapabilmeyi çok isterim. Bunu gerçekleştirebilmek için gerçekten sizlerin desteğine ihtiyacım var.
Eğer bu size doğru bir amaç gibi geliyorsa ve uygulamanın uzun bir süre boyunca kullanacağınız bir şey olduğunu düşünüyorsanız, aşağıdaki bağlantılardan birini kullanarak bana destek olabilirsiniz.
## Bağış
- [Aylık bağış](https://github.com/sponsors/alextran1502) via GitHub Sponsors
- [Bir seferlik bağış](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) via GitHub Sponsors
- [Librepay](https://liberapay.com/alex.tran1502/)
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX

View File

@@ -23,6 +23,7 @@
<p align="center">
<a href="README.md">English</a>
<a href="README_tr_TR.md">Türkçe</a>
</p>

20
cli/.editorconfig Normal file
View File

@@ -0,0 +1,20 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
charset = utf-8
trim_trailing_whitespace = true
[*.{ts,js}]
quote_type = single
[*.{md,mdx}]
max_line_length = off
trim_trailing_whitespace = false
[*.{yml,yaml}]
quote_type = double

1
cli/.eslintignore Normal file
View File

@@ -0,0 +1 @@
/dist

23
cli/.eslintrc.js Normal file
View File

@@ -0,0 +1,23 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
sourceType: 'module',
tsconfigRootDir: __dirname,
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'prettier/prettier': 0,
},
};

13
cli/.gitignore vendored Normal file
View File

@@ -0,0 +1,13 @@
*-debug.log
*-error.log
/.nyc_output
/dist
/lib
/tmp
/yarn.lock
node_modules
oclif.manifest.json
.vscode
.idea
/coverage/

18
cli/.prettierignore Normal file
View File

@@ -0,0 +1,18 @@
.DS_Store
node_modules
/build
/package
.env
.env.*
!.env.example
src/api/open-api
*.md
*.json
coverage
dist
**/migrations/**
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

6
cli/.prettierrc Normal file
View File

@@ -0,0 +1,6 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 120,
"semi": true
}

46
cli/README.md Normal file
View File

@@ -0,0 +1,46 @@
A command-line interface for interfacing with Immich
# Getting started
$ ts-node cli/src
To start using the CLI, you need to login with an API key first:
$ ts-node cli/src login-key https://your-immich-instance/api your-api-key
NOTE: This will store your api key under ~/.config/immich/auth.yml
Next, you can run commands:
$ ts-node cli/src server-info
When you're done, log out to remove the credentials from your filesystem
$ ts-node cli/src logout
# Usage
```
Usage: immich [options] [command]
Immich command line interface
Options:
-h, --help display help for command
Commands:
upload [options] [paths...] Upload assets
import [options] [paths...] Import existing assets
server-info Display server information
login-key [instanceUrl] [apiKey] Login using an API key
help [command] display help for command
```
# Todo
- Sidecar should check both .jpg.xmp and .xmp
- Sidecar check could be case-insensitive
# Known issues
- Upload can't use sdk due to multiple issues

8
cli/jest.config.ts Normal file
View File

@@ -0,0 +1,8 @@
import type { Config } from 'jest';
const config: Config = {
preset: 'ts-jest',
setupFilesAfterEnv: ['jest-extended/all'],
};
export default config;

6261
cli/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

49
cli/package.json Normal file
View File

@@ -0,0 +1,49 @@
{
"name": "immich-cli",
"dependencies": {
"axios": "^1.4.0",
"form-data": "^4.0.0",
"mime-types": "^2.1.35",
"systeminformation": "^5.18.4"
},
"devDependencies": {
"@types/byte-size": "^8.1.0",
"@types/chai": "^4.3.5",
"@types/cli-progress": "^3.11.0",
"@types/jest": "^29.5.2",
"@types/js-yaml": "^4.0.5",
"@types/mime-types": "^2.1.1",
"@types/mock-fs": "^4.13.1",
"@types/node": "^20.3.1",
"@typescript-eslint/eslint-plugin": "^5.60.1",
"byte-size": "^8.1.1",
"chai": "^4.3.7",
"cli-progress": "^3.12.0",
"commander": "^11.0.0",
"eslint": "^8.43.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-jest": "^27.2.2",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-unicorn": "^47.0.0",
"glob": "^10.3.1",
"jest": "^29.5.0",
"jest-extended": "^4.0.0",
"jest-message-util": "^29.5.0",
"jest-mock-axios": "^4.7.2",
"jest-when": "^3.5.2",
"mock-fs": "^5.2.0",
"picomatch": "^2.3.1",
"ts-jest": "^29.1.0",
"ts-node": "^10.9.1",
"tslib": "^2.5.3",
"typescript": "^4.9.4",
"yaml": "^2.3.1"
},
"scripts": {
"lint": "eslint \"src/**/*.ts\" --max-warnings 0",
"prepack": "yarn build ",
"test": "jest",
"test:cov": "jest --coverage",
"format": "prettier --check ."
}
}

View File

@@ -0,0 +1,3 @@
// ./__mocks__/axios.js
import mockAxios from 'jest-mock-axios';
export default mockAxios;

50
cli/src/api/client.ts Normal file
View File

@@ -0,0 +1,50 @@
import {
AlbumApi,
APIKeyApi,
AssetApi,
AuthenticationApi,
Configuration,
JobApi,
OAuthApi,
ServerInfoApi,
SystemConfigApi,
UserApi,
} from './open-api';
import { ApiConfiguration } from '../cores/api-configuration';
export class ImmichApi {
public userApi: UserApi;
public albumApi: AlbumApi;
public assetApi: AssetApi;
public authenticationApi: AuthenticationApi;
public oauthApi: OAuthApi;
public serverInfoApi: ServerInfoApi;
public jobApi: JobApi;
public keyApi: APIKeyApi;
public systemConfigApi: SystemConfigApi;
private readonly config;
public readonly apiConfiguration: ApiConfiguration;
constructor(instanceUrl: string, apiKey: string) {
this.apiConfiguration = new ApiConfiguration(instanceUrl, apiKey);
this.config = new Configuration({
basePath: instanceUrl,
baseOptions: {
headers: {
'x-api-key': apiKey,
},
},
});
this.userApi = new UserApi(this.config);
this.albumApi = new AlbumApi(this.config);
this.assetApi = new AssetApi(this.config);
this.authenticationApi = new AuthenticationApi(this.config);
this.oauthApi = new OAuthApi(this.config);
this.serverInfoApi = new ServerInfoApi(this.config);
this.jobApi = new JobApi(this.config);
this.keyApi = new APIKeyApi(this.config);
this.systemConfigApi = new SystemConfigApi(this.config);
}
}

4
cli/src/api/open-api/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
wwwroot/*.js
node_modules
typings
dist

View File

@@ -0,0 +1 @@
# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm

View File

@@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md

View File

@@ -0,0 +1,9 @@
.gitignore
.npmignore
.openapi-generator-ignore
api.ts
base.ts
common.ts
configuration.ts
git_push.sh
index.ts

View File

@@ -0,0 +1 @@
6.5.0

12656
cli/src/api/open-api/api.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,72 @@
/* tslint:disable */
/* eslint-disable */
/**
* Immich
* Immich API
*
* The version of the OpenAPI document: 1.67.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import type { Configuration } from './configuration';
// Some imports not used depending on template conditions
// @ts-ignore
import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios';
import globalAxios from 'axios';
export const BASE_PATH = "/api".replace(/\/+$/, "");
/**
*
* @export
*/
export const COLLECTION_FORMATS = {
csv: ",",
ssv: " ",
tsv: "\t",
pipes: "|",
};
/**
*
* @export
* @interface RequestArgs
*/
export interface RequestArgs {
url: string;
options: AxiosRequestConfig;
}
/**
*
* @export
* @class BaseAPI
*/
export class BaseAPI {
protected configuration: Configuration | undefined;
constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) {
if (configuration) {
this.configuration = configuration;
this.basePath = configuration.basePath || this.basePath;
}
}
};
/**
*
* @export
* @class RequiredError
* @extends {Error}
*/
export class RequiredError extends Error {
constructor(public field: string, msg?: string) {
super(msg);
this.name = "RequiredError"
}
}

View File

@@ -0,0 +1,150 @@
/* tslint:disable */
/* eslint-disable */
/**
* Immich
* Immich API
*
* The version of the OpenAPI document: 1.67.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import type { Configuration } from "./configuration";
import type { RequestArgs } from "./base";
import type { AxiosInstance, AxiosResponse } from 'axios';
import { RequiredError } from "./base";
/**
*
* @export
*/
export const DUMMY_BASE_URL = 'https://example.com'
/**
*
* @throws {RequiredError}
* @export
*/
export const assertParamExists = function (functionName: string, paramName: string, paramValue: unknown) {
if (paramValue === null || paramValue === undefined) {
throw new RequiredError(paramName, `Required parameter ${paramName} was null or undefined when calling ${functionName}.`);
}
}
/**
*
* @export
*/
export const setApiKeyToObject = async function (object: any, keyParamName: string, configuration?: Configuration) {
if (configuration && configuration.apiKey) {
const localVarApiKeyValue = typeof configuration.apiKey === 'function'
? await configuration.apiKey(keyParamName)
: await configuration.apiKey;
object[keyParamName] = localVarApiKeyValue;
}
}
/**
*
* @export
*/
export const setBasicAuthToObject = function (object: any, configuration?: Configuration) {
if (configuration && (configuration.username || configuration.password)) {
object["auth"] = { username: configuration.username, password: configuration.password };
}
}
/**
*
* @export
*/
export const setBearerAuthToObject = async function (object: any, configuration?: Configuration) {
if (configuration && configuration.accessToken) {
const accessToken = typeof configuration.accessToken === 'function'
? await configuration.accessToken()
: await configuration.accessToken;
object["Authorization"] = "Bearer " + accessToken;
}
}
/**
*
* @export
*/
export const setOAuthToObject = async function (object: any, name: string, scopes: string[], configuration?: Configuration) {
if (configuration && configuration.accessToken) {
const localVarAccessTokenValue = typeof configuration.accessToken === 'function'
? await configuration.accessToken(name, scopes)
: await configuration.accessToken;
object["Authorization"] = "Bearer " + localVarAccessTokenValue;
}
}
function setFlattenedQueryParams(urlSearchParams: URLSearchParams, parameter: any, key: string = ""): void {
if (parameter == null) return;
if (typeof parameter === "object") {
if (Array.isArray(parameter)) {
(parameter as any[]).forEach(item => setFlattenedQueryParams(urlSearchParams, item, key));
}
else {
Object.keys(parameter).forEach(currentKey =>
setFlattenedQueryParams(urlSearchParams, parameter[currentKey], `${key}${key !== '' ? '.' : ''}${currentKey}`)
);
}
}
else {
if (urlSearchParams.has(key)) {
urlSearchParams.append(key, parameter);
}
else {
urlSearchParams.set(key, parameter);
}
}
}
/**
*
* @export
*/
export const setSearchParams = function (url: URL, ...objects: any[]) {
const searchParams = new URLSearchParams(url.search);
setFlattenedQueryParams(searchParams, objects);
url.search = searchParams.toString();
}
/**
*
* @export
*/
export const serializeDataIfNeeded = function (value: any, requestOptions: any, configuration?: Configuration) {
const nonString = typeof value !== 'string';
const needsSerialization = nonString && configuration && configuration.isJsonMime
? configuration.isJsonMime(requestOptions.headers['Content-Type'])
: nonString;
return needsSerialization
? JSON.stringify(value !== undefined ? value : {})
: (value || "");
}
/**
*
* @export
*/
export const toPathString = function (url: URL) {
return url.pathname + url.search + url.hash
}
/**
*
* @export
*/
export const createRequestFunction = function (axiosArgs: RequestArgs, globalAxios: AxiosInstance, BASE_PATH: string, configuration?: Configuration) {
return <T = unknown, R = AxiosResponse<T>>(axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
const axiosRequestArgs = {...axiosArgs.options, url: (configuration?.basePath || basePath) + axiosArgs.url};
return axios.request<T, R>(axiosRequestArgs);
};
}

View File

@@ -0,0 +1,101 @@
/* tslint:disable */
/* eslint-disable */
/**
* Immich
* Immich API
*
* The version of the OpenAPI document: 1.67.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
export interface ConfigurationParameters {
apiKey?: string | Promise<string> | ((name: string) => string) | ((name: string) => Promise<string>);
username?: string;
password?: string;
accessToken?: string | Promise<string> | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise<string>);
basePath?: string;
baseOptions?: any;
formDataCtor?: new () => any;
}
export class Configuration {
/**
* parameter for apiKey security
* @param name security name
* @memberof Configuration
*/
apiKey?: string | Promise<string> | ((name: string) => string) | ((name: string) => Promise<string>);
/**
* parameter for basic security
*
* @type {string}
* @memberof Configuration
*/
username?: string;
/**
* parameter for basic security
*
* @type {string}
* @memberof Configuration
*/
password?: string;
/**
* parameter for oauth2 security
* @param name security name
* @param scopes oauth2 scope
* @memberof Configuration
*/
accessToken?: string | Promise<string> | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise<string>);
/**
* override base path
*
* @type {string}
* @memberof Configuration
*/
basePath?: string;
/**
* base options for axios calls
*
* @type {any}
* @memberof Configuration
*/
baseOptions?: any;
/**
* The FormData constructor that will be used to create multipart form data
* requests. You can inject this here so that execution environments that
* do not support the FormData class can still run the generated client.
*
* @type {new () => FormData}
*/
formDataCtor?: new () => any;
constructor(param: ConfigurationParameters = {}) {
this.apiKey = param.apiKey;
this.username = param.username;
this.password = param.password;
this.accessToken = param.accessToken;
this.basePath = param.basePath;
this.baseOptions = param.baseOptions;
this.formDataCtor = param.formDataCtor;
}
/**
* Check if the given MIME is a JSON MIME.
* JSON MIME examples:
* application/json
* application/json; charset=UTF8
* APPLICATION/JSON
* application/vnd.company+json
* @param mime - MIME (Multipurpose Internet Mail Extensions)
* @return True if the given MIME is JSON, false otherwise.
*/
public isJsonMime(mime: string): boolean {
const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i');
return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json');
}
}

View File

@@ -0,0 +1,57 @@
#!/bin/sh
# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/
#
# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com"
git_user_id=$1
git_repo_id=$2
release_note=$3
git_host=$4
if [ "$git_host" = "" ]; then
git_host="github.com"
echo "[INFO] No command line input provided. Set \$git_host to $git_host"
fi
if [ "$git_user_id" = "" ]; then
git_user_id="GIT_USER_ID"
echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id"
fi
if [ "$git_repo_id" = "" ]; then
git_repo_id="GIT_REPO_ID"
echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id"
fi
if [ "$release_note" = "" ]; then
release_note="Minor update"
echo "[INFO] No command line input provided. Set \$release_note to $release_note"
fi
# Initialize the local directory as a Git repository
git init
# Adds the files in the local repository and stages them for commit.
git add .
# Commits the tracked changes and prepares them to be pushed to a remote repository.
git commit -m "$release_note"
# Sets the new remote
git_remote=$(git remote)
if [ "$git_remote" = "" ]; then # git remote not defined
if [ "$GIT_TOKEN" = "" ]; then
echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment."
git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git
else
git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git
fi
fi
git pull origin master
# Pushes (Forces) the changes in the local repository up to the remote repository
echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git"
git push origin master 2>&1 | grep -v 'To https'

View File

@@ -0,0 +1,18 @@
/* tslint:disable */
/* eslint-disable */
/**
* Immich
* Immich API
*
* The version of the OpenAPI document: 1.67.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
export * from "./api";
export * from "./configuration";

View File

@@ -0,0 +1,38 @@
import { ImmichApi } from '../api/client';
import path from 'node:path';
import { SessionService } from '../services/session.service';
import { LoginError } from '../cores/errors/login-error';
import { exit } from 'node:process';
import os from 'os';
import { ServerVersionReponseDto, UserResponseDto } from 'src/api/open-api';
export abstract class BaseCommand {
protected sessionService!: SessionService;
protected immichApi!: ImmichApi;
protected deviceId!: string;
protected user!: UserResponseDto;
protected serverVersion!: ServerVersionReponseDto;
protected configDir;
protected authPath;
constructor() {
const userHomeDir = os.homedir();
this.configDir = path.join(userHomeDir, '.config/immich/');
this.sessionService = new SessionService(this.configDir);
this.authPath = path.join(this.configDir, 'auth.yml');
}
public async connect(): Promise<void> {
try {
this.immichApi = await this.sessionService.connect();
} catch (error) {
if (error instanceof LoginError) {
console.log(error.message);
exit(1);
} else {
throw error;
}
}
}
}

View File

@@ -0,0 +1,9 @@
import { BaseCommand } from '../../cli/base-command';
export default class LoginKey extends BaseCommand {
public async run(instanceUrl: string, apiKey: string): Promise<void> {
console.log('Executing API key auth flow...');
await this.sessionService.keyLogin(instanceUrl, apiKey);
}
}

View File

@@ -0,0 +1,13 @@
import { BaseCommand } from '../cli/base-command';
export default class Logout extends BaseCommand {
public static readonly description = 'Logout and remove persisted credentials';
public async run(): Promise<void> {
console.log('Executing logout flow...');
await this.sessionService.logout();
console.log('Successfully logged out');
}
}

View File

@@ -0,0 +1,15 @@
import { BaseCommand } from '../cli/base-command';
export default class ServerInfo extends BaseCommand {
static description = 'Display server information';
static enableJsonFlag = true;
public async run() {
console.log('Getting server information');
await this.connect();
const { data: versionInfo } = await this.immichApi.serverInfoApi.getServerVersion();
console.log(versionInfo);
}
}

173
cli/src/commands/upload.ts Normal file
View File

@@ -0,0 +1,173 @@
import { BaseCommand } from '../cli/base-command';
import { CrawledAsset } from '../cores/models/crawled-asset';
import { CrawlService, UploadService } from '../services';
import * as si from 'systeminformation';
import FormData from 'form-data';
import { UploadOptionsDto } from '../cores/dto/upload-options-dto';
import { CrawlOptionsDto } from '../cores/dto/crawl-options-dto';
import cliProgress from 'cli-progress';
import byteSize from 'byte-size';
export default class Upload extends BaseCommand {
private crawlService = new CrawlService();
private uploadService!: UploadService;
deviceId!: string;
uploadLength!: number;
dryRun = false;
public async run(paths: string[], options: UploadOptionsDto): Promise<void> {
await this.connect();
const uuid = await si.uuid();
this.deviceId = uuid.os || 'CLI';
this.uploadService = new UploadService(this.immichApi.apiConfiguration);
this.dryRun = options.dryRun;
const crawlOptions = new CrawlOptionsDto();
crawlOptions.pathsToCrawl = paths;
crawlOptions.recursive = options.recursive;
crawlOptions.excludePatterns = options.excludePatterns;
const crawledFiles: string[] = await this.crawlService.crawl(crawlOptions);
if (crawledFiles.length === 0) {
console.log('No assets found, exiting');
return;
}
const assetsToUpload = crawledFiles.map((path) => new CrawledAsset(path));
const uploadProgress = new cliProgress.SingleBar(
{
format: '{bar} | {percentage}% | ETA: {eta_formatted} | {value_formatted}/{total_formatted}: {filename}',
},
cliProgress.Presets.shades_classic,
);
let totalSize = 0;
let sizeSoFar = 0;
let totalSizeUploaded = 0;
let uploadCounter = 0;
for (const asset of assetsToUpload) {
// Compute total size first
await asset.process();
totalSize += asset.fileSize;
}
uploadProgress.start(totalSize, 0);
uploadProgress.update({ value_formatted: 0, total_formatted: byteSize(totalSize) });
for (const asset of assetsToUpload) {
uploadProgress.update({
filename: asset.path,
});
try {
if (options.import) {
const importData = {
assetPath: asset.path,
deviceAssetId: asset.deviceAssetId,
deviceId: this.deviceId,
fileCreatedAt: asset.fileCreatedAt,
fileModifiedAt: asset.fileModifiedAt,
isFavorite: false,
};
if (!this.dryRun) {
await this.uploadService.import(importData);
}
} else {
await this.uploadAsset(asset, options.skipHash);
}
} catch (error) {
uploadProgress.stop();
throw error;
}
sizeSoFar += asset.fileSize;
if (!asset.skipped) {
totalSizeUploaded += asset.fileSize;
uploadCounter++;
}
uploadProgress.update(sizeSoFar, { value_formatted: byteSize(sizeSoFar) });
}
uploadProgress.stop();
let messageStart;
if (this.dryRun) {
messageStart = 'Would have ';
} else {
messageStart = 'Successfully ';
}
if (options.import) {
console.log(`${messageStart} imported ${uploadCounter} assets (${byteSize(totalSizeUploaded)})`);
} else {
if (uploadCounter === 0) {
console.log('All assets were already uploaded, nothing to do.');
} else {
console.log(`${messageStart} uploaded ${uploadCounter} assets (${byteSize(totalSizeUploaded)})`);
}
if (options.delete) {
if (this.dryRun) {
console.log(`Would now have deleted assets, but skipped due to dry run`);
} else {
console.log('Deleting assets that have been uploaded...');
const deletionProgress = new cliProgress.SingleBar(cliProgress.Presets.shades_classic);
deletionProgress.start(crawledFiles.length, 0);
for (const asset of assetsToUpload) {
if (!this.dryRun) {
await asset.delete();
}
deletionProgress.increment();
}
deletionProgress.stop();
console.log('Deletion complete');
}
}
}
}
private async uploadAsset(asset: CrawledAsset, skipHash = false) {
await asset.readData();
let skipUpload = false;
if (!skipHash) {
const checksum = await asset.hash();
const checkResponse = await this.uploadService.checkIfAssetAlreadyExists(asset.path, checksum);
skipUpload = checkResponse.data.results[0].action === 'reject';
}
if (skipUpload) {
asset.skipped = true;
} else {
const uploadFormData = new FormData();
uploadFormData.append('deviceAssetId', asset.deviceAssetId);
uploadFormData.append('deviceId', this.deviceId);
uploadFormData.append('fileCreatedAt', asset.fileCreatedAt);
uploadFormData.append('fileModifiedAt', asset.fileModifiedAt);
uploadFormData.append('isFavorite', String(false));
uploadFormData.append('assetData', asset.assetData, { filename: asset.path });
if (asset.sidecarData) {
uploadFormData.append('sidecarData', asset.sidecarData, {
filename: asset.sidecarPath,
contentType: 'application/xml',
});
}
if (!this.dryRun) {
await this.uploadService.upload(uploadFormData);
}
}
}
}

View File

@@ -0,0 +1,9 @@
export class ApiConfiguration {
public readonly instanceUrl!: string;
public readonly apiKey!: string;
constructor(instanceUrl: string, apiKey: string) {
this.instanceUrl = instanceUrl;
this.apiKey = apiKey;
}
}

View File

@@ -0,0 +1,58 @@
// Check asset-upload.config.spec.ts for complete list
// TODO: we should get this list from the server via API in the future
// Videos
const videos = ['mp4', 'webm', 'mov', '3gp', 'avi', 'm2ts', 'mts', 'mpg', 'flv', 'mkv', 'wmv'];
// Images
const heic = ['heic', 'heif'];
const jpeg = ['jpg', 'jpeg'];
const png = ['png'];
const gif = ['gif'];
const tiff = ['tif', 'tiff'];
const webp = ['webp'];
const dng = ['dng'];
const other = [
'3fr',
'ari',
'arw',
'avif',
'cap',
'cin',
'cr2',
'cr3',
'crw',
'dcr',
'nef',
'erf',
'fff',
'iiq',
'jxl',
'k25',
'kdc',
'mrw',
'orf',
'ori',
'pef',
'raf',
'raw',
'rwl',
'sr2',
'srf',
'srw',
'orf',
'ori',
'x3f',
];
export const ACCEPTED_FILE_EXTENSIONS = [
...videos,
...jpeg,
...png,
...heic,
...gif,
...tiff,
...webp,
...dng,
...other,
];

View File

@@ -0,0 +1,6 @@
export class CrawlOptionsDto {
pathsToCrawl!: string[];
recursive = false;
includeHidden = false;
excludePatterns!: string[];
}

View File

@@ -0,0 +1,8 @@
export class UploadOptionsDto {
recursive = false;
excludePatterns!: string[];
dryRun = false;
skipHash = false;
delete = false;
import = false;
}

View File

@@ -0,0 +1,11 @@
export class LoginError extends Error {
constructor(message: string) {
super(message);
// assign the error class name in your custom error (as a shortcut)
this.name = this.constructor.name;
// capturing the stack trace keeps the reference to your error class
Error.captureStackTrace(this, this.constructor);
}
}

2
cli/src/cores/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export * from './constants';
export * from './models';

View File

@@ -0,0 +1,58 @@
import * as fs from 'fs';
import { basename } from 'node:path';
import crypto from 'crypto';
export class CrawledAsset {
public path: string;
public assetData?: fs.ReadStream;
public deviceAssetId?: string;
public fileCreatedAt?: string;
public fileModifiedAt?: string;
public sidecarData?: Buffer;
public sidecarPath?: string;
public fileSize!: number;
public skipped = false;
constructor(path: string) {
this.path = path;
}
async readData() {
this.assetData = fs.createReadStream(this.path);
}
async process() {
const stats = await fs.promises.stat(this.path);
this.deviceAssetId = `${basename(this.path)}-${stats.size}`.replace(/\s+/g, '');
this.fileCreatedAt = stats.ctime.toISOString();
this.fileModifiedAt = stats.mtime.toISOString();
this.fileSize = stats.size;
// TODO: doesn't xmp replace the file extension? Will need investigation
const sideCarPath = `${this.path}.xmp`;
try {
fs.accessSync(sideCarPath, fs.constants.R_OK);
this.sidecarData = await fs.promises.readFile(sideCarPath);
this.sidecarPath = sideCarPath;
} catch (error) {}
}
async delete(): Promise<void> {
return fs.promises.unlink(this.path);
}
public async hash(): Promise<string> {
const sha1 = (filePath: string) => {
const hash = crypto.createHash('sha1');
return new Promise<string>((resolve, reject) => {
const rs = fs.createReadStream(filePath);
rs.on('error', reject);
rs.on('data', (chunk) => hash.update(chunk));
rs.on('end', () => resolve(hash.digest('hex')));
});
};
return await sha1(this.path);
}
}

View File

@@ -0,0 +1 @@
export * from './crawled-asset';

61
cli/src/index.ts Normal file
View File

@@ -0,0 +1,61 @@
import { program, Option } from 'commander';
import Upload from './commands/upload';
import ServerInfo from './commands/server-info';
import LoginKey from './commands/login/key';
program.name('immich').description('Immich command line interface');
program
.command('upload')
.description('Upload assets')
.usage('[options] [paths...]')
.addOption(new Option('-r, --recursive', 'Recursive').env('IMMICH_RECURSIVE').default(false))
.addOption(new Option('-i, --ignore [paths...]', 'Paths to ignore').env('IMMICH_IGNORE_PATHS'))
.addOption(new Option('-h, --skip-hash', "Don't hash files before upload").env('IMMICH_SKIP_HASH').default(false))
.addOption(
new Option('-n, --dry-run', "Don't perform any actions, just show what will be done")
.env('IMMICH_DRY_RUN')
.default(false),
)
.addOption(new Option('--delete', 'Delete local assets after upload').env('IMMICH_DELETE_ASSETS'))
.argument('[paths...]', 'One or more paths to assets to be uploaded')
.action((paths, options) => {
options.excludePatterns = options.ignore;
new Upload().run(paths, options);
});
program
.command('import')
.description('Import existing assets')
.usage('[options] [paths...]')
.addOption(new Option('-r, --recursive', 'Recursive').env('IMMICH_RECURSIVE').default(false))
.addOption(
new Option('-n, --dry-run', "Don't perform any actions, just show what will be done")
.env('IMMICH_DRY_RUN')
.default(false),
)
.addOption(new Option('-i, --ignore [paths...]', 'Paths to ignore').env('IMMICH_IGNORE_PATHS').default(false))
.argument('[paths...]', 'One or more paths to assets to be uploaded')
.action((paths, options) => {
options.import = true;
new Upload().run(paths, options);
});
program
.command('server-info')
.description('Display server information')
.action(() => {
new ServerInfo().run();
});
program
.command('login-key')
.description('Login using an API key')
.argument('[instanceUrl]')
.argument('[apiKey]')
.action((paths, options) => {
new LoginKey().run(paths, options);
});
program.parse(process.argv);

View File

@@ -0,0 +1,235 @@
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable @typescript-eslint/no-unused-vars */
import { CrawlService } from './crawl.service';
import mockfs from 'mock-fs';
import { toIncludeSameMembers } from 'jest-extended';
import { CrawlOptionsDto } from '../cores/dto/crawl-options-dto';
const matchers = require('jest-extended');
expect.extend(matchers);
const crawlService = new CrawlService();
describe('CrawlService', () => {
beforeAll(() => {
// Write a dummy output before mock-fs to prevent some annoying errors
console.log();
});
it('should crawl a single directory', async () => {
mockfs({
'/photos/image.jpg': '',
});
const options = new CrawlOptionsDto();
options.pathsToCrawl = ['/photos/'];
const paths: string[] = await crawlService.crawl(options);
expect(paths).toIncludeSameMembers(['/photos/image.jpg']);
});
it('should crawl a single file', async () => {
mockfs({
'/photos/image.jpg': '',
});
const options = new CrawlOptionsDto();
options.pathsToCrawl = ['/photos/image.jpg'];
const paths: string[] = await crawlService.crawl(options);
expect(paths).toIncludeSameMembers(['/photos/image.jpg']);
});
it('should crawl a file and a directory', async () => {
mockfs({
'/photos/image.jpg': '',
'/images/photo.jpg': '',
});
const options = new CrawlOptionsDto();
options.pathsToCrawl = ['/photos/image.jpg', '/images/'];
const paths: string[] = await crawlService.crawl(options);
expect(paths).toIncludeSameMembers(['/photos/image.jpg', '/images/photo.jpg']);
});
it('should exclude by file extension', async () => {
mockfs({
'/photos/image.jpg': '',
'/photos/image.tif': '',
});
const options = new CrawlOptionsDto();
options.pathsToCrawl = ['/photos/'];
options.excludePatterns = ['**/*.tif'];
const paths: string[] = await crawlService.crawl(options);
expect(paths).toIncludeSameMembers(['/photos/image.jpg']);
});
it('should exclude by file extension without case sensitivity', async () => {
mockfs({
'/photos/image.jpg': '',
'/photos/image.tif': '',
});
const options = new CrawlOptionsDto();
options.pathsToCrawl = ['/photos/'];
options.excludePatterns = ['**/*.TIF'];
const paths: string[] = await crawlService.crawl(options);
expect(paths).toIncludeSameMembers(['/photos/image.jpg']);
});
it('should exclude by folder', async () => {
mockfs({
'/photos/image.jpg': '',
'/photos/raw/image.jpg': '',
'/photos/raw2/image.jpg': '',
'/photos/folder/raw/image.jpg': '',
'/photos/crawl/image.jpg': '',
});
const options = new CrawlOptionsDto();
options.pathsToCrawl = ['/photos/'];
options.excludePatterns = ['**/raw/**'];
options.recursive = true;
const paths: string[] = await crawlService.crawl(options);
expect(paths).toIncludeSameMembers(['/photos/image.jpg', '/photos/raw2/image.jpg', '/photos/crawl/image.jpg']);
});
it('should crawl multiple paths', async () => {
mockfs({
'/photos/image1.jpg': '',
'/images/image2.jpg': '',
'/albums/image3.jpg': '',
});
const options = new CrawlOptionsDto();
options.pathsToCrawl = ['/photos/', '/images/', '/albums/'];
options.recursive = false;
const paths: string[] = await crawlService.crawl(options);
expect(paths).toIncludeSameMembers(['/photos/image1.jpg', '/images/image2.jpg', '/albums/image3.jpg']);
});
it('should crawl a single path without trailing slash', async () => {
mockfs({
'/photos/image.jpg': '',
});
const options = new CrawlOptionsDto();
options.pathsToCrawl = ['/photos'];
const paths: string[] = await crawlService.crawl(options);
expect(paths).toIncludeSameMembers(['/photos/image.jpg']);
});
it('should crawl a single path without recursion', async () => {
mockfs({
'/photos/image.jpg': '',
'/photos/subfolder/image1.jpg': '',
'/photos/subfolder/image2.jpg': '',
'/image1.jpg': '',
});
const options = new CrawlOptionsDto();
options.pathsToCrawl = ['/photos/'];
const paths: string[] = await crawlService.crawl(options);
expect(paths).toIncludeSameMembers(['/photos/image.jpg']);
});
it('should crawl a single path with recursion', async () => {
mockfs({
'/photos/image.jpg': '',
'/photos/subfolder/image1.jpg': '',
'/photos/subfolder/image2.jpg': '',
'/image1.jpg': '',
});
const options = new CrawlOptionsDto();
options.pathsToCrawl = ['/photos/'];
options.recursive = true;
const paths: string[] = await crawlService.crawl(options);
expect(paths).toIncludeSameMembers([
'/photos/image.jpg',
'/photos/subfolder/image1.jpg',
'/photos/subfolder/image2.jpg',
]);
});
it('should filter file extensions', async () => {
mockfs({
'/photos/image.jpg': '',
'/photos/image.txt': '',
'/photos/1': '',
});
const options = new CrawlOptionsDto();
options.pathsToCrawl = ['/photos/'];
const paths: string[] = await crawlService.crawl(options);
expect(paths).toIncludeSameMembers(['/photos/image.jpg']);
});
it('should include photo and video extensions', async () => {
mockfs({
'/photos/image.jpg': '',
'/photos/image.jpeg': '',
'/photos/image.heic': '',
'/photos/image.heif': '',
'/photos/image.png': '',
'/photos/image.gif': '',
'/photos/image.tif': '',
'/photos/image.tiff': '',
'/photos/image.webp': '',
'/photos/image.dng': '',
'/photos/image.nef': '',
'/videos/video.mp4': '',
'/videos/video.mov': '',
'/videos/video.webm': '',
});
const options = new CrawlOptionsDto();
options.pathsToCrawl = ['/photos/', '/videos/'];
const paths: string[] = await crawlService.crawl(options);
expect(paths).toIncludeSameMembers([
'/photos/image.jpg',
'/photos/image.jpeg',
'/photos/image.heic',
'/photos/image.heif',
'/photos/image.png',
'/photos/image.gif',
'/photos/image.tif',
'/photos/image.tiff',
'/photos/image.webp',
'/photos/image.dng',
'/photos/image.nef',
'/videos/video.mp4',
'/videos/video.mov',
'/videos/video.webm',
]);
});
it('should check file extensions without case sensitivity', async () => {
mockfs({
'/photos/image.jpg': '',
'/photos/image.Jpg': '',
'/photos/image.jpG': '',
'/photos/image.JPG': '',
'/photos/image.jpEg': '',
'/photos/image.TIFF': '',
'/photos/image.tif': '',
'/photos/image.dng': '',
'/photos/image.NEF': '',
});
const options = new CrawlOptionsDto();
options.pathsToCrawl = ['/photos/'];
const paths: string[] = await crawlService.crawl(options);
expect(paths).toIncludeSameMembers([
'/photos/image.jpg',
'/photos/image.Jpg',
'/photos/image.jpG',
'/photos/image.JPG',
'/photos/image.jpEg',
'/photos/image.TIFF',
'/photos/image.tif',
'/photos/image.dng',
'/photos/image.NEF',
]);
});
afterEach(() => {
mockfs.restore();
});
});

View File

@@ -0,0 +1,47 @@
import { CrawlOptionsDto } from 'src/cores/dto/crawl-options-dto';
import { ACCEPTED_FILE_EXTENSIONS } from '../cores';
import { glob } from 'glob';
import * as fs from 'fs';
export class CrawlService {
public async crawl(crawlOptions: CrawlOptionsDto): Promise<string[]> {
const pathsToCrawl: string[] = crawlOptions.pathsToCrawl;
const directories: string[] = [];
const crawledFiles: string[] = [];
for await (const currentPath of pathsToCrawl) {
const stats = await fs.promises.stat(currentPath);
if (stats.isFile() || stats.isSymbolicLink()) {
crawledFiles.push(currentPath);
} else {
directories.push(currentPath);
}
}
let searchPattern: string;
if (directories.length === 1) {
searchPattern = directories[0];
} else if (directories.length === 0) {
return crawledFiles;
} else {
searchPattern = '{' + directories.join(',') + '}';
}
if (crawlOptions.recursive) {
searchPattern = searchPattern + '/**/';
}
searchPattern = `${searchPattern}/*.{${ACCEPTED_FILE_EXTENSIONS.join(',')}}`;
const globbedFiles = await glob(searchPattern, {
nocase: true,
nodir: true,
ignore: crawlOptions.excludePatterns,
});
const returnedFiles = crawledFiles.concat(globbedFiles);
returnedFiles.sort();
return returnedFiles;
}
}

View File

@@ -0,0 +1,2 @@
export * from './upload.service';
export * from './crawl.service';

View File

@@ -0,0 +1,95 @@
import { SessionService } from './session.service';
import mockfs from 'mock-fs';
import fs from 'node:fs';
import yaml from 'yaml';
import { LoginError } from '../cores/errors/login-error';
const mockPingServer = jest.fn(() => Promise.resolve({ data: { res: 'pong' } }));
const mockUserInfo = jest.fn(() => Promise.resolve({ data: { email: 'admin@example.com' } }));
jest.mock('../api/open-api', () => {
return {
__esModule: true,
...jest.requireActual('../api/open-api'),
UserApi: jest.fn().mockImplementation(() => {
return { getMyUserInfo: mockUserInfo };
}),
ServerInfoApi: jest.fn().mockImplementation(() => {
return { pingServer: mockPingServer };
}),
};
});
describe('SessionService', () => {
let sessionService: SessionService;
beforeAll(() => {
// Write a dummy output before mock-fs to prevent some annoying errors
console.log();
});
beforeEach(() => {
const configDir = '/config';
sessionService = new SessionService(configDir);
});
it('should connect to immich', async () => {
mockfs({
'/config/auth.yml': 'apiKey: pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg\ninstanceUrl: https://test/api',
});
await sessionService.connect();
expect(mockPingServer).toHaveBeenCalledTimes(1);
});
it('should error if no auth file exists', async () => {
mockfs();
await sessionService.connect().catch((error) => {
expect(error.message).toEqual('No auth file exist. Please login first');
});
});
it('should error if auth file is missing instance URl', async () => {
mockfs({
'/config/auth.yml': 'foo: pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg\napiKey: https://test/api',
});
await sessionService.connect().catch((error) => {
expect(error).toBeInstanceOf(LoginError);
expect(error.message).toEqual('Instance URL missing in auth config file /config/auth.yml');
});
});
it('should error if auth file is missing api key', async () => {
mockfs({
'/config/auth.yml': 'instanceUrl: pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg\nbar: https://test/api',
});
await sessionService.connect().catch((error) => {
expect(error).toBeInstanceOf(LoginError);
expect(error.message).toEqual('API key missing in auth config file /config/auth.yml');
});
});
it('should create auth file when logged in', async () => {
mockfs();
await sessionService.keyLogin('https://test/api', 'pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg');
const data: string = await fs.promises.readFile('/config/auth.yml', 'utf8');
const authConfig = yaml.parse(data);
expect(authConfig.instanceUrl).toBe('https://test/api');
expect(authConfig.apiKey).toBe('pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg');
});
it('should delete auth file when logging out', async () => {
mockfs({
'/config/auth.yml': 'apiKey: pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg\ninstanceUrl: https://test/api',
});
await sessionService.logout();
await fs.promises.access('/auth.yml', fs.constants.F_OK).catch((error) => {
expect(error.message).toContain('ENOENT');
});
});
afterEach(() => {
mockfs.restore();
});
});

View File

@@ -0,0 +1,81 @@
import fs from 'node:fs';
import yaml from 'yaml';
import path from 'node:path';
import { ImmichApi } from '../api/client';
import { LoginError } from '../cores/errors/login-error';
export class SessionService {
readonly configDir: string;
readonly authPath!: string;
private api!: ImmichApi;
constructor(configDir: string) {
this.configDir = configDir;
this.authPath = path.join(this.configDir, 'auth.yml');
}
public async connect(): Promise<ImmichApi> {
await fs.promises.access(this.authPath, fs.constants.F_OK).catch((error) => {
if (error.code === 'ENOENT') {
throw new LoginError('No auth file exist. Please login first');
}
});
const data: string = await fs.promises.readFile(this.authPath, 'utf8');
const parsedConfig = yaml.parse(data);
const instanceUrl: string = parsedConfig.instanceUrl;
const apiKey: string = parsedConfig.apiKey;
if (!instanceUrl) {
throw new LoginError('Instance URL missing in auth config file ' + this.authPath);
}
if (!apiKey) {
throw new LoginError('API key missing in auth config file ' + this.authPath);
}
this.api = new ImmichApi(instanceUrl, apiKey);
await this.ping();
return this.api;
}
public async keyLogin(instanceUrl: string, apiKey: string): Promise<ImmichApi> {
this.api = new ImmichApi(instanceUrl, apiKey);
// Check if server and api key are valid
const { data: userInfo } = await this.api.userApi.getMyUserInfo().catch((error) => {
throw new LoginError(`Failed to connect to the server: ${error.message}`);
});
console.log(`Logged in as ${userInfo.email}`);
if (!fs.existsSync(this.configDir)) {
// Create config folder if it doesn't exist
fs.mkdirSync(this.configDir, { recursive: true });
}
fs.writeFileSync(this.authPath, yaml.stringify({ instanceUrl, apiKey }));
console.log('Wrote auth info to ' + this.authPath);
return this.api;
}
public async logout(): Promise<void> {
if (fs.existsSync(this.authPath)) {
fs.unlinkSync(this.authPath);
console.log('Removed auth file ' + this.authPath);
}
}
private async ping(): Promise<void> {
const { data: pingResponse } = await this.api.serverInfoApi.pingServer().catch((error) => {
throw new Error(`Failed to connect to the server: ${error.message}`);
});
if (pingResponse.res !== 'pong') {
throw new Error('Unexpected ping reply');
}
}
}

View File

@@ -0,0 +1,35 @@
import { UploadService } from './upload.service';
import mockfs from 'mock-fs';
import axios from 'axios';
import mockAxios from 'jest-mock-axios';
import FormData from 'form-data';
import { ApiConfiguration } from '../cores/api-configuration';
describe('UploadService', () => {
let uploadService: UploadService;
beforeAll(() => {
// Write a dummy output before mock-fs to prevent some annoying errors
console.log();
});
beforeEach(() => {
const apiConfiguration = new ApiConfiguration('https://example.com/api', 'key');
uploadService = new UploadService(apiConfiguration);
});
it('should upload a single file', async () => {
const data = new FormData();
uploadService.upload(data);
mockAxios.mockResponse();
expect(axios).toHaveBeenCalled();
});
afterEach(() => {
mockfs.restore();
mockAxios.reset();
});
});

View File

@@ -0,0 +1,65 @@
import axios, { AxiosRequestConfig } from 'axios';
import FormData from 'form-data';
import { ApiConfiguration } from '../cores/api-configuration';
export class UploadService {
private readonly uploadConfig: AxiosRequestConfig<any>;
private readonly checkAssetExistenceConfig: AxiosRequestConfig<any>;
private readonly importConfig: AxiosRequestConfig<any>;
constructor(apiConfiguration: ApiConfiguration) {
this.uploadConfig = {
method: 'post',
maxRedirects: 0,
url: `${apiConfiguration.instanceUrl}/asset/upload`,
headers: {
'x-api-key': apiConfiguration.apiKey,
},
maxContentLength: Number.POSITIVE_INFINITY,
maxBodyLength: Number.POSITIVE_INFINITY,
};
this.importConfig = {
method: 'post',
maxRedirects: 0,
url: `${apiConfiguration.instanceUrl}/asset/import`,
headers: {
'x-api-key': apiConfiguration.apiKey,
'Content-Type': 'application/json',
},
maxContentLength: Number.POSITIVE_INFINITY,
maxBodyLength: Number.POSITIVE_INFINITY,
};
this.checkAssetExistenceConfig = {
method: 'post',
maxRedirects: 0,
url: `${apiConfiguration.instanceUrl}/asset/bulk-upload-check`,
headers: {
'x-api-key': apiConfiguration.apiKey,
'Content-Type': 'application/json',
},
};
}
public checkIfAssetAlreadyExists(path: string, checksum: string): Promise<any> {
this.checkAssetExistenceConfig.data = JSON.stringify({ assets: [{ id: path, checksum: checksum }] });
// TODO: retry on 500 errors?
return axios(this.checkAssetExistenceConfig);
}
public upload(data: FormData): Promise<any> {
this.uploadConfig.data = data;
// TODO: retry on 500 errors?
return axios(this.uploadConfig);
}
public import(data: any): Promise<any> {
this.importConfig.data = data;
// TODO: retry on 500 errors?
return axios(this.importConfig);
}
}

7
cli/test/tsconfig.json Normal file
View File

@@ -0,0 +1,7 @@
{
"extends": "../tsconfig",
"compilerOptions": {
"noEmit": true
},
"references": [{ "path": ".." }]
}

3
cli/testSetup.js Normal file
View File

@@ -0,0 +1,3 @@
// add all jest-extended matchers
import * as matchers from 'jest-extended';
expect.extend(matchers);

25
cli/tsconfig.json Normal file
View File

@@ -0,0 +1,25 @@
{
"compilerOptions": {
"module": "commonjs",
"strict": true,
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"target": "es2017",
"moduleResolution": "node16",
"sourceMap": true,
"outDir": "./dist",
"incremental": true,
"skipLibCheck": true,
"esModuleInterop": true,
"baseUrl": "./",
"paths": {
"@test": ["test"],
"@test/*": ["test/*"]
}
},
"exclude": ["dist", "node_modules", "upload"]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

View File

@@ -1,32 +0,0 @@
# Development Setup
## Lint / format extensions
Setting these in the IDE give a better developer experience auto-formatting code on save and providing instant feedback on lint issues.
### VSCode
Install Prettier, ESLint and Svelte extensions.
in User `settings.json` (`cmd + shift + p` and search for Open User Settings JSON) add the following:
```json
{
"editor.formatOnSave": true,
"[javascript][typescript][css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.tabSize": 2,
"editor.formatOnSave": true
},
"[svelte]": {
"editor.defaultFormatter": "svelte.svelte-vscode",
"editor.tabSize": 2
},
"svelte.enable-ts-plugin": true,
"eslint.validate": ["javascript", "svelte"]
}
```
## Running tests / checks
In both server and web:
`npm run check:all`

View File

@@ -10,12 +10,7 @@ REDIS_HOSTNAME=immich-redis-test
# Upload File Config
UPLOAD_LOCATION=./upload
# MAPBOX
## ENABLE_MAPBOX is either true of false -> if true, you have to provide MAPBOX_KEY
ENABLE_MAPBOX=false
# WEB
MAPBOX_KEY=
VITE_SERVER_ENDPOINT=http://localhost:2283/api
TYPESENSE_ENABLED=false

View File

@@ -35,8 +35,7 @@ services:
ports:
- 3003:3003
volumes:
- ../machine-learning/src:/usr/src/app
- ${UPLOAD_LOCATION}:/usr/src/app/upload
- ../machine-learning/app:/usr/src/app
- model-cache:/cache
env_file:
- .env
@@ -44,7 +43,7 @@ services:
- NODE_ENV=development
depends_on:
- database
restart: always
restart: unless-stopped
immich-microservices:
container_name: immich_microservices
@@ -89,13 +88,13 @@ services:
volumes:
- ../web:/usr/src/app
- /usr/src/app/node_modules
restart: always
restart: unless-stopped
depends_on:
- immich-server
typesense:
container_name: immich_typesense
image: typesense/typesense:0.24.0
image: typesense/typesense:0.24.1@sha256:9bcff2b829f12074426ca044b56160ca9d777a0c488303469143dd9f8259d4dd
environment:
- TYPESENSE_API_KEY=${TYPESENSE_API_KEY}
- TYPESENSE_DATA_DIR=/data
@@ -106,11 +105,11 @@ services:
redis:
container_name: immich_redis
image: redis:6.2
image: redis:6.2-alpine@sha256:70a7a5b641117670beae0d80658430853896b5ef269ccf00d1827427e3263fa3
database:
container_name: immich_postgres
image: postgres:14
image: postgres:14-alpine@sha256:28407a9961e76f2d285dc6991e8e48893503cc3836a4755bbc2d40bcc272a441
env_file:
- .env
environment:
@@ -135,11 +134,10 @@ services:
dockerfile: Dockerfile
ports:
- 2283:8080
logging:
driver: none
depends_on:
- immich-server
restart: always
- immich-web
restart: unless-stopped
volumes:
pgdata:

View File

@@ -25,12 +25,12 @@ services:
- immich-test-network
immich-redis-test:
container_name: immich-redis-test
image: redis:6.2
image: redis:6.2-alpine@sha256:70a7a5b641117670beae0d80658430853896b5ef269ccf00d1827427e3263fa3
networks:
- immich-test-network
immich-database-test:
container_name: immich-database-test
image: postgres:14
image: postgres:14-alpine@sha256:28407a9961e76f2d285dc6991e8e48893503cc3836a4755bbc2d40bcc272a441
env_file:
- .env.test
environment:

View File

@@ -3,8 +3,8 @@ version: "3.8"
services:
immich-server:
container_name: immich_server
image: ghcr.io/immich-app/immich-server:release
entrypoint: ["/bin/sh", "./start-server.sh"]
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
command: [ "start.sh", "immich" ]
volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload
env_file:
@@ -17,8 +17,8 @@ services:
immich-microservices:
container_name: immich_microservices
image: ghcr.io/immich-app/immich-server:release
entrypoint: ["/bin/sh", "./start-microservices.sh"]
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
command: [ "start.sh", "microservices" ]
volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload
env_file:
@@ -31,9 +31,8 @@ services:
immich-machine-learning:
container_name: immich_machine_learning
image: ghcr.io/immich-app/immich-machine-learning:release
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload
- model-cache:/cache
env_file:
- .env
@@ -41,15 +40,14 @@ services:
immich-web:
container_name: immich_web
image: ghcr.io/immich-app/immich-web:release
entrypoint: ["/bin/sh", "./entrypoint.sh"]
image: ghcr.io/immich-app/immich-web:${IMMICH_VERSION:-release}
env_file:
- .env
restart: always
typesense:
container_name: immich_typesense
image: typesense/typesense:0.24.0
image: typesense/typesense:0.24.1@sha256:9bcff2b829f12074426ca044b56160ca9d777a0c488303469143dd9f8259d4dd
environment:
- TYPESENSE_API_KEY=${TYPESENSE_API_KEY}
- TYPESENSE_DATA_DIR=/data
@@ -61,12 +59,12 @@ services:
redis:
container_name: immich_redis
image: redis:6.2
image: redis:6.2-alpine@sha256:70a7a5b641117670beae0d80658430853896b5ef269ccf00d1827427e3263fa3
restart: always
database:
container_name: immich_postgres
image: postgres:14
image: postgres:14-alpine@sha256:28407a9961e76f2d285dc6991e8e48893503cc3836a4755bbc2d40bcc272a441
env_file:
- .env
environment:
@@ -80,17 +78,16 @@ services:
immich-proxy:
container_name: immich_proxy
image: ghcr.io/immich-app/immich-proxy:release
image: ghcr.io/immich-app/immich-proxy:${IMMICH_VERSION:-release}
environment:
# Make sure these values get passed through from the env file
- IMMICH_SERVER_URL
- IMMICH_WEB_URL
ports:
- 2283:8080
logging:
driver: none
depends_on:
- immich-server
- immich-web
restart: always
volumes:

View File

@@ -2,6 +2,8 @@
# Database
###################################################################################
# NOTE: The following four database variables support Docker secrets by adding a *_FILE suffix to the variable name
# See the docker-compose documentation on secrets for additional details: https://docs.docker.com/compose/compose-file/compose-file-v3/#secrets
DB_HOSTNAME=immich_postgres
DB_USERNAME=postgres
DB_PASSWORD=postgres
@@ -50,11 +52,11 @@ TYPESENSE_API_KEY=some-random-text
# TYPESENSE_URL uses base64 encoding for the nodes json.
# Example JSON that was used:
# [
# { 'host': 'typesense-1.example.net', 'port': '443', 'protocol': 'https' },
# { 'host': 'typesense-2.example.net', 'port': '443', 'protocol': 'https' },
# { 'host': 'typesense-3.example.net', 'port': '443', 'protocol': 'https' },
# ]
# TYPESENSE_URL=ha://WwogICAgeyAnaG9zdCc6ICd0eXBlc2Vuc2UtMS5leGFtcGxlLm5ldCcsICdwb3J0JzogJzQ0MycsICdwcm90b2NvbCc6ICdodHRwcycgfSwKICAgIHsgJ2hvc3QnOiAndHlwZXNlbnNlLTIuZXhhbXBsZS5uZXQnLCAncG9ydCc6ICc0NDMnLCAncHJvdG9jb2wnOiAnaHR0cHMnIH0sCiAgICB7ICdob3N0JzogJ3R5cGVzZW5zZS0zLmV4YW1wbGUubmV0JywgJ3BvcnQnOiAnNDQzJywgJ3Byb3RvY29sJzogJ2h0dHBzJyB9LApd
# { "host": "typesense-1.example.net", "port": "443", "protocol": "https" },
# { "host": "typesense-2.example.net", "port": "443", "protocol": "https" },
# { "host": "typesense-3.example.net", "port": "443", "protocol": "https" },
# ]
# TYPESENSE_URL=ha://WwogIHsgImhvc3QiOiAidHlwZXNlbnNlLTEuZXhhbXBsZS5uZXQiLCAicG9ydCI6ICI0NDMiLCAicHJvdG9jb2wiOiAiaHR0cHMiIH0sCiAgeyAiaG9zdCI6ICJ0eXBlc2Vuc2UtMi5leGFtcGxlLm5ldCIsICJwb3J0IjogIjQ0MyIsICJwcm90b2NvbCI6ICJodHRwcyIgfSwKICB7ICJob3N0IjogInR5cGVzZW5zZS0zLmV4YW1wbGUubmV0IiwgInBvcnQiOiAiNDQzIiwgInByb3RvY29sIjogImh0dHBzIiB9Cl0=
###################################################################################
# Reverse Geocoding
@@ -103,3 +105,12 @@ IMMICH_MACHINE_LEARNING_URL=http://immich-machine-learning:3003
####################################################################################
#IMMICH_API_URL_EXTERNAL=http://localhost:3001
###################################################################################
# Immich Version - Optional
#
# This allows all immich docker images to be pinned to a specific version. By default,
# the version is "release" but could be a specific version, like "v1.59.0".
###################################################################################
#IMMICH_VERSION=

View File

@@ -0,0 +1,105 @@
---
title: June 2023 update
authors: [alextran]
tags: [update]
---
Hello everybody, Alex here!
I am back with another update on Immich. It has been only a month since my last update (May 18th, 2023), but it seems forever. I think the rapid releases of Immich and the amount of work make the perspective of time change in Immichs world. We have some exciting updates that I think you will like.
Before going into detail, on behalf of the core team, I would like to thank all of you for loving Immich and contributing to the project. Thank you for helping me make Immich an enjoyable alternative solution to Google Photos so that you have complete control of your data and privacy. I know we are still young and have a lot of work to do, but I am confident we will get there with help from the community. I appreciate all of you from the bottom of my heart!
<!--truncate-->
And now, to the exciting part, what is new in Immichs world?
- Initial support for existing gallery.
- Memory feature.
- Support XMP sidecar.
- Support more raw formats.
- Justified layout for web timeline and blurred thumbnail hash.
- Mechanism to host machine learning on a completely different machine.
## Support for existing gallery
I know this is the most controversial feature when it comes to Immichs way of ingesting photos and videos. For many users, having to upload photos and videos to Immich is simply not working. We listen, discuss, and digest this feature internally more than you imagine because it is not a simple feature to tackle while keeping the performance and the user experience at the top level, which is Immichs primary goal.
Thankfully, we have many great contributors and developers that want to make this come true. So we came up with an initial implementation of this feature in the form of a supporting read-only gallery.
To be concise, Immich can now read in the gallery files, register the path into the database, and then generate necessary files and put them through Immichs machine learning pipeline so you can use all the goodness of Immich without the need to upload them. Since this is the initial implementation, some actions/behavior are not yet supported, and we aim to build toward them in future releases, namely:
- Assets are not automatically synced and must instead be manually synced with the CLI tool.
- Only new files that are added to the gallery will be detected.
- Deleted and moved files will not be detected.
You can find more information on how to use the feature by reading the documentation [here](/docs/features/read-only-gallery).
## Memory feature
This is considered a fun feature that the team and I wanted to build for so long, but we had to put it off because of the refactoring of the code base. The code base is now in a good enough form to circle back and add more exciting features.
This memory feature is very much similar to GPhotos' implementation of “x years since…”. We are aiming to add more categories of memories in the future, such as “Spotlight of the day” or “Day of the Week highlights”
<iframe
width="560"
height="315"
src="https://www.youtube.com/embed/j5XZKvViPew"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen
></iframe>
This feature is now available on the web and will be ported to the mobile app in the near future.
## Support XMP Sidecar
Immich can now import/upload XMP sidecars from the CLI and use the information as the metadata of assets.
## Support more raw formats.
With the recent updates on the dependencies of Immich, we are now extending and hardening support for multiple raw formats. So users with DSLR or mirrorless cameras can now upload their original files to Immich and have them displayed in high-quality thumbnails on the web and mobile view.
## Justified layout for web timeline and blurred thumbnail hash
This is an aesthetic improvement in user experience when browsing the timeline. Photos and videos are now displayed correctly with perspective orientation, making the browsing experience more pleasurable.
To further improve the browsing experience, we now added a blur hash to the thumbnail, so the transition is more natural with a dreamy fade in effect, similar to how our brain goes from faded to vivid memory
<iframe
width="560"
height="315"
src="https://www.youtube.com/embed/b95FLmGHRFc"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen
></iframe>
## Hosting machine learning container on a different machine
With more capabilities Immich is building toward, machine learning will get more powerful and therefore require more resources to run effectively. However, we understand that users might not have the best server resources where they host the Immich instance. Therefore, we changed how machine learning interacts and receives the photos and videos to run through its inference pipeline.
The machine learning container is now a headless system that can run on any machine. As long as your Immich instance can communicate with the system running the machine learning container, it can send the files and receive the required information to make Immich powerful in terms of searching and intelligence. This helps you to utilize a more powerful machine in your home/infrastructure to perform the CPU-intensive tasks while letting Immich only handle the I/O operations for a pleasant and smooth experience.
---
So, those are the highlights for the team and the community after a busy month. There are a lot more changes and improvements. I encourage you to read some release notes, starting from version [v1.57.0](https://github.com/immich-app/immich/releases/tag/v1.57.0) to now.
Thank you, and I am asking for your support for the project. I hope to be a full-time maintainer of Immich one day to dedicate myself to the project as my life works for the community and my family. You can find the support channels below:
- Monthly donation via [GitHub Sponsors](https://github.com/sponsors/alextran1502)
- One-time donation via [GitHub Sponsors](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502)
- [Liberapay](https://liberapay.com/alex.tran1502/)
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
- Give a project a star - the contributors love gazing at the stars and seeing their creations shining in the sky.
Join our friendly [Discord](https://discord.gg/D8JsnBEuKb) to talk and discuss Immich, tech, or anything
Cheer!
Until next time!
Alex

View File

@@ -13,26 +13,22 @@ docker exec -t immich_postgres pg_dumpall -c -U postgres | gzip > "/path/to/back
```
```bash title='Restore'
gunzip < /path/to/backup/dump.sql.gz | docker exec -i immich_postgres psql -U postgres -d immich
docker-compose down -v # CAUTION! Deletes all Immich data to start from scratch.
docker-compose pull # Update to latest version of Immich (if desired)
docker-compose create # Create Docker containers for Immich apps without running them.
docker start immich_postgres # Start Postgres server
sleep 10 # Wait for Postgres server to start up
gunzip < "/path/to/backup/dump.sql.gz" | docker exec -i immich_postgres psql -U postgres -d immich # Restore Backup
docker-compose up -d # Start remainder of Immich apps
```
Note that 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, Postgres conflicts may be encountered upon database restoration (relation already exists, violated foreign key constraints, multiple primary keys, etc.).
The database dumps can also be automated (using [this image](https://github.com/prodrigestivill/docker-postgres-backup-local)) by editing the docker compose file to match the following:
```
container_name: immich_postgres
image: postgres:14
env_file:
- .env
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_DB: ${DB_DATABASE_NAME}
PGDATA: /var/lib/postgresql/data
volumes:
- pgdata:/var/lib/postgresql/data
- ./db_dumps:/db_dumps
restart: always
```yaml
services:
...
backup:
container_name: immich_db_dumper
image: prodrigestivill/postgres-backup-local

View File

@@ -15,7 +15,34 @@ While the reverse proxy provided by Immich works well for basic deployments, som
## Adding a Custom Reverse Proxy
Users can deploy a custom reverse proxy that forwards requests to Immich's reverse proxy. This way, the new reverse proxy can handle TLS termination, load balancing, or other advanced features, while still delegating routing decisions to Immich's reverse proxy. All reverse proxies between Immich and the user must forward all headers and set the `Host`, `X-Forwarded-Host`, `X-Forwarded-Proto` and `X-Forwarded-For` headers to their appropriate values. By following these practices, you ensure that all custom reverse proxies are fully compatible with Immich.
Users can deploy a custom reverse proxy that forwards requests to Immich's reverse proxy. This way, the new reverse proxy can handle TLS termination, load balancing, or other advanced features, while still delegating routing decisions to Immich's reverse proxy. All reverse proxies between Immich and the user must forward all headers and set the `Host`, `X-Forwarded-Host`, `X-Forwarded-Proto` and `X-Forwarded-For` headers to their appropriate values. Additionally, your reverse proxy should allow for big enough uploads. By following these practices, you ensure that all custom reverse proxies are fully compatible with Immich.
### Nginx example config
Below is an example config for nginx:
```nginx
server {
server_name <snip>
# https://github.com/immich-app/immich/blob/main/nginx/templates/default.conf.template#L28
client_max_body_size 50000M;
location / {
proxy_pass http://<snip>:2283;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# http://nginx.org/en/docs/http/websocket.html
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
}
}
```
## Replacing the Default Reverse Proxy

View File

@@ -1,6 +1,6 @@
# Server Commands
The `immich-server` docker image comes preinstalled with an administrative CLI (`immich`) that supports the following commands:
The `immich-server` docker image comes preinstalled with an administrative CLI (`immich-admin`) that supports the following commands:
| Command | Description |
| ------------------------ | ------------------------------------- |

View File

@@ -0,0 +1,14 @@
# Database Migrations
After making any changes in the `server/src/infra/database/entities`, a database migration need to run in order to register the changes in the database. Follow the steps below to create a new migration.
1. Run the command
```bash
npm run typeorm:migrations:generate ./src/infra/<migration-name>
```
2. Check if the migration file makes sense.
3. Move the migration file to folder `./src/infra/database/migrations` in your code editor.
The server will automatically detect `*.ts` file changes and restart. Part of the server start-up process includes running any new migrations, so it will be applied immediately.

View File

@@ -1,7 +1,17 @@
---
sidebar_position: 5
---
# OpenAPI
# Open API
Immich uses the [OpenAPI](https://swagger.io/specification/) standard to generate API documentation. To view the published docs see [here](/docs/api).
Immich uses the [Open API](https://swagger.io/specification/) standard to generate API documentation. To view the published docs see [here](/docs/api).
## Generator
OpenAPI is used to generate the client (Typescript, Dart) SDK. `openapi-generator-cli` can be installed [here](https://openapi-generator.tech/docs/installation/). The generated SDK is based on the `immich-openapi-specs.json` file, which is autogenerated by the server when running in development mode. The `immich-openapi-specs.json` file can be modified with `@nestjs/swagger` decorators used or referenced by controller endpoints. See the [NestJS OpenAPI docs](https://docs.nestjs.com/openapi/types-and-parameters) for more info. When you add a new endpoint or modify an existing one, you must run the command below to update the client SDK.
```bash
npm run api:generate # Run from the `server/` directory
```
You can find the generated client SDK in the `web/src/api` for Typescript SDK and `mobile/openapi` for Dart SDK.
:::tip
This can also be run via `make api` from the project root directory (not in the `server` folder)
:::

View File

@@ -1,16 +1,8 @@
---
sidebar_position: 3
---
# Contributing
Contributions are welcome!
## PR Checklist
# PR Checklist
When contributing code through a pull request, please check the following:
### Web Checks
## Web Checks
- [ ] `npm run lint` (linting via ESLint)
- [ ] `npm run format` (formatting via Prettier)
@@ -21,7 +13,7 @@ When contributing code through a pull request, please check the following:
Run all web checks with `npm run check:all`
:::
### Server Checks
## Server Checks
- [ ] `npm run lint` (linting via ESLint)
- [ ] `npm run format` (formatting via Prettier)
@@ -32,12 +24,10 @@ Run all web checks with `npm run check:all`
Run all server checks with `npm run check:all`
:::
### Open API
## OpenAPI
The Open API client libraries need to be regenerated whenever there are changes to the `immich-openapi-specs.json` file.
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](/docs/developer/open-api.md) for more details.
- [ ] `npm run api:generate`
## Database Migrations
:::tip
This can also be run via `make api` from the project root directory (not in the `server` folder)
:::
A database migration needs to be generated whenever there are changes to `server/src/infra/src/entities`. See [Database Migration](/docs/developer/database-migrations.md) for more details.

View File

@@ -10,9 +10,9 @@ sidebar_position: 2
This environment includes the following services:
- Core server - `/server/apps/immich`
- Core server - `/server/src/immich`
- Machine learning - `/machine-learning`
- Microservices - `/server/apps/microservicess`
- Microservices - `/server/src/microservicess`
- Web app - `/web`
- Redis
- PostgreSQL development database with exposed port `5432` so you can use any database client to acess it
@@ -92,27 +92,3 @@ in User `settings.json` (`cmd + shift + p` and search for `Open User Settings JS
}
}
```
## OpenAPI generator
OpenAPI is used to generate the client (Typescript, Dart) SDK. `openapi-generator-cli` can be installed [here](https://openapi-generator.tech/docs/installation/). When you add a new or modify an existing endpoint, you must run the command below to update the client SDK.
```bash
npm run api:generate # Run from the `server` directory
```
You can find the generated client SDK in the `web/src/api` for Typescript SDK and `mobile/openapi` for Dart SDK.
## Database migrations
After making any changes in the `server/libs/database/src/entities`, a database migration need to run in order to register the changes in the database. Follow the steps below to create a new migration.
1. Attached to the server container shell.
2. Run
```bash
npm run typeorm -- migration:generate ./libs/infra/src/db/<migration-name> -d ./libs/infra/src/db/config/database.config.ts
```
3. Check if the migration file makes sense.
4. Move the migration file to folder `server/libs/database/src/migrations` in your code editor.

View File

@@ -15,6 +15,25 @@ You can use the CLI to upload an existing gallery to the Immich server
npm i -g immich
```
Pre-installed on the `immich-server` container and can be easily accessed through
```
immich
```
### Options
| Parameter | Description |
| ---------------- | ------------------------------------------------------------------- |
| --yes / -y | Assume yes on all interactive prompts |
| --recursive / -r | Include subfolders |
| --delete / -da | Delete local assets after upload |
| --key / -k | User's API key |
| --server / -s | Immich's server address |
| --threads / -t | Number of threads to use (Default 5) |
| --album/ -al | Create albums for assets based on the parent folder or a given name |
| --import/ -i | Import gallery (assets are not uploaded) |
## Quick Start
Specify user's credential, Immich's server address and port and the directory you would like to upload videos/photos from.
@@ -29,26 +48,16 @@ By default, subfolders are not included. To upload a directory including subfold
immich upload --key HFEJ38DNSDUEG --server http://192.168.1.216:2283/api --recursive directory/
```
---
### Options
| Parameter | Description |
| ---------------- | ------------------------------------------------------------------- |
| --yes / -y | Assume yes on all interactive prompts |
| --recursive / -r | Include subfolders |
| --delete / -da | Delete local assets after upload |
| --key / -k | User's API key |
| --server / -s | Immich's server address |
| --threads / -t | Number of threads to use (Default 5) |
| --album/ -al | Create albums for assets based on the parent folder or a given name |
### Obtain the API Key
The API key can be obtained in the user setting panel on the web interface.
![Obtain Api Key](./img/obtain-api-key.png)
---
## Uploading existing libraries
### Run via Docker
You can run the CLI inside of a docker container to avoid needing to install anything.
@@ -76,10 +85,10 @@ If you are running the CLI container on the same machine as your Immich server,
1. Find the internal Docker network used by Immich via `docker network ls`.
2. Adapt the above command to pass the `--network <immich_network>` argument to `docker run`, substituting `<immich_network>` with the result from step 1.
3. Use `--server http://immich-server:3001/` for the upload command instead of the external address.
3. Use `--server http://immich-server:3001` for the upload command instead of the external address.
```bash title="Upload to internal address"
docker run --network immich_default -it --rm -v "$(pwd):/import" ghcr.io/immich-app/immich-cli:latest upload --key HFEJ38DNSDUEG --server http://immich-server:3001/
docker run --network immich_default -it --rm -v "$(pwd):/import" ghcr.io/immich-app/immich-cli:latest upload --key HFEJ38DNSDUEG --server http://immich-server:3001
```
:::
@@ -101,3 +110,64 @@ npm run build
```bash title="Run the command"
node bin/index.js upload --key HFEJ38DNSDUEG --server http://192.168.1.216:2283/api --recursive your/asset/directory
```
---
## Importing existing libraries
If you do not wish to upload files into the server, existing files can be imported into the immich gallery through the use of the `--import` flag.
```
immich upload --key HFEJ38DNSDUEG --server http://192.168.1.216:2283/api --recursive directory/ --import
```
```
immich upload --key HFEJ38DNSDUEG --server http://192.168.1.216:2283/api file1.jpg file2.jpg --import
```
The `immich-server` and `immich-microservices` containers must be able to access the files, or directories at the path referenced in the command. The directories referenced must be set under a user's `External Path` setting. More detailed instructions can be found [here](/docs/features/read-only-gallery).
:::tip Matching volume references
The import command is most easily run on the machine running the immich service, as the path to the files on the machine running the command and the server much match identically.
If you are running immich within docker, the volume pointing to your existing library should be identical with your host machine.
```diff title="docker-compose.yml"
immich-server:
container_name: immich_server
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
command: [ "start.sh", "immich" ]
volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload
+ - /path/to/media:/path/to/media
env_file:
- .env
depends_on:
- redis
- database
- typesense
restart: always
immich-microservices:
container_name: immich_microservices
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
command: [ "start.sh", "microservices" ]
volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload
+ - /path/to/media:/path/to/media
env_file:
- .env
depends_on:
- redis
- database
- typesense
restart: always
```
The proper command for above would be as shown below. You should have access to `/path/to/media` exactly on the environment the CLI command is being run on
```
immich upload --key HFEJ38DNSDUEG --server http://192.168.1.216:2283/api --recursive /path/to/media --import
```
:::

View File

@@ -0,0 +1,15 @@
# Facial Recognition
Immich recognizes faces in your photos and videos and groups them together. You can then assign names to the faces and search for them.
The list of people is shown in the Explore page.
<img src={require('./img/facial-recognition-1.png').default} title='Facial Recognition 1' />
Upon clicking on a person, a list of assets that contain their face will be shown.
<img src={require('./img/facial-recognition-2.png').default} title='Facial Recognition 2' />
The asset detail view will also show the faces that are recognized in the asset.
<img src={require('./img/facial-recognition-3.png').default} title='Facial Recognition 3' />

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@@ -0,0 +1,17 @@
# Partner Sharing
Immich allows you to share your library with other users. They can then view your library and download the assets.
You can manage one or multiple users to have access to your library from the [User Settings](docs/features/user-settings.md) page.
<img src={require('./img/partner-sharing-1.png').default} title='Partner Sharing 1' />
<img src={require('./img/partner-sharing-2.png').default} title='Partner Sharing 2' />
Accessing the shared library can be done from the Sharing page.
<img src={require('./img/partner-sharing-3.png').default} title='Partner Sharing 3' />
:::tip Sharing specific assets
For sharing a specific set of assets, you can use the shared album feature of Immich.
:::

View File

@@ -0,0 +1,103 @@
# Read-only Gallery [Experimental]
## Overview
This feature enables users to use an existing gallery without uploading the assets to Immich.
Upon syncing the file information, it will be read by Immich to generate supported files.
:::caution
This feature is still in an experimental stage. And this is an initial implementation and will receive improvements in the future.
The current limitations of this feature are:
- Assets are not automatically synced and must instead be manually synced with the CLI tool.
- Only new files that are added to the gallery will be detected.
- Deleted and moved files will not be detected.
:::
## Usage
:::tip Example scenario
On the VM/system that Immich is running, I have 2 galleries that I want to use with Immich.
- My gallery is stored at `/mnt/media/precious-memory`
- My wife's gallery is stored at `/mnt/media/childhood-memory`
We will use those values in the steps below.
:::
### Mount the gallery to the containers.
`immich-server` and `immich-microservices` containers will need access to the gallery. Mount the directory path as in the example below
```diff title="docker-compose.yml"
immich-server:
container_name: immich_server
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
command: [ "start.sh", "immich" ]
volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload
+ - /mnt/media/precious-memory:/mnt/media/precious-memory:ro
+ - /mnt/media/childhood-memory:/mnt/media/childhood-memory:ro
env_file:
- .env
depends_on:
- redis
- database
- typesense
restart: always
immich-microservices:
container_name: immich_microservices
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
command: [ "start.sh", "microservices" ]
volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload
+ - /mnt/media/precious-memory:/mnt/media/precious-memory:ro
+ - /mnt/media/childhood-memory:/mnt/media/childhood-memory:ro
env_file:
- .env
depends_on:
- redis
- database
- typesense
restart: always
```
:::tip
Internal and external path have to be identical.
:::
_Remember to bring the container down/up to register the changes. Make sure you can see the mounted path in the container._
### Register the path for the user.
This action is done by the admin of the instance.
- Navigate to `Administration > Users` page on the web.
- Click on the user edit button.
- Add the gallery path to the `External Path` field for the corresponding user and confirm the changes.
<img src={require('./img/me.png').default} width='33%' title='My Account Storage Path' />
<img src={require('./img/my-wife.png').default} width='33%' title='My Wifes Account Storage Path' />
### Sync with the CLI tool.
- Install or update the [CLI Tool](/docs/features/bulk-upload.md). The import feature is supported from version `v0.39.0` of the CLI
- Run the command below to sync the gallery with Immich.
```bash title="Import my gallery"
immich upload --key <my-api-key> --server http://my-server-ip:2283/api /mnt/media/precious-memory --recursive --import
```
```bash title="Import my wife gallery"
immich upload --key <my-wife-api-key> --server http://my-server-ip:2283/api /mnt/media/childhood-memory --recursive --import
```
The `--import` flag will tell Immich to import the files by path instead of uploading them.

View File

@@ -0,0 +1,13 @@
# XMP Sidecars
Immich can ingest XMP sidecars on file upload (via the CLI) as well as detect new sidecars that are placed in the filesystem for existing images.
<img src={require('./img/xmp-sidecars.png').default} title='XMP sidecars' />
XMP sidecars are external XML files that contain metadata related to media files. Many applications read and write these files either exclusively or in addition to the metadata written to image files. They can be a powerful tool for editing and storing metadata of a media file without modifying the media file itself. When Immich receives or detects an XMP sidecar for a media file, it will attempt to extract the metadata from both the sidecar as well as the media file. It will prioritize the metadata for fields in the sidecar but will fall back and use the metadata in the media file if necessary.
When importing files via the CLI bulk uploader, Immich will automatically detect XMP sidecar files as files that exist next to the original media file and have the exact same name with an additional `.xmp` file extension (i.e., `PXL_20230401_203352928.MP.jpg` and `PXL_20230401_203352928.MP.jpg.xmp`).
There are 2 administrator jobs associated with sidecar files: `SYNC` and `DISCOVER`. The sync job will re-scan all media with existing sidecar files and queue them for a metadata refresh. This is a great use case when third-party applications are used to modify the metadata of media. The discover job will attempt to scan the filesystem for new sidecar files for all media that does not currently have a sidecar file associated with it.
<img src={require('./img/sidecar-jobs.png').default} title='Sidecar Administrator Jobs' />

View File

@@ -136,6 +136,15 @@ IMMICH_MACHINE_LEARNING_URL=http://immich-machine-learning:3003
####################################################################################
#IMMICH_API_URL_EXTERNAL=http://localhost:3001
###################################################################################
# Immich Version - Optional
#
# This allows all immich docker images to be pinned to a specific version. By default,
# the version is "release" but could be a specific version, like "v1.59.0".
###################################################################################
#IMMICH_VERSION=
```
</details>
@@ -159,6 +168,8 @@ For more information on how to use the application, please refer to the [Post In
### Step 4 - Upgrading
If `IMMICH_VERSION` is set, it will need to be updated to the latest or desired version.
When a new version of Immich is [released](https://github.com/immich-app/immich/releases), the application can be upgraded with the following commands, run in the directory with the `docker-compose.yml` file:
```bash title="Upgrade Immich"

View File

@@ -0,0 +1,186 @@
# Environment Variables
## Docker Compose
| Variable | Description | Default | Services |
| :---------------- | :-------------------- | :-------: | :------------------------------------------------------------- |
| `IMMICH_VERSION` | Image tags | `release` | server, microservices, machine learning, web, proxy, typesense |
| `UPLOAD_LOCATION` | Host Path for uploads | | server, microservices |
:::tip
These environment variables are used by the `docker-compose.yml` file and do **NOT** affect the containers directly.
:::
## General
| Variable | Description | Default | Services |
| :-------------------------- | :------------------------------------------- | :----------: | :------------------------------------------- |
| `TZ` | Timezone | | microservices |
| `NODE_ENV` | Environment (production, development) | `production` | server, microservices, machine learning, web |
| `LOG_LEVEL` | Log Level (verbose, debug, log, warn, error) | `log` | server, microservices |
| `IMMICH_MEDIA_LOCATION` | Media Location | `./upload` | server, microservices |
| `PUBLIC_LOGIN_PAGE_MESSAGE` | Public Login Page Message | | web |
:::tip
`TZ` is only used by the `exiftool` as a fallback in case the timezone cannot be determined from the image metadata.
`exiftool` is only present in the microservices container.
:::
## Geocoding
| Variable | Description | Default | Services |
| :--------------------------------- | :---------------------------------- | :--------------------------: | :------------ |
| `DISABLE_REVERSE_GEOCODING` | Disable Reverse Geocoding Precision | `false` | microservices |
| `REVERSE_GEOCODING_PRECISION` | Reverse Geocoding Precision | `3` | microservices |
| `REVERSE_GEOCODING_DUMP_DIRECTORY` | Reverse Geocoding Dump Directory | `./.reverse-geocoding-dump/` | microservices |
## Ports
| Variable | Description | Default | Services |
| :---------------------- | :-------------------- | :-----: | :--------------- |
| `PORT` | Web Port | `3000` | web |
| `SERVER_PORT` | Server Port | `3001` | server |
| `MICROSERVICES_PORT` | Microservices Port | `3002` | microservices |
| `MACHINE_LEARNING_PORT` | Machine Learning Port | `3003` | machine learning |
## URLs
| Variable | Description | Default | Services |
| :---------------------------- | :------------------------------------------------------- | :-----------------------------------: | :-------------------- |
| `IMMICH_WEB_URL` | Immich Web URL | `http://immich-web:3000` | proxy |
| `IMMICH_SERVER_URL` | Immich Server URL | `http://immich-server:3001` | web, proxy |
| `IMMICH_MACHINE_LEARNING_URL` | Immich Machine Learning URL, set `"false"` to disable ML | `http://immich-machine-learning:3003` | server, microservices |
| `PUBLIC_IMMICH_SERVER_URL` | Public Immich URL | `http://immich-server:3001` | web |
| `IMMICH_API_URL_EXTERNAL` | Immich API URL External | `/api` | web |
:::info
The above paths are modifying the internal paths of the containers.
:::
## Database
| Variable | Description | Default | Services |
| :------------ | :---------------- | :---------: | :-------------------- |
| `DB_URL` | Database URL | | server, microservices |
| `DB_HOSTNAME` | Database Host | `localhost` | server, microservices |
| `DB_PORT` | Database Port | `5432` | server, microservices |
| `DB_USERNAME` | Database User | `postgres` | server, microservices |
| `DB_PASSWORD` | Database Password | `postgres` | server, microservices |
| `DB_DATABASE` | Database Name | `immich` | server, microservices |
:::info
When `DB_URL` is defined, the other database (`DB_*`) variables are ignored.
:::
## Redis
| Variable | Description | Default | Services |
| :--------------- | :------------- | :------------: | :-------------------- |
| `REDIS_URL` | Redis URL | | server, microservices |
| `REDIS_HOSTNAME` | Redis Host | `immich_redis` | server, microservices |
| `REDIS_PORT` | Redis Port | `6379` | server, microservices |
| `REDIS_DBINDEX` | Redis DB Index | `0` | server, microservices |
| `REDIS_USERNAME` | Redis Username | | server, microservices |
| `REDIS_PASSWORD` | Redis Password | | server, microservices |
| `REDIS_SOCKET` | Redis Socket | | server, microservices |
:::info
`REDIS_URL` must start with `ioredis://` and then include a `base64` encoded JSON string for the configuration.
More info can be found in the upstream [ioredis](https://ioredis.readthedocs.io/en/latest/API/) documentation.
- When `REDIS_URL` is defined, the other redis (`REDIS_*`) variables are ignored.
- When `REDIS_SOCKET` is defined, the other redis (`REDIS_*`) variables are ignored.
:::
Redis (Sentinel) URL example JSON before encoding:
```json
{
"sentinels": [
{
"host": "redis-sentinel-node-0",
"port": 26379
},
{
"host": "redis-sentinel-node-1",
"port": 26379
},
{
"host": "redis-sentinel-node-2",
"port": 26379
}
],
"name": "redis-sentinel"
}
```
## Typesense
| Variable | Description | Default | Services |
| :------------------- | :----------------------- | :---------: | :------------------------------- |
| `TYPESENSE_ENABLED` | Enable Typesense | | server, microservices |
| `TYPESENSE_URL` | Typesense URL | | server, microservices |
| `TYPESENSE_HOST` | Typesense Host | `typesense` | server, microservices |
| `TYPESENSE_PORT` | Typesense Port | `8108` | server, microservices |
| `TYPESENSE_PROTOCOL` | Typesense Protocol | `http` | server, microservices |
| `TYPESENSE_API_KEY` | Typesense API Key | | server, microservices, typesense |
| `TYPESENSE_DATA_DIR` | Typesense Data Directory | `/data` | typesense |
:::info
`TYPESENSE_URL` must start with `ha://` and then include a `base64` encoded JSON string for the configuration.
`TYPESENSE_ENABLED`: Anything other than `false`, behaves as `true`.
Even undefined is treated as `true`.
- When `TYPESENSE_URL` is defined, the other typesense (`TYPESENSE_*`) variables are ignored.
:::
Typesense URL example JSON before encoding:
```json
[
{
"host": "typesense-1.example.net",
"port": "443",
"protocol": "https"
},
{
"host": "typesense-2.example.net",
"port": "443",
"protocol": "https"
},
{
"host": "typesense-3.example.net",
"port": "443",
"protocol": "https"
}
]
```
## Machine Learning
| Variable | Description | Default | Services |
| :------------------------------------------ | :----------------------------- | :-------------------: | :--------------- |
| `MACHINE_LEARNING_MIN_FACE_SCORE` | Minimum Face Score | `0.7` | machine learning |
| `MACHINE_LEARNING_MODEL_TTL` | Model TTL | `300` | machine learning |
| `MACHINE_LEARNING_EAGER_STARTUP` | Eager Startup | `true` | machine learning |
| `MACHINE_LEARNING_MIN_TAG_SCORE` | Minimum Tag Score | `0.9` | machine learning |
| `MACHINE_LEARNING_FACIAL_RECOGNITION_MODEL` | Facial Recognition Model | `buffalo_l` | machine learning |
| `MACHINE_LEARNING_CLIP_TEXT_MODEL` | Clip Text Model | `clip-ViT-B-32` | machine learning |
| `MACHINE_LEARNING_CLIP_IMAGE_MODEL` | Clip Image Model | `clip-ViT-B-32` | machine learning |
| `MACHINE_LEARNING_CLASSIFICATION_MODEL` | Classification Model | `microsoft/resnet-50` | machine learning |
| `MACHINE_LEARNING_CACHE_FOLDER` | ML Cache Location | `/cache` | machine learning |
| `TRANSFORMERS_CACHE` | ML Transformers Cache Location | `/cache` | machine learning |

View File

@@ -18,5 +18,5 @@ You can also use Podman to run the application. However, additional configuratio
## Hardware
- **OS**: Preferred unix-based operating system (Ubuntu, Debian, MacOS, etc). Windows works too, with [Docker Desktop on Windows](https://docs.docker.com/desktop/install/windows-install/)
- **RAM**: At least 2GB, preferred 4GB.
- **RAM**: At least 4GB, preferred 6GB.
- **CPU**: At least 2 cores, preferred 4 cores.

View File

@@ -106,13 +106,13 @@ const config = {
label: 'API',
},
{
href: 'https://github.com/immich-app/immich',
label: 'GitHub',
to: '/blog',
position: 'right',
label: 'Blog',
},
{
href: 'https://github.com/orgs/immich-app/projects/1',
label: 'Roadmap',
href: 'https://github.com/immich-app/immich',
label: 'GitHub',
position: 'right',
},
],
@@ -153,10 +153,6 @@ const config = {
label: 'GitHub',
href: 'https://github.com/immich-app/immich',
},
{
label: 'Roadmap',
href: 'https://github.com/orgs/immich-app/projects/1',
},
],
},
],

2244
docs/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -17,14 +17,14 @@
"check": "tsc"
},
"dependencies": {
"@docusaurus/core": "2.1.0",
"@docusaurus/preset-classic": "2.1.0",
"@docusaurus/core": "^2.4.1",
"@docusaurus/preset-classic": "^2.4.1",
"@mdx-js/react": "^1.6.22",
"autoprefixer": "^10.4.13",
"clsx": "^1.2.1",
"docusaurus-lunr-search": "^2.3.2",
"docusaurus-preset-openapi": "^0.6.3",
"postcss": "^8.4.20",
"postcss": "^8.4.25",
"prism-react-renderer": "^1.3.5",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@@ -32,10 +32,10 @@
"url": "^0.11.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "2.1.0",
"@docusaurus/module-type-aliases": "^2.4.1",
"@tsconfig/docusaurus": "^1.0.5",
"prettier": "^2.8.8",
"typescript": "^4.7.4"
"typescript": "^5.1.6"
},
"browserslist": {
"production": [

View File

@@ -31,7 +31,20 @@ function HomepageHeader() {
</Link>
</div>
<img src="/img/immich-screenshots.webp" alt="logo" />
<img src="/img/immich-screenshots.png" alt="logo" />
<div className="flex flex-col sm:flex-row place-items-center place-content-center mt-4 gap-1">
<div className="h-24">
<a href="https://play.google.com/store/apps/details?id=app.alextran.immich">
<img className="h-24" alt="Get it on Google Play" src="/img/google-play-badge.png" />
</a>
</div>
<div className="h-24">
<a href="https://apps.apple.com/sg/app/immich/id1613945652">
<img className="h-24 sm:p-3.5 p-3" alt="Download on the App Store" src="/img/ios-app-store-badge.svg" />
</a>
</div>
</div>
</section>
</header>
);
@@ -40,7 +53,7 @@ function HomepageHeader() {
export default function Home(): JSX.Element {
return (
<Layout
title={`Home`}
title="Home"
description="immich Self-hosted photo and video backup solution directly from your mobile phone "
noFooter={true}
>

BIN
docs/static/img/google-play-badge.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
docs/static/img/immich-screenshots.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

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