mirror of
https://github.com/immich-app/immich.git
synced 2025-12-16 17:47:45 -08:00
Compare commits
360 Commits
v1.128.0
...
update-exi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edac67acd2 | ||
|
|
f410b58035 | ||
|
|
8943ec23ba | ||
|
|
04b03f2924 | ||
|
|
cf2c0260a6 | ||
|
|
ae8af84101 | ||
|
|
4794eeca88 | ||
|
|
ac65d46ec6 | ||
|
|
e5ca79dd44 | ||
|
|
49be6d7fd8 | ||
|
|
15c6506aee | ||
|
|
2c31a11e41 | ||
|
|
b6c5a03533 | ||
|
|
75bc32b47b | ||
|
|
fdbe6d649f | ||
|
|
2b131fe935 | ||
|
|
6ae24fbbd4 | ||
|
|
7f116d8e98 | ||
|
|
bd0840c411 | ||
|
|
a5123dec1a | ||
|
|
ffd18c5459 | ||
|
|
8242ff9bab | ||
|
|
8203b6c450 | ||
|
|
b352cf3336 | ||
|
|
96ed9a8c4a | ||
|
|
e7a5b96ed0 | ||
|
|
51c2c60231 | ||
|
|
43d585ce55 | ||
|
|
042da669d1 | ||
|
|
a724f147fe | ||
|
|
1e4b9ae5b7 | ||
|
|
99cddf1fd6 | ||
|
|
30d33f968f | ||
|
|
31ee19181a | ||
|
|
b58a450152 | ||
|
|
b87ba6865b | ||
|
|
565cceb323 | ||
|
|
f096dd0cc0 | ||
|
|
a3c3f9cfcb | ||
|
|
7b6a4be30c | ||
|
|
720189e2c2 | ||
|
|
dfab32c8f2 | ||
|
|
60174d662d | ||
|
|
8b6a765e12 | ||
|
|
2248a38567 | ||
|
|
97e52c5156 | ||
|
|
e8b4ac0522 | ||
|
|
548298b0c7 | ||
|
|
40cff2893c | ||
|
|
b621281351 | ||
|
|
4336afd6bf | ||
|
|
5a456ef277 | ||
|
|
5cb5fcbf62 | ||
|
|
95e3b15776 | ||
|
|
50335dc363 | ||
|
|
6e62c09d84 | ||
|
|
00d3b8d83a | ||
|
|
d911b76c08 | ||
|
|
502854cee1 | ||
|
|
59e5c82569 | ||
|
|
e4b0c00885 | ||
|
|
946507231d | ||
|
|
20ba800a50 | ||
|
|
f434e858ed | ||
|
|
3e03c47fbf | ||
|
|
9aa3850769 | ||
|
|
628dcdeebf | ||
|
|
11bfde2aa8 | ||
|
|
69b1ac47ea | ||
|
|
4f81265694 | ||
|
|
3428a876c7 | ||
|
|
bd822657d3 | ||
|
|
9e7744a9ab | ||
|
|
7729fe80fa | ||
|
|
68e24ad168 | ||
|
|
186c573565 | ||
|
|
5b63b9fc8b | ||
|
|
5c80e8734b | ||
|
|
a5093a9434 | ||
|
|
637ad1fdcb | ||
|
|
6789c2ac19 | ||
|
|
838a8dd9a6 | ||
|
|
d71c5602c3 | ||
|
|
8c50e3e80e | ||
|
|
efcb1129ce | ||
|
|
faabda4446 | ||
|
|
b8b2898c87 | ||
|
|
b25914c2a5 | ||
|
|
d613f15606 | ||
|
|
a831876fdc | ||
|
|
09f4476f97 | ||
|
|
238c151ac3 | ||
|
|
e4f83680d9 | ||
|
|
74f7fd4b53 | ||
|
|
f4dbfd856e | ||
|
|
55a3c30664 | ||
|
|
6fa0cb534a | ||
|
|
9f0dbfc150 | ||
|
|
6419ac74af | ||
|
|
d2bcf5d716 | ||
|
|
c8331f111f | ||
|
|
4b4bcd23f4 | ||
|
|
3fde5a8328 | ||
|
|
cc3ea32cd2 | ||
|
|
431cf281da | ||
|
|
8f786fd7dd | ||
|
|
3e73765375 | ||
|
|
411521b21d | ||
|
|
e163808348 | ||
|
|
411772123f | ||
|
|
84c35e35d6 | ||
|
|
f7d730eb05 | ||
|
|
16e0166d22 | ||
|
|
43f8f473e9 | ||
|
|
cc393b2b7b | ||
|
|
6341962de4 | ||
|
|
c26b28f6a4 | ||
|
|
c72c82c401 | ||
|
|
fecf3809a6 | ||
|
|
619bd72de9 | ||
|
|
fd4a5f71b5 | ||
|
|
2f8725c66f | ||
|
|
9fbd6369b9 | ||
|
|
c547d849d9 | ||
|
|
6ba94ac2f2 | ||
|
|
dfb0626c91 | ||
|
|
392ce7deb2 | ||
|
|
75df8fc10e | ||
|
|
4cf7c55680 | ||
|
|
b8ff93a3c9 | ||
|
|
37eb70c1eb | ||
|
|
aa4d6405f4 | ||
|
|
ae447542a4 | ||
|
|
90f21d9047 | ||
|
|
567a92fe77 | ||
|
|
8d6f5a2da9 | ||
|
|
69662e1ab4 | ||
|
|
42b1efb679 | ||
|
|
b8bc11b0d9 | ||
|
|
91065db3ff | ||
|
|
c14668bdd4 | ||
|
|
9757f70064 | ||
|
|
4a0045db44 | ||
|
|
a651a4bf0e | ||
|
|
8bc80076bb | ||
|
|
89656472ef | ||
|
|
d9c6ec06e5 | ||
|
|
4bfef2460a | ||
|
|
ad151130f9 | ||
|
|
a77608e36b | ||
|
|
9e015c7f97 | ||
|
|
df8ba21b7d | ||
|
|
a285b1898e | ||
|
|
6a8e38042d | ||
|
|
55b52ecbec | ||
|
|
b5d5c40c69 | ||
|
|
b00da18e84 | ||
|
|
3c87341902 | ||
|
|
bcd9248b43 | ||
|
|
dbc279f843 | ||
|
|
21954939cf | ||
|
|
d537f2c2d1 | ||
|
|
1820c0aa0d | ||
|
|
0d805a1f5b | ||
|
|
f5e6042eb1 | ||
|
|
8de71ddaf3 | ||
|
|
7075c5b393 | ||
|
|
9398b0d4b3 | ||
|
|
1a0a9ef36c | ||
|
|
ce456709b5 | ||
|
|
bc90678276 | ||
|
|
217a90bf61 | ||
|
|
62ba8c3e71 | ||
|
|
564724b398 | ||
|
|
cedeba8723 | ||
|
|
1d994333a6 | ||
|
|
db8155f738 | ||
|
|
4d723f4b56 | ||
|
|
898b3e75c2 | ||
|
|
8c2d02c362 | ||
|
|
d7a6e78bf0 | ||
|
|
8723f585e0 | ||
|
|
9f46ba8eb4 | ||
|
|
fe19f9ba84 | ||
|
|
b609f35841 | ||
|
|
9cf3b88f80 | ||
|
|
e96ffd43e7 | ||
|
|
dd263b010c | ||
|
|
6c2985df26 | ||
|
|
2b37caba03 | ||
|
|
6a40aa83b7 | ||
|
|
93907a89d8 | ||
|
|
3ce8608662 | ||
|
|
d0e283f687 | ||
|
|
f8b40188e2 | ||
|
|
9105e696bf | ||
|
|
0a8135dde4 | ||
|
|
0bb95544e5 | ||
|
|
14c3b99c0f | ||
|
|
1e184a70f1 | ||
|
|
9a4495eb5b | ||
|
|
8ad95b368b | ||
|
|
b778a86c99 | ||
|
|
a65ce2ac55 | ||
|
|
f69d7e7bad | ||
|
|
858d1e9d9b | ||
|
|
a1a61f19eb | ||
|
|
996ffed5eb | ||
|
|
2d7a94ce23 | ||
|
|
72a7be26c0 | ||
|
|
77fad86b82 | ||
|
|
52d90a8280 | ||
|
|
d1c8fe5303 | ||
|
|
a75718ce99 | ||
|
|
d72d715f6b | ||
|
|
16fd19994b | ||
|
|
83ed03920e | ||
|
|
9c825e15de | ||
|
|
b8acae2f21 | ||
|
|
c80afea468 | ||
|
|
6caa11d079 | ||
|
|
653fa3f0b1 | ||
|
|
2be8b6c16d | ||
|
|
6bb0aa217c | ||
|
|
04fd83d9da | ||
|
|
ba9e3715f0 | ||
|
|
ac1b2d2fab | ||
|
|
d7e0f0e70e | ||
|
|
decc878267 | ||
|
|
e0a09f2ea0 | ||
|
|
b9ecdf9286 | ||
|
|
4c719cc3bb | ||
|
|
81df812f56 | ||
|
|
f0f0056fe3 | ||
|
|
48dddb78d4 | ||
|
|
5d86e6d2d3 | ||
|
|
75fa305e98 | ||
|
|
8cd5aec4c5 | ||
|
|
cb489a1aa9 | ||
|
|
1382b27349 | ||
|
|
1b35400043 | ||
|
|
a96bba4b26 | ||
|
|
e97df503f2 | ||
|
|
fe959b2f05 | ||
|
|
f794c3e0df | ||
|
|
57272904d6 | ||
|
|
2496bd7514 | ||
|
|
c6ede48e59 | ||
|
|
70a08707d2 | ||
|
|
2f8e89c7ec | ||
|
|
9870ad9687 | ||
|
|
097749d872 | ||
|
|
bdabea4030 | ||
|
|
6da77600e5 | ||
|
|
573d9a7733 | ||
|
|
2aac679185 | ||
|
|
82624b0979 | ||
|
|
17c5094719 | ||
|
|
051431b757 | ||
|
|
6c5f99c47a | ||
|
|
1e127ae3a1 | ||
|
|
fd46d43726 | ||
|
|
5252c013ec | ||
|
|
3f06a494a9 | ||
|
|
086d8a448a | ||
|
|
8ace44fb95 | ||
|
|
ce74f765b1 | ||
|
|
b0bf4e4fff | ||
|
|
2d106755f6 | ||
|
|
f82786a297 | ||
|
|
c12986d38c | ||
|
|
19c40e3be9 | ||
|
|
9959755dda | ||
|
|
fdf2331c82 | ||
|
|
e03d7f888e | ||
|
|
2eeed6524f | ||
|
|
d45fa491ce | ||
|
|
5c82c485d7 | ||
|
|
feb65bf5a7 | ||
|
|
2cdbb0a37c | ||
|
|
fe931faf17 | ||
|
|
4ebc25c754 | ||
|
|
deb399ea15 | ||
|
|
d01b7a0d67 | ||
|
|
3af26ee94a | ||
|
|
bc61497461 | ||
|
|
1ed1a0a1fc | ||
|
|
2875303b4c | ||
|
|
d84009648e | ||
|
|
fc2df05190 | ||
|
|
69b5365965 | ||
|
|
c110c9b00e | ||
|
|
b241a80339 | ||
|
|
31dd15ce8a | ||
|
|
6108587c8b | ||
|
|
3e50f668d9 | ||
|
|
9b82617e22 | ||
|
|
76cb32d8d0 | ||
|
|
e8f3348833 | ||
|
|
9922c8de59 | ||
|
|
3f4bbab4eb | ||
|
|
2da9e3152b | ||
|
|
56b85f7479 | ||
|
|
8b43066632 | ||
|
|
20acdcd884 | ||
|
|
22d348beca | ||
|
|
3b0af1c8a9 | ||
|
|
61c8237a4d | ||
|
|
d740f0283a | ||
|
|
4ada28ac99 | ||
|
|
63c01b78e2 | ||
|
|
1423cfd53c | ||
|
|
867eec86f5 | ||
|
|
86e8effd8e | ||
|
|
49d393216a | ||
|
|
75c9f63757 | ||
|
|
63984890df | ||
|
|
1356468c38 | ||
|
|
c23c53bf6f | ||
|
|
0dcfc43461 | ||
|
|
d1fd0076cc | ||
|
|
ff19502035 | ||
|
|
6ef069b537 | ||
|
|
a03e999bde | ||
|
|
ad1ba4be5f | ||
|
|
f89e74181b | ||
|
|
e2c34f17ba | ||
|
|
23b1256592 | ||
|
|
7bbc1d9f68 | ||
|
|
8b24c31d20 | ||
|
|
7f61ac6983 | ||
|
|
4db8f0c666 | ||
|
|
3d6a6f77a8 | ||
|
|
5698f446f7 | ||
|
|
eb74fafb00 | ||
|
|
24da25dbbf | ||
|
|
9b842d4cca | ||
|
|
a99bd94717 | ||
|
|
4b568dcbb3 | ||
|
|
12ab56c885 | ||
|
|
eed6465b41 | ||
|
|
5f6c16080b | ||
|
|
a2aab1f373 | ||
|
|
8e076ecfe4 | ||
|
|
fe702ba6d7 | ||
|
|
869839f642 | ||
|
|
8885e3105e | ||
|
|
6e51c4ec71 | ||
|
|
6bf2e8dbcb | ||
|
|
366f23774a | ||
|
|
fd5e931617 | ||
|
|
d8d87bb565 | ||
|
|
6cc1978b2d | ||
|
|
506d2d0f81 | ||
|
|
f13d13b2ea | ||
|
|
2510684bf7 | ||
|
|
c8eef5ad4d | ||
|
|
0cb3dc6211 | ||
|
|
f11080cc2d | ||
|
|
efcf773ea0 |
@@ -1,10 +1,10 @@
|
|||||||
ARG BASEIMAGE=mcr.microsoft.com/devcontainers/typescript-node:22@sha256:9791f4aa527774bc370c6bd2f6705ce5a686f1e6f204badd8dfaacce28c631ae
|
ARG BASEIMAGE=mcr.microsoft.com/devcontainers/typescript-node:22@sha256:b0b88ef6a5abf21194343d2c5b2829dddd9be1142f65f6a5e4390a51d5a70dd8
|
||||||
FROM ${BASEIMAGE}
|
FROM ${BASEIMAGE}
|
||||||
|
|
||||||
# Flutter SDK
|
# Flutter SDK
|
||||||
# https://flutter.dev/docs/development/tools/sdk/releases?tab=linux
|
# https://flutter.dev/docs/development/tools/sdk/releases?tab=linux
|
||||||
ENV FLUTTER_CHANNEL="stable"
|
ENV FLUTTER_CHANNEL="stable"
|
||||||
ENV FLUTTER_VERSION="3.24.5"
|
ENV FLUTTER_VERSION="3.29.1"
|
||||||
ENV FLUTTER_HOME=/flutter
|
ENV FLUTTER_HOME=/flutter
|
||||||
ENV PATH=${PATH}:${FLUTTER_HOME}/bin
|
ENV PATH=${PATH}:${FLUTTER_HOME}/bin
|
||||||
|
|
||||||
|
|||||||
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -6,6 +6,9 @@ mobile/openapi/**/*.dart linguist-generated=true
|
|||||||
mobile/lib/**/*.g.dart -diff -merge
|
mobile/lib/**/*.g.dart -diff -merge
|
||||||
mobile/lib/**/*.g.dart linguist-generated=true
|
mobile/lib/**/*.g.dart linguist-generated=true
|
||||||
|
|
||||||
|
mobile/lib/**/*.drift.dart -diff -merge
|
||||||
|
mobile/lib/**/*.drift.dart linguist-generated=true
|
||||||
|
|
||||||
open-api/typescript-sdk/fetch-client.ts -diff -merge
|
open-api/typescript-sdk/fetch-client.ts -diff -merge
|
||||||
open-api/typescript-sdk/fetch-client.ts linguist-generated=true
|
open-api/typescript-sdk/fetch-client.ts linguist-generated=true
|
||||||
|
|
||||||
|
|||||||
1
.github/.nvmrc
vendored
Normal file
1
.github/.nvmrc
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
22.14.0
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
title: "[Feature] feature-name-goes-here"
|
title: '[Feature] feature-name-goes-here'
|
||||||
labels: ["feature"]
|
labels: ['feature']
|
||||||
|
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
@@ -11,9 +11,9 @@ body:
|
|||||||
|
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: I have searched the existing feature requests to make sure this is not a duplicate request.
|
label: I have searched the existing feature requests, both open and closed, to make sure this is not a duplicate request.
|
||||||
options:
|
options:
|
||||||
- label: "Yes"
|
- label: 'Yes'
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|||||||
14
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
14
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -1,6 +1,13 @@
|
|||||||
name: Report an issue with Immich
|
name: Report an issue with Immich
|
||||||
description: Report an issue with Immich
|
description: Report an issue with Immich
|
||||||
body:
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: I have searched the existing issues, both open and closed, to make sure this is not a duplicate report.
|
||||||
|
options:
|
||||||
|
- label: 'Yes'
|
||||||
|
required: true
|
||||||
|
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
@@ -77,7 +84,7 @@ body:
|
|||||||
id: repro
|
id: repro
|
||||||
attributes:
|
attributes:
|
||||||
label: Reproduction steps
|
label: Reproduction steps
|
||||||
description: "How do you trigger this bug? Please walk us through it step by step."
|
description: 'How do you trigger this bug? Please walk us through it step by step.'
|
||||||
value: |
|
value: |
|
||||||
1.
|
1.
|
||||||
2.
|
2.
|
||||||
@@ -90,12 +97,13 @@ body:
|
|||||||
id: logs
|
id: logs
|
||||||
attributes:
|
attributes:
|
||||||
label: Relevant log output
|
label: Relevant log output
|
||||||
description: Please copy and paste any relevant logs below. (code formatting is
|
description:
|
||||||
|
Please copy and paste any relevant logs below. (code formatting is
|
||||||
enabled, no need for backticks)
|
enabled, no need for backticks)
|
||||||
render: shell
|
render: shell
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Additional information
|
label: Additional information
|
||||||
|
|||||||
28
.github/package-lock.json
generated
vendored
Normal file
28
.github/package-lock.json
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": ".github",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "^3.5.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prettier": {
|
||||||
|
"version": "3.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
|
||||||
|
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin/prettier.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
.github/package.json
vendored
Normal file
9
.github/package.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"format": "prettier --check .",
|
||||||
|
"format:fix": "prettier --write ."
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "^3.5.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
4
.github/pull_request_template.md
vendored
4
.github/pull_request_template.md
vendored
@@ -32,5 +32,5 @@ The `/api/something` endpoint is now `/api/something-else`
|
|||||||
- [ ] I have confirmed that any new dependencies are strictly necessary.
|
- [ ] I have confirmed that any new dependencies are strictly necessary.
|
||||||
- [ ] I have written tests for new code (if applicable)
|
- [ ] I have written tests for new code (if applicable)
|
||||||
- [ ] I have followed naming conventions/patterns in the surrounding code
|
- [ ] I have followed naming conventions/patterns in the surrounding code
|
||||||
- [ ] All code in `src/services` uses repositories implementations for database calls, filesystem operations, etc.
|
- [ ] All code in `src/services/` uses repositories implementations for database calls, filesystem operations, etc.
|
||||||
- [ ] All code in `src/repositories/` is pretty basic/simple and does not have any immich specific logic (that belongs in `src/services`)
|
- [ ] All code in `src/repositories/` is pretty basic/simple and does not have any immich specific logic (that belongs in `src/services/`)
|
||||||
|
|||||||
66
.github/release.yml
vendored
66
.github/release.yml
vendored
@@ -1,33 +1,33 @@
|
|||||||
changelog:
|
changelog:
|
||||||
categories:
|
categories:
|
||||||
- title: 🚨 Breaking Changes
|
- title: 🚨 Breaking Changes
|
||||||
labels:
|
labels:
|
||||||
- changelog:breaking-change
|
- changelog:breaking-change
|
||||||
|
|
||||||
- title: 🫥 Deprecated Changes
|
- title: 🫥 Deprecated Changes
|
||||||
labels:
|
labels:
|
||||||
- changelog:deprecated
|
- changelog:deprecated
|
||||||
|
|
||||||
- title: 🔒 Security
|
- title: 🔒 Security
|
||||||
labels:
|
labels:
|
||||||
- changelog:security
|
- changelog:security
|
||||||
|
|
||||||
- title: 🚀 Features
|
- title: 🚀 Features
|
||||||
labels:
|
labels:
|
||||||
- changelog:feature
|
- changelog:feature
|
||||||
|
|
||||||
- title: 🌟 Enhancements
|
- title: 🌟 Enhancements
|
||||||
labels:
|
labels:
|
||||||
- changelog:enhancement
|
- changelog:enhancement
|
||||||
|
|
||||||
- title: 🐛 Bug fixes
|
- title: 🐛 Bug fixes
|
||||||
labels:
|
labels:
|
||||||
- changelog:bugfix
|
- changelog:bugfix
|
||||||
|
|
||||||
- title: 📚 Documentation
|
- title: 📚 Documentation
|
||||||
labels:
|
labels:
|
||||||
- changelog:documentation
|
- changelog:documentation
|
||||||
|
|
||||||
- title: 🌐 Translations
|
- title: 🌐 Translations
|
||||||
labels:
|
labels:
|
||||||
- changelog:translation
|
- changelog:translation
|
||||||
|
|||||||
12
.github/workflows/build-mobile.yml
vendored
12
.github/workflows/build-mobile.yml
vendored
@@ -22,9 +22,9 @@ jobs:
|
|||||||
should_run: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
should_run: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
mobile:
|
mobile:
|
||||||
@@ -51,18 +51,18 @@ jobs:
|
|||||||
ref="${input_ref:-$github_ref}"
|
ref="${input_ref:-$github_ref}"
|
||||||
echo "ref=$ref" >> $GITHUB_OUTPUT
|
echo "ref=$ref" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
with:
|
||||||
ref: ${{ steps.get-ref.outputs.ref }}
|
ref: ${{ steps.get-ref.outputs.ref }}
|
||||||
|
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: 'zulu'
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
cache: 'gradle'
|
cache: 'gradle'
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
flutter-version-file: ./mobile/pubspec.yaml
|
||||||
@@ -89,7 +89,7 @@ jobs:
|
|||||||
flutter build apk --release --split-per-abi --target-platform android-arm,android-arm64,android-x64
|
flutter build apk --release --split-per-abi --target-platform android-arm,android-arm64,android-x64
|
||||||
|
|
||||||
- name: Publish Android Artifact
|
- name: Publish Android Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
with:
|
with:
|
||||||
name: release-apk-signed
|
name: release-apk-signed
|
||||||
path: mobile/build/app/outputs/flutter-apk/*.apk
|
path: mobile/build/app/outputs/flutter-apk/*.apk
|
||||||
|
|||||||
2
.github/workflows/cache-cleanup.yml
vendored
2
.github/workflows/cache-cleanup.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Cleanup
|
- name: Cleanup
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
17
.github/workflows/cli.yml
vendored
17
.github/workflows/cli.yml
vendored
@@ -6,7 +6,6 @@ on:
|
|||||||
- 'cli/**'
|
- 'cli/**'
|
||||||
- '.github/workflows/cli.yml'
|
- '.github/workflows/cli.yml'
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
|
||||||
paths:
|
paths:
|
||||||
- 'cli/**'
|
- 'cli/**'
|
||||||
- '.github/workflows/cli.yml'
|
- '.github/workflows/cli.yml'
|
||||||
@@ -29,9 +28,9 @@ jobs:
|
|||||||
working-directory: ./cli
|
working-directory: ./cli
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
# Setup .npmrc file to publish to npm
|
# Setup .npmrc file to publish to npm
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
@@ -53,16 +52,16 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3.4.0
|
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.9.0
|
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
@@ -77,7 +76,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Generate docker image tags
|
- name: Generate docker image tags
|
||||||
id: metadata
|
id: metadata
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
||||||
with:
|
with:
|
||||||
flavor: |
|
flavor: |
|
||||||
latest=false
|
latest=false
|
||||||
@@ -88,7 +87,7 @@ jobs:
|
|||||||
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
|
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
|
||||||
|
|
||||||
- name: Build and push image
|
- name: Build and push image
|
||||||
uses: docker/build-push-action@v6.13.0
|
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
||||||
with:
|
with:
|
||||||
file: cli/Dockerfile
|
file: cli/Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
|
|||||||
63
.github/workflows/codeql-analysis.yml
vendored
63
.github/workflows/codeql-analysis.yml
vendored
@@ -9,14 +9,14 @@
|
|||||||
# the `language` matrix defined below to confirm you have the correct set of
|
# the `language` matrix defined below to confirm you have the correct set of
|
||||||
# supported CodeQL languages.
|
# supported CodeQL languages.
|
||||||
#
|
#
|
||||||
name: "CodeQL"
|
name: 'CodeQL'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main" ]
|
branches: ['main']
|
||||||
pull_request:
|
pull_request:
|
||||||
# The branches below must be a subset of the branches above
|
# The branches below must be a subset of the branches above
|
||||||
branches: [ "main" ]
|
branches: ['main']
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '20 13 * * 1'
|
- cron: '20 13 * * 1'
|
||||||
|
|
||||||
@@ -36,43 +36,42 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
language: [ 'javascript', 'python' ]
|
language: ['javascript', 'python']
|
||||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v3
|
uses: github/codeql-action/init@45775bd8235c68ba998cffa5171334d58593da47 # v3
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
# By default, queries listed here will override any specified in a config file.
|
# By default, queries listed here will override any specified in a config file.
|
||||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
|
|
||||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||||
# queries: security-extended,security-and-quality
|
# queries: security-extended,security-and-quality
|
||||||
|
|
||||||
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
|
- name: Autobuild
|
||||||
|
uses: github/codeql-action/autobuild@45775bd8235c68ba998cffa5171334d58593da47 # v3
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
- name: Autobuild
|
|
||||||
uses: github/codeql-action/autobuild@v3
|
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||||
|
|
||||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
# - run: |
|
||||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
# echo "Run, Build Application using script"
|
||||||
|
# ./location_of_script_within_repo/buildscript.sh
|
||||||
|
|
||||||
# - run: |
|
- name: Perform CodeQL Analysis
|
||||||
# echo "Run, Build Application using script"
|
uses: github/codeql-action/analyze@45775bd8235c68ba998cffa5171334d58593da47 # v3
|
||||||
# ./location_of_script_within_repo/buildscript.sh
|
with:
|
||||||
|
category: '/language:${{matrix.language}}'
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v3
|
|
||||||
with:
|
|
||||||
category: "/language:${{matrix.language}}"
|
|
||||||
|
|||||||
121
.github/workflows/docker.yml
vendored
121
.github/workflows/docker.yml
vendored
@@ -5,7 +5,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
@@ -24,9 +23,9 @@ jobs:
|
|||||||
should_run_ml: ${{ steps.found_paths.outputs.machine-learning == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
should_run_ml: ${{ steps.found_paths.outputs.machine-learning == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
server:
|
server:
|
||||||
@@ -50,23 +49,23 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
suffix: ["", "-cuda", "-openvino", "-armnn"]
|
suffix: ['', '-cuda', '-rocm', '-openvino', '-armnn', '-rknn']
|
||||||
steps:
|
steps:
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Re-tag image
|
- name: Re-tag image
|
||||||
run: |
|
run: |
|
||||||
REGISTRY_NAME="ghcr.io"
|
REGISTRY_NAME="ghcr.io"
|
||||||
REPOSITORY=${{ github.repository_owner }}/immich-machine-learning
|
REPOSITORY=${{ github.repository_owner }}/immich-machine-learning
|
||||||
TAG_OLD=main${{ matrix.suffix }}
|
TAG_OLD=main${{ matrix.suffix }}
|
||||||
TAG_PR=${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }}
|
TAG_PR=${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }}
|
||||||
TAG_COMMIT=commit-${{ github.event_name != 'pull_request' && github.sha || github.event.pull_request.head.sha }}${{ matrix.suffix }}
|
TAG_COMMIT=commit-${{ github.event_name != 'pull_request' && github.sha || github.event.pull_request.head.sha }}${{ matrix.suffix }}
|
||||||
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_PR $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_PR $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
||||||
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_COMMIT $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_COMMIT $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
||||||
|
|
||||||
retag_server:
|
retag_server:
|
||||||
name: Re-Tag Server
|
name: Re-Tag Server
|
||||||
@@ -75,10 +74,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
suffix: [""]
|
suffix: ['']
|
||||||
steps:
|
steps:
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
@@ -121,6 +120,11 @@ jobs:
|
|||||||
device: cuda
|
device: cuda
|
||||||
suffix: -cuda
|
suffix: -cuda
|
||||||
|
|
||||||
|
- platform: linux/amd64
|
||||||
|
runner: mich
|
||||||
|
device: rocm
|
||||||
|
suffix: -rocm
|
||||||
|
|
||||||
- platform: linux/amd64
|
- platform: linux/amd64
|
||||||
runner: ubuntu-latest
|
runner: ubuntu-latest
|
||||||
device: openvino
|
device: openvino
|
||||||
@@ -131,6 +135,11 @@ jobs:
|
|||||||
device: armnn
|
device: armnn
|
||||||
suffix: -armnn
|
suffix: -armnn
|
||||||
|
|
||||||
|
- platform: linux/arm64
|
||||||
|
runner: ubuntu-24.04-arm
|
||||||
|
device: rknn
|
||||||
|
suffix: -rknn
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Prepare
|
- name: Prepare
|
||||||
run: |
|
run: |
|
||||||
@@ -138,13 +147,13 @@ jobs:
|
|||||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.9.0
|
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
@@ -171,7 +180,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build and push image
|
- name: Build and push image
|
||||||
id: build
|
id: build
|
||||||
uses: docker/build-push-action@v6.13.0
|
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
||||||
with:
|
with:
|
||||||
context: ${{ env.context }}
|
context: ${{ env.context }}
|
||||||
file: ${{ env.file }}
|
file: ${{ env.file }}
|
||||||
@@ -196,7 +205,7 @@ jobs:
|
|||||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||||
|
|
||||||
- name: Upload digest
|
- name: Upload digest
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
with:
|
with:
|
||||||
name: ml-digests-${{ matrix.device }}-${{ env.PLATFORM_PAIR }}
|
name: ml-digests-${{ matrix.device }}-${{ env.PLATFORM_PAIR }}
|
||||||
path: ${{ runner.temp }}/digests/*
|
path: ${{ runner.temp }}/digests/*
|
||||||
@@ -216,15 +225,19 @@ jobs:
|
|||||||
- device: cpu
|
- device: cpu
|
||||||
- device: cuda
|
- device: cuda
|
||||||
suffix: -cuda
|
suffix: -cuda
|
||||||
|
- device: rocm
|
||||||
|
suffix: -rocm
|
||||||
- device: openvino
|
- device: openvino
|
||||||
suffix: -openvino
|
suffix: -openvino
|
||||||
- device: armnn
|
- device: armnn
|
||||||
suffix: -armnn
|
suffix: -armnn
|
||||||
|
- device: rknn
|
||||||
|
suffix: -rknn
|
||||||
needs:
|
needs:
|
||||||
- build_and_push_ml
|
- build_and_push_ml
|
||||||
steps:
|
steps:
|
||||||
- name: Download digests
|
- name: Download digests
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
||||||
with:
|
with:
|
||||||
path: ${{ runner.temp }}/digests
|
path: ${{ runner.temp }}/digests
|
||||||
pattern: ml-digests-${{ matrix.device }}-*
|
pattern: ml-digests-${{ matrix.device }}-*
|
||||||
@@ -232,43 +245,44 @@ jobs:
|
|||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
if: ${{ github.event_name == 'release' }}
|
if: ${{ github.event_name == 'release' }}
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Login to GHCR
|
- name: Login to GHCR
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
||||||
|
|
||||||
- name: Generate docker image tags
|
- name: Generate docker image tags
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
||||||
env:
|
env:
|
||||||
DOCKER_METADATA_PR_HEAD_SHA: "true"
|
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
||||||
with:
|
with:
|
||||||
flavor: |
|
flavor: |
|
||||||
# Disable latest tag
|
# Disable latest tag
|
||||||
latest=false
|
latest=false
|
||||||
|
suffix=${{ matrix.suffix }}
|
||||||
images: |
|
images: |
|
||||||
name=${{ env.GHCR_REPO }}
|
name=${{ env.GHCR_REPO }}
|
||||||
name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }}
|
name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }}
|
||||||
tags: |
|
tags: |
|
||||||
# Tag with branch name
|
# Tag with branch name
|
||||||
type=ref,event=branch,suffix=${{ matrix.suffix }}
|
type=ref,event=branch
|
||||||
# Tag with pr-number
|
# Tag with pr-number
|
||||||
type=ref,event=pr,suffix=${{ matrix.suffix }}
|
type=ref,event=pr
|
||||||
# Tag with long commit sha hash
|
# Tag with long commit sha hash
|
||||||
type=sha,format=long,prefix=commit-,suffix=${{ matrix.suffix }}
|
type=sha,format=long,prefix=commit-
|
||||||
# Tag with git tag on release
|
# Tag with git tag on release
|
||||||
type=ref,event=tag,suffix=${{ matrix.suffix }}
|
type=ref,event=tag
|
||||||
type=raw,value=release,enable=${{ github.event_name == 'release' }},suffix=${{ matrix.suffix }}
|
type=raw,value=release,enable=${{ github.event_name == 'release' }}
|
||||||
|
|
||||||
- name: Create manifest list and push
|
- name: Create manifest list and push
|
||||||
working-directory: ${{ runner.temp }}/digests
|
working-directory: ${{ runner.temp }}/digests
|
||||||
@@ -301,13 +315,13 @@ jobs:
|
|||||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
@@ -334,7 +348,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build and push image
|
- name: Build and push image
|
||||||
id: build
|
id: build
|
||||||
uses: docker/build-push-action@v6.13.0
|
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
||||||
with:
|
with:
|
||||||
context: ${{ env.context }}
|
context: ${{ env.context }}
|
||||||
file: ${{ env.file }}
|
file: ${{ env.file }}
|
||||||
@@ -359,7 +373,7 @@ jobs:
|
|||||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||||
|
|
||||||
- name: Upload digest
|
- name: Upload digest
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
with:
|
with:
|
||||||
name: server-digests-${{ env.PLATFORM_PAIR }}
|
name: server-digests-${{ env.PLATFORM_PAIR }}
|
||||||
path: ${{ runner.temp }}/digests/*
|
path: ${{ runner.temp }}/digests/*
|
||||||
@@ -377,7 +391,7 @@ jobs:
|
|||||||
- build_and_push_server
|
- build_and_push_server
|
||||||
steps:
|
steps:
|
||||||
- name: Download digests
|
- name: Download digests
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
||||||
with:
|
with:
|
||||||
path: ${{ runner.temp }}/digests
|
path: ${{ runner.temp }}/digests
|
||||||
pattern: server-digests-*
|
pattern: server-digests-*
|
||||||
@@ -385,43 +399,44 @@ jobs:
|
|||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
if: ${{ github.event_name == 'release' }}
|
if: ${{ github.event_name == 'release' }}
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Login to GHCR
|
- name: Login to GHCR
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
||||||
|
|
||||||
- name: Generate docker image tags
|
- name: Generate docker image tags
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
||||||
env:
|
env:
|
||||||
DOCKER_METADATA_PR_HEAD_SHA: "true"
|
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
||||||
with:
|
with:
|
||||||
flavor: |
|
flavor: |
|
||||||
# Disable latest tag
|
# Disable latest tag
|
||||||
latest=false
|
latest=false
|
||||||
|
suffix=${{ matrix.suffix }}
|
||||||
images: |
|
images: |
|
||||||
name=${{ env.GHCR_REPO }}
|
name=${{ env.GHCR_REPO }}
|
||||||
name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }}
|
name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }}
|
||||||
tags: |
|
tags: |
|
||||||
# Tag with branch name
|
# Tag with branch name
|
||||||
type=ref,event=branch,suffix=${{ matrix.suffix }}
|
type=ref,event=branch
|
||||||
# Tag with pr-number
|
# Tag with pr-number
|
||||||
type=ref,event=pr,suffix=${{ matrix.suffix }}
|
type=ref,event=pr
|
||||||
# Tag with long commit sha hash
|
# Tag with long commit sha hash
|
||||||
type=sha,format=long,prefix=commit-,suffix=${{ matrix.suffix }}
|
type=sha,format=long,prefix=commit-
|
||||||
# Tag with git tag on release
|
# Tag with git tag on release
|
||||||
type=ref,event=tag,suffix=${{ matrix.suffix }}
|
type=ref,event=tag
|
||||||
type=raw,value=release,enable=${{ github.event_name == 'release' }},suffix=${{ matrix.suffix }}
|
type=raw,value=release,enable=${{ github.event_name == 'release' }}
|
||||||
|
|
||||||
- name: Create manifest list and push
|
- name: Create manifest list and push
|
||||||
working-directory: ${{ runner.temp }}/digests
|
working-directory: ${{ runner.temp }}/digests
|
||||||
|
|||||||
11
.github/workflows/docs-build.yml
vendored
11
.github/workflows/docs-build.yml
vendored
@@ -3,7 +3,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
@@ -18,9 +17,9 @@ jobs:
|
|||||||
should_run: ${{ steps.found_paths.outputs.docs == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
should_run: ${{ steps.found_paths.outputs.docs == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
docs:
|
docs:
|
||||||
@@ -42,10 +41,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './docs/.nvmrc'
|
node-version-file: './docs/.nvmrc'
|
||||||
|
|
||||||
@@ -59,7 +58,7 @@ jobs:
|
|||||||
run: npm run build
|
run: npm run build
|
||||||
|
|
||||||
- name: Upload build output
|
- name: Upload build output
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
with:
|
with:
|
||||||
name: docs-build-output
|
name: docs-build-output
|
||||||
path: docs/build/
|
path: docs/build/
|
||||||
|
|||||||
42
.github/workflows/docs-deploy.yml
vendored
42
.github/workflows/docs-deploy.yml
vendored
@@ -1,7 +1,7 @@
|
|||||||
name: Docs deploy
|
name: Docs deploy
|
||||||
on:
|
on:
|
||||||
workflow_run:
|
workflow_run:
|
||||||
workflows: ["Docs build"]
|
workflows: ['Docs build']
|
||||||
types:
|
types:
|
||||||
- completed
|
- completed
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ jobs:
|
|||||||
run: echo 'The triggering workflow did not succeed' && exit 1
|
run: echo 'The triggering workflow did not succeed' && exit 1
|
||||||
- name: Get artifact
|
- name: Get artifact
|
||||||
id: get-artifact
|
id: get-artifact
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||||
@@ -35,7 +35,7 @@ jobs:
|
|||||||
return { found: true, id: matchArtifact.id };
|
return { found: true, id: matchArtifact.id };
|
||||||
- name: Determine deploy parameters
|
- name: Determine deploy parameters
|
||||||
id: parameters
|
id: parameters
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const eventType = context.payload.workflow_run.event;
|
const eventType = context.payload.workflow_run.event;
|
||||||
@@ -98,11 +98,11 @@ jobs:
|
|||||||
if: ${{ fromJson(needs.checks.outputs.artifact).found && fromJson(needs.checks.outputs.parameters).shouldDeploy }}
|
if: ${{ fromJson(needs.checks.outputs.artifact).found && fromJson(needs.checks.outputs.parameters).shouldDeploy }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Load parameters
|
- name: Load parameters
|
||||||
id: parameters
|
id: parameters
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const json = `${{ needs.checks.outputs.parameters }}`;
|
const json = `${{ needs.checks.outputs.parameters }}`;
|
||||||
@@ -115,7 +115,7 @@ jobs:
|
|||||||
echo "Starting docs deployment for ${{ steps.parameters.outputs.event }} ${{ steps.parameters.outputs.name }}"
|
echo "Starting docs deployment for ${{ steps.parameters.outputs.event }} ${{ steps.parameters.outputs.name }}"
|
||||||
|
|
||||||
- name: Download artifact
|
- name: Download artifact
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
let artifact = ${{ needs.checks.outputs.artifact }};
|
let artifact = ${{ needs.checks.outputs.artifact }};
|
||||||
@@ -138,12 +138,12 @@ jobs:
|
|||||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
|
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
|
||||||
uses: gruntwork-io/terragrunt-action@v2
|
uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2
|
||||||
with:
|
with:
|
||||||
tg_version: "0.58.12"
|
tg_version: '0.58.12'
|
||||||
tofu_version: "1.7.1"
|
tofu_version: '1.7.1'
|
||||||
tg_dir: "deployment/modules/cloudflare/docs"
|
tg_dir: 'deployment/modules/cloudflare/docs'
|
||||||
tg_command: "apply"
|
tg_command: 'apply'
|
||||||
|
|
||||||
- name: Deploy Docs Subdomain Output
|
- name: Deploy Docs Subdomain Output
|
||||||
id: docs-output
|
id: docs-output
|
||||||
@@ -153,12 +153,12 @@ jobs:
|
|||||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
|
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
|
||||||
uses: gruntwork-io/terragrunt-action@v2
|
uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2
|
||||||
with:
|
with:
|
||||||
tg_version: "0.58.12"
|
tg_version: '0.58.12'
|
||||||
tofu_version: "1.7.1"
|
tofu_version: '1.7.1'
|
||||||
tg_dir: "deployment/modules/cloudflare/docs"
|
tg_dir: 'deployment/modules/cloudflare/docs'
|
||||||
tg_command: "output -json"
|
tg_command: 'output -json'
|
||||||
|
|
||||||
- name: Output Cleaning
|
- name: Output Cleaning
|
||||||
id: clean
|
id: clean
|
||||||
@@ -167,13 +167,13 @@ jobs:
|
|||||||
echo "output=$TG_OUT" >> $GITHUB_OUTPUT
|
echo "output=$TG_OUT" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Publish to Cloudflare Pages
|
- name: Publish to Cloudflare Pages
|
||||||
uses: cloudflare/pages-action@v1
|
uses: cloudflare/pages-action@f0a1cd58cd66095dee69bfa18fa5efd1dde93bca # v1
|
||||||
with:
|
with:
|
||||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN_PAGES_UPLOAD }}
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN_PAGES_UPLOAD }}
|
||||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
projectName: ${{ fromJson(steps.clean.outputs.output).pages_project_name.value }}
|
projectName: ${{ fromJson(steps.clean.outputs.output).pages_project_name.value }}
|
||||||
workingDirectory: "docs"
|
workingDirectory: 'docs'
|
||||||
directory: "build"
|
directory: 'build'
|
||||||
branch: ${{ steps.parameters.outputs.name }}
|
branch: ${{ steps.parameters.outputs.name }}
|
||||||
wranglerVersion: '3'
|
wranglerVersion: '3'
|
||||||
|
|
||||||
@@ -184,7 +184,7 @@ jobs:
|
|||||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
|
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
|
||||||
uses: gruntwork-io/terragrunt-action@v2
|
uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2
|
||||||
with:
|
with:
|
||||||
tg_version: '0.58.12'
|
tg_version: '0.58.12'
|
||||||
tofu_version: '1.7.1'
|
tofu_version: '1.7.1'
|
||||||
@@ -192,7 +192,7 @@ jobs:
|
|||||||
tg_command: 'apply'
|
tg_command: 'apply'
|
||||||
|
|
||||||
- name: Comment
|
- name: Comment
|
||||||
uses: actions-cool/maintain-one-comment@v3
|
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3
|
||||||
if: ${{ steps.parameters.outputs.event == 'pr' }}
|
if: ${{ steps.parameters.outputs.event == 'pr' }}
|
||||||
with:
|
with:
|
||||||
number: ${{ fromJson(needs.checks.outputs.parameters).pr_number }}
|
number: ${{ fromJson(needs.checks.outputs.parameters).pr_number }}
|
||||||
|
|||||||
18
.github/workflows/docs-destroy.yml
vendored
18
.github/workflows/docs-destroy.yml
vendored
@@ -9,24 +9,24 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Destroy Docs Subdomain
|
- name: Destroy Docs Subdomain
|
||||||
env:
|
env:
|
||||||
TF_VAR_prefix_name: "pr-${{ github.event.number }}"
|
TF_VAR_prefix_name: 'pr-${{ github.event.number }}'
|
||||||
TF_VAR_prefix_event_type: "pr"
|
TF_VAR_prefix_event_type: 'pr'
|
||||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
|
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
|
||||||
uses: gruntwork-io/terragrunt-action@v2
|
uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2
|
||||||
with:
|
with:
|
||||||
tg_version: "0.58.12"
|
tg_version: '0.58.12'
|
||||||
tofu_version: "1.7.1"
|
tofu_version: '1.7.1'
|
||||||
tg_dir: "deployment/modules/cloudflare/docs"
|
tg_dir: 'deployment/modules/cloudflare/docs'
|
||||||
tg_command: "destroy -refresh=false"
|
tg_command: 'destroy -refresh=false'
|
||||||
|
|
||||||
- name: Comment
|
- name: Comment
|
||||||
uses: actions-cool/maintain-one-comment@v3
|
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3
|
||||||
with:
|
with:
|
||||||
number: ${{ github.event.number }}
|
number: ${{ github.event.number }}
|
||||||
delete: true
|
delete: true
|
||||||
|
|||||||
11
.github/workflows/fix-format.yml
vendored
11
.github/workflows/fix-format.yml
vendored
@@ -13,19 +13,19 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@v1
|
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: 'Checkout'
|
- name: 'Checkout'
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.ref }}
|
ref: ${{ github.event.pull_request.head.ref }}
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
|
||||||
@@ -33,13 +33,13 @@ jobs:
|
|||||||
run: make install-all && make format-all
|
run: make install-all && make format-all
|
||||||
|
|
||||||
- name: Commit and push
|
- name: Commit and push
|
||||||
uses: EndBug/add-and-commit@v9
|
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9
|
||||||
with:
|
with:
|
||||||
default_author: github_actions
|
default_author: github_actions
|
||||||
message: 'chore: fix formatting'
|
message: 'chore: fix formatting'
|
||||||
|
|
||||||
- name: Remove label
|
- name: Remove label
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
@@ -49,4 +49,3 @@ jobs:
|
|||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
name: 'fix:formatting'
|
name: 'fix:formatting'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
6
.github/workflows/pr-label-validation.yml
vendored
6
.github/workflows/pr-label-validation.yml
vendored
@@ -12,11 +12,11 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Require PR to have a changelog label
|
- name: Require PR to have a changelog label
|
||||||
uses: mheap/github-action-required-labels@v5
|
uses: mheap/github-action-required-labels@388fd6af37b34cdfe5a23b37060e763217e58b03 # v5
|
||||||
with:
|
with:
|
||||||
mode: exactly
|
mode: exactly
|
||||||
count: 1
|
count: 1
|
||||||
use_regex: true
|
use_regex: true
|
||||||
labels: "changelog:.*"
|
labels: 'changelog:.*'
|
||||||
add_comment: true
|
add_comment: true
|
||||||
message: "Label error. Requires {{errorString}} {{count}} of: {{ provided }}. Found: {{ applied }}. A maintainer will add the required label."
|
message: 'Label error. Requires {{errorString}} {{count}} of: {{ provided }}. Found: {{ applied }}. A maintainer will add the required label.'
|
||||||
|
|||||||
6
.github/workflows/pr-labeler.yml
vendored
6
.github/workflows/pr-labeler.yml
vendored
@@ -1,6 +1,6 @@
|
|||||||
name: "Pull Request Labeler"
|
name: 'Pull Request Labeler'
|
||||||
on:
|
on:
|
||||||
- pull_request_target
|
- pull_request_target
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
labeler:
|
labeler:
|
||||||
@@ -9,4 +9,4 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/labeler@v5
|
- uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: PR Conventional Commit Validation
|
- name: PR Conventional Commit Validation
|
||||||
uses: ytanikin/PRConventionalCommits@1.3.0
|
uses: ytanikin/PRConventionalCommits@b628c5a234cc32513014b7bfdd1e47b532124d98 # 1.3.0
|
||||||
with:
|
with:
|
||||||
task_types: '["feat","fix","docs","test","ci","refactor","perf","chore","revert"]'
|
task_types: '["feat","fix","docs","test","ci","refactor","perf","chore","revert"]'
|
||||||
add_label: 'false'
|
add_label: 'false'
|
||||||
|
|||||||
20
.github/workflows/prepare-release.yml
vendored
20
.github/workflows/prepare-release.yml
vendored
@@ -31,25 +31,25 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@v1
|
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
with:
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
|
|
||||||
- name: Install Poetry
|
- name: Install uv
|
||||||
run: pipx install poetry
|
uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5
|
||||||
|
|
||||||
- name: Bump version
|
- name: Bump version
|
||||||
run: misc/release/pump-version.sh -s "${{ inputs.serverBump }}" -m "${{ inputs.mobileBump }}"
|
run: misc/release/pump-version.sh -s "${{ inputs.serverBump }}" -m "${{ inputs.mobileBump }}"
|
||||||
|
|
||||||
- name: Commit and tag
|
- name: Commit and tag
|
||||||
id: push-tag
|
id: push-tag
|
||||||
uses: EndBug/add-and-commit@v9
|
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9
|
||||||
with:
|
with:
|
||||||
default_author: github_actions
|
default_author: github_actions
|
||||||
message: 'chore: version ${{ env.IMMICH_VERSION }}'
|
message: 'chore: version ${{ env.IMMICH_VERSION }}'
|
||||||
@@ -70,23 +70,23 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@v1
|
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
with:
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
|
|
||||||
- name: Download APK
|
- name: Download APK
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
||||||
with:
|
with:
|
||||||
name: release-apk-signed
|
name: release-apk-signed
|
||||||
|
|
||||||
- name: Create draft release
|
- name: Create draft release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
tag_name: ${{ env.IMMICH_VERSION }}
|
tag_name: ${{ env.IMMICH_VERSION }}
|
||||||
|
|||||||
10
.github/workflows/preview-label.yaml
vendored
10
.github/workflows/preview-label.yaml
vendored
@@ -2,7 +2,7 @@ name: Preview label
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [labeled]
|
types: [labeled, closed]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
comment-status:
|
comment-status:
|
||||||
@@ -11,10 +11,10 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: mshick/add-pr-comment@v2
|
- uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2
|
||||||
with:
|
with:
|
||||||
message-id: "preview-status"
|
message-id: 'preview-status'
|
||||||
message: "Deploying preview environment to https://pr-${{ github.event.pull_request.number }}.preview.internal.immich.cloud/"
|
message: 'Deploying preview environment to https://pr-${{ github.event.pull_request.number }}.preview.internal.immich.cloud/'
|
||||||
|
|
||||||
remove-label:
|
remove-label:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v7
|
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
github.rest.issues.removeLabel({
|
github.rest.issues.removeLabel({
|
||||||
|
|||||||
4
.github/workflows/sdk.yml
vendored
4
.github/workflows/sdk.yml
vendored
@@ -15,9 +15,9 @@ jobs:
|
|||||||
run:
|
run:
|
||||||
working-directory: ./open-api/typescript-sdk
|
working-directory: ./open-api/typescript-sdk
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
# Setup .npmrc file to publish to npm
|
# Setup .npmrc file to publish to npm
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './open-api/typescript-sdk/.nvmrc'
|
node-version-file: './open-api/typescript-sdk/.nvmrc'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|||||||
33
.github/workflows/static_analysis.yml
vendored
33
.github/workflows/static_analysis.yml
vendored
@@ -16,9 +16,9 @@ jobs:
|
|||||||
should_run: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
should_run: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
mobile:
|
mobile:
|
||||||
@@ -38,10 +38,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
flutter-version-file: ./mobile/pubspec.yaml
|
||||||
@@ -50,6 +50,26 @@ jobs:
|
|||||||
run: dart pub get
|
run: dart pub get
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
|
|
||||||
|
- name: Run Build Runner
|
||||||
|
run: make build
|
||||||
|
working-directory: ./mobile
|
||||||
|
|
||||||
|
- name: Find file changes
|
||||||
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20
|
||||||
|
id: verify-changed-files
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
mobile/**/*.g.dart
|
||||||
|
mobile/**/*.gr.dart
|
||||||
|
mobile/**/*.drift.dart
|
||||||
|
|
||||||
|
- name: Verify files have not changed
|
||||||
|
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
||||||
|
run: |
|
||||||
|
echo "ERROR: Generated files not up to date! Run make_build inside the mobile directory"
|
||||||
|
echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}"
|
||||||
|
exit 1
|
||||||
|
|
||||||
- name: Run dart analyze
|
- name: Run dart analyze
|
||||||
run: dart analyze --fatal-infos
|
run: dart analyze --fatal-infos
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
@@ -61,8 +81,3 @@ jobs:
|
|||||||
- name: Run dart custom_lint
|
- name: Run dart custom_lint
|
||||||
run: dart run custom_lint
|
run: dart run custom_lint
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
|
|
||||||
# Enable after riverpod generator migration is completed
|
|
||||||
# - name: Run dart custom lint
|
|
||||||
# run: dart run custom_lint
|
|
||||||
# working-directory: ./mobile
|
|
||||||
|
|||||||
119
.github/workflows/test.yml
vendored
119
.github/workflows/test.yml
vendored
@@ -21,11 +21,12 @@ jobs:
|
|||||||
should_run_ml: ${{ steps.found_paths.outputs.machine-learning == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
should_run_ml: ${{ steps.found_paths.outputs.machine-learning == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
should_run_e2e_web: ${{ steps.found_paths.outputs.e2e == 'true' || steps.found_paths.outputs.web == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
should_run_e2e_web: ${{ steps.found_paths.outputs.e2e == 'true' || steps.found_paths.outputs.web == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
should_run_e2e_server_cli: ${{ steps.found_paths.outputs.e2e == 'true' || steps.found_paths.outputs.server == 'true' || steps.found_paths.outputs.cli == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
should_run_e2e_server_cli: ${{ steps.found_paths.outputs.e2e == 'true' || steps.found_paths.outputs.server == 'true' || steps.found_paths.outputs.cli == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
|
should_run_.github: ${{ steps.found_paths.outputs['.github'] == 'true' || steps.should_force.outputs.should_force == 'true' }} # redundant to have should_force but if someone changes the trigger then this won't have to be changed
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
web:
|
web:
|
||||||
@@ -45,6 +46,8 @@ jobs:
|
|||||||
- 'machine-learning/**'
|
- 'machine-learning/**'
|
||||||
workflow:
|
workflow:
|
||||||
- '.github/workflows/test.yml'
|
- '.github/workflows/test.yml'
|
||||||
|
.github:
|
||||||
|
- '.github/**'
|
||||||
|
|
||||||
- name: Check if we should force jobs to run
|
- name: Check if we should force jobs to run
|
||||||
id: should_force
|
id: should_force
|
||||||
@@ -61,10 +64,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
|
||||||
@@ -98,10 +101,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
|
|
||||||
@@ -139,10 +142,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
|
|
||||||
@@ -173,10 +176,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './web/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
|
|
||||||
@@ -218,10 +221,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
|
||||||
@@ -257,10 +260,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
|
||||||
@@ -282,12 +285,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
|
||||||
@@ -324,12 +327,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
|
||||||
@@ -360,9 +363,9 @@ jobs:
|
|||||||
if: ${{ needs.pre-job.outputs.should_run_mobile == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run_mobile == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
flutter-version-file: ./mobile/pubspec.yaml
|
||||||
@@ -379,34 +382,60 @@ jobs:
|
|||||||
run:
|
run:
|
||||||
working-directory: ./machine-learning
|
working-directory: ./machine-learning
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
- name: Install poetry
|
- name: Install uv
|
||||||
run: pipx install poetry
|
uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5
|
||||||
with:
|
# TODO: add caching when supported (https://github.com/actions/setup-python/pull/818)
|
||||||
python-version: 3.11
|
# with:
|
||||||
cache: 'poetry'
|
# python-version: 3.11
|
||||||
|
# cache: 'uv'
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
poetry install --with dev --with cpu
|
uv sync --extra cpu
|
||||||
- name: Lint with ruff
|
- name: Lint with ruff
|
||||||
run: |
|
run: |
|
||||||
poetry run ruff check --output-format=github app export
|
uv run ruff check --output-format=github immich_ml
|
||||||
- name: Check black formatting
|
- name: Check black formatting
|
||||||
run: |
|
run: |
|
||||||
poetry run black --check app export
|
uv run black --check immich_ml
|
||||||
- name: Run mypy type checking
|
- name: Run mypy type checking
|
||||||
run: |
|
run: |
|
||||||
poetry run mypy --install-types --non-interactive --strict app/
|
uv run mypy --strict immich_ml/
|
||||||
- name: Run tests and coverage
|
- name: Run tests and coverage
|
||||||
run: |
|
run: |
|
||||||
poetry run pytest app --cov=app --cov-report term-missing
|
uv run pytest --cov=immich_ml --cov-report term-missing
|
||||||
|
|
||||||
|
github-files-formatting:
|
||||||
|
name: .github Files Formatting
|
||||||
|
needs: pre-job
|
||||||
|
if: ${{ needs.pre-job.outputs['should_run_.github'] == 'true' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./.github
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
|
with:
|
||||||
|
node-version-file: './.github/.nvmrc'
|
||||||
|
|
||||||
|
- name: Run npm install
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Run formatter
|
||||||
|
run: npm run format
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
shellcheck:
|
shellcheck:
|
||||||
name: ShellCheck
|
name: ShellCheck
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
- name: Run ShellCheck
|
- name: Run ShellCheck
|
||||||
uses: ludeeus/action-shellcheck@master
|
uses: ludeeus/action-shellcheck@master
|
||||||
with:
|
with:
|
||||||
@@ -420,10 +449,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
|
||||||
@@ -437,7 +466,7 @@ jobs:
|
|||||||
run: make open-api
|
run: make open-api
|
||||||
|
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
uses: tj-actions/verify-changed-files@v20
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20
|
||||||
id: verify-changed-files
|
id: verify-changed-files
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
@@ -457,7 +486,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0
|
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
||||||
env:
|
env:
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
@@ -475,10 +504,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
|
||||||
@@ -489,27 +518,27 @@ jobs:
|
|||||||
run: npm run build
|
run: npm run build
|
||||||
|
|
||||||
- name: Run existing migrations
|
- name: Run existing migrations
|
||||||
run: npm run typeorm:migrations:run
|
run: npm run migrations:run
|
||||||
|
|
||||||
- name: Test npm run schema:reset command works
|
- name: Test npm run schema:reset command works
|
||||||
run: npm run typeorm:schema:reset
|
run: npm run typeorm:schema:reset
|
||||||
|
|
||||||
- name: Generate new migrations
|
- name: Generate new migrations
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: npm run typeorm:migrations:generate ./src/migrations/TestMigration
|
run: npm run migrations:generate TestMigration
|
||||||
|
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
uses: tj-actions/verify-changed-files@v20
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20
|
||||||
id: verify-changed-files
|
id: verify-changed-files
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
server/src/migrations/
|
server/src
|
||||||
- name: Verify migration files have not changed
|
- name: Verify migration files have not changed
|
||||||
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
||||||
run: |
|
run: |
|
||||||
echo "ERROR: Generated migration files not up to date!"
|
echo "ERROR: Generated migration files not up to date!"
|
||||||
echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}"
|
echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}"
|
||||||
cat ./src/migrations/*-TestMigration.ts
|
cat ./src/*-TestMigration.ts
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
- name: Run SQL generation
|
- name: Run SQL generation
|
||||||
@@ -518,7 +547,7 @@ jobs:
|
|||||||
DB_URL: postgres://postgres:postgres@localhost:5432/immich
|
DB_URL: postgres://postgres:postgres@localhost:5432/immich
|
||||||
|
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
uses: tj-actions/verify-changed-files@v20
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20
|
||||||
id: verify-changed-sql-files
|
id: verify-changed-sql-files
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
|
|||||||
57
.github/workflows/weblate-lock.yml
vendored
Normal file
57
.github/workflows/weblate-lock.yml
vendored
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
name: Weblate checks
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
pre-job:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
should_run: ${{ steps.found_paths.outputs.i18n == 'true' && github.head_ref != 'chore/translations'}}
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
- id: found_paths
|
||||||
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
i18n:
|
||||||
|
- 'i18n/!(en)**\.json'
|
||||||
|
- name: Debug
|
||||||
|
run: |
|
||||||
|
echo "Should run: ${{ steps.found_paths.outputs.i18n == 'true' && github.head_ref != 'chore/translations'}}"
|
||||||
|
echo "Found i18n paths: ${{ steps.found_paths.outputs.i18n }}"
|
||||||
|
echo "Head ref: ${{ github.head_ref }}"
|
||||||
|
|
||||||
|
enforce-lock:
|
||||||
|
name: Check Weblate Lock
|
||||||
|
needs: [pre-job]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run == 'true' }}
|
||||||
|
steps:
|
||||||
|
- name: Check weblate lock
|
||||||
|
run: |
|
||||||
|
if [[ "false" = $(curl https://hosted.weblate.org/api/components/immich/immich/lock/ | jq .locked) ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
- name: Find Pull Request
|
||||||
|
uses: juliangruber/find-pull-request-action@48b6133aa6c826f267ebd33aa2d29470f9d9e7d0 # v1
|
||||||
|
id: find-pr
|
||||||
|
with:
|
||||||
|
branch: chore/translations
|
||||||
|
- name: Fail if existing weblate PR
|
||||||
|
if: ${{ steps.find-pr.outputs.number }}
|
||||||
|
run: exit 1
|
||||||
|
success-check-lock:
|
||||||
|
name: Weblate Lock Check Success
|
||||||
|
needs: [enforce-lock]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: always()
|
||||||
|
steps:
|
||||||
|
- name: Any jobs failed?
|
||||||
|
if: ${{ contains(needs.*.result, 'failure') }}
|
||||||
|
run: exit 1
|
||||||
|
- name: All jobs passed or skipped
|
||||||
|
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
||||||
|
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
||||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -39,6 +39,7 @@
|
|||||||
],
|
],
|
||||||
"explorer.fileNesting.enabled": true,
|
"explorer.fileNesting.enabled": true,
|
||||||
"explorer.fileNesting.patterns": {
|
"explorer.fileNesting.patterns": {
|
||||||
"*.ts": "${capture}.spec.ts,${capture}.mock.ts"
|
"*.ts": "${capture}.spec.ts,${capture}.mock.ts",
|
||||||
|
"*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
10
Makefile
10
Makefile
@@ -39,7 +39,7 @@ attach-server:
|
|||||||
renovate:
|
renovate:
|
||||||
LOG_LEVEL=debug npx renovate --platform=local --repository-cache=reset
|
LOG_LEVEL=debug npx renovate --platform=local --repository-cache=reset
|
||||||
|
|
||||||
MODULES = e2e server web cli sdk docs
|
MODULES = e2e server web cli sdk docs .github
|
||||||
|
|
||||||
audit-%:
|
audit-%:
|
||||||
npm --prefix $(subst sdk,open-api/typescript-sdk,$*) audit fix
|
npm --prefix $(subst sdk,open-api/typescript-sdk,$*) audit fix
|
||||||
@@ -77,14 +77,14 @@ test-medium:
|
|||||||
test-medium-dev:
|
test-medium-dev:
|
||||||
docker exec -it immich_server /bin/sh -c "npm run test:medium"
|
docker exec -it immich_server /bin/sh -c "npm run test:medium"
|
||||||
|
|
||||||
build-all: $(foreach M,$(filter-out e2e,$(MODULES)),build-$M) ;
|
build-all: $(foreach M,$(filter-out e2e .github,$(MODULES)),build-$M) ;
|
||||||
install-all: $(foreach M,$(MODULES),install-$M) ;
|
install-all: $(foreach M,$(MODULES),install-$M) ;
|
||||||
check-all: $(foreach M,$(filter-out sdk cli docs,$(MODULES)),check-$M) ;
|
check-all: $(foreach M,$(filter-out sdk cli docs .github,$(MODULES)),check-$M) ;
|
||||||
lint-all: $(foreach M,$(filter-out sdk docs,$(MODULES)),lint-$M) ;
|
lint-all: $(foreach M,$(filter-out sdk docs .github,$(MODULES)),lint-$M) ;
|
||||||
format-all: $(foreach M,$(filter-out sdk,$(MODULES)),format-$M) ;
|
format-all: $(foreach M,$(filter-out sdk,$(MODULES)),format-$M) ;
|
||||||
audit-all: $(foreach M,$(MODULES),audit-$M) ;
|
audit-all: $(foreach M,$(MODULES),audit-$M) ;
|
||||||
hygiene-all: lint-all format-all check-all sql audit-all;
|
hygiene-all: lint-all format-all check-all sql audit-all;
|
||||||
test-all: $(foreach M,$(filter-out sdk docs,$(MODULES)),test-$M) ;
|
test-all: $(foreach M,$(filter-out sdk docs .github,$(MODULES)),test-$M) ;
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
find . -name "node_modules" -type d -prune -exec rm -rf '{}' +
|
find . -name "node_modules" -type d -prune -exec rm -rf '{}' +
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -1,11 +1,11 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<br/>
|
<br/>
|
||||||
<a href="https://opensource.org/license/agpl-v3"><img src="https://img.shields.io/badge/License-AGPL_v3-blue.svg?color=3F51B5&style=for-the-badge&label=License&logoColor=000000&labelColor=ececec" alt="License: AGPLv3"></a>
|
<a href="https://opensource.org/license/agpl-v3"><img src="https://img.shields.io/badge/License-AGPL_v3-blue.svg?color=3F51B5&style=for-the-badge&label=License&logoColor=000000&labelColor=ececec" alt="License: AGPLv3"></a>
|
||||||
<a href="https://discord.immich.app">
|
<a href="https://discord.immich.app">
|
||||||
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" alt="Discord"/>
|
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" alt="Discord"/>
|
||||||
</a>
|
</a>
|
||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@@ -61,9 +61,7 @@
|
|||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
Access the demo [here](https://demo.immich.app). The demo is running on a Free-tier Oracle VM in Amsterdam with a 2.4Ghz quad-core ARM64 CPU and 24GB RAM.
|
Access the demo [here](https://demo.immich.app). For the mobile app, you can use `https://demo.immich.app` for the `Server Endpoint URL`.
|
||||||
|
|
||||||
For the mobile app, you can use `https://demo.immich.app/api` for the `Server Endpoint URL`
|
|
||||||
|
|
||||||
### Login credentials
|
### Login credentials
|
||||||
|
|
||||||
@@ -104,7 +102,7 @@ For the mobile app, you can use `https://demo.immich.app/api` for the `Server En
|
|||||||
| Read-only gallery | Yes | Yes |
|
| Read-only gallery | Yes | Yes |
|
||||||
| Stacked Photos | Yes | Yes |
|
| Stacked Photos | Yes | Yes |
|
||||||
| Tags | No | Yes |
|
| Tags | No | Yes |
|
||||||
| Folder View | No | Yes |
|
| Folder View | Yes | Yes |
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:22.13.1-alpine3.20@sha256:c52e20859a92b3eccbd3a36c5e1a90adc20617d8d421d65e8a622e87b5dac963 AS core
|
FROM node:22.14.0-alpine3.20@sha256:40be979442621049f40b1d51a26b55e281246b5de4e5f51a18da7beb6e17e3f9 AS core
|
||||||
|
|
||||||
WORKDIR /usr/src/open-api/typescript-sdk
|
WORKDIR /usr/src/open-api/typescript-sdk
|
||||||
COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./
|
COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./
|
||||||
|
|||||||
@@ -1,39 +1,29 @@
|
|||||||
import { FlatCompat } from '@eslint/eslintrc';
|
|
||||||
import js from '@eslint/js';
|
import js from '@eslint/js';
|
||||||
import typescriptEslint from '@typescript-eslint/eslint-plugin';
|
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
||||||
import tsParser from '@typescript-eslint/parser';
|
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
|
||||||
import globals from 'globals';
|
import globals from 'globals';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import typescriptEslint from 'typescript-eslint';
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
const compat = new FlatCompat({
|
|
||||||
baseDirectory: __dirname,
|
|
||||||
recommendedConfig: js.configs.recommended,
|
|
||||||
allConfig: js.configs.all,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default [
|
export default typescriptEslint.config([
|
||||||
|
eslintPluginUnicorn.configs.recommended,
|
||||||
|
eslintPluginPrettierRecommended,
|
||||||
|
js.configs.recommended,
|
||||||
|
typescriptEslint.configs.recommended,
|
||||||
{
|
{
|
||||||
ignores: ['eslint.config.mjs', 'dist'],
|
ignores: ['eslint.config.mjs', 'dist'],
|
||||||
},
|
},
|
||||||
...compat.extends(
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
'plugin:prettier/recommended',
|
|
||||||
'plugin:unicorn/recommended',
|
|
||||||
),
|
|
||||||
{
|
{
|
||||||
plugins: {
|
|
||||||
'@typescript-eslint': typescriptEslint,
|
|
||||||
},
|
|
||||||
|
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
globals: {
|
globals: {
|
||||||
...globals.node,
|
...globals.node,
|
||||||
},
|
},
|
||||||
|
|
||||||
parser: tsParser,
|
parser: typescriptEslint.parser,
|
||||||
ecmaVersion: 5,
|
ecmaVersion: 5,
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
|
|
||||||
@@ -58,4 +48,4 @@ export default [
|
|||||||
'object-shorthand': ['error', 'always'],
|
'object-shorthand': ['error', 'always'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
]);
|
||||||
|
|||||||
1419
cli/package-lock.json
generated
1419
cli/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.52",
|
"version": "2.2.61",
|
||||||
"description": "Command Line Interface (CLI) for Immich",
|
"description": "Command Line Interface (CLI) for Immich",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": "./dist/index.js",
|
"exports": "./dist/index.js",
|
||||||
@@ -19,10 +19,9 @@
|
|||||||
"@types/byte-size": "^8.1.0",
|
"@types/byte-size": "^8.1.0",
|
||||||
"@types/cli-progress": "^3.11.0",
|
"@types/cli-progress": "^3.11.0",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
|
"@types/micromatch": "^4.0.9",
|
||||||
"@types/mock-fs": "^4.13.1",
|
"@types/mock-fs": "^4.13.1",
|
||||||
"@types/node": "^22.13.4",
|
"@types/node": "^22.14.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.15.0",
|
|
||||||
"@typescript-eslint/parser": "^8.15.0",
|
|
||||||
"@vitest/coverage-v8": "^3.0.0",
|
"@vitest/coverage-v8": "^3.0.0",
|
||||||
"byte-size": "^9.0.0",
|
"byte-size": "^9.0.0",
|
||||||
"cli-progress": "^3.12.0",
|
"cli-progress": "^3.12.0",
|
||||||
@@ -30,12 +29,13 @@
|
|||||||
"eslint": "^9.14.0",
|
"eslint": "^9.14.0",
|
||||||
"eslint-config-prettier": "^10.0.0",
|
"eslint-config-prettier": "^10.0.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"eslint-plugin-unicorn": "^56.0.1",
|
"eslint-plugin-unicorn": "^57.0.0",
|
||||||
"globals": "^15.9.0",
|
"globals": "^16.0.0",
|
||||||
"mock-fs": "^5.2.0",
|
"mock-fs": "^5.2.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"prettier-plugin-organize-imports": "^4.0.0",
|
"prettier-plugin-organize-imports": "^4.0.0",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
|
"typescript-eslint": "^8.28.0",
|
||||||
"vite": "^6.0.0",
|
"vite": "^6.0.0",
|
||||||
"vite-tsconfig-paths": "^5.0.0",
|
"vite-tsconfig-paths": "^5.0.0",
|
||||||
"vitest": "^3.0.0",
|
"vitest": "^3.0.0",
|
||||||
@@ -62,9 +62,11 @@
|
|||||||
"node": ">=20.0.0"
|
"node": ">=20.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"chokidar": "^4.0.3",
|
||||||
"fast-glob": "^3.3.2",
|
"fast-glob": "^3.3.2",
|
||||||
"fastq": "^1.17.1",
|
"fastq": "^1.17.1",
|
||||||
"lodash-es": "^4.17.21"
|
"lodash-es": "^4.17.21",
|
||||||
|
"micromatch": "^4.0.8"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "22.14.0"
|
"node": "22.14.0"
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import * as fs from 'node:fs';
|
import * as fs from 'node:fs';
|
||||||
import * as os from 'node:os';
|
import * as os from 'node:os';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
import { describe, expect, it, vi } from 'vitest';
|
import { setTimeout as sleep } from 'node:timers/promises';
|
||||||
|
import { describe, expect, it, MockedFunction, vi } from 'vitest';
|
||||||
|
|
||||||
import { Action, checkBulkUpload, defaults, Reason } from '@immich/sdk';
|
import { Action, checkBulkUpload, defaults, getSupportedMediaTypes, Reason } from '@immich/sdk';
|
||||||
import createFetchMock from 'vitest-fetch-mock';
|
import createFetchMock from 'vitest-fetch-mock';
|
||||||
|
|
||||||
import { checkForDuplicates, getAlbumName, uploadFiles, UploadOptionsDto } from './asset';
|
import { checkForDuplicates, getAlbumName, startWatch, uploadFiles, UploadOptionsDto } from 'src/commands/asset';
|
||||||
|
|
||||||
vi.mock('@immich/sdk');
|
vi.mock('@immich/sdk');
|
||||||
|
|
||||||
@@ -199,3 +200,112 @@ describe('checkForDuplicates', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('startWatch', () => {
|
||||||
|
let testFolder: string;
|
||||||
|
let checkBulkUploadMocked: MockedFunction<typeof checkBulkUpload>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
|
||||||
|
vi.mocked(getSupportedMediaTypes).mockResolvedValue({
|
||||||
|
image: ['.jpg'],
|
||||||
|
sidecar: ['.xmp'],
|
||||||
|
video: ['.mp4'],
|
||||||
|
});
|
||||||
|
|
||||||
|
testFolder = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'test-startWatch-'));
|
||||||
|
checkBulkUploadMocked = vi.mocked(checkBulkUpload);
|
||||||
|
checkBulkUploadMocked.mockResolvedValue({
|
||||||
|
results: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should start watching a directory and upload new files', async () => {
|
||||||
|
const testFilePath = path.join(testFolder, 'test.jpg');
|
||||||
|
|
||||||
|
await startWatch([testFolder], { concurrency: 1 }, { batchSize: 1, debounceTimeMs: 10 });
|
||||||
|
await sleep(100); // to debounce the watcher from considering the test file as a existing file
|
||||||
|
await fs.promises.writeFile(testFilePath, 'testjpg');
|
||||||
|
|
||||||
|
await vi.waitUntil(() => checkBulkUploadMocked.mock.calls.length > 0, 3000);
|
||||||
|
expect(checkBulkUpload).toHaveBeenCalledWith({
|
||||||
|
assetBulkUploadCheckDto: {
|
||||||
|
assets: [
|
||||||
|
expect.objectContaining({
|
||||||
|
id: testFilePath,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter out unsupported files', async () => {
|
||||||
|
const testFilePath = path.join(testFolder, 'test.jpg');
|
||||||
|
const unsupportedFilePath = path.join(testFolder, 'test.txt');
|
||||||
|
|
||||||
|
await startWatch([testFolder], { concurrency: 1 }, { batchSize: 1, debounceTimeMs: 10 });
|
||||||
|
await sleep(100); // to debounce the watcher from considering the test file as a existing file
|
||||||
|
await fs.promises.writeFile(testFilePath, 'testjpg');
|
||||||
|
await fs.promises.writeFile(unsupportedFilePath, 'testtxt');
|
||||||
|
|
||||||
|
await vi.waitUntil(() => checkBulkUploadMocked.mock.calls.length > 0, 3000);
|
||||||
|
expect(checkBulkUpload).toHaveBeenCalledWith({
|
||||||
|
assetBulkUploadCheckDto: {
|
||||||
|
assets: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: testFilePath,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(checkBulkUpload).not.toHaveBeenCalledWith({
|
||||||
|
assetBulkUploadCheckDto: {
|
||||||
|
assets: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: unsupportedFilePath,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filger out ignored patterns', async () => {
|
||||||
|
const testFilePath = path.join(testFolder, 'test.jpg');
|
||||||
|
const ignoredPattern = 'ignored';
|
||||||
|
const ignoredFolder = path.join(testFolder, ignoredPattern);
|
||||||
|
await fs.promises.mkdir(ignoredFolder, { recursive: true });
|
||||||
|
const ignoredFilePath = path.join(ignoredFolder, 'ignored.jpg');
|
||||||
|
|
||||||
|
await startWatch([testFolder], { concurrency: 1, ignore: ignoredPattern }, { batchSize: 1, debounceTimeMs: 10 });
|
||||||
|
await sleep(100); // to debounce the watcher from considering the test file as a existing file
|
||||||
|
await fs.promises.writeFile(testFilePath, 'testjpg');
|
||||||
|
await fs.promises.writeFile(ignoredFilePath, 'ignoredjpg');
|
||||||
|
|
||||||
|
await vi.waitUntil(() => checkBulkUploadMocked.mock.calls.length > 0, 3000);
|
||||||
|
expect(checkBulkUpload).toHaveBeenCalledWith({
|
||||||
|
assetBulkUploadCheckDto: {
|
||||||
|
assets: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: testFilePath,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(checkBulkUpload).not.toHaveBeenCalledWith({
|
||||||
|
assetBulkUploadCheckDto: {
|
||||||
|
assets: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: ignoredFilePath,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await fs.promises.rm(testFolder, { recursive: true, force: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -12,13 +12,18 @@ import {
|
|||||||
getSupportedMediaTypes,
|
getSupportedMediaTypes,
|
||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
import byteSize from 'byte-size';
|
import byteSize from 'byte-size';
|
||||||
|
import { Matcher, watch as watchFs } from 'chokidar';
|
||||||
import { MultiBar, Presets, SingleBar } from 'cli-progress';
|
import { MultiBar, Presets, SingleBar } from 'cli-progress';
|
||||||
import { chunk } from 'lodash-es';
|
import { chunk } from 'lodash-es';
|
||||||
|
import micromatch from 'micromatch';
|
||||||
import { Stats, createReadStream } from 'node:fs';
|
import { Stats, createReadStream } from 'node:fs';
|
||||||
import { stat, unlink } from 'node:fs/promises';
|
import { stat, unlink } from 'node:fs/promises';
|
||||||
import path, { basename } from 'node:path';
|
import path, { basename } from 'node:path';
|
||||||
import { Queue } from 'src/queue';
|
import { Queue } from 'src/queue';
|
||||||
import { BaseOptions, authenticate, crawl, sha1 } from 'src/utils';
|
import { BaseOptions, Batcher, authenticate, crawl, sha1 } from 'src/utils';
|
||||||
|
|
||||||
|
const UPLOAD_WATCH_BATCH_SIZE = 100;
|
||||||
|
const UPLOAD_WATCH_DEBOUNCE_TIME_MS = 10_000;
|
||||||
|
|
||||||
const s = (count: number) => (count === 1 ? '' : 's');
|
const s = (count: number) => (count === 1 ? '' : 's');
|
||||||
|
|
||||||
@@ -36,6 +41,8 @@ export interface UploadOptionsDto {
|
|||||||
albumName?: string;
|
albumName?: string;
|
||||||
includeHidden?: boolean;
|
includeHidden?: boolean;
|
||||||
concurrency: number;
|
concurrency: number;
|
||||||
|
progress?: boolean;
|
||||||
|
watch?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class UploadFile extends File {
|
class UploadFile extends File {
|
||||||
@@ -55,19 +62,94 @@ class UploadFile extends File {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uploadBatch = async (files: string[], options: UploadOptionsDto) => {
|
||||||
|
const { newFiles, duplicates } = await checkForDuplicates(files, options);
|
||||||
|
const newAssets = await uploadFiles(newFiles, options);
|
||||||
|
await updateAlbums([...newAssets, ...duplicates], options);
|
||||||
|
await deleteFiles(newFiles, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const startWatch = async (
|
||||||
|
paths: string[],
|
||||||
|
options: UploadOptionsDto,
|
||||||
|
{
|
||||||
|
batchSize = UPLOAD_WATCH_BATCH_SIZE,
|
||||||
|
debounceTimeMs = UPLOAD_WATCH_DEBOUNCE_TIME_MS,
|
||||||
|
}: { batchSize?: number; debounceTimeMs?: number } = {},
|
||||||
|
) => {
|
||||||
|
const watcherIgnored: Matcher[] = [];
|
||||||
|
const { image, video } = await getSupportedMediaTypes();
|
||||||
|
const extensions = new Set([...image, ...video]);
|
||||||
|
|
||||||
|
if (options.ignore) {
|
||||||
|
watcherIgnored.push((path) => micromatch.contains(path, `**/${options.ignore}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
const pathsBatcher = new Batcher<string>({
|
||||||
|
batchSize,
|
||||||
|
debounceTimeMs,
|
||||||
|
onBatch: async (paths: string[]) => {
|
||||||
|
const uniquePaths = [...new Set(paths)];
|
||||||
|
await uploadBatch(uniquePaths, options);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onFile = async (path: string, stats?: Stats) => {
|
||||||
|
if (stats?.isDirectory()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ext = '.' + path.split('.').pop()?.toLowerCase();
|
||||||
|
if (!ext || !extensions.has(ext)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.progress) {
|
||||||
|
// logging when progress is disabled as it can cause issues with the progress bar rendering
|
||||||
|
console.log(`Change detected: ${path}`);
|
||||||
|
}
|
||||||
|
pathsBatcher.add(path);
|
||||||
|
};
|
||||||
|
const fsWatcher = watchFs(paths, {
|
||||||
|
ignoreInitial: true,
|
||||||
|
ignored: watcherIgnored,
|
||||||
|
alwaysStat: true,
|
||||||
|
awaitWriteFinish: true,
|
||||||
|
depth: options.recursive ? undefined : 1,
|
||||||
|
persistent: true,
|
||||||
|
})
|
||||||
|
.on('add', onFile)
|
||||||
|
.on('change', onFile)
|
||||||
|
.on('error', (error) => console.error(`Watcher error: ${error}`));
|
||||||
|
|
||||||
|
process.on('SIGINT', async () => {
|
||||||
|
console.log('Exiting...');
|
||||||
|
await fsWatcher.close();
|
||||||
|
process.exit();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const upload = async (paths: string[], baseOptions: BaseOptions, options: UploadOptionsDto) => {
|
export const upload = async (paths: string[], baseOptions: BaseOptions, options: UploadOptionsDto) => {
|
||||||
await authenticate(baseOptions);
|
await authenticate(baseOptions);
|
||||||
|
|
||||||
const scanFiles = await scan(paths, options);
|
const scanFiles = await scan(paths, options);
|
||||||
|
|
||||||
if (scanFiles.length === 0) {
|
if (scanFiles.length === 0) {
|
||||||
console.log('No files found, exiting');
|
if (options.watch) {
|
||||||
return;
|
console.log('No files found initially.');
|
||||||
|
} else {
|
||||||
|
console.log('No files found, exiting');
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { newFiles, duplicates } = await checkForDuplicates(scanFiles, options);
|
if (options.watch) {
|
||||||
const newAssets = await uploadFiles(newFiles, options);
|
console.log('Watching for changes...');
|
||||||
await updateAlbums([...newAssets, ...duplicates], options);
|
await startWatch(paths, options);
|
||||||
await deleteFiles(newFiles, options);
|
// watcher does not handle the initial scan
|
||||||
|
// as the scan() is a more efficient quick start with batched results
|
||||||
|
}
|
||||||
|
|
||||||
|
await uploadBatch(scanFiles, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
const scan = async (pathsToCrawl: string[], options: UploadOptionsDto) => {
|
const scan = async (pathsToCrawl: string[], options: UploadOptionsDto) => {
|
||||||
@@ -85,19 +167,25 @@ const scan = async (pathsToCrawl: string[], options: UploadOptionsDto) => {
|
|||||||
return files;
|
return files;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const checkForDuplicates = async (files: string[], { concurrency, skipHash }: UploadOptionsDto) => {
|
export const checkForDuplicates = async (files: string[], { concurrency, skipHash, progress }: UploadOptionsDto) => {
|
||||||
if (skipHash) {
|
if (skipHash) {
|
||||||
console.log('Skipping hash check, assuming all files are new');
|
console.log('Skipping hash check, assuming all files are new');
|
||||||
return { newFiles: files, duplicates: [] };
|
return { newFiles: files, duplicates: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
const multiBar = new MultiBar(
|
let multiBar: MultiBar | undefined;
|
||||||
{ format: '{message} | {bar} | {percentage}% | ETA: {eta}s | {value}/{total} assets' },
|
|
||||||
Presets.shades_classic,
|
|
||||||
);
|
|
||||||
|
|
||||||
const hashProgressBar = multiBar.create(files.length, 0, { message: 'Hashing files ' });
|
if (progress) {
|
||||||
const checkProgressBar = multiBar.create(files.length, 0, { message: 'Checking for duplicates' });
|
multiBar = new MultiBar(
|
||||||
|
{ format: '{message} | {bar} | {percentage}% | ETA: {eta}s | {value}/{total} assets' },
|
||||||
|
Presets.shades_classic,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log(`Received ${files.length} files, hashing...`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const hashProgressBar = multiBar?.create(files.length, 0, { message: 'Hashing files ' });
|
||||||
|
const checkProgressBar = multiBar?.create(files.length, 0, { message: 'Checking for duplicates' });
|
||||||
|
|
||||||
const newFiles: string[] = [];
|
const newFiles: string[] = [];
|
||||||
const duplicates: Asset[] = [];
|
const duplicates: Asset[] = [];
|
||||||
@@ -117,7 +205,7 @@ export const checkForDuplicates = async (files: string[], { concurrency, skipHas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkProgressBar.increment(assets.length);
|
checkProgressBar?.increment(assets.length);
|
||||||
},
|
},
|
||||||
{ concurrency, retry: 3 },
|
{ concurrency, retry: 3 },
|
||||||
);
|
);
|
||||||
@@ -137,7 +225,7 @@ export const checkForDuplicates = async (files: string[], { concurrency, skipHas
|
|||||||
void checkBulkUploadQueue.push(batch);
|
void checkBulkUploadQueue.push(batch);
|
||||||
}
|
}
|
||||||
|
|
||||||
hashProgressBar.increment();
|
hashProgressBar?.increment();
|
||||||
return results;
|
return results;
|
||||||
},
|
},
|
||||||
{ concurrency, retry: 3 },
|
{ concurrency, retry: 3 },
|
||||||
@@ -155,7 +243,7 @@ export const checkForDuplicates = async (files: string[], { concurrency, skipHas
|
|||||||
|
|
||||||
await checkBulkUploadQueue.drained();
|
await checkBulkUploadQueue.drained();
|
||||||
|
|
||||||
multiBar.stop();
|
multiBar?.stop();
|
||||||
|
|
||||||
console.log(`Found ${newFiles.length} new files and ${duplicates.length} duplicate${s(duplicates.length)}`);
|
console.log(`Found ${newFiles.length} new files and ${duplicates.length} duplicate${s(duplicates.length)}`);
|
||||||
|
|
||||||
@@ -171,7 +259,10 @@ export const checkForDuplicates = async (files: string[], { concurrency, skipHas
|
|||||||
return { newFiles, duplicates };
|
return { newFiles, duplicates };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const uploadFiles = async (files: string[], { dryRun, concurrency }: UploadOptionsDto): Promise<Asset[]> => {
|
export const uploadFiles = async (
|
||||||
|
files: string[],
|
||||||
|
{ dryRun, concurrency, progress }: UploadOptionsDto,
|
||||||
|
): Promise<Asset[]> => {
|
||||||
if (files.length === 0) {
|
if (files.length === 0) {
|
||||||
console.log('All assets were already uploaded, nothing to do.');
|
console.log('All assets were already uploaded, nothing to do.');
|
||||||
return [];
|
return [];
|
||||||
@@ -191,12 +282,20 @@ export const uploadFiles = async (files: string[], { dryRun, concurrency }: Uplo
|
|||||||
return files.map((filepath) => ({ id: '', filepath }));
|
return files.map((filepath) => ({ id: '', filepath }));
|
||||||
}
|
}
|
||||||
|
|
||||||
const uploadProgress = new SingleBar(
|
let uploadProgress: SingleBar | undefined;
|
||||||
{ format: 'Uploading assets | {bar} | {percentage}% | ETA: {eta_formatted} | {value_formatted}/{total_formatted}' },
|
|
||||||
Presets.shades_classic,
|
if (progress) {
|
||||||
);
|
uploadProgress = new SingleBar(
|
||||||
uploadProgress.start(totalSize, 0);
|
{
|
||||||
uploadProgress.update({ value_formatted: 0, total_formatted: byteSize(totalSize) });
|
format: 'Uploading assets | {bar} | {percentage}% | ETA: {eta_formatted} | {value_formatted}/{total_formatted}',
|
||||||
|
},
|
||||||
|
Presets.shades_classic,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log(`Uploading ${files.length} asset${s(files.length)} (${byteSize(totalSize)})`);
|
||||||
|
}
|
||||||
|
uploadProgress?.start(totalSize, 0);
|
||||||
|
uploadProgress?.update({ value_formatted: 0, total_formatted: byteSize(totalSize) });
|
||||||
|
|
||||||
let duplicateCount = 0;
|
let duplicateCount = 0;
|
||||||
let duplicateSize = 0;
|
let duplicateSize = 0;
|
||||||
@@ -222,7 +321,7 @@ export const uploadFiles = async (files: string[], { dryRun, concurrency }: Uplo
|
|||||||
successSize += stats.size ?? 0;
|
successSize += stats.size ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadProgress.update(successSize, { value_formatted: byteSize(successSize + duplicateSize) });
|
uploadProgress?.update(successSize, { value_formatted: byteSize(successSize + duplicateSize) });
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
@@ -235,7 +334,7 @@ export const uploadFiles = async (files: string[], { dryRun, concurrency }: Uplo
|
|||||||
|
|
||||||
await queue.drained();
|
await queue.drained();
|
||||||
|
|
||||||
uploadProgress.stop();
|
uploadProgress?.stop();
|
||||||
|
|
||||||
console.log(`Successfully uploaded ${successCount} new asset${s(successCount)} (${byteSize(successSize)})`);
|
console.log(`Successfully uploaded ${successCount} new asset${s(successCount)} (${byteSize(successSize)})`);
|
||||||
if (duplicateCount > 0) {
|
if (duplicateCount > 0) {
|
||||||
|
|||||||
@@ -69,6 +69,13 @@ program
|
|||||||
.default(4),
|
.default(4),
|
||||||
)
|
)
|
||||||
.addOption(new Option('--delete', 'Delete local assets after upload').env('IMMICH_DELETE_ASSETS'))
|
.addOption(new Option('--delete', 'Delete local assets after upload').env('IMMICH_DELETE_ASSETS'))
|
||||||
|
.addOption(new Option('--no-progress', 'Hide progress bars').env('IMMICH_PROGRESS_BAR').default(true))
|
||||||
|
.addOption(
|
||||||
|
new Option('--watch', 'Watch for changes and upload automatically')
|
||||||
|
.env('IMMICH_WATCH_CHANGES')
|
||||||
|
.default(false)
|
||||||
|
.implies({ progress: false }),
|
||||||
|
)
|
||||||
.argument('[paths...]', 'One or more paths to assets to be uploaded')
|
.argument('[paths...]', 'One or more paths to assets to be uploaded')
|
||||||
.action((paths, options) => upload(paths, program.opts(), options));
|
.action((paths, options) => upload(paths, program.opts(), options));
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import mockfs from 'mock-fs';
|
import mockfs from 'mock-fs';
|
||||||
import { readFileSync } from 'node:fs';
|
import { readFileSync } from 'node:fs';
|
||||||
import { CrawlOptions, crawl } from 'src/utils';
|
import { Batcher, CrawlOptions, crawl } from 'src/utils';
|
||||||
|
import { Mock } from 'vitest';
|
||||||
|
|
||||||
interface Test {
|
interface Test {
|
||||||
test: string;
|
test: string;
|
||||||
@@ -303,3 +304,38 @@ describe('crawl', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Batcher', () => {
|
||||||
|
let batcher: Batcher;
|
||||||
|
let onBatch: Mock;
|
||||||
|
beforeEach(() => {
|
||||||
|
onBatch = vi.fn();
|
||||||
|
batcher = new Batcher({ batchSize: 2, onBatch });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should trigger onBatch() when a batch limit is reached', async () => {
|
||||||
|
batcher.add('a');
|
||||||
|
batcher.add('b');
|
||||||
|
batcher.add('c');
|
||||||
|
expect(onBatch).toHaveBeenCalledOnce();
|
||||||
|
expect(onBatch).toHaveBeenCalledWith(['a', 'b']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should trigger onBatch() when flush() is called', async () => {
|
||||||
|
batcher.add('a');
|
||||||
|
batcher.flush();
|
||||||
|
expect(onBatch).toHaveBeenCalledOnce();
|
||||||
|
expect(onBatch).toHaveBeenCalledWith(['a']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should trigger onBatch() when debounce time reached', async () => {
|
||||||
|
vi.useFakeTimers();
|
||||||
|
batcher = new Batcher({ batchSize: 2, debounceTimeMs: 100, onBatch });
|
||||||
|
batcher.add('a');
|
||||||
|
expect(onBatch).not.toHaveBeenCalled();
|
||||||
|
vi.advanceTimersByTime(200);
|
||||||
|
expect(onBatch).toHaveBeenCalledOnce();
|
||||||
|
expect(onBatch).toHaveBeenCalledWith(['a']);
|
||||||
|
vi.useRealTimers();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -172,3 +172,64 @@ export const sha1 = (filepath: string) => {
|
|||||||
rs.on('end', () => resolve(hash.digest('hex')));
|
rs.on('end', () => resolve(hash.digest('hex')));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Batches items and calls onBatch to process them
|
||||||
|
* when the batch size is reached or the debounce time has passed.
|
||||||
|
*/
|
||||||
|
export class Batcher<T = unknown> {
|
||||||
|
private items: T[] = [];
|
||||||
|
private readonly batchSize: number;
|
||||||
|
private readonly debounceTimeMs?: number;
|
||||||
|
private readonly onBatch: (items: T[]) => void;
|
||||||
|
private debounceTimer?: NodeJS.Timeout;
|
||||||
|
|
||||||
|
constructor({
|
||||||
|
batchSize,
|
||||||
|
debounceTimeMs,
|
||||||
|
onBatch,
|
||||||
|
}: {
|
||||||
|
batchSize: number;
|
||||||
|
debounceTimeMs?: number;
|
||||||
|
onBatch: (items: T[]) => Promise<void>;
|
||||||
|
}) {
|
||||||
|
this.batchSize = batchSize;
|
||||||
|
this.debounceTimeMs = debounceTimeMs;
|
||||||
|
this.onBatch = onBatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
private setDebounceTimer() {
|
||||||
|
if (this.debounceTimer) {
|
||||||
|
clearTimeout(this.debounceTimer);
|
||||||
|
}
|
||||||
|
if (this.debounceTimeMs) {
|
||||||
|
this.debounceTimer = setTimeout(() => this.flush(), this.debounceTimeMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private clearDebounceTimer() {
|
||||||
|
if (this.debounceTimer) {
|
||||||
|
clearTimeout(this.debounceTimer);
|
||||||
|
this.debounceTimer = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
add(item: T) {
|
||||||
|
this.items.push(item);
|
||||||
|
this.setDebounceTimer();
|
||||||
|
if (this.items.length >= this.batchSize) {
|
||||||
|
this.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flush() {
|
||||||
|
this.clearDebounceTimer();
|
||||||
|
if (this.items.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onBatch(this.items);
|
||||||
|
|
||||||
|
this.items = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ services:
|
|||||||
context: ../
|
context: ../
|
||||||
dockerfile: server/Dockerfile
|
dockerfile: server/Dockerfile
|
||||||
target: dev
|
target: dev
|
||||||
restart: always
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- ../server:/usr/src/app
|
- ../server:/usr/src/app
|
||||||
- ../open-api:/usr/src/open-api
|
- ../open-api:/usr/src/open-api
|
||||||
@@ -95,12 +95,12 @@ services:
|
|||||||
image: immich-machine-learning-dev:latest
|
image: immich-machine-learning-dev:latest
|
||||||
# extends:
|
# extends:
|
||||||
# file: hwaccel.ml.yml
|
# file: hwaccel.ml.yml
|
||||||
# service: cpu # set to one of [armnn, cuda, openvino, openvino-wsl] for accelerated inference
|
# service: cpu # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference
|
||||||
build:
|
build:
|
||||||
context: ../machine-learning
|
context: ../machine-learning
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
args:
|
args:
|
||||||
- DEVICE=cpu # set to one of [armnn, cuda, openvino, openvino-wsl] for accelerated inference
|
- DEVICE=cpu # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference
|
||||||
ports:
|
ports:
|
||||||
- 3003:3003
|
- 3003:3003
|
||||||
volumes:
|
volumes:
|
||||||
@@ -116,13 +116,13 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: redis:6.2-alpine@sha256:148bb5411c184abd288d9aaed139c98123eeb8824c5d3fce03cf721db58066d8
|
image: docker.io/valkey/valkey:8-bookworm@sha256:42cba146593a5ea9a622002c1b7cba5da7be248650cbb64ecb9c6c33d29794b1
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
|
|
||||||
database:
|
database:
|
||||||
container_name: immich_postgres
|
container_name: immich_postgres
|
||||||
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0
|
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -38,12 +38,12 @@ services:
|
|||||||
image: immich-machine-learning:latest
|
image: immich-machine-learning:latest
|
||||||
# extends:
|
# extends:
|
||||||
# file: hwaccel.ml.yml
|
# file: hwaccel.ml.yml
|
||||||
# service: cpu # set to one of [armnn, cuda, openvino, openvino-wsl] for accelerated inference
|
# service: cpu # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference
|
||||||
build:
|
build:
|
||||||
context: ../machine-learning
|
context: ../machine-learning
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
args:
|
args:
|
||||||
- DEVICE=cpu # set to one of [armnn, cuda, openvino, openvino-wsl] for accelerated inference
|
- DEVICE=cpu # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference
|
||||||
ports:
|
ports:
|
||||||
- 3003:3003
|
- 3003:3003
|
||||||
volumes:
|
volumes:
|
||||||
@@ -56,14 +56,14 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: redis:6.2-alpine@sha256:148bb5411c184abd288d9aaed139c98123eeb8824c5d3fce03cf721db58066d8
|
image: docker.io/valkey/valkey:8-bookworm@sha256:42cba146593a5ea9a622002c1b7cba5da7be248650cbb64ecb9c6c33d29794b1
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
database:
|
database:
|
||||||
container_name: immich_postgres
|
container_name: immich_postgres
|
||||||
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0
|
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
environment:
|
environment:
|
||||||
@@ -77,22 +77,12 @@ services:
|
|||||||
- 5432:5432
|
- 5432:5432
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: >-
|
test: >-
|
||||||
pg_isready --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" || exit 1;
|
pg_isready --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" || exit 1; Chksum="$$(psql --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" --tuples-only --no-align --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1
|
||||||
Chksum="$$(psql --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" --tuples-only --no-align
|
|
||||||
--command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')";
|
|
||||||
echo "checksum failure count is $$Chksum";
|
|
||||||
[ "$$Chksum" = '0' ] || exit 1
|
|
||||||
interval: 5m
|
interval: 5m
|
||||||
start_interval: 30s
|
start_interval: 30s
|
||||||
start_period: 5m
|
start_period: 5m
|
||||||
command: >-
|
command: >-
|
||||||
postgres
|
postgres -c shared_preload_libraries=vectors.so -c 'search_path="$$user", public, vectors' -c logging_collector=on -c max_wal_size=2GB -c shared_buffers=512MB -c wal_compression=on
|
||||||
-c shared_preload_libraries=vectors.so
|
|
||||||
-c 'search_path="$$user", public, vectors'
|
|
||||||
-c logging_collector=on
|
|
||||||
-c max_wal_size=2GB
|
|
||||||
-c shared_buffers=512MB
|
|
||||||
-c wal_compression=on
|
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
|
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
|
||||||
@@ -100,7 +90,7 @@ services:
|
|||||||
container_name: immich_prometheus
|
container_name: immich_prometheus
|
||||||
ports:
|
ports:
|
||||||
- 9090:9090
|
- 9090:9090
|
||||||
image: prom/prometheus@sha256:5888c188cf09e3f7eebc97369c3b2ce713e844cdbd88ccf36f5047c958aea120
|
image: prom/prometheus@sha256:502ad90314c7485892ce696cb14a99fceab9fc27af29f4b427f41bd39701a199
|
||||||
volumes:
|
volumes:
|
||||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||||
- prometheus-data:/prometheus
|
- prometheus-data:/prometheus
|
||||||
@@ -109,10 +99,10 @@ services:
|
|||||||
# add data source for http://immich-prometheus:9090 to get started
|
# add data source for http://immich-prometheus:9090 to get started
|
||||||
immich-grafana:
|
immich-grafana:
|
||||||
container_name: immich_grafana
|
container_name: immich_grafana
|
||||||
command: ['./run.sh', '-disable-reporting']
|
command: [ './run.sh', '-disable-reporting' ]
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
image: grafana/grafana:11.5.1-ubuntu@sha256:9a4ab78cec1a2ec7d1ca5dfd5aacec6412706a1bc9e971fc7184e2f6696a63f5
|
image: grafana/grafana:11.6.0-ubuntu@sha256:fd8fa48213c624e1a95122f1d93abbf1cf1cbe85fc73212c1e599dbd76c63ff8
|
||||||
volumes:
|
volumes:
|
||||||
- grafana-data:/var/lib/grafana
|
- grafana-data:/var/lib/grafana
|
||||||
|
|
||||||
|
|||||||
@@ -33,12 +33,12 @@ services:
|
|||||||
|
|
||||||
immich-machine-learning:
|
immich-machine-learning:
|
||||||
container_name: immich_machine_learning
|
container_name: immich_machine_learning
|
||||||
# For hardware acceleration, add one of -[armnn, cuda, openvino] to the image tag.
|
# For hardware acceleration, add one of -[armnn, cuda, rocm, openvino, rknn] to the image tag.
|
||||||
# Example tag: ${IMMICH_VERSION:-release}-cuda
|
# Example tag: ${IMMICH_VERSION:-release}-cuda
|
||||||
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
|
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
|
||||||
# extends: # uncomment this section for hardware acceleration - see https://immich.app/docs/features/ml-hardware-acceleration
|
# extends: # uncomment this section for hardware acceleration - see https://immich.app/docs/features/ml-hardware-acceleration
|
||||||
# file: hwaccel.ml.yml
|
# file: hwaccel.ml.yml
|
||||||
# service: cpu # set to one of [armnn, cuda, openvino, openvino-wsl] for accelerated inference - use the `-wsl` version for WSL2 where applicable
|
# service: cpu # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference - use the `-wsl` version for WSL2 where applicable
|
||||||
volumes:
|
volumes:
|
||||||
- model-cache:/cache
|
- model-cache:/cache
|
||||||
env_file:
|
env_file:
|
||||||
@@ -49,14 +49,14 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/redis:6.2-alpine@sha256:148bb5411c184abd288d9aaed139c98123eeb8824c5d3fce03cf721db58066d8
|
image: docker.io/valkey/valkey:8-bookworm@sha256:42cba146593a5ea9a622002c1b7cba5da7be248650cbb64ecb9c6c33d29794b1
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
database:
|
database:
|
||||||
container_name: immich_postgres
|
container_name: immich_postgres
|
||||||
image: docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0
|
image: docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||||
POSTGRES_USER: ${DB_USERNAME}
|
POSTGRES_USER: ${DB_USERNAME}
|
||||||
@@ -67,22 +67,12 @@ services:
|
|||||||
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
|
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: >-
|
test: >-
|
||||||
pg_isready --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" || exit 1;
|
pg_isready --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" || exit 1; Chksum="$$(psql --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" --tuples-only --no-align --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1
|
||||||
Chksum="$$(psql --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" --tuples-only --no-align
|
|
||||||
--command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')";
|
|
||||||
echo "checksum failure count is $$Chksum";
|
|
||||||
[ "$$Chksum" = '0' ] || exit 1
|
|
||||||
interval: 5m
|
interval: 5m
|
||||||
start_interval: 30s
|
start_interval: 30s
|
||||||
start_period: 5m
|
start_period: 5m
|
||||||
command: >-
|
command: >-
|
||||||
postgres
|
postgres -c shared_preload_libraries=vectors.so -c 'search_path="$$user", public, vectors' -c logging_collector=on -c max_wal_size=2GB -c shared_buffers=512MB -c wal_compression=on
|
||||||
-c shared_preload_libraries=vectors.so
|
|
||||||
-c 'search_path="$$user", public, vectors'
|
|
||||||
-c logging_collector=on
|
|
||||||
-c max_wal_size=2GB
|
|
||||||
-c shared_buffers=512MB
|
|
||||||
-c wal_compression=on
|
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
# The location where your uploaded files are stored
|
# The location where your uploaded files are stored
|
||||||
UPLOAD_LOCATION=./library
|
UPLOAD_LOCATION=./library
|
||||||
# The location where your database files are stored
|
|
||||||
|
# The location where your database files are stored. Network shares are not supported for the database
|
||||||
DB_DATA_LOCATION=./postgres
|
DB_DATA_LOCATION=./postgres
|
||||||
|
|
||||||
# To set a timezone, uncomment the next line and change Etc/UTC to a TZ identifier from this list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
|
# To set a timezone, uncomment the next line and change Etc/UTC to a TZ identifier from this list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
|
||||||
|
|||||||
@@ -13,6 +13,13 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- /lib/firmware/mali_csffw.bin:/lib/firmware/mali_csffw.bin:ro # Mali firmware for your chipset (not always required depending on the driver)
|
- /lib/firmware/mali_csffw.bin:/lib/firmware/mali_csffw.bin:ro # Mali firmware for your chipset (not always required depending on the driver)
|
||||||
- /usr/lib/libmali.so:/usr/lib/libmali.so:ro # Mali driver for your chipset (always required)
|
- /usr/lib/libmali.so:/usr/lib/libmali.so:ro # Mali driver for your chipset (always required)
|
||||||
|
|
||||||
|
rknn:
|
||||||
|
security_opt:
|
||||||
|
- systempaths=unconfined
|
||||||
|
- apparmor=unconfined
|
||||||
|
devices:
|
||||||
|
- /dev/dri:/dev/dri
|
||||||
|
|
||||||
cpu: {}
|
cpu: {}
|
||||||
|
|
||||||
@@ -26,6 +33,13 @@ services:
|
|||||||
capabilities:
|
capabilities:
|
||||||
- gpu
|
- gpu
|
||||||
|
|
||||||
|
rocm:
|
||||||
|
group_add:
|
||||||
|
- video
|
||||||
|
devices:
|
||||||
|
- /dev/dri:/dev/dri
|
||||||
|
- /dev/kfd:/dev/kfd
|
||||||
|
|
||||||
openvino:
|
openvino:
|
||||||
device_cgroup_rules:
|
device_cgroup_rules:
|
||||||
- 'c 189:* rmw'
|
- 'c 189:* rmw'
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ See [Backup and Restore](/docs/administration/backup-and-restore.md).
|
|||||||
|
|
||||||
### Does Immich support reading existing face tag metadata?
|
### Does Immich support reading existing face tag metadata?
|
||||||
|
|
||||||
No, it currently does not. There is an [open feature request on GitHub](https://github.com/immich-app/immich/discussions/4348).
|
Yes, it creates new faces and persons from the imported asset metadata. For details see the [feature request #4348](https://github.com/immich-app/immich/discussions/4348) and [PR #6455](https://github.com/immich-app/immich/pull/6455).
|
||||||
|
|
||||||
### Does Immich support the filtering of NSFW images?
|
### Does Immich support the filtering of NSFW images?
|
||||||
|
|
||||||
@@ -262,7 +262,7 @@ No, this is not supported. Only models listed in the [Hugging Face][huggingface]
|
|||||||
|
|
||||||
### I want to be able to search in other languages besides English. How can I do that?
|
### I want to be able to search in other languages besides English. How can I do that?
|
||||||
|
|
||||||
You can change to a multilingual CLIP model. See [here](/docs/features/searching#clip-model) for instructions.
|
You can change to a multilingual CLIP model. See [here](/docs/features/searching#clip-models) for instructions.
|
||||||
|
|
||||||
### Does Immich support Facial Recognition for videos?
|
### Does Immich support Facial Recognition for videos?
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,13 @@ As mentioned above, you should make your own backup of these together with the a
|
|||||||
You can adjust the schedule and amount of kept backups in the [admin settings](http://my.immich.app/admin/system-settings?isOpen=backup).
|
You can adjust the schedule and amount of kept backups in the [admin settings](http://my.immich.app/admin/system-settings?isOpen=backup).
|
||||||
By default, Immich will keep the last 14 backups and create a new backup every day at 2:00 AM.
|
By default, Immich will keep the last 14 backups and create a new backup every day at 2:00 AM.
|
||||||
|
|
||||||
|
#### Trigger Backup
|
||||||
|
|
||||||
|
You are able to trigger a backup in the [admin job status page](http://my.immich.app/admin/jobs-status).
|
||||||
|
Visit the page, open the "Create job" modal from the top right, select "Backup Database" and click "Confirm".
|
||||||
|
A job will run and trigger a backup, you can verify this worked correctly by checking the logs or the backup folder.
|
||||||
|
This backup will count towards the last X backups that will be kept based on your settings.
|
||||||
|
|
||||||
#### Restoring
|
#### Restoring
|
||||||
|
|
||||||
We hope to make restoring simpler in future versions, for now you can find the backups in the `UPLOAD_LOCATION/backups` folder on your host.
|
We hope to make restoring simpler in future versions, for now you can find the backups in the `UPLOAD_LOCATION/backups` folder on your host.
|
||||||
@@ -53,7 +60,7 @@ docker compose create # Create Docker containers for Immich apps witho
|
|||||||
docker start immich_postgres # Start Postgres server
|
docker start immich_postgres # Start Postgres server
|
||||||
sleep 10 # Wait for Postgres server to start up
|
sleep 10 # Wait for Postgres server to start up
|
||||||
# Check the database user if you deviated from the default
|
# Check the database user if you deviated from the default
|
||||||
gunzip < "/path/to/backup/dump.sql.gz" \
|
gunzip --stdout "/path/to/backup/dump.sql.gz" \
|
||||||
| sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" \
|
| sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" \
|
||||||
| docker exec -i immich_postgres psql --dbname=postgres --username=<DB_USERNAME> # Restore Backup
|
| docker exec -i immich_postgres psql --dbname=postgres --username=<DB_USERNAME> # Restore Backup
|
||||||
docker compose up -d # Start remainder of Immich apps
|
docker compose up -d # Start remainder of Immich apps
|
||||||
@@ -76,8 +83,8 @@ docker compose create # Create Docker containers for
|
|||||||
docker start immich_postgres # Start Postgres server
|
docker start immich_postgres # Start Postgres server
|
||||||
sleep 10 # Wait for Postgres server to start up
|
sleep 10 # Wait for Postgres server to start up
|
||||||
docker exec -it immich_postgres bash # Enter the Docker shell and run the following command
|
docker exec -it immich_postgres bash # Enter the Docker shell and run the following command
|
||||||
# Check the database user if you deviated from the default. If your backup ends in `.gz`, replace `cat` with `gunzip`
|
# Check the database user if you deviated from the default. If your backup ends in `.gz`, replace `cat` with `gunzip --stdout`
|
||||||
cat < "/dump.sql" | sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" | psql --dbname=postgres --username=<DB_USERNAME>
|
cat "/dump.sql" | sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" | psql --dbname=postgres --username=<DB_USERNAME>
|
||||||
exit # Exit the Docker shell
|
exit # Exit the Docker shell
|
||||||
docker compose up -d # Start remainder of Immich apps
|
docker compose up -d # Start remainder of Immich apps
|
||||||
```
|
```
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 16 KiB |
@@ -11,6 +11,7 @@ The `immich-server` docker image comes preinstalled with an administrative CLI (
|
|||||||
| `enable-oauth-login` | Enable OAuth login |
|
| `enable-oauth-login` | Enable OAuth login |
|
||||||
| `disable-oauth-login` | Disable OAuth login |
|
| `disable-oauth-login` | Disable OAuth login |
|
||||||
| `list-users` | List Immich users |
|
| `list-users` | List Immich users |
|
||||||
|
| `version` | Print Immich version |
|
||||||
|
|
||||||
## How to run a command
|
## How to run a command
|
||||||
|
|
||||||
@@ -80,3 +81,10 @@ immich-admin list-users
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Print Immich Version
|
||||||
|
|
||||||
|
```
|
||||||
|
immich-admin version
|
||||||
|
v1.129.0
|
||||||
|
```
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ Admin can send a welcome email if the Email option is set, you can learn here ho
|
|||||||
|
|
||||||
Admin can specify the storage quota for the user as the instance's admin; once the limit is reached, the user won't be able to upload to the instance anymore.
|
Admin can specify the storage quota for the user as the instance's admin; once the limit is reached, the user won't be able to upload to the instance anymore.
|
||||||
|
|
||||||
In order to select a storage quota, click on the pencil icon and enter the storage quota in GiB. You can choose an unlimited quota using the value 0 (default).
|
In order to select a storage quota, click on the pencil icon and enter the storage quota in GiB. You can choose an unlimited quota by leaving it empty (default).
|
||||||
|
|
||||||
:::tip
|
:::tip
|
||||||
The system administrator can see the usage quota percentage of all users in Server Stats page.
|
The system administrator can see the usage quota percentage of all users in Server Stats page.
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ You begin by authenticating to your Immich server. For instance:
|
|||||||
immich login http://192.168.1.216:2283/api HFEJ38DNSDUEG
|
immich login http://192.168.1.216:2283/api HFEJ38DNSDUEG
|
||||||
```
|
```
|
||||||
|
|
||||||
This will store your credentials in a `auth.yml` file in the configuration directory which defaults to `~/.config/`. The directory can be set with the `-d` option or the environment variable `IMMICH_CONFIG_DIR`. Please keep the file secure, either by performing the logout command after you are done, or deleting it manually.
|
This will store your credentials in a `auth.yml` file in the configuration directory which defaults to `~/.config/immich/`. The directory can be set with the `-d` option or the environment variable `IMMICH_CONFIG_DIR`. Please keep the file secure, either by performing the logout command after you are done, or deleting it manually.
|
||||||
|
|
||||||
Once you are authenticated, you can upload assets to your Immich server.
|
Once you are authenticated, you can upload assets to your Immich server.
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ Navigating to Administration > Settings > Machine Learning Settings > Facial Rec
|
|||||||
|
|
||||||
:::tip
|
:::tip
|
||||||
It's better to only tweak the parameters here than to set them to something very different unless you're ready to test a variety of options. If you do need to set a parameter to a strict setting, relaxing other settings can be a good option to compensate, and vice versa.
|
It's better to only tweak the parameters here than to set them to something very different unless you're ready to test a variety of options. If you do need to set a parameter to a strict setting, relaxing other settings can be a good option to compensate, and vice versa.
|
||||||
|
|
||||||
|
You can learn how the tune the result in this [Guide](/docs/guides/better-facial-clusters)
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### Facial recognition model
|
### Facial recognition model
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 4.9 MiB After Width: | Height: | Size: 4.9 MiB |
@@ -37,7 +37,7 @@ To validate that Immich can reach your external library, start a shell inside th
|
|||||||
|
|
||||||
### Exclusion Patterns
|
### Exclusion Patterns
|
||||||
|
|
||||||
By default, all files in the import paths will be added to the library. If there are files that should not be added, exclusion patterns can be used to exclude them. Exclusion patterns are glob patterns are matched against the full file path. If a file matches an exclusion pattern, it will not be added to the library. Exclusion patterns can be added in the Scan Settings page for each library. Under the hood, Immich uses the [glob](https://www.npmjs.com/package/glob) package to match patterns, so please refer to [their documentation](https://github.com/isaacs/node-glob#glob-primer) to see what patterns are supported.
|
By default, all files in the import paths will be added to the library. If there are files that should not be added, exclusion patterns can be used to exclude them. Exclusion patterns are glob patterns are matched against the full file path. If a file matches an exclusion pattern, it will not be added to the library. Exclusion patterns can be added in the Scan Settings page for each library.
|
||||||
|
|
||||||
Some basic examples:
|
Some basic examples:
|
||||||
|
|
||||||
@@ -48,7 +48,11 @@ Some basic examples:
|
|||||||
|
|
||||||
Special characters such as @ should be escaped, for instance:
|
Special characters such as @ should be escaped, for instance:
|
||||||
|
|
||||||
- `**/\@eadir/**` will exclude all files in any directory named `@eadir`
|
- `**/\@eaDir/**` will exclude all files in any directory named `@eaDir`
|
||||||
|
|
||||||
|
:::info
|
||||||
|
Internally, Immich uses the [glob](https://www.npmjs.com/package/glob) package to process exclusion patterns, and sometimes those patterns are translated into [Postgres LIKE patterns](https://www.postgresql.org/docs/current/functions-matching.html). The intention is to support basic folder exclusions but we recommend against advanced usage since those can't reliably be translated to the Postgres syntax. Please refer to the [glob documentation](https://github.com/isaacs/node-glob#glob-primer) for a basic overview on glob patterns.
|
||||||
|
:::
|
||||||
|
|
||||||
### Automatic watching (EXPERIMENTAL)
|
### Automatic watching (EXPERIMENTAL)
|
||||||
|
|
||||||
@@ -68,7 +72,7 @@ In rare cases, the library watcher can hang, preventing Immich from starting up.
|
|||||||
|
|
||||||
### Nightly job
|
### Nightly job
|
||||||
|
|
||||||
There is an automatic scan job that is scheduled to run once a day. This job also cleans up any libraries stuck in deletion.
|
There is an automatic scan job that is scheduled to run once a day. This job also cleans up any libraries stuck in deletion. It is possible to trigger the cleanup by clicking "Scan all libraries" in the library managment page.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -91,7 +95,7 @@ The `immich-server` container will need access to the gallery. Modify your docke
|
|||||||
+ - /mnt/nas/christmas-trip:/mnt/media/christmas-trip:ro
|
+ - /mnt/nas/christmas-trip:/mnt/media/christmas-trip:ro
|
||||||
+ - /home/user/old-pics:/mnt/media/old-pics:ro
|
+ - /home/user/old-pics:/mnt/media/old-pics:ro
|
||||||
+ - /mnt/media/videos:/mnt/media/videos:ro
|
+ - /mnt/media/videos:/mnt/media/videos:ro
|
||||||
+ - /mnt/media/videos2:/mnt/media/videos2 # the files in this folder can be deleted, as it does not end with :ro
|
+ - /mnt/media/videos2:/mnt/media/videos2 # WARNING: Immich will be able to delete the files in this folder, as it does not end with :ro
|
||||||
+ - "C:/Users/user_name/Desktop/my media:/mnt/media/my-media:ro" # import path in Windows system.
|
+ - "C:/Users/user_name/Desktop/my media:/mnt/media/my-media:ro" # import path in Windows system.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ You do not need to redo any machine learning jobs after enabling hardware accele
|
|||||||
|
|
||||||
- ARM NN (Mali)
|
- ARM NN (Mali)
|
||||||
- CUDA (NVIDIA GPUs with [compute capability](https://developer.nvidia.com/cuda-gpus) 5.2 or higher)
|
- CUDA (NVIDIA GPUs with [compute capability](https://developer.nvidia.com/cuda-gpus) 5.2 or higher)
|
||||||
|
- ROCm (AMD GPUs)
|
||||||
- OpenVINO (Intel GPUs such as Iris Xe and Arc)
|
- OpenVINO (Intel GPUs such as Iris Xe and Arc)
|
||||||
|
- RKNN (Rockchip)
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
@@ -19,6 +21,7 @@ You do not need to redo any machine learning jobs after enabling hardware accele
|
|||||||
- Only Linux and Windows (through WSL2) servers are supported.
|
- Only Linux and Windows (through WSL2) servers are supported.
|
||||||
- ARM NN is only supported on devices with Mali GPUs. Other Arm devices are not supported.
|
- ARM NN is only supported on devices with Mali GPUs. Other Arm devices are not supported.
|
||||||
- Some models may not be compatible with certain backends. CUDA is the most reliable.
|
- Some models may not be compatible with certain backends. CUDA is the most reliable.
|
||||||
|
- Search latency isn't improved by ARM NN due to model compatibility issues preventing its use. However, smart search jobs do make use of ARM NN.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
@@ -33,6 +36,7 @@ You do not need to redo any machine learning jobs after enabling hardware accele
|
|||||||
- The `hwaccel.ml.yml` file assumes the path to it is `/usr/lib/libmali.so`, so update accordingly if it is elsewhere
|
- The `hwaccel.ml.yml` file assumes the path to it is `/usr/lib/libmali.so`, so update accordingly if it is elsewhere
|
||||||
- The `hwaccel.ml.yml` file assumes an additional file `/lib/firmware/mali_csffw.bin`, so update accordingly if your device's driver does not require this file
|
- The `hwaccel.ml.yml` file assumes an additional file `/lib/firmware/mali_csffw.bin`, so update accordingly if your device's driver does not require this file
|
||||||
- Optional: Configure your `.env` file, see [environment variables](/docs/install/environment-variables) for ARM NN specific settings
|
- Optional: Configure your `.env` file, see [environment variables](/docs/install/environment-variables) for ARM NN specific settings
|
||||||
|
- In particular, the `MACHINE_LEARNING_ANN_FP16_TURBO` can significantly improve performance at the cost of very slightly lower accuracy
|
||||||
|
|
||||||
#### CUDA
|
#### CUDA
|
||||||
|
|
||||||
@@ -41,22 +45,38 @@ You do not need to redo any machine learning jobs after enabling hardware accele
|
|||||||
- The installed driver must be >= 535 (it must support CUDA 12.2).
|
- The installed driver must be >= 535 (it must support CUDA 12.2).
|
||||||
- On Linux (except for WSL2), you also need to have [NVIDIA Container Toolkit][nvct] installed.
|
- On Linux (except for WSL2), you also need to have [NVIDIA Container Toolkit][nvct] installed.
|
||||||
|
|
||||||
|
#### ROCm
|
||||||
|
|
||||||
|
- The GPU must be supported by ROCm. If it isn't officially supported, you can attempt to use the `HSA_OVERRIDE_GFX_VERSION` environmental variable: `HSA_OVERRIDE_GFX_VERSION=<a supported version, e.g. 10.3.0>`. If this doesn't work, you might need to also set `HSA_USE_SVM=0`.
|
||||||
|
- The ROCm image is quite large and requires at least 35GiB of free disk space. However, pulling later updates to the service through Docker will generally only amount to a few hundred megabytes as the rest will be cached.
|
||||||
|
- This backend is new and may experience some issues. For example, GPU power consumption can be higher than usual after running inference, even if the machine learning service is idle. In this case, it will only go back to normal after being idle for 5 minutes (configurable with the [MACHINE_LEARNING_MODEL_TTL](/docs/install/environment-variables) setting).
|
||||||
|
|
||||||
#### OpenVINO
|
#### OpenVINO
|
||||||
|
|
||||||
- Integrated GPUs are more likely to experience issues than discrete GPUs, especially for older processors or servers with low RAM.
|
- Integrated GPUs are more likely to experience issues than discrete GPUs, especially for older processors or servers with low RAM.
|
||||||
- Ensure the server's kernel version is new enough to use the device for hardware accceleration.
|
- Ensure the server's kernel version is new enough to use the device for hardware accceleration.
|
||||||
- Expect higher RAM usage when using OpenVINO compared to CPU processing.
|
- Expect higher RAM usage when using OpenVINO compared to CPU processing.
|
||||||
|
|
||||||
|
#### RKNN
|
||||||
|
|
||||||
|
- You must have a supported Rockchip SoC: only RK3566, RK3568, RK3576 and RK3588 are supported at this moment.
|
||||||
|
- Make sure you have the appropriate linux kernel driver installed
|
||||||
|
- This is usually pre-installed on the device vendor's Linux images
|
||||||
|
- RKNPU driver V0.9.8 or later must be available in the host server
|
||||||
|
- You may confirm this by running `cat /sys/kernel/debug/rknpu/version` to check the version
|
||||||
|
- Optional: Configure your `.env` file, see [environment variables](/docs/install/environment-variables) for RKNN specific settings
|
||||||
|
- In particular, setting `MACHINE_LEARNING_RKNN_THREADS` to 2 or 3 can _dramatically_ improve performance for RK3576 and RK3588 compared to the default of 1, at the expense of multiplying the amount of RAM each model uses by that amount.
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
1. If you do not already have it, download the latest [`hwaccel.ml.yml`][hw-file] file and ensure it's in the same folder as the `docker-compose.yml`.
|
1. If you do not already have it, download the latest [`hwaccel.ml.yml`][hw-file] file and ensure it's in the same folder as the `docker-compose.yml`.
|
||||||
2. In the `docker-compose.yml` under `immich-machine-learning`, uncomment the `extends` section and change `cpu` to the appropriate backend.
|
2. In the `docker-compose.yml` under `immich-machine-learning`, uncomment the `extends` section and change `cpu` to the appropriate backend.
|
||||||
3. Still in `immich-machine-learning`, add one of -[armnn, cuda, openvino] to the `image` section's tag at the end of the line.
|
3. Still in `immich-machine-learning`, add one of -[armnn, cuda, rocm, openvino, rknn] to the `image` section's tag at the end of the line.
|
||||||
4. Redeploy the `immich-machine-learning` container with these updated settings.
|
4. Redeploy the `immich-machine-learning` container with these updated settings.
|
||||||
|
|
||||||
### Confirming Device Usage
|
### Confirming Device Usage
|
||||||
|
|
||||||
You can confirm the device is being recognized and used by checking its utilization. There are many tools to display this, such as `nvtop` for NVIDIA or Intel and `intel_gpu_top` for Intel.
|
You can confirm the device is being recognized and used by checking its utilization. There are many tools to display this, such as `nvtop` for NVIDIA or Intel, `intel_gpu_top` for Intel, and `radeontop` for AMD.
|
||||||
|
|
||||||
You can also check the logs of the `immich-machine-learning` container. When a Smart Search or Face Detection job begins, or when you search with text in Immich, you should either see a log for `Available ORT providers` containing the relevant provider (e.g. `CUDAExecutionProvider` in the case of CUDA), or a `Loaded ANN model` log entry without errors in the case of ARM NN.
|
You can also check the logs of the `immich-machine-learning` container. When a Smart Search or Face Detection job begins, or when you search with text in Immich, you should either see a log for `Available ORT providers` containing the relevant provider (e.g. `CUDAExecutionProvider` in the case of CUDA), or a `Loaded ANN model` log entry without errors in the case of ARM NN.
|
||||||
|
|
||||||
@@ -127,3 +147,12 @@ Note that you should increase job concurrencies to increase overall utilization
|
|||||||
- If you encounter an error when a model is running, try a different model to see if the issue is model-specific.
|
- If you encounter an error when a model is running, try a different model to see if the issue is model-specific.
|
||||||
- You may want to increase concurrency past the default for higher utilization. However, keep in mind that this will also increase VRAM consumption.
|
- You may want to increase concurrency past the default for higher utilization. However, keep in mind that this will also increase VRAM consumption.
|
||||||
- Larger models benefit more from hardware acceleration, if you have the VRAM for them.
|
- Larger models benefit more from hardware acceleration, if you have the VRAM for them.
|
||||||
|
- Compared to ARM NN, RKNPU has:
|
||||||
|
- Wider model support (including for search, which ARM NN does not accelerate)
|
||||||
|
- Less heat generation
|
||||||
|
- Very slightly lower accuracy (RKNPU always uses FP16, while ARM NN by default uses higher precision FP32 unless `MACHINE_LEARNING_ANN_FP16_TURBO` is enabled)
|
||||||
|
- Varying speed (tested on RK3588):
|
||||||
|
- If `MACHINE_LEARNING_RKNN_THREADS` is at the default of 1, RKNPU will have substantially lower throughput for ML jobs than ARM NN in most cases, but similar latency (such as when searching)
|
||||||
|
- If `MACHINE_LEARNING_RKNN_THREADS` is set to 3, it will be somewhat faster than ARM NN at FP32, but somewhat slower than ARM NN if `MACHINE_LEARNING_ANN_FP16_TURBO` is enabled
|
||||||
|
- When other tasks also use the GPU (like transcoding), RKNPU has a significant advantage over ARM NN as it uses the otherwise idle NPU instead of competing for GPU usage
|
||||||
|
- Lower RAM usage if `MACHINE_LEARNING_RKNN_THREADS` is at the default of 1, but significantly higher if greater than 1 (which is necessary for it to fully utilize the NPU and hence be comparable in speed to ARM NN)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@ For the full list, refer to the [Immich source code](https://github.com/immich-a
|
|||||||
| `JPEG 2000` | `.jp2` | :white_check_mark: | |
|
| `JPEG 2000` | `.jp2` | :white_check_mark: | |
|
||||||
| `JPEG` | `.webp` `.jpg` `.jpe` `.insp` | :white_check_mark: | |
|
| `JPEG` | `.webp` `.jpg` `.jpe` `.insp` | :white_check_mark: | |
|
||||||
| `JPEG XL` | `.jxl` | :white_check_mark: | |
|
| `JPEG XL` | `.jxl` | :white_check_mark: | |
|
||||||
| `PNG` | `.webp` | :white_check_mark: | |
|
| `PNG` | `.png` | :white_check_mark: | |
|
||||||
| `PSD` | `.psd` | :white_check_mark: | Adobe Photoshop |
|
| `PSD` | `.psd` | :white_check_mark: | Adobe Photoshop |
|
||||||
| `RAW` | `.raw` | :white_check_mark: | |
|
| `RAW` | `.raw` | :white_check_mark: | |
|
||||||
| `RW2` | `.rw2` | :white_check_mark: | |
|
| `RW2` | `.rw2` | :white_check_mark: | |
|
||||||
|
|||||||
72
docs/docs/guides/better-facial-clusters.md
Normal file
72
docs/docs/guides/better-facial-clusters.md
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# Better Facial Recognition Clusters
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
This guide explains how to optimize facial recognition in systems with large image libraries. By following these steps, you'll achieve better clustering of faces, reducing the need for manual merging.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
- **Best Suited For:** Large image libraries after importing a significant number of images.
|
||||||
|
- **Warning:** This method deletes all previously assigned names.
|
||||||
|
- **Tip:** **Always take a [backup](/docs/administration/backup-and-restore#database) before proceeding!**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step-by-Step Instructions
|
||||||
|
|
||||||
|
### Objective
|
||||||
|
|
||||||
|
To enhance face clustering and ensure the model effectively identifies faces using qualitative initial data.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Steps
|
||||||
|
|
||||||
|
#### 1. Adjust Machine Learning Settings
|
||||||
|
|
||||||
|
Navigate to:
|
||||||
|
**Admin → Administration → Settings → Machine Learning Settings**
|
||||||
|
|
||||||
|
Make the following changes:
|
||||||
|
|
||||||
|
- **Maximum recognition distance (Optional):**
|
||||||
|
Lower this value, e.g., to **0.4**, if the library contains people with similar facial features.
|
||||||
|
- **Minimum recognized faces:**
|
||||||
|
Set this to a **high value** (e.g., 20 For libraries with a large amount of assets (~100K+), and 10 for libraries with medium amount of assets (~40K+)).
|
||||||
|
> A high value ensures clusters only include faces that appear at least 20/`value` times in the library, improving the initial clustering process.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 2. Run Reset Jobs
|
||||||
|
|
||||||
|
Go to:
|
||||||
|
**Admin → Administration → Settings → Jobs**
|
||||||
|
|
||||||
|
Perform the following:
|
||||||
|
|
||||||
|
1. **FACIAL RECOGNITION → Reset**
|
||||||
|
|
||||||
|
> These reset jobs rebuild the recognition model based on the new settings.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 3. Refine Recognition with Lower Thresholds
|
||||||
|
|
||||||
|
Once the reset jobs are complete, refine the recognition as follows:
|
||||||
|
|
||||||
|
- **Step 1:**
|
||||||
|
Return to **Minimum recognized faces** in Machine Learning Settings and lower the value to **10** (In medium libraries we will lower the value from 10 to 5).
|
||||||
|
|
||||||
|
> Run the job: **FACIAL RECOGNITION → MISSING Mode**
|
||||||
|
|
||||||
|
- **Step 2:**
|
||||||
|
Lower the value again to **3**.
|
||||||
|
> Run the job: **FACIAL RECOGNITION → MISSING Mode**
|
||||||
|
|
||||||
|
:::tip try different values
|
||||||
|
For certain libraries with a larger or smaller amount of assets, other settings will be better or worse. It is recommended to try different values **before assigning names** and see which settings work best for your library.
|
||||||
|
:::
|
||||||
|
|
||||||
|
---
|
||||||
@@ -31,6 +31,10 @@ SELECT * FROM "assets" WHERE "originalPath" LIKE 'upload/library/admin/2023/%';
|
|||||||
SELECT * FROM "assets" WHERE "id" = '9f94e60f-65b6-47b7-ae44-a4df7b57f0e9';
|
SELECT * FROM "assets" WHERE "id" = '9f94e60f-65b6-47b7-ae44-a4df7b57f0e9';
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```sql title="Find by partial ID"
|
||||||
|
SELECT * FROM "assets" WHERE "id"::text LIKE '%ab431d3a%';
|
||||||
|
```
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
You can calculate the checksum for a particular file by using the command `sha1sum <filename>`.
|
You can calculate the checksum for a particular file by using the command `sha1sum <filename>`.
|
||||||
:::
|
:::
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ name: immich_remote_ml
|
|||||||
services:
|
services:
|
||||||
immich-machine-learning:
|
immich-machine-learning:
|
||||||
container_name: immich_machine_learning
|
container_name: immich_machine_learning
|
||||||
# For hardware acceleration, add one of -[armnn, cuda, openvino] to the image tag.
|
# For hardware acceleration, add one of -[armnn, cuda, rocm, openvino, rknn] to the image tag.
|
||||||
# Example tag: ${IMMICH_VERSION:-release}-cuda
|
# Example tag: ${IMMICH_VERSION:-release}-cuda
|
||||||
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
|
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
|
||||||
# extends:
|
# extends:
|
||||||
# file: hwaccel.ml.yml
|
# file: hwaccel.ml.yml
|
||||||
# service: # set to one of [armnn, cuda, openvino, openvino-wsl] for accelerated inference - use the `-wsl` version for WSL2 where applicable
|
# service: # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference - use the `-wsl` version for WSL2 where applicable
|
||||||
volumes:
|
volumes:
|
||||||
- model-cache:/cache
|
- model-cache:/cache
|
||||||
restart: always
|
restart: always
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 100
|
||||||
|
---
|
||||||
|
|
||||||
# Config File
|
# Config File
|
||||||
|
|
||||||
A config file can be provided as an alternative to the UI configuration.
|
A config file can be provided as an alternative to the UI configuration.
|
||||||
|
|||||||
@@ -69,39 +69,7 @@ If you get an error `can't set healthcheck.start_interval as feature require Doc
|
|||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
Read the [Post Installation](/docs/install/post-install.mdx) steps or setup optional features below.
|
Read the [Post Installation](/docs/install/post-install.mdx) steps and [upgrade instructions](/docs/install/upgrading.md).
|
||||||
|
|
||||||
### Setting up optional features
|
|
||||||
|
|
||||||
- [External Libraries](/docs/features/libraries.md): Adding your existing photo library to Immich
|
|
||||||
- [Hardware Transcoding](/docs/features/hardware-transcoding.md): Speeding up video transcoding
|
|
||||||
- [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md): Speeding up various machine learning tasks in Immich
|
|
||||||
|
|
||||||
### Upgrading
|
|
||||||
|
|
||||||
:::danger Read the release notes
|
|
||||||
Immich is currently under heavy development, which means you can expect [breaking changes][breaking] and bugs. Therefore, we recommend reading the release notes prior to updating and to take special care when using automated tools like [Watchtower][watchtower].
|
|
||||||
|
|
||||||
You can see versions that had breaking changes [here][breaking].
|
|
||||||
:::
|
|
||||||
|
|
||||||
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][releases], the application can be upgraded and restarted with the following commands, run in the directory with the `docker-compose.yml` file:
|
|
||||||
|
|
||||||
```bash title="Upgrade and restart Immich"
|
|
||||||
docker compose pull && docker compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
To clean up disk space, the old version's obsolete container images can be deleted with the following command:
|
|
||||||
|
|
||||||
```bash title="Clean up unused Docker images"
|
|
||||||
docker image prune
|
|
||||||
```
|
|
||||||
|
|
||||||
[compose-file]: https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
[compose-file]: https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
||||||
[env-file]: https://github.com/immich-app/immich/releases/latest/download/example.env
|
[env-file]: https://github.com/immich-app/immich/releases/latest/download/example.env
|
||||||
[watchtower]: https://containrrr.dev/watchtower/
|
|
||||||
[breaking]: https://github.com/immich-app/immich/discussions?discussions_q=label%3Achangelog%3Abreaking-change+sort%3Adate_created
|
|
||||||
[container-auth]: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#authenticating-to-the-container-registry
|
|
||||||
[releases]: https://github.com/immich-app/immich/releases
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ Just restarting the containers does not replace the environment within the conta
|
|||||||
|
|
||||||
In order to recreate the container using docker compose, run `docker compose up -d`.
|
In order to recreate the container using docker compose, run `docker compose up -d`.
|
||||||
In most cases docker will recognize that the `.env` file has changed and recreate the affected containers.
|
In most cases docker will recognize that the `.env` file has changed and recreate the affected containers.
|
||||||
If this should not work, try running `docker compose up -d --force-recreate`.
|
If this does not work, try running `docker compose up -d --force-recreate`.
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
@@ -20,8 +20,8 @@ If this should not work, try running `docker compose up -d --force-recreate`.
|
|||||||
| Variable | Description | Default | Containers |
|
| Variable | Description | Default | Containers |
|
||||||
| :----------------- | :------------------------------ | :-------: | :----------------------- |
|
| :----------------- | :------------------------------ | :-------: | :----------------------- |
|
||||||
| `IMMICH_VERSION` | Image tags | `release` | server, machine learning |
|
| `IMMICH_VERSION` | Image tags | `release` | server, machine learning |
|
||||||
| `UPLOAD_LOCATION` | Host Path for uploads | | server |
|
| `UPLOAD_LOCATION` | Host path for uploads | | server |
|
||||||
| `DB_DATA_LOCATION` | Host Path for Postgres database | | database |
|
| `DB_DATA_LOCATION` | Host path for Postgres database | | database |
|
||||||
|
|
||||||
:::tip
|
:::tip
|
||||||
These environment variables are used by the `docker-compose.yml` file and do **NOT** affect the containers directly.
|
These environment variables are used by the `docker-compose.yml` file and do **NOT** affect the containers directly.
|
||||||
@@ -33,15 +33,15 @@ These environment variables are used by the `docker-compose.yml` file and do **N
|
|||||||
| :---------------------------------- | :---------------------------------------------------------------------------------------- | :--------------------------: | :----------------------- | :----------------- |
|
| :---------------------------------- | :---------------------------------------------------------------------------------------- | :--------------------------: | :----------------------- | :----------------- |
|
||||||
| `TZ` | Timezone | <sup>\*1</sup> | server | microservices |
|
| `TZ` | Timezone | <sup>\*1</sup> | server | microservices |
|
||||||
| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices |
|
| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices |
|
||||||
| `IMMICH_LOG_LEVEL` | Log Level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices |
|
| `IMMICH_LOG_LEVEL` | Log level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices |
|
||||||
| `IMMICH_MEDIA_LOCATION` | Media Location inside the container ⚠️**You probably shouldn't set this**<sup>\*2</sup>⚠️ | `./upload`<sup>\*3</sup> | server | api, microservices |
|
| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**<sup>\*2</sup>⚠️ | `./upload`<sup>\*3</sup> | server | api, microservices |
|
||||||
| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices |
|
| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices |
|
||||||
| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | |
|
| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | |
|
||||||
| `CPU_CORES` | Amount of cores available to the immich server | auto-detected cpu core count | server | |
|
| `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | |
|
||||||
| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api |
|
| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api |
|
||||||
| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices |
|
| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices |
|
||||||
| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices |
|
| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices |
|
||||||
| `IMMICH_TRUSTED_PROXIES` | List of comma separated IPs set as trusted proxies | | server | api |
|
| `IMMICH_TRUSTED_PROXIES` | List of comma-separated IPs set as trusted proxies | | server | api |
|
||||||
| `IMMICH_IGNORE_MOUNT_CHECK_ERRORS` | See [System Integrity](/docs/administration/system-integrity) | | server | api, microservices |
|
| `IMMICH_IGNORE_MOUNT_CHECK_ERRORS` | See [System Integrity](/docs/administration/system-integrity) | | server | api, microservices |
|
||||||
|
|
||||||
\*1: `TZ` should be set to a `TZ identifier` from [this list][tz-list]. For example, `TZ="Etc/UTC"`.
|
\*1: `TZ` should be set to a `TZ identifier` from [this list][tz-list]. For example, `TZ="Etc/UTC"`.
|
||||||
@@ -50,7 +50,7 @@ These environment variables are used by the `docker-compose.yml` file and do **N
|
|||||||
\*2: This path is where the Immich code looks for the files, which is internal to the docker container. Setting it to a path on your host will certainly break things, you should use the `UPLOAD_LOCATION` variable instead.
|
\*2: This path is where the Immich code looks for the files, which is internal to the docker container. Setting it to a path on your host will certainly break things, you should use the `UPLOAD_LOCATION` variable instead.
|
||||||
|
|
||||||
\*3: With the default `WORKDIR` of `/usr/src/app`, this path will resolve to `/usr/src/app/upload`.
|
\*3: With the default `WORKDIR` of `/usr/src/app`, this path will resolve to `/usr/src/app/upload`.
|
||||||
It only need to be set if the Immich deployment method is changing.
|
It only needs to be set if the Immich deployment method is changing.
|
||||||
|
|
||||||
## Workers
|
## Workers
|
||||||
|
|
||||||
@@ -75,12 +75,12 @@ Information on the current workers can be found [here](/docs/administration/jobs
|
|||||||
| Variable | Description | Default | Containers |
|
| Variable | Description | Default | Containers |
|
||||||
| :---------------------------------- | :----------------------------------------------------------------------- | :----------: | :----------------------------- |
|
| :---------------------------------- | :----------------------------------------------------------------------- | :----------: | :----------------------------- |
|
||||||
| `DB_URL` | Database URL | | server |
|
| `DB_URL` | Database URL | | server |
|
||||||
| `DB_HOSTNAME` | Database Host | `database` | server |
|
| `DB_HOSTNAME` | Database host | `database` | server |
|
||||||
| `DB_PORT` | Database Port | `5432` | server |
|
| `DB_PORT` | Database port | `5432` | server |
|
||||||
| `DB_USERNAME` | Database User | `postgres` | server, database<sup>\*1</sup> |
|
| `DB_USERNAME` | Database user | `postgres` | server, database<sup>\*1</sup> |
|
||||||
| `DB_PASSWORD` | Database Password | `postgres` | server, database<sup>\*1</sup> |
|
| `DB_PASSWORD` | Database password | `postgres` | server, database<sup>\*1</sup> |
|
||||||
| `DB_DATABASE_NAME` | Database Name | `immich` | server, database<sup>\*1</sup> |
|
| `DB_DATABASE_NAME` | Database name | `immich` | server, database<sup>\*1</sup> |
|
||||||
| `DB_VECTOR_EXTENSION`<sup>\*2</sup> | Database Vector Extension (one of [`pgvector`, `pgvecto.rs`]) | `pgvecto.rs` | server |
|
| `DB_VECTOR_EXTENSION`<sup>\*2</sup> | Database vector extension (one of [`pgvector`, `pgvecto.rs`]) | `pgvecto.rs` | server |
|
||||||
| `DB_SKIP_MIGRATIONS` | Whether to skip running migrations on startup (one of [`true`, `false`]) | `false` | server |
|
| `DB_SKIP_MIGRATIONS` | Whether to skip running migrations on startup (one of [`true`, `false`]) | `false` | server |
|
||||||
|
|
||||||
\*1: The values of `DB_USERNAME`, `DB_PASSWORD`, and `DB_DATABASE_NAME` are passed to the Postgres container as the variables `POSTGRES_USER`, `POSTGRES_PASSWORD`, and `POSTGRES_DB` in `docker-compose.yml`.
|
\*1: The values of `DB_USERNAME`, `DB_PASSWORD`, and `DB_DATABASE_NAME` are passed to the Postgres container as the variables `POSTGRES_USER`, `POSTGRES_PASSWORD`, and `POSTGRES_DB` in `docker-compose.yml`.
|
||||||
@@ -103,18 +103,18 @@ When `DB_URL` is defined, the `DB_HOSTNAME`, `DB_PORT`, `DB_USERNAME`, `DB_PASSW
|
|||||||
| Variable | Description | Default | Containers |
|
| Variable | Description | Default | Containers |
|
||||||
| :--------------- | :------------- | :-----: | :--------- |
|
| :--------------- | :------------- | :-----: | :--------- |
|
||||||
| `REDIS_URL` | Redis URL | | server |
|
| `REDIS_URL` | Redis URL | | server |
|
||||||
| `REDIS_SOCKET` | Redis Socket | | server |
|
| `REDIS_SOCKET` | Redis socket | | server |
|
||||||
| `REDIS_HOSTNAME` | Redis Host | `redis` | server |
|
| `REDIS_HOSTNAME` | Redis host | `redis` | server |
|
||||||
| `REDIS_PORT` | Redis Port | `6379` | server |
|
| `REDIS_PORT` | Redis port | `6379` | server |
|
||||||
| `REDIS_USERNAME` | Redis Username | | server |
|
| `REDIS_USERNAME` | Redis username | | server |
|
||||||
| `REDIS_PASSWORD` | Redis Password | | server |
|
| `REDIS_PASSWORD` | Redis password | | server |
|
||||||
| `REDIS_DBINDEX` | Redis DB Index | `0` | server |
|
| `REDIS_DBINDEX` | Redis DB index | `0` | server |
|
||||||
|
|
||||||
:::info
|
:::info
|
||||||
All `REDIS_` variables must be provided to all Immich workers, including `api` and `microservices`.
|
All `REDIS_` variables must be provided to all Immich workers, including `api` and `microservices`.
|
||||||
|
|
||||||
`REDIS_URL` must start with `ioredis://` and then include a `base64` encoded JSON string for the configuration.
|
`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] documentation.
|
More information can be found in the upstream [ioredis] documentation.
|
||||||
|
|
||||||
When `REDIS_URL` or `REDIS_SOCKET` are defined, the `REDIS_HOSTNAME`, `REDIS_PORT`, `REDIS_USERNAME`, `REDIS_PASSWORD`, and `REDIS_DBINDEX` variables are ignored.
|
When `REDIS_URL` or `REDIS_SOCKET` are defined, the `REDIS_HOSTNAME`, `REDIS_PORT`, `REDIS_USERNAME`, `REDIS_PASSWORD`, and `REDIS_DBINDEX` variables are ignored.
|
||||||
:::
|
:::
|
||||||
@@ -170,6 +170,8 @@ Redis (Sentinel) URL example JSON before encoding:
|
|||||||
| `MACHINE_LEARNING_MAX_BATCH_SIZE__FACIAL_RECOGNITION` | Set the maximum number of faces that will be processed at once by the facial recognition model | None (`1` if using OpenVINO) | machine learning |
|
| `MACHINE_LEARNING_MAX_BATCH_SIZE__FACIAL_RECOGNITION` | Set the maximum number of faces that will be processed at once by the facial recognition model | None (`1` if using OpenVINO) | machine learning |
|
||||||
| `MACHINE_LEARNING_PING_TIMEOUT` | How long (ms) to wait for a PING response when checking if an ML server is available | `2000` | server |
|
| `MACHINE_LEARNING_PING_TIMEOUT` | How long (ms) to wait for a PING response when checking if an ML server is available | `2000` | server |
|
||||||
| `MACHINE_LEARNING_AVAILABILITY_BACKOFF_TIME` | How long to ignore ML servers that are offline before trying again | `30000` | server |
|
| `MACHINE_LEARNING_AVAILABILITY_BACKOFF_TIME` | How long to ignore ML servers that are offline before trying again | `30000` | server |
|
||||||
|
| `MACHINE_LEARNING_RKNN` | Enable RKNN hardware acceleration if supported | `True` | machine learning |
|
||||||
|
| `MACHINE_LEARNING_RKNN_THREADS` | How many threads of RKNN runtime should be spinned up while inferencing. | `1` | machine learning |
|
||||||
|
|
||||||
\*1: It is recommended to begin with this parameter when changing the concurrency levels of the machine learning service and then tune the other ones.
|
\*1: It is recommended to begin with this parameter when changing the concurrency levels of the machine learning service and then tune the other ones.
|
||||||
|
|
||||||
@@ -181,7 +183,11 @@ Redis (Sentinel) URL example JSON before encoding:
|
|||||||
|
|
||||||
:::info
|
:::info
|
||||||
|
|
||||||
Other machine learning parameters can be tuned from the admin UI.
|
While the `textual` model is the only one required for smart search, some users may experience slow first searches
|
||||||
|
due to backups triggering loading of the other models into memory, which blocks other requests until completed.
|
||||||
|
To avoid this, you can preload the other models (`visual`, `recognition`, and `detection`) if you have enough RAM to do so.
|
||||||
|
|
||||||
|
Additional machine learning parameters can be tuned from the admin UI.
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
@@ -212,7 +218,7 @@ the `_FILE` variable should be set to the path of a file containing the variable
|
|||||||
details on how to use Docker Secrets in the Postgres image.
|
details on how to use Docker Secrets in the Postgres image.
|
||||||
|
|
||||||
\*2: See [this comment][docker-secrets-example] for an example of how
|
\*2: See [this comment][docker-secrets-example] for an example of how
|
||||||
to use use a Docker secret for the password in the Redis container.
|
to use a Docker secret for the password in the Redis container.
|
||||||
|
|
||||||
[tz-list]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
|
[tz-list]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
|
||||||
[docker-secrets-example]: https://github.com/docker-library/redis/issues/46#issuecomment-335326234
|
[docker-secrets-example]: https://github.com/docker-library/redis/issues/46#issuecomment-335326234
|
||||||
|
|||||||
@@ -41,3 +41,9 @@ A list of common steps to take after installing Immich include:
|
|||||||
## Step 7 - Setup Server Backups
|
## Step 7 - Setup Server Backups
|
||||||
|
|
||||||
<ServerBackup />
|
<ServerBackup />
|
||||||
|
|
||||||
|
## Setting up optional features
|
||||||
|
|
||||||
|
- [External Libraries](/docs/features/libraries.md): Adding your existing photo library to Immich
|
||||||
|
- [Hardware Transcoding](/docs/features/hardware-transcoding.md): Speeding up video transcoding
|
||||||
|
- [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md): Speeding up various machine learning tasks in Immich
|
||||||
|
|||||||
@@ -67,10 +67,4 @@ Click "**Edit Rules**" and add the following firewall rules:
|
|||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
Read the [Post Installation](/docs/install/post-install.mdx) steps or setup optional features below.
|
Read the [Post Installation](/docs/install/post-install.mdx) steps and [upgrade instructions](/docs/install/upgrading.md).
|
||||||
|
|
||||||
### Setting up optional features
|
|
||||||
|
|
||||||
- [External Libraries](/docs/features/libraries.md): Adding your existing photo library to Immich
|
|
||||||
- [Hardware Transcoding](/docs/features/hardware-transcoding.md): Speeding up video transcoding
|
|
||||||
- [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md): Speeding up various machine learning tasks in Immich
|
|
||||||
|
|||||||
@@ -247,6 +247,10 @@ Some examples are: `IMMICH_VERSION`, `UPLOAD_LOCATION`, `DB_DATA_LOCATION`, `TZ`
|
|||||||
|
|
||||||
## Updating the App
|
## Updating the App
|
||||||
|
|
||||||
|
:::danger
|
||||||
|
Make sure to read the general [upgrade instructions](/docs/install/upgrading.md).
|
||||||
|
:::
|
||||||
|
|
||||||
When updates become available, SCALE alerts and provides easy updates.
|
When updates become available, SCALE alerts and provides easy updates.
|
||||||
To update the app to the latest version:
|
To update the app to the latest version:
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ alt="Select Plugins > Compose.Manager > Add New Stack > Label it Immich"
|
|||||||
7. Paste the entire contents of the [Immich example.env](https://github.com/immich-app/immich/releases/latest/download/example.env) file into the Unraid editor, then **before saving** edit the following:
|
7. Paste the entire contents of the [Immich example.env](https://github.com/immich-app/immich/releases/latest/download/example.env) file into the Unraid editor, then **before saving** edit the following:
|
||||||
|
|
||||||
- `UPLOAD_LOCATION`: Create a folder in your Images Unraid share and place the **absolute** location here > For example my _"images"_ share has a folder within it called _"immich"_. If I browse to this directory in the terminal and type `pwd` the output is `/mnt/user/images/immich`. This is the exact value I need to enter as my `UPLOAD_LOCATION`
|
- `UPLOAD_LOCATION`: Create a folder in your Images Unraid share and place the **absolute** location here > For example my _"images"_ share has a folder within it called _"immich"_. If I browse to this directory in the terminal and type `pwd` the output is `/mnt/user/images/immich`. This is the exact value I need to enter as my `UPLOAD_LOCATION`
|
||||||
- `DB_DATA_LOCATION`: Change this to use an Unraid share (preferably a cache pool, e.g. `/mnt/user/appdata`). If left at default it will try to use Unraid's `/boot/config/plugins/compose.manager/projects/[stack_name]/postgres` folder which it doesn't have permissions to, resulting in this container continuously restarting.
|
- `DB_DATA_LOCATION`: Change this to use an Unraid share (preferably a cache pool, e.g. `/mnt/user/appdata/postgresql/data`). This uses the `appdata` share. Do also create the `postgresql` folder, by running `mkdir /mnt/user/{share_location}/postgresql/data`. If left at default it will try to use Unraid's `/boot/config/plugins/compose.manager/projects/[stack_name]/postgres` folder which it doesn't have permissions to, resulting in this container continuously restarting.
|
||||||
|
|
||||||
<img
|
<img
|
||||||
src={require('./img/unraid05.webp').default}
|
src={require('./img/unraid05.webp').default}
|
||||||
@@ -131,6 +131,10 @@ For more information on how to use the application once installed, please refer
|
|||||||
|
|
||||||
## Updating Steps
|
## Updating Steps
|
||||||
|
|
||||||
|
:::danger
|
||||||
|
Make sure to read the general [upgrade instructions](/docs/install/upgrading.md).
|
||||||
|
:::
|
||||||
|
|
||||||
Updating is extremely easy however it's important to be aware that containers managed via the Docker Compose Manager plugin do not integrate with Unraid's native dockerman UI, the label "_update ready_" will always be present on containers installed via the Docker Compose Manager.
|
Updating is extremely easy however it's important to be aware that containers managed via the Docker Compose Manager plugin do not integrate with Unraid's native dockerman UI, the label "_update ready_" will always be present on containers installed via the Docker Compose Manager.
|
||||||
|
|
||||||
<img
|
<img
|
||||||
|
|||||||
29
docs/docs/install/upgrading.md
Normal file
29
docs/docs/install/upgrading.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 95
|
||||||
|
---
|
||||||
|
|
||||||
|
# Upgrading
|
||||||
|
|
||||||
|
:::danger Read the release notes
|
||||||
|
Immich is currently under heavy development, which means you can expect [breaking changes][breaking] and bugs. You should read the release notes prior to updating and take special care when using automated tools like [Watchtower][watchtower].
|
||||||
|
|
||||||
|
You can see versions that had breaking changes [here][breaking].
|
||||||
|
:::
|
||||||
|
|
||||||
|
When a new version of Immich is [released][releases], you should read the release notes and account for any breaking changes noted (as mentioned above).
|
||||||
|
If you use `IMMICH_VERSION` in your `.env` file, it will need to be updated to the latest or desired version.
|
||||||
|
After that, the application can be upgraded and restarted with the following commands, run in the directory with the `docker-compose.yml` file:
|
||||||
|
|
||||||
|
```bash title="Upgrade and restart Immich"
|
||||||
|
docker compose pull && docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
To clean up disk space, the old version's obsolete container images can be deleted with the following command:
|
||||||
|
|
||||||
|
```bash title="Clean up unused Docker images"
|
||||||
|
docker image prune
|
||||||
|
```
|
||||||
|
|
||||||
|
[watchtower]: https://containrrr.dev/watchtower/
|
||||||
|
[breaking]: https://github.com/immich-app/immich/discussions?discussions_q=label%3Achangelog%3Abreaking-change+sort%3Adate_created
|
||||||
|
[releases]: https://github.com/immich-app/immich/releases
|
||||||
@@ -1,2 +1,7 @@
|
|||||||
Now that you have imported some pictures, you should setup server backups to preserve your memories.
|
Now that you have imported some pictures, you should setup server backups to preserve your memories.
|
||||||
You can do so by following our [backup guide](/docs/administration/backup-and-restore.md).
|
You can do so by following our [backup guide](/docs/administration/backup-and-restore.md).
|
||||||
|
|
||||||
|
:::danger
|
||||||
|
Immich is still under heavy development _and_ handles very important data.
|
||||||
|
It is essential that you set up good backups, and test them.
|
||||||
|
:::
|
||||||
|
|||||||
36
docs/package-lock.json
generated
36
docs/package-lock.json
generated
@@ -5308,9 +5308,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/autoprefixer": {
|
"node_modules/autoprefixer": {
|
||||||
"version": "10.4.20",
|
"version": "10.4.21",
|
||||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
|
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
|
||||||
"integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==",
|
"integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -5327,11 +5327,11 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"browserslist": "^4.23.3",
|
"browserslist": "^4.24.4",
|
||||||
"caniuse-lite": "^1.0.30001646",
|
"caniuse-lite": "^1.0.30001702",
|
||||||
"fraction.js": "^4.3.7",
|
"fraction.js": "^4.3.7",
|
||||||
"normalize-range": "^0.1.2",
|
"normalize-range": "^0.1.2",
|
||||||
"picocolors": "^1.0.1",
|
"picocolors": "^1.1.1",
|
||||||
"postcss-value-parser": "^4.2.0"
|
"postcss-value-parser": "^4.2.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -5781,9 +5781,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001695",
|
"version": "1.0.30001706",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001706.tgz",
|
||||||
"integrity": "sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==",
|
"integrity": "sha512-3ZczoTApMAZwPKYWmwVbQMFpXBDds3/0VciVoUwPUbldlYyVLmRVuRs/PcUZtHpbLRpzzDvrvnFuREsGt6lUug==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -14070,9 +14070,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.2",
|
"version": "8.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
|
||||||
"integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==",
|
"integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -15734,9 +15734,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
"node_modules/prettier": {
|
||||||
"version": "3.5.1",
|
"version": "3.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
|
||||||
"integrity": "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==",
|
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -18377,9 +18377,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.7.3",
|
"version": "5.8.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
|
||||||
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
|
"integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export const discordPath =
|
export const discordPath =
|
||||||
'M 9.1367188 3.8691406 C 9.1217187 3.8691406 9.1067969 3.8700938 9.0917969 3.8710938 C 8.9647969 3.8810937 5.9534375 4.1403594 4.0234375 5.6933594 C 3.0154375 6.6253594 1 12.073203 1 16.783203 C 1 16.866203 1.0215 16.946531 1.0625 17.019531 C 2.4535 19.462531 6.2473281 20.102859 7.1113281 20.130859 L 7.1269531 20.130859 C 7.2799531 20.130859 7.4236719 20.057594 7.5136719 19.933594 L 8.3886719 18.732422 C 6.0296719 18.122422 4.8248594 17.086391 4.7558594 17.025391 C 4.5578594 16.850391 4.5378906 16.549563 4.7128906 16.351562 C 4.8068906 16.244563 4.9383125 16.189453 5.0703125 16.189453 C 5.1823125 16.189453 5.2957188 16.228594 5.3867188 16.308594 C 5.4157187 16.334594 7.6340469 18.216797 11.998047 18.216797 C 16.370047 18.216797 18.589328 16.325641 18.611328 16.306641 C 18.702328 16.227641 18.815734 16.189453 18.927734 16.189453 C 19.059734 16.189453 19.190156 16.243562 19.285156 16.351562 C 19.459156 16.549563 19.441141 16.851391 19.244141 17.025391 C 19.174141 17.087391 17.968375 18.120469 15.609375 18.730469 L 16.484375 19.933594 C 16.574375 20.057594 16.718094 20.130859 16.871094 20.130859 L 16.886719 20.130859 C 17.751719 20.103859 21.5465 19.463531 22.9375 17.019531 C 22.9785 16.947531 23 16.866203 23 16.783203 C 23 12.073203 20.984172 6.624875 19.951172 5.671875 C 18.047172 4.140875 15.036203 3.8820937 14.908203 3.8710938 C 14.895203 3.8700938 14.880188 3.8691406 14.867188 3.8691406 C 14.681188 3.8691406 14.510594 3.9793906 14.433594 4.1503906 C 14.427594 4.1623906 14.362062 4.3138281 14.289062 4.5488281 C 15.548063 4.7608281 17.094141 5.1895937 18.494141 6.0585938 C 18.718141 6.1975938 18.787437 6.4917969 18.648438 6.7167969 C 18.558438 6.8627969 18.402188 6.9433594 18.242188 6.9433594 C 18.156188 6.9433594 18.069234 6.9200937 17.990234 6.8710938 C 15.584234 5.3800938 12.578 5.3046875 12 5.3046875 C 11.422 5.3046875 8.4157187 5.3810469 6.0117188 6.8730469 C 5.9327188 6.9210469 5.8457656 6.9433594 5.7597656 6.9433594 C 5.5997656 6.9433594 5.4425625 6.86475 5.3515625 6.71875 C 5.2115625 6.49375 5.2818594 6.1985938 5.5058594 6.0585938 C 6.9058594 5.1905937 8.4528906 4.7627812 9.7128906 4.5507812 C 9.6388906 4.3147813 9.5714062 4.1643437 9.5664062 4.1523438 C 9.4894063 3.9813438 9.3217188 3.8691406 9.1367188 3.8691406 z M 12 7.3046875 C 12.296 7.3046875 14.950594 7.3403125 16.933594 8.5703125 C 17.326594 8.8143125 17.777234 8.9453125 18.240234 8.9453125 C 18.633234 8.9453125 19.010656 8.8555 19.347656 8.6875 C 19.964656 10.2405 20.690828 12.686219 20.923828 15.199219 C 20.883828 15.143219 20.840922 15.089109 20.794922 15.037109 C 20.324922 14.498109 19.644687 14.191406 18.929688 14.191406 C 18.332687 14.191406 17.754078 14.405437 17.330078 14.773438 C 17.257078 14.832437 15.505 16.21875 12 16.21875 C 8.496 16.21875 6.7450313 14.834687 6.7070312 14.804688 C 6.2540312 14.407687 5.6742656 14.189453 5.0722656 14.189453 C 4.3612656 14.189453 3.6838438 14.494391 3.2148438 15.025391 C 3.1658438 15.080391 3.1201719 15.138266 3.0761719 15.197266 C 3.3091719 12.686266 4.0344375 10.235594 4.6484375 8.6835938 C 4.9864375 8.8525938 5.3657656 8.9433594 5.7597656 8.9433594 C 6.2217656 8.9433594 6.6724531 8.8143125 7.0644531 8.5703125 C 9.0494531 7.3393125 11.704 7.3046875 12 7.3046875 z M 8.890625 10.044922 C 7.966625 10.044922 7.2167969 10.901031 7.2167969 11.957031 C 7.2167969 13.013031 7.965625 13.869141 8.890625 13.869141 C 9.815625 13.869141 10.564453 13.013031 10.564453 11.957031 C 10.564453 10.900031 9.815625 10.044922 8.890625 10.044922 z M 15.109375 10.044922 C 14.185375 10.044922 13.435547 10.901031 13.435547 11.957031 C 13.435547 13.013031 14.184375 13.869141 15.109375 13.869141 C 16.034375 13.869141 16.783203 13.013031 16.783203 11.957031 C 16.783203 10.900031 16.033375 10.044922 15.109375 10.044922 z';
|
'M81.15,0c-1.2376,2.1973-2.3489,4.4704-3.3591,6.794-9.5975-1.4396-19.3718-1.4396-28.9945,0-.985-2.3236-2.1216-4.5967-3.3591-6.794-9.0166,1.5407-17.8059,4.2431-26.1405,8.0568C2.779,32.5304-1.6914,56.3725.5312,79.8863c9.6732,7.1476,20.5083,12.603,32.0505,16.0884,2.6014-3.4854,4.8998-7.1981,6.8698-11.0623-3.738-1.3891-7.3497-3.1318-10.8098-5.1523.9092-.6567,1.7932-1.3386,2.6519-1.9953,20.281,9.547,43.7696,9.547,64.0758,0,.8587.7072,1.7427,1.3891,2.6519,1.9953-3.4601,2.0457-7.0718,3.7632-10.835,5.1776,1.97,3.8642,4.2683,7.5769,6.8698,11.0623,11.5419-3.4854,22.3769-8.9156,32.0509-16.0631,2.626-27.2771-4.496-50.9172-18.817-71.8548C98.9811,4.2684,90.1918,1.5659,81.1752.0505l-.0252-.0505ZM42.2802,65.4144c-6.2383,0-11.4159-5.6575-11.4159-12.6535s4.9755-12.6788,11.3907-12.6788,11.5169,5.708,11.4159,12.6788c-.101,6.9708-5.026,12.6535-11.3907,12.6535ZM84.3576,65.4144c-6.2637,0-11.3907-5.6575-11.3907-12.6535s4.9755-12.6788,11.3907-12.6788,11.4917,5.708,11.3906,12.6788c-.101,6.9708-5.026,12.6535-11.3906,12.6535Z';
|
||||||
|
export const discordViewBox = '0 0 126.644 96';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import React, { useEffect, useState } from 'react';
|
|||||||
|
|
||||||
export default function VersionSwitcher(): JSX.Element {
|
export default function VersionSwitcher(): JSX.Element {
|
||||||
const [versions, setVersions] = useState([]);
|
const [versions, setVersions] = useState([]);
|
||||||
const [label, setLabel] = useState('Versions');
|
const [activeLabel, setLabel] = useState('Versions');
|
||||||
|
|
||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
|
|
||||||
@@ -48,12 +48,13 @@ export default function VersionSwitcher(): JSX.Element {
|
|||||||
versions.length > 0 && (
|
versions.length > 0 && (
|
||||||
<DropdownNavbarItem
|
<DropdownNavbarItem
|
||||||
className="version-switcher-34ab39"
|
className="version-switcher-34ab39"
|
||||||
label={label}
|
label={activeLabel}
|
||||||
mobile={windowSize === 'mobile'}
|
mobile={windowSize === 'mobile'}
|
||||||
items={versions.map(({ label, url }) => ({
|
items={versions.map(({ label, url }) => ({
|
||||||
label,
|
label,
|
||||||
to: new URL(location.pathname + location.search + location.hash, url).href,
|
to: new URL(location.pathname + location.search + location.hash, url).href,
|
||||||
target: '_self',
|
target: '_self',
|
||||||
|
className: label === activeLabel ? 'dropdown__link--active menu__link--active' : '', // workaround because React Router `<NavLink>` only supports using URL path for checking if active: https://v5.reactrouter.com/web/api/NavLink/isactive-func
|
||||||
}))}
|
}))}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Link from '@docusaurus/Link';
|
import Link from '@docusaurus/Link';
|
||||||
import Layout from '@theme/Layout';
|
import Layout from '@theme/Layout';
|
||||||
import { useColorMode } from '@docusaurus/theme-common';
|
import { discordPath, discordViewBox } from '@site/src/components/svg-paths';
|
||||||
import { discordPath } from '@site/src/components/svg-paths';
|
import ThemedImage from '@theme/ThemedImage';
|
||||||
import Icon from '@mdi/react';
|
import Icon from '@mdi/react';
|
||||||
function HomepageHeader() {
|
function HomepageHeader() {
|
||||||
const { isDarkTheme } = useColorMode();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header>
|
<header>
|
||||||
<div className="top-[calc(12%)] md:top-[calc(30%)] h-screen w-full absolute -z-10">
|
<div className="top-[calc(12%)] md:top-[calc(30%)] h-screen w-full absolute -z-10">
|
||||||
@@ -14,8 +12,8 @@ function HomepageHeader() {
|
|||||||
<div className="w-full h-[120vh] absolute left-0 top-0 backdrop-blur-3xl bg-immich-bg/40 dark:bg-transparent"></div>
|
<div className="w-full h-[120vh] absolute left-0 top-0 backdrop-blur-3xl bg-immich-bg/40 dark:bg-transparent"></div>
|
||||||
</div>
|
</div>
|
||||||
<section className="text-center pt-12 sm:pt-24 bg-immich-bg/50 dark:bg-immich-dark-bg/80">
|
<section className="text-center pt-12 sm:pt-24 bg-immich-bg/50 dark:bg-immich-dark-bg/80">
|
||||||
<img
|
<ThemedImage
|
||||||
src={isDarkTheme ? 'img/logomark-dark.svg' : 'img/logomark-light.svg'}
|
sources={{ dark: 'img/logomark-dark.svg', light: 'img/logomark-light.svg' }}
|
||||||
className="h-[115px] w-[115px] mb-2 antialiased rounded-none"
|
className="h-[115px] w-[115px] mb-2 antialiased rounded-none"
|
||||||
alt="Immich logo"
|
alt="Immich logo"
|
||||||
/>
|
/>
|
||||||
@@ -35,7 +33,6 @@ function HomepageHeader() {
|
|||||||
sacrificing your privacy.
|
sacrificing your privacy.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col sm:flex-row place-items-center place-content-center mt-9 gap-4 ">
|
<div className="flex flex-col sm:flex-row place-items-center place-content-center mt-9 gap-4 ">
|
||||||
<Link
|
<Link
|
||||||
className="flex place-items-center place-content-center py-3 px-8 border bg-immich-primary dark:bg-immich-dark-primary rounded-xl no-underline hover:no-underline text-white hover:text-gray-50 dark:text-immich-dark-bg font-bold uppercase"
|
className="flex place-items-center place-content-center py-3 px-8 border bg-immich-primary dark:bg-immich-dark-primary rounded-xl no-underline hover:no-underline text-white hover:text-gray-50 dark:text-immich-dark-bg font-bold uppercase"
|
||||||
@@ -58,27 +55,27 @@ function HomepageHeader() {
|
|||||||
Buy Merch
|
Buy Merch
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="my-12 flex gap-1 font-medium place-items-center place-content-center text-immich-primary dark:text-immich-dark-primary">
|
<div className="my-12 flex gap-1 font-medium place-items-center place-content-center text-immich-primary dark:text-immich-dark-primary">
|
||||||
<Icon path={discordPath} size={1} />
|
<Icon
|
||||||
|
path={discordPath}
|
||||||
|
viewBox={discordViewBox} /* viewBox may show an error in your IDE but it is normal. */
|
||||||
|
size={1}
|
||||||
|
/>
|
||||||
<Link to="https://discord.immich.app/">Join our Discord</Link>
|
<Link to="https://discord.immich.app/">Join our Discord</Link>
|
||||||
</div>
|
</div>
|
||||||
<img
|
<ThemedImage
|
||||||
src={isDarkTheme ? '/img/screenshot-dark.webp' : '/img/screenshot-light.webp'}
|
sources={{ dark: '/img/screenshot-dark.webp', light: '/img/screenshot-light.webp' }}
|
||||||
alt="screenshots"
|
alt="screenshots"
|
||||||
className="w-[95%] lg:w-[85%] xl:w-[70%] 2xl:w-[60%] "
|
className="w-[95%] lg:w-[85%] xl:w-[70%] 2xl:w-[60%] "
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="mx-[25%] m-auto my-14 md:my-28">
|
<div className="mx-[25%] m-auto my-14 md:my-28">
|
||||||
<hr className="border bg-gray-500 dark:bg-gray-400" />
|
<hr className="border bg-gray-500 dark:bg-gray-400" />
|
||||||
</div>
|
</div>
|
||||||
|
<ThemedImage
|
||||||
<img
|
sources={{ dark: 'img/logomark-dark.svg', light: 'img/logomark-light.svg' }}
|
||||||
src={isDarkTheme ? 'img/logomark-dark.svg' : 'img/logomark-light.svg'}
|
|
||||||
className="h-[115px] w-[115px] mb-2 antialiased rounded-none"
|
className="h-[115px] w-[115px] mb-2 antialiased rounded-none"
|
||||||
alt="Immich logo"
|
alt="Immich logo"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<p className="font-bold text-2xl md:text-5xl ">Download the mobile app</p>
|
<p className="font-bold text-2xl md:text-5xl ">Download the mobile app</p>
|
||||||
<p className="text-lg">
|
<p className="text-lg">
|
||||||
@@ -97,9 +94,8 @@ function HomepageHeader() {
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<ThemedImage
|
||||||
<img
|
sources={{ dark: '/img/app-qr-code-dark.svg', light: '/img/app-qr-code-light.svg' }}
|
||||||
src={isDarkTheme ? '/img/app-qr-code-dark.svg' : '/img/app-qr-code-light.svg'}
|
|
||||||
alt="app qr code"
|
alt="app qr code"
|
||||||
width={'150px'}
|
width={'150px'}
|
||||||
className="shadow-lg p-3 my-8 dark:bg-immich-dark-bg "
|
className="shadow-lg p-3 my-8 dark:bg-immich-dark-bg "
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Link from '@docusaurus/Link';
|
import Link from '@docusaurus/Link';
|
||||||
import Layout from '@theme/Layout';
|
import Layout from '@theme/Layout';
|
||||||
import { useColorMode } from '@docusaurus/theme-common';
|
|
||||||
function HomepageHeader() {
|
function HomepageHeader() {
|
||||||
const { isDarkTheme } = useColorMode();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header>
|
<header>
|
||||||
<section className="max-w-[900px] m-4 p-4 md:p-6 md:m-auto md:my-12 border border-red-400 rounded-2xl bg-slate-200 dark:bg-immich-dark-gray">
|
<section className="max-w-[900px] m-4 p-4 md:p-6 md:m-auto md:my-12 border border-red-400 rounded-2xl bg-slate-200 dark:bg-immich-dark-gray">
|
||||||
|
|||||||
@@ -242,6 +242,13 @@ const roadmap: Item[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const milestones: Item[] = [
|
const milestones: Item[] = [
|
||||||
|
{
|
||||||
|
icon: mdiStar,
|
||||||
|
iconColor: 'gold',
|
||||||
|
title: '60,000 Stars',
|
||||||
|
description: 'Reached 60K Stars on GitHub!',
|
||||||
|
getDateLabel: withLanguage(new Date(2025, 2, 4)),
|
||||||
|
},
|
||||||
withRelease({
|
withRelease({
|
||||||
icon: mdiLinkEdit,
|
icon: mdiLinkEdit,
|
||||||
iconColor: 'crimson',
|
iconColor: 'crimson',
|
||||||
|
|||||||
36
docs/static/archived-versions.json
vendored
36
docs/static/archived-versions.json
vendored
@@ -1,4 +1,40 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"label": "v1.131.3",
|
||||||
|
"url": "https://v1.131.3.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.131.2",
|
||||||
|
"url": "https://v1.131.2.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.131.1",
|
||||||
|
"url": "https://v1.131.1.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.131.0",
|
||||||
|
"url": "https://v1.131.0.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.130.3",
|
||||||
|
"url": "https://v1.130.3.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.130.2",
|
||||||
|
"url": "https://v1.130.2.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.130.1",
|
||||||
|
"url": "https://v1.130.1.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.130.0",
|
||||||
|
"url": "https://v1.130.0.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.129.0",
|
||||||
|
"url": "https://v1.129.0.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.128.0",
|
"label": "v1.128.0",
|
||||||
"url": "https://v1.128.0.archive.immich.app"
|
"url": "https://v1.128.0.archive.immich.app"
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ services:
|
|||||||
image: redis:6.2-alpine@sha256:148bb5411c184abd288d9aaed139c98123eeb8824c5d3fce03cf721db58066d8
|
image: redis:6.2-alpine@sha256:148bb5411c184abd288d9aaed139c98123eeb8824c5d3fce03cf721db58066d8
|
||||||
|
|
||||||
database:
|
database:
|
||||||
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0
|
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
||||||
command: -c fsync=off -c shared_preload_libraries=vectors.so
|
command: -c fsync=off -c shared_preload_libraries=vectors.so
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
|
|||||||
@@ -1,39 +1,29 @@
|
|||||||
import { FlatCompat } from '@eslint/eslintrc';
|
|
||||||
import js from '@eslint/js';
|
import js from '@eslint/js';
|
||||||
import typescriptEslint from '@typescript-eslint/eslint-plugin';
|
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
||||||
import tsParser from '@typescript-eslint/parser';
|
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
|
||||||
import globals from 'globals';
|
import globals from 'globals';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import typescriptEslint from 'typescript-eslint';
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
const compat = new FlatCompat({
|
|
||||||
baseDirectory: __dirname,
|
|
||||||
recommendedConfig: js.configs.recommended,
|
|
||||||
allConfig: js.configs.all,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default [
|
export default typescriptEslint.config([
|
||||||
|
eslintPluginUnicorn.configs.recommended,
|
||||||
|
eslintPluginPrettierRecommended,
|
||||||
|
js.configs.recommended,
|
||||||
|
typescriptEslint.configs.recommended,
|
||||||
{
|
{
|
||||||
ignores: ['eslint.config.mjs'],
|
ignores: ['eslint.config.mjs'],
|
||||||
},
|
},
|
||||||
...compat.extends(
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
'plugin:prettier/recommended',
|
|
||||||
'plugin:unicorn/recommended',
|
|
||||||
),
|
|
||||||
{
|
{
|
||||||
plugins: {
|
|
||||||
'@typescript-eslint': typescriptEslint,
|
|
||||||
},
|
|
||||||
|
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
globals: {
|
globals: {
|
||||||
...globals.node,
|
...globals.node,
|
||||||
},
|
},
|
||||||
|
|
||||||
parser: tsParser,
|
parser: typescriptEslint.parser,
|
||||||
ecmaVersion: 5,
|
ecmaVersion: 5,
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
|
|
||||||
@@ -62,4 +52,4 @@ export default [
|
|||||||
'object-shorthand': ['error', 'always'],
|
'object-shorthand': ['error', 'always'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
]);
|
||||||
|
|||||||
1695
e2e/package-lock.json
generated
1695
e2e/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "1.128.0",
|
"version": "1.131.3",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -25,20 +25,18 @@
|
|||||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||||
"@playwright/test": "^1.44.1",
|
"@playwright/test": "^1.44.1",
|
||||||
"@types/luxon": "^3.4.2",
|
"@types/luxon": "^3.4.2",
|
||||||
"@types/node": "^22.13.4",
|
"@types/node": "^22.14.0",
|
||||||
"@types/oidc-provider": "^8.5.1",
|
"@types/oidc-provider": "^8.5.1",
|
||||||
"@types/pg": "^8.11.0",
|
"@types/pg": "^8.11.0",
|
||||||
"@types/pngjs": "^6.0.4",
|
"@types/pngjs": "^6.0.4",
|
||||||
"@types/supertest": "^6.0.2",
|
"@types/supertest": "^6.0.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.15.0",
|
|
||||||
"@typescript-eslint/parser": "^8.15.0",
|
|
||||||
"@vitest/coverage-v8": "^3.0.0",
|
"@vitest/coverage-v8": "^3.0.0",
|
||||||
"eslint": "^9.14.0",
|
"eslint": "^9.14.0",
|
||||||
"eslint-config-prettier": "^10.0.0",
|
"eslint-config-prettier": "^10.0.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"eslint-plugin-unicorn": "^56.0.1",
|
"eslint-plugin-unicorn": "^57.0.0",
|
||||||
"exiftool-vendored": "^28.3.1",
|
"exiftool-vendored": "^29.3.0",
|
||||||
"globals": "^15.9.0",
|
"globals": "^16.0.0",
|
||||||
"jose": "^5.6.3",
|
"jose": "^5.6.3",
|
||||||
"luxon": "^3.4.4",
|
"luxon": "^3.4.4",
|
||||||
"oidc-provider": "^8.5.1",
|
"oidc-provider": "^8.5.1",
|
||||||
@@ -49,6 +47,7 @@
|
|||||||
"socket.io-client": "^4.7.4",
|
"socket.io-client": "^4.7.4",
|
||||||
"supertest": "^7.0.0",
|
"supertest": "^7.0.0",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
|
"typescript-eslint": "^8.28.0",
|
||||||
"utimes": "^5.2.1",
|
"utimes": "^5.2.1",
|
||||||
"vitest": "^3.0.0"
|
"vitest": "^3.0.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
AssetResponseDto,
|
AssetResponseDto,
|
||||||
AssetTypeEnum,
|
AssetTypeEnum,
|
||||||
getAssetInfo,
|
getAssetInfo,
|
||||||
getConfig,
|
|
||||||
getMyUser,
|
getMyUser,
|
||||||
LoginResponseDto,
|
LoginResponseDto,
|
||||||
SharedLinkType,
|
SharedLinkType,
|
||||||
@@ -45,8 +44,6 @@ const locationAssetFilepath = `${testAssetDir}/metadata/gps-position/thompson-sp
|
|||||||
const ratingAssetFilepath = `${testAssetDir}/metadata/rating/mongolels.jpg`;
|
const ratingAssetFilepath = `${testAssetDir}/metadata/rating/mongolels.jpg`;
|
||||||
const facesAssetFilepath = `${testAssetDir}/metadata/faces/portrait.jpg`;
|
const facesAssetFilepath = `${testAssetDir}/metadata/faces/portrait.jpg`;
|
||||||
|
|
||||||
const getSystemConfig = (accessToken: string) => getConfig({ headers: asBearerAuth(accessToken) });
|
|
||||||
|
|
||||||
const readTags = async (bytes: Buffer, filename: string) => {
|
const readTags = async (bytes: Buffer, filename: string) => {
|
||||||
const filepath = join(tempDir, filename);
|
const filepath = join(tempDir, filename);
|
||||||
await writeFile(filepath, bytes);
|
await writeFile(filepath, bytes);
|
||||||
@@ -228,7 +225,7 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should get the asset faces', async () => {
|
it('should get the asset faces', async () => {
|
||||||
const config = await getSystemConfig(admin.accessToken);
|
const config = await utils.getSystemConfig(admin.accessToken);
|
||||||
config.metadata.faces.import = true;
|
config.metadata.faces.import = true;
|
||||||
await updateConfig({ systemConfigDto: config }, { headers: asBearerAuth(admin.accessToken) });
|
await updateConfig({ systemConfigDto: config }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
|
||||||
@@ -1260,6 +1257,7 @@ describe('/asset', () => {
|
|||||||
|
|
||||||
for (const { id, status } of assets) {
|
for (const { id, status } of assets) {
|
||||||
expect(status).toBe(AssetMediaStatus.Created);
|
expect(status).toBe(AssetMediaStatus.Created);
|
||||||
|
// longer timeout as the thumbnail generation from full-size raw files can take a while
|
||||||
await utils.waitForWebsocketEvent({ event: 'assetUpload', id });
|
await utils.waitForWebsocketEvent({ event: 'assetUpload', id });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { JobCommand, JobName, LoginResponseDto } from '@immich/sdk';
|
import { JobCommand, JobName, LoginResponseDto, updateConfig } from '@immich/sdk';
|
||||||
|
import { cpSync, rmSync } from 'node:fs';
|
||||||
import { readFile } from 'node:fs/promises';
|
import { readFile } from 'node:fs/promises';
|
||||||
import { basename } from 'node:path';
|
import { basename } from 'node:path';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { app, testAssetDir, utils } from 'src/utils';
|
import { app, asBearerAuth, testAssetDir, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { afterEach, beforeAll, describe, expect, it } from 'vitest';
|
import { afterEach, beforeAll, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
@@ -20,6 +21,33 @@ describe('/jobs', () => {
|
|||||||
command: JobCommand.Resume,
|
command: JobCommand.Resume,
|
||||||
force: false,
|
force: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await utils.jobCommand(admin.accessToken, JobName.ThumbnailGeneration, {
|
||||||
|
command: JobCommand.Resume,
|
||||||
|
force: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.jobCommand(admin.accessToken, JobName.FaceDetection, {
|
||||||
|
command: JobCommand.Resume,
|
||||||
|
force: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.jobCommand(admin.accessToken, JobName.SmartSearch, {
|
||||||
|
command: JobCommand.Resume,
|
||||||
|
force: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.jobCommand(admin.accessToken, JobName.DuplicateDetection, {
|
||||||
|
command: JobCommand.Resume,
|
||||||
|
force: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const config = await utils.getSystemConfig(admin.accessToken);
|
||||||
|
config.machineLearning.duplicateDetection.enabled = false;
|
||||||
|
config.machineLearning.enabled = false;
|
||||||
|
config.metadata.faces.import = false;
|
||||||
|
config.machineLearning.clip.enabled = false;
|
||||||
|
await updateConfig({ systemConfigDto: config }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require authentication', async () => {
|
it('should require authentication', async () => {
|
||||||
@@ -29,14 +57,7 @@ describe('/jobs', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should queue metadata extraction for missing assets', async () => {
|
it('should queue metadata extraction for missing assets', async () => {
|
||||||
const path1 = `${testAssetDir}/formats/raw/Nikon/D700/philadelphia.nef`;
|
const path = `${testAssetDir}/formats/raw/Nikon/D700/philadelphia.nef`;
|
||||||
const path2 = `${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`;
|
|
||||||
|
|
||||||
await utils.createAsset(admin.accessToken, {
|
|
||||||
assetData: { bytes: await readFile(path1), filename: basename(path1) },
|
|
||||||
});
|
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
|
||||||
|
|
||||||
await utils.jobCommand(admin.accessToken, JobName.MetadataExtraction, {
|
await utils.jobCommand(admin.accessToken, JobName.MetadataExtraction, {
|
||||||
command: JobCommand.Pause,
|
command: JobCommand.Pause,
|
||||||
@@ -44,7 +65,7 @@ describe('/jobs', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { id } = await utils.createAsset(admin.accessToken, {
|
const { id } = await utils.createAsset(admin.accessToken, {
|
||||||
assetData: { bytes: await readFile(path2), filename: basename(path2) },
|
assetData: { bytes: await readFile(path), filename: basename(path) },
|
||||||
});
|
});
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
||||||
@@ -82,5 +103,123 @@ describe('/jobs', () => {
|
|||||||
expect(asset.exifInfo?.make).toBe('NIKON CORPORATION');
|
expect(asset.exifInfo?.make).toBe('NIKON CORPORATION');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not re-extract metadata for existing assets', async () => {
|
||||||
|
const path = `${testAssetDir}/temp/metadata/asset.jpg`;
|
||||||
|
|
||||||
|
cpSync(`${testAssetDir}/formats/raw/Nikon/D700/philadelphia.nef`, path);
|
||||||
|
|
||||||
|
const { id } = await utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: { bytes: await readFile(path), filename: basename(path) },
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
||||||
|
|
||||||
|
{
|
||||||
|
const asset = await utils.getAssetInfo(admin.accessToken, id);
|
||||||
|
|
||||||
|
expect(asset.exifInfo).toBeDefined();
|
||||||
|
expect(asset.exifInfo?.model).toBe('NIKON D700');
|
||||||
|
}
|
||||||
|
|
||||||
|
cpSync(`${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`, path);
|
||||||
|
|
||||||
|
await utils.jobCommand(admin.accessToken, JobName.MetadataExtraction, {
|
||||||
|
command: JobCommand.Start,
|
||||||
|
force: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
||||||
|
|
||||||
|
{
|
||||||
|
const asset = await utils.getAssetInfo(admin.accessToken, id);
|
||||||
|
|
||||||
|
expect(asset.exifInfo).toBeDefined();
|
||||||
|
expect(asset.exifInfo?.model).toBe('NIKON D700');
|
||||||
|
}
|
||||||
|
|
||||||
|
rmSync(path);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should queue thumbnail extraction for assets missing thumbs', async () => {
|
||||||
|
const path = `${testAssetDir}/albums/nature/tanners_ridge.jpg`;
|
||||||
|
|
||||||
|
await utils.jobCommand(admin.accessToken, JobName.ThumbnailGeneration, {
|
||||||
|
command: JobCommand.Pause,
|
||||||
|
force: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { id } = await utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: { bytes: await readFile(path), filename: basename(path) },
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, JobName.MetadataExtraction);
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, JobName.ThumbnailGeneration);
|
||||||
|
|
||||||
|
const assetBefore = await utils.getAssetInfo(admin.accessToken, id);
|
||||||
|
expect(assetBefore.thumbhash).toBeNull();
|
||||||
|
|
||||||
|
await utils.jobCommand(admin.accessToken, JobName.ThumbnailGeneration, {
|
||||||
|
command: JobCommand.Empty,
|
||||||
|
force: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, JobName.MetadataExtraction);
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, JobName.ThumbnailGeneration);
|
||||||
|
|
||||||
|
await utils.jobCommand(admin.accessToken, JobName.ThumbnailGeneration, {
|
||||||
|
command: JobCommand.Resume,
|
||||||
|
force: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.jobCommand(admin.accessToken, JobName.ThumbnailGeneration, {
|
||||||
|
command: JobCommand.Start,
|
||||||
|
force: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, JobName.MetadataExtraction);
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, JobName.ThumbnailGeneration);
|
||||||
|
|
||||||
|
const assetAfter = await utils.getAssetInfo(admin.accessToken, id);
|
||||||
|
expect(assetAfter.thumbhash).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not reload existing thumbnail when running thumb job for missing assets', async () => {
|
||||||
|
const path = `${testAssetDir}/temp/thumbs/asset1.jpg`;
|
||||||
|
|
||||||
|
cpSync(`${testAssetDir}/albums/nature/tanners_ridge.jpg`, path);
|
||||||
|
|
||||||
|
const { id } = await utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: { bytes: await readFile(path), filename: basename(path) },
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, JobName.MetadataExtraction);
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, JobName.ThumbnailGeneration);
|
||||||
|
|
||||||
|
const assetBefore = await utils.getAssetInfo(admin.accessToken, id);
|
||||||
|
|
||||||
|
cpSync(`${testAssetDir}/albums/nature/notocactus_minimus.jpg`, path);
|
||||||
|
|
||||||
|
await utils.jobCommand(admin.accessToken, JobName.ThumbnailGeneration, {
|
||||||
|
command: JobCommand.Resume,
|
||||||
|
force: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// This runs the missing thumbnail job
|
||||||
|
await utils.jobCommand(admin.accessToken, JobName.ThumbnailGeneration, {
|
||||||
|
command: JobCommand.Start,
|
||||||
|
force: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, JobName.MetadataExtraction);
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, JobName.ThumbnailGeneration);
|
||||||
|
|
||||||
|
const assetAfter = await utils.getAssetInfo(admin.accessToken, id);
|
||||||
|
|
||||||
|
// Asset 1 thumbnail should be untouched since its thumb should not have been reloaded, even though the file was changed
|
||||||
|
expect(assetAfter.thumbhash).toEqual(assetBefore.thumbhash);
|
||||||
|
|
||||||
|
rmSync(path);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -329,7 +329,7 @@ describe('/libraries', () => {
|
|||||||
const library = await utils.createLibrary(admin.accessToken, {
|
const library = await utils.createLibrary(admin.accessToken, {
|
||||||
ownerId: admin.userId,
|
ownerId: admin.userId,
|
||||||
importPaths: [`${testAssetDirInternal}/temp`],
|
importPaths: [`${testAssetDirInternal}/temp`],
|
||||||
exclusionPatterns: ['**/directoryA'],
|
exclusionPatterns: ['**/directoryA/**'],
|
||||||
});
|
});
|
||||||
|
|
||||||
await utils.scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
@@ -337,7 +337,82 @@ describe('/libraries', () => {
|
|||||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
expect(assets.count).toBe(1);
|
expect(assets.count).toBe(1);
|
||||||
expect(assets.items[0].originalPath.includes('directoryB'));
|
|
||||||
|
expect(assets.items).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ originalPath: expect.stringContaining('directoryB/assetB.png') }),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should scan external library with multiple exclusion patterns', async () => {
|
||||||
|
const library = await utils.createLibrary(admin.accessToken, {
|
||||||
|
ownerId: admin.userId,
|
||||||
|
importPaths: [`${testAssetDirInternal}/temp`],
|
||||||
|
exclusionPatterns: ['**/directoryA/**', '**/directoryB/**'],
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.scan(admin.accessToken, library.id);
|
||||||
|
|
||||||
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
|
expect(assets.count).toBe(0);
|
||||||
|
|
||||||
|
expect(assets.items).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove assets covered by a new exclusion pattern', async () => {
|
||||||
|
const library = await utils.createLibrary(admin.accessToken, {
|
||||||
|
ownerId: admin.userId,
|
||||||
|
importPaths: [`${testAssetDirInternal}/temp`],
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.scan(admin.accessToken, library.id);
|
||||||
|
|
||||||
|
{
|
||||||
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
|
expect(assets.count).toBe(2);
|
||||||
|
|
||||||
|
expect(assets.items).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ originalPath: expect.stringContaining('directoryA/assetA.png') }),
|
||||||
|
expect.objectContaining({ originalPath: expect.stringContaining('directoryB/assetB.png') }),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await utils.updateLibrary(admin.accessToken, library.id, {
|
||||||
|
exclusionPatterns: ['**/directoryA/**'],
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.scan(admin.accessToken, library.id);
|
||||||
|
|
||||||
|
{
|
||||||
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
|
expect(assets.count).toBe(1);
|
||||||
|
|
||||||
|
expect(assets.items).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ originalPath: expect.stringContaining('directoryB/assetB.png') }),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await utils.updateLibrary(admin.accessToken, library.id, {
|
||||||
|
exclusionPatterns: ['**/directoryA/**', '**/directoryB/**'],
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.scan(admin.accessToken, library.id);
|
||||||
|
|
||||||
|
{
|
||||||
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
|
expect(assets.count).toBe(0);
|
||||||
|
|
||||||
|
expect(assets.items).toEqual([]);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should scan multiple import paths', async () => {
|
it('should scan multiple import paths', async () => {
|
||||||
@@ -454,6 +529,133 @@ describe('/libraries', () => {
|
|||||||
utils.removeImageFile(`${testAssetDir}/temp/folder${char}2/asset2.png`);
|
utils.removeImageFile(`${testAssetDir}/temp/folder${char}2/asset2.png`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should respect exclusion patterns when using multiple import paths', async () => {
|
||||||
|
// https://github.com/immich-app/immich/issues/17121
|
||||||
|
const library = await utils.createLibrary(admin.accessToken, {
|
||||||
|
ownerId: admin.userId,
|
||||||
|
importPaths: [`${testAssetDirInternal}/temp/exclusion/`, `${testAssetDirInternal}/temp/exclusion2/`],
|
||||||
|
});
|
||||||
|
|
||||||
|
const excludedFolder = `Raw`;
|
||||||
|
|
||||||
|
utils.createImageFile(`${testAssetDir}/temp/exclusion/asset1.png`);
|
||||||
|
utils.createImageFile(`${testAssetDir}/temp/exclusion/${excludedFolder}/asset2.png`);
|
||||||
|
|
||||||
|
await utils.scan(admin.accessToken, library.id);
|
||||||
|
|
||||||
|
{
|
||||||
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
|
expect(assets.items).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ originalPath: expect.stringContaining(`/asset1.png`) }),
|
||||||
|
expect.objectContaining({ originalPath: expect.stringContaining(`${excludedFolder}/asset2.png`) }),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await utils.scan(admin.accessToken, library.id);
|
||||||
|
|
||||||
|
{
|
||||||
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
|
expect(assets.items).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ originalPath: expect.stringContaining(`/asset1.png`) }),
|
||||||
|
expect.objectContaining({ originalPath: expect.stringContaining(`${excludedFolder}/asset2.png`) }),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await utils.updateLibrary(admin.accessToken, library.id, { exclusionPatterns: [`**/${excludedFolder}/**`] });
|
||||||
|
await utils.scan(admin.accessToken, library.id);
|
||||||
|
|
||||||
|
{
|
||||||
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
|
expect(assets.items).toEqual([
|
||||||
|
expect.objectContaining({ originalPath: expect.stringContaining(`/asset1.png`) }),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
await utils.scan(admin.accessToken, library.id);
|
||||||
|
|
||||||
|
{
|
||||||
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
|
expect(assets.items).toEqual([
|
||||||
|
expect.objectContaining({ originalPath: expect.stringContaining(`/asset1.png`) }),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.removeImageFile(`${testAssetDir}/temp/exclusion/asset1.png`);
|
||||||
|
utils.removeImageFile(`${testAssetDir}/temp/exclusion/${excludedFolder}/asset2.png`);
|
||||||
|
});
|
||||||
|
|
||||||
|
const annoyingExclusionPatterns = ['@', '#', '$', '%', '^', '&', '='];
|
||||||
|
|
||||||
|
it.each(annoyingExclusionPatterns)('should support exclusion patterns with %s', async (char) => {
|
||||||
|
const library = await utils.createLibrary(admin.accessToken, {
|
||||||
|
ownerId: admin.userId,
|
||||||
|
importPaths: [`${testAssetDirInternal}/temp/exclusion/`],
|
||||||
|
});
|
||||||
|
|
||||||
|
const excludedFolder = `${char}folder`;
|
||||||
|
|
||||||
|
utils.createImageFile(`${testAssetDir}/temp/exclusion/asset1.png`);
|
||||||
|
utils.createImageFile(`${testAssetDir}/temp/exclusion/${excludedFolder}/asset2.png`);
|
||||||
|
|
||||||
|
await utils.scan(admin.accessToken, library.id);
|
||||||
|
|
||||||
|
{
|
||||||
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
|
expect(assets.items).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ originalPath: expect.stringContaining(`/asset1.png`) }),
|
||||||
|
expect.objectContaining({ originalPath: expect.stringContaining(`${excludedFolder}/asset2.png`) }),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await utils.scan(admin.accessToken, library.id);
|
||||||
|
|
||||||
|
{
|
||||||
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
|
expect(assets.items).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ originalPath: expect.stringContaining(`/asset1.png`) }),
|
||||||
|
expect.objectContaining({ originalPath: expect.stringContaining(`${excludedFolder}/asset2.png`) }),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await utils.updateLibrary(admin.accessToken, library.id, { exclusionPatterns: [`**/${excludedFolder}/**`] });
|
||||||
|
await utils.scan(admin.accessToken, library.id);
|
||||||
|
|
||||||
|
{
|
||||||
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
|
expect(assets.items).toEqual([
|
||||||
|
expect.objectContaining({ originalPath: expect.stringContaining(`/asset1.png`) }),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
await utils.scan(admin.accessToken, library.id);
|
||||||
|
|
||||||
|
{
|
||||||
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
|
expect(assets.items).toEqual([
|
||||||
|
expect.objectContaining({ originalPath: expect.stringContaining(`/asset1.png`) }),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.removeImageFile(`${testAssetDir}/temp/exclusion/asset1.png`);
|
||||||
|
utils.removeImageFile(`${testAssetDir}/temp/exclusion/${excludedFolder}/asset2.png`);
|
||||||
|
});
|
||||||
|
|
||||||
it('should reimport a modified file', async () => {
|
it('should reimport a modified file', async () => {
|
||||||
const library = await utils.createLibrary(admin.accessToken, {
|
const library = await utils.createLibrary(admin.accessToken, {
|
||||||
ownerId: admin.userId,
|
ownerId: admin.userId,
|
||||||
@@ -490,7 +692,7 @@ describe('/libraries', () => {
|
|||||||
utils.removeImageFile(`${testAssetDir}/temp/reimport/asset.jpg`);
|
utils.removeImageFile(`${testAssetDir}/temp/reimport/asset.jpg`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not reimport unmodified files', async () => {
|
it('should not reimport a file with unchanged timestamp', async () => {
|
||||||
const library = await utils.createLibrary(admin.accessToken, {
|
const library = await utils.createLibrary(admin.accessToken, {
|
||||||
ownerId: admin.userId,
|
ownerId: admin.userId,
|
||||||
importPaths: [`${testAssetDirInternal}/temp/reimport`],
|
importPaths: [`${testAssetDirInternal}/temp/reimport`],
|
||||||
@@ -933,6 +1135,8 @@ describe('/libraries', () => {
|
|||||||
|
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
|
expect(assets.count).toBe(1);
|
||||||
|
|
||||||
utils.renameImageFile(`${testAssetDir}/temp/offline/offline.png`, `${testAssetDir}/temp/offline.png`);
|
utils.renameImageFile(`${testAssetDir}/temp/offline/offline.png`, `${testAssetDir}/temp/offline.png`);
|
||||||
|
|
||||||
await utils.scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
@@ -963,6 +1167,58 @@ describe('/libraries', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should set a trashed offline asset to online but keep it in trash', async () => {
|
||||||
|
utils.createImageFile(`${testAssetDir}/temp/offline/offline.png`);
|
||||||
|
|
||||||
|
const library = await utils.createLibrary(admin.accessToken, {
|
||||||
|
ownerId: admin.userId,
|
||||||
|
importPaths: [`${testAssetDirInternal}/temp/offline`],
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.scan(admin.accessToken, library.id);
|
||||||
|
|
||||||
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
|
expect(assets.count).toBe(1);
|
||||||
|
|
||||||
|
await utils.deleteAssets(admin.accessToken, [assets.items[0].id]);
|
||||||
|
|
||||||
|
{
|
||||||
|
const trashedAsset = await utils.getAssetInfo(admin.accessToken, assets.items[0].id);
|
||||||
|
|
||||||
|
expect(trashedAsset.isTrashed).toBe(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.renameImageFile(`${testAssetDir}/temp/offline/offline.png`, `${testAssetDir}/temp/offline.png`);
|
||||||
|
|
||||||
|
await utils.scan(admin.accessToken, library.id);
|
||||||
|
|
||||||
|
const offlineAsset = await utils.getAssetInfo(admin.accessToken, assets.items[0].id);
|
||||||
|
expect(offlineAsset.isTrashed).toBe(true);
|
||||||
|
expect(offlineAsset.originalPath).toBe(`${testAssetDirInternal}/temp/offline/offline.png`);
|
||||||
|
expect(offlineAsset.isOffline).toBe(true);
|
||||||
|
|
||||||
|
{
|
||||||
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id, withDeleted: true });
|
||||||
|
expect(assets.count).toBe(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.renameImageFile(`${testAssetDir}/temp/offline.png`, `${testAssetDir}/temp/offline/offline.png`);
|
||||||
|
|
||||||
|
await utils.scan(admin.accessToken, library.id);
|
||||||
|
|
||||||
|
const backOnlineAsset = await utils.getAssetInfo(admin.accessToken, assets.items[0].id);
|
||||||
|
|
||||||
|
expect(backOnlineAsset.originalPath).toBe(`${testAssetDirInternal}/temp/offline/offline.png`);
|
||||||
|
expect(backOnlineAsset.isOffline).toBe(false);
|
||||||
|
expect(backOnlineAsset.isTrashed).toBe(true);
|
||||||
|
|
||||||
|
{
|
||||||
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id, withDeleted: true });
|
||||||
|
expect(assets.count).toBe(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('should not set an offline asset to online if its file exists, is not covered by an exclusion pattern, but is outside of all import paths', async () => {
|
it('should not set an offline asset to online if its file exists, is not covered by an exclusion pattern, but is outside of all import paths', async () => {
|
||||||
utils.createImageFile(`${testAssetDir}/temp/offline/offline.png`);
|
utils.createImageFile(`${testAssetDir}/temp/offline/offline.png`);
|
||||||
|
|
||||||
@@ -1024,16 +1280,17 @@ describe('/libraries', () => {
|
|||||||
|
|
||||||
await utils.scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
|
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
{
|
||||||
|
const { assets: assetsBefore } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
expect(assetsBefore.count).toBe(1);
|
||||||
|
}
|
||||||
|
|
||||||
utils.renameImageFile(`${testAssetDir}/temp/offline/offline.png`, `${testAssetDir}/temp/offline.png`);
|
utils.renameImageFile(`${testAssetDir}/temp/offline/offline.png`, `${testAssetDir}/temp/offline.png`);
|
||||||
|
|
||||||
await utils.scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
|
|
||||||
{
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id, withDeleted: true });
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id, withDeleted: true });
|
expect(assets.count).toBe(1);
|
||||||
expect(assets.count).toBe(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const offlineAsset = await utils.getAssetInfo(admin.accessToken, assets.items[0].id);
|
const offlineAsset = await utils.getAssetInfo(admin.accessToken, assets.items[0].id);
|
||||||
|
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ describe('/people', () => {
|
|||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
name: 'New Person',
|
name: 'New Person',
|
||||||
birthDate: '1990-01-01T00:00:00.000Z',
|
birthDate: '1990-01-01',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -262,7 +262,7 @@ describe('/people', () => {
|
|||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
.send({ birthDate: '1990-01-01' });
|
.send({ birthDate: '1990-01-01' });
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toMatchObject({ birthDate: '1990-01-01T00:00:00.000Z' });
|
expect(body).toMatchObject({ birthDate: '1990-01-01' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should clear a date of birth', async () => {
|
it('should clear a date of birth', async () => {
|
||||||
|
|||||||
@@ -633,7 +633,6 @@ describe('/search', () => {
|
|||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
'Andalusia',
|
'Andalusia',
|
||||||
'Berlin',
|
|
||||||
'Glarus',
|
'Glarus',
|
||||||
'Greater Accra',
|
'Greater Accra',
|
||||||
'Havana',
|
'Havana',
|
||||||
@@ -642,6 +641,7 @@ describe('/search', () => {
|
|||||||
'Mississippi',
|
'Mississippi',
|
||||||
'New York',
|
'New York',
|
||||||
'Shanghai',
|
'Shanghai',
|
||||||
|
'State of Berlin',
|
||||||
'St.-Petersburg',
|
'St.-Petersburg',
|
||||||
'Tbilisi',
|
'Tbilisi',
|
||||||
'Tokyo',
|
'Tokyo',
|
||||||
@@ -657,7 +657,6 @@ describe('/search', () => {
|
|||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
'Andalusia',
|
'Andalusia',
|
||||||
'Berlin',
|
|
||||||
'Glarus',
|
'Glarus',
|
||||||
'Greater Accra',
|
'Greater Accra',
|
||||||
'Havana',
|
'Havana',
|
||||||
@@ -666,6 +665,7 @@ describe('/search', () => {
|
|||||||
'Mississippi',
|
'Mississippi',
|
||||||
'New York',
|
'New York',
|
||||||
'Shanghai',
|
'Shanghai',
|
||||||
|
'State of Berlin',
|
||||||
'St.-Petersburg',
|
'St.-Petersburg',
|
||||||
'Tbilisi',
|
'Tbilisi',
|
||||||
'Tokyo',
|
'Tokyo',
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ describe('/shared-links', () => {
|
|||||||
const resp = await request(shareUrl).get(`/${linkWithAssets.key}`);
|
const resp = await request(shareUrl).get(`/${linkWithAssets.key}`);
|
||||||
expect(resp.status).toBe(200);
|
expect(resp.status).toBe(200);
|
||||||
expect(resp.header['content-type']).toContain('text/html');
|
expect(resp.header['content-type']).toContain('text/html');
|
||||||
expect(resp.text).toContain(`<meta property="og:image" content="http://`);
|
expect(resp.text).toContain(`<meta property="og:image" content="https://my.immich.app`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -246,15 +246,7 @@ describe('/shared-links', () => {
|
|||||||
const { status, body } = await request(app).get('/shared-links/me').query({ key: linkWithMetadata.key });
|
const { status, body } = await request(app).get('/shared-links/me').query({ key: linkWithMetadata.key });
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body.assets).toHaveLength(1);
|
expect(body.assets).toHaveLength(0);
|
||||||
expect(body.assets[0]).toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
originalFileName: 'example.png',
|
|
||||||
localDateTime: expect.any(String),
|
|
||||||
fileCreatedAt: expect.any(String),
|
|
||||||
exifInfo: expect.any(Object),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
expect(body.album).toBeDefined();
|
expect(body.album).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const tests: Test[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: 'should support paths with an asterisk',
|
test: 'should support paths with an asterisk',
|
||||||
paths: [`/photos\*/image1.jpg`],
|
paths: [`/photos*/image1.jpg`],
|
||||||
files: {
|
files: {
|
||||||
'/photos*/image1.jpg': true,
|
'/photos*/image1.jpg': true,
|
||||||
'/photos*/image2.jpg': false,
|
'/photos*/image2.jpg': false,
|
||||||
@@ -40,7 +40,7 @@ const tests: Test[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: 'should support paths with a single quote',
|
test: 'should support paths with a single quote',
|
||||||
paths: [`/photos\'/image1.jpg`],
|
paths: [`/photos'/image1.jpg`],
|
||||||
files: {
|
files: {
|
||||||
"/photos'/image1.jpg": true,
|
"/photos'/image1.jpg": true,
|
||||||
"/photos'/image2.jpg": false,
|
"/photos'/image2.jpg": false,
|
||||||
@@ -49,7 +49,7 @@ const tests: Test[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: 'should support paths with a double quote',
|
test: 'should support paths with a double quote',
|
||||||
paths: [`/photos\"/image1.jpg`],
|
paths: [`/photos"/image1.jpg`],
|
||||||
files: {
|
files: {
|
||||||
'/photos"/image1.jpg': true,
|
'/photos"/image1.jpg': true,
|
||||||
'/photos"/image2.jpg': false,
|
'/photos"/image2.jpg': false,
|
||||||
@@ -67,7 +67,7 @@ const tests: Test[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: 'should support paths with an opening brace',
|
test: 'should support paths with an opening brace',
|
||||||
paths: [`/photos\{/image1.jpg`],
|
paths: [`/photos{/image1.jpg`],
|
||||||
files: {
|
files: {
|
||||||
'/photos{/image1.jpg': true,
|
'/photos{/image1.jpg': true,
|
||||||
'/photos{/image2.jpg': false,
|
'/photos{/image2.jpg': false,
|
||||||
@@ -76,7 +76,7 @@ const tests: Test[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: 'should support paths with a closing brace',
|
test: 'should support paths with a closing brace',
|
||||||
paths: [`/photos\}/image1.jpg`],
|
paths: [`/photos}/image1.jpg`],
|
||||||
files: {
|
files: {
|
||||||
'/photos}/image1.jpg': true,
|
'/photos}/image1.jpg': true,
|
||||||
'/photos}/image2.jpg': false,
|
'/photos}/image2.jpg': false,
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import {
|
|||||||
deleteAssets,
|
deleteAssets,
|
||||||
getAllJobsStatus,
|
getAllJobsStatus,
|
||||||
getAssetInfo,
|
getAssetInfo,
|
||||||
|
getConfig,
|
||||||
getConfigDefaults,
|
getConfigDefaults,
|
||||||
login,
|
login,
|
||||||
scanLibrary,
|
scanLibrary,
|
||||||
@@ -121,6 +122,7 @@ const execPromise = promisify(exec);
|
|||||||
const onEvent = ({ event, id }: { event: EventType; id: string }) => {
|
const onEvent = ({ event, id }: { event: EventType; id: string }) => {
|
||||||
// console.log(`Received event: ${event} [id=${id}]`);
|
// console.log(`Received event: ${event} [id=${id}]`);
|
||||||
const set = events[event];
|
const set = events[event];
|
||||||
|
|
||||||
set.add(id);
|
set.add(id);
|
||||||
|
|
||||||
const idCallback = idCallbacks[id];
|
const idCallback = idCallbacks[id];
|
||||||
@@ -415,6 +417,8 @@ export const utils = {
|
|||||||
rmSync(path, { recursive: true });
|
rmSync(path, { recursive: true });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getSystemConfig: (accessToken: string) => getConfig({ headers: asBearerAuth(accessToken) }),
|
||||||
|
|
||||||
getAssetInfo: (accessToken: string, id: string) => getAssetInfo({ id }, { headers: asBearerAuth(accessToken) }),
|
getAssetInfo: (accessToken: string, id: string) => getAssetInfo({ id }, { headers: asBearerAuth(accessToken) }),
|
||||||
|
|
||||||
checkExistingAssets: (accessToken: string, checkExistingAssetsDto: CheckExistingAssetsDto) =>
|
checkExistingAssets: (accessToken: string, checkExistingAssetsDto: CheckExistingAssetsDto) =>
|
||||||
@@ -489,7 +493,7 @@ export const utils = {
|
|||||||
value: accessToken,
|
value: accessToken,
|
||||||
domain,
|
domain,
|
||||||
path: '/',
|
path: '/',
|
||||||
expires: 1_742_402_728,
|
expires: 2_058_028_213,
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: false,
|
secure: false,
|
||||||
sameSite: 'Lax',
|
sameSite: 'Lax',
|
||||||
@@ -499,7 +503,7 @@ export const utils = {
|
|||||||
value: 'password',
|
value: 'password',
|
||||||
domain,
|
domain,
|
||||||
path: '/',
|
path: '/',
|
||||||
expires: 1_742_402_728,
|
expires: 2_058_028_213,
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: false,
|
secure: false,
|
||||||
sameSite: 'Lax',
|
sameSite: 'Lax',
|
||||||
@@ -509,7 +513,7 @@ export const utils = {
|
|||||||
value: 'true',
|
value: 'true',
|
||||||
domain,
|
domain,
|
||||||
path: '/',
|
path: '/',
|
||||||
expires: 1_742_402_728,
|
expires: 2_058_028_213,
|
||||||
httpOnly: false,
|
httpOnly: false,
|
||||||
secure: false,
|
secure: false,
|
||||||
sameSite: 'Lax',
|
sameSite: 'Lax',
|
||||||
@@ -533,6 +537,7 @@ export const utils = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
waitForQueueFinish: (accessToken: string, queue: keyof AllJobStatusResponseDto, ms?: number) => {
|
waitForQueueFinish: (accessToken: string, queue: keyof AllJobStatusResponseDto, ms?: number) => {
|
||||||
|
// eslint-disable-next-line no-async-promise-executor
|
||||||
return new Promise<void>(async (resolve, reject) => {
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
const timeout = setTimeout(() => reject(new Error('Timed out waiting for queue to empty')), ms || 10_000);
|
const timeout = setTimeout(() => reject(new Error('Timed out waiting for queue to empty')), ms || 10_000);
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,14 @@ function imageLocator(page: Page) {
|
|||||||
test.describe('Photo Viewer', () => {
|
test.describe('Photo Viewer', () => {
|
||||||
let admin: LoginResponseDto;
|
let admin: LoginResponseDto;
|
||||||
let asset: AssetMediaResponseDto;
|
let asset: AssetMediaResponseDto;
|
||||||
|
let rawAsset: AssetMediaResponseDto;
|
||||||
|
|
||||||
test.beforeAll(async () => {
|
test.beforeAll(async () => {
|
||||||
utils.initSdk();
|
utils.initSdk();
|
||||||
await utils.resetDatabase();
|
await utils.resetDatabase();
|
||||||
admin = await utils.adminSetup();
|
admin = await utils.adminSetup();
|
||||||
asset = await utils.createAsset(admin.accessToken);
|
asset = await utils.createAsset(admin.accessToken);
|
||||||
|
rawAsset = await utils.createAsset(admin.accessToken, { assetData: { filename: 'test.arw' } });
|
||||||
});
|
});
|
||||||
|
|
||||||
test.beforeEach(async ({ context, page }) => {
|
test.beforeEach(async ({ context, page }) => {
|
||||||
@@ -36,7 +38,7 @@ test.describe('Photo Viewer', () => {
|
|||||||
await expect(page.getByTestId('loading-spinner')).toBeVisible();
|
await expect(page.getByTestId('loading-spinner')).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('loads high resolution photo when zoomed', async ({ page }) => {
|
test('loads original photo when zoomed', async ({ page }) => {
|
||||||
await page.goto(`/photos/${asset.id}`);
|
await page.goto(`/photos/${asset.id}`);
|
||||||
await expect.poll(async () => await imageLocator(page).getAttribute('src')).toContain('thumbnail');
|
await expect.poll(async () => await imageLocator(page).getAttribute('src')).toContain('thumbnail');
|
||||||
const box = await imageLocator(page).boundingBox();
|
const box = await imageLocator(page).boundingBox();
|
||||||
@@ -47,6 +49,17 @@ test.describe('Photo Viewer', () => {
|
|||||||
await expect.poll(async () => await imageLocator(page).getAttribute('src')).toContain('original');
|
await expect.poll(async () => await imageLocator(page).getAttribute('src')).toContain('original');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('loads fullsize image when zoomed and original is web-incompatible', async ({ page }) => {
|
||||||
|
await page.goto(`/photos/${rawAsset.id}`);
|
||||||
|
await expect.poll(async () => await imageLocator(page).getAttribute('src')).toContain('thumbnail');
|
||||||
|
const box = await imageLocator(page).boundingBox();
|
||||||
|
expect(box).toBeTruthy();
|
||||||
|
const { x, y, width, height } = box!;
|
||||||
|
await page.mouse.move(x + width / 2, y + height / 2);
|
||||||
|
await page.mouse.wheel(0, -1);
|
||||||
|
await expect.poll(async () => await imageLocator(page).getAttribute('src')).toContain('fullsize');
|
||||||
|
});
|
||||||
|
|
||||||
test('reloads photo when checksum changes', async ({ page }) => {
|
test('reloads photo when checksum changes', async ({ page }) => {
|
||||||
await page.goto(`/photos/${asset.id}`);
|
await page.goto(`/photos/${asset.id}`);
|
||||||
await expect.poll(async () => await imageLocator(page).getAttribute('src')).toContain('thumbnail');
|
await expect.poll(async () => await imageLocator(page).getAttribute('src')).toContain('thumbnail');
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ test.describe('Shared Links', () => {
|
|||||||
await page.goto(`/share/${sharedLink.key}`);
|
await page.goto(`/share/${sharedLink.key}`);
|
||||||
await page.getByRole('heading', { name: 'Test Album' }).waitFor();
|
await page.getByRole('heading', { name: 'Test Album' }).waitFor();
|
||||||
await page.locator(`[data-asset-id="${asset.id}"]`).hover();
|
await page.locator(`[data-asset-id="${asset.id}"]`).hover();
|
||||||
await page.waitForSelector('#asset-group-by-date svg');
|
await page.waitForSelector('[data-group] svg');
|
||||||
await page.getByRole('checkbox').click();
|
await page.getByRole('checkbox').click();
|
||||||
await page.getByRole('button', { name: 'Download' }).click();
|
await page.getByRole('button', { name: 'Download' }).click();
|
||||||
await page.getByText('DOWNLOADING', { exact: true }).waitFor();
|
await page.getByText('DOWNLOADING', { exact: true }).waitFor();
|
||||||
|
|||||||
18
i18n/af.json
18
i18n/af.json
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"about": "Verfris",
|
"about": "Oor",
|
||||||
"account": "Rekening",
|
"account": "Rekening",
|
||||||
"account_settings": "Rekeninginstellings",
|
"account_settings": "Rekeninginstellings",
|
||||||
"acknowledge": "Erken",
|
"acknowledge": "Erken",
|
||||||
@@ -56,15 +56,16 @@
|
|||||||
"duplicate_detection_job_description": "Begin masjienleer op bates om soortgelyke beelde op te spoor. Maak staat op Smart Search",
|
"duplicate_detection_job_description": "Begin masjienleer op bates om soortgelyke beelde op te spoor. Maak staat op Smart Search",
|
||||||
"exclusion_pattern_description": "Met uitsluitingspatrone kan jy lêers en vouers ignoreer wanneer jy jou biblioteek skandeer. Dit is nuttig as jy vouers het wat lêers bevat wat jy nie wil invoer nie, soos RAW-lêers.",
|
"exclusion_pattern_description": "Met uitsluitingspatrone kan jy lêers en vouers ignoreer wanneer jy jou biblioteek skandeer. Dit is nuttig as jy vouers het wat lêers bevat wat jy nie wil invoer nie, soos RAW-lêers.",
|
||||||
"external_library_created_at": "Eksterne biblioteek (geskep op {date})",
|
"external_library_created_at": "Eksterne biblioteek (geskep op {date})",
|
||||||
"external_library_management": "Eksterne Biblioteek-opsies",
|
"external_library_management": "Eksterne Biblioteekbestuur",
|
||||||
"face_detection": "Gesigsopsporing",
|
"face_detection": "Gesig deteksie",
|
||||||
"failed_job_command": "Opdrag {command} het misluk vir werk: {job}",
|
"failed_job_command": "Opdrag {command} het misluk vir werk: {job}",
|
||||||
"force_delete_user_warning": "WAARSKUWING: Dit sal onmiddellik die gebruiker en alle bates verwyder. Dit kan nie ontdoen word nie en die lêers kan nie herstel word nie.",
|
"force_delete_user_warning": "WAARSKUWING: Dit sal onmiddellik die gebruiker en alle bates verwyder. Dit kan nie ontdoen word nie en die lêers kan nie herstel word nie.",
|
||||||
"forcing_refresh_library_files": "Forseer herlaai van alle biblioteeklêers",
|
"forcing_refresh_library_files": "Forseer herlaai van alle biblioteeklêers",
|
||||||
"image_format": "Formaat",
|
"image_format": "Formaat",
|
||||||
"image_format_description": "WebP produseer kleiner lêers as JPEG, maar is stadiger om te enkodeer.",
|
"image_format_description": "WebP produseer kleiner lêers as JPEG, maar is stadiger om te enkodeer.",
|
||||||
"image_prefer_embedded_preview": "Verkies ingebedde voorskou",
|
"image_prefer_embedded_preview": "Verkies ingebedde voorskou",
|
||||||
"image_prefer_wide_gamut": "Verkies wye spektrum",
|
"image_prefer_wide_gamut": "Verkies wide gamut",
|
||||||
|
"image_prefer_wide_gamut_setting_description": "Gebruik Display P3 vir kleinkiekies. Dit behou die lewendheid van beelde met wye kleurruimtes beter, maar beelde kan anders verskyn op ou apparate met 'n ou blaaierweergawe. sRGB-beelde gebruik steeds sRGB om kleurverskuiwings te voorkom.",
|
||||||
"image_preview_description": "Mediumgrootte prent met gestroopte metadata, wat gebruik word wanneer 'n enkele bate bekyk word en vir masjienleer",
|
"image_preview_description": "Mediumgrootte prent met gestroopte metadata, wat gebruik word wanneer 'n enkele bate bekyk word en vir masjienleer",
|
||||||
"image_preview_quality_description": "Voorskou kwaliteit van 1-100. Hoër is beter, maar produseer groter lêers en kan app-reaksie verminder. Die stel van 'n lae waarde kan masjienleerkwaliteit beïnvloed.",
|
"image_preview_quality_description": "Voorskou kwaliteit van 1-100. Hoër is beter, maar produseer groter lêers en kan app-reaksie verminder. Die stel van 'n lae waarde kan masjienleerkwaliteit beïnvloed.",
|
||||||
"image_preview_title": "Voorskou Instellings",
|
"image_preview_title": "Voorskou Instellings",
|
||||||
@@ -72,7 +73,14 @@
|
|||||||
"image_resolution": "Resolusie",
|
"image_resolution": "Resolusie",
|
||||||
"image_resolution_description": "Hoër resolusies kan meer detail bewaar, maar neem langer om te enkodeer, het groter lêergroottes en kan app-reaksie verminder.",
|
"image_resolution_description": "Hoër resolusies kan meer detail bewaar, maar neem langer om te enkodeer, het groter lêergroottes en kan app-reaksie verminder.",
|
||||||
"image_settings": "Prent Instellings",
|
"image_settings": "Prent Instellings",
|
||||||
"image_settings_description": "Bestuur die kwaliteit en resolusie van gegenereerde beelde"
|
"image_settings_description": "Bestuur die kwaliteit en resolusie van gegenereerde beelde",
|
||||||
|
"image_thumbnail_description": "Klein kleinkiekies sonder metadata, gebruik om groepe foto's soos die tydlyn te bekyk",
|
||||||
|
"image_thumbnail_quality_description": "Kleinkiekiekwaliteit van 1-100. Hoër is beter, maar produseer groter lêers en kan die toepassing vertraag.",
|
||||||
|
"image_thumbnail_title": "Kleinkiekie-instellings",
|
||||||
|
"job_concurrency": "{job} gelyktydigheid",
|
||||||
|
"job_created": "Taak gemaak",
|
||||||
|
"job_not_concurrency_safe": "Hierdie taak kan nie gelyktydig uitgevoer word nie.",
|
||||||
|
"job_settings": "Agtergrondtaakinstellings"
|
||||||
},
|
},
|
||||||
"search_by_description": "Soek by beskrywing",
|
"search_by_description": "Soek by beskrywing",
|
||||||
"search_by_description_example": "Stapdag in Sapa"
|
"search_by_description_example": "Stapdag in Sapa"
|
||||||
|
|||||||
25
i18n/ar.json
25
i18n/ar.json
@@ -41,6 +41,7 @@
|
|||||||
"backup_settings": "إعدادات النسخ الاحتياطي",
|
"backup_settings": "إعدادات النسخ الاحتياطي",
|
||||||
"backup_settings_description": "إدارة إعدادات النسخ الاحتياطي لقاعدة البيانات",
|
"backup_settings_description": "إدارة إعدادات النسخ الاحتياطي لقاعدة البيانات",
|
||||||
"check_all": "اختر الكل",
|
"check_all": "اختر الكل",
|
||||||
|
"cleanup": "تنظيف",
|
||||||
"cleared_jobs": "تم إخلاء مهام: {job}",
|
"cleared_jobs": "تم إخلاء مهام: {job}",
|
||||||
"config_set_by_file": "الإعدادات حاليًا معينة عن طريق ملف الاعدادات",
|
"config_set_by_file": "الإعدادات حاليًا معينة عن طريق ملف الاعدادات",
|
||||||
"confirm_delete_library": "هل أنت متأكد أنك تريد حذف مكتبة {library}؟",
|
"confirm_delete_library": "هل أنت متأكد أنك تريد حذف مكتبة {library}؟",
|
||||||
@@ -96,7 +97,7 @@
|
|||||||
"library_scanning_enable_description": "تفعيل فحص المكتبة الدوري",
|
"library_scanning_enable_description": "تفعيل فحص المكتبة الدوري",
|
||||||
"library_settings": "المكتبة الخارجية",
|
"library_settings": "المكتبة الخارجية",
|
||||||
"library_settings_description": "إدارة إعدادات المكتبة الخارجية",
|
"library_settings_description": "إدارة إعدادات المكتبة الخارجية",
|
||||||
"library_tasks_description": "قم بتنفيذ مهام المكتبة",
|
"library_tasks_description": "مسح المكتبات الخارجية للعثور على الأصول الجديدة و/أو المتغيرة",
|
||||||
"library_watching_enable_description": "راقب المكتبات الخارجية لتتبع تغييرات الملفات",
|
"library_watching_enable_description": "راقب المكتبات الخارجية لتتبع تغييرات الملفات",
|
||||||
"library_watching_settings": "مراقبة المكتبات (تجريبي)",
|
"library_watching_settings": "مراقبة المكتبات (تجريبي)",
|
||||||
"library_watching_settings_description": "راقب تلقائيًا التغييرات في الملفات",
|
"library_watching_settings_description": "راقب تلقائيًا التغييرات في الملفات",
|
||||||
@@ -131,7 +132,7 @@
|
|||||||
"machine_learning_smart_search_description": "البحث عن الصور بشكل دلالي باستخدام تضمينات CLIP",
|
"machine_learning_smart_search_description": "البحث عن الصور بشكل دلالي باستخدام تضمينات CLIP",
|
||||||
"machine_learning_smart_search_enabled": "تفعيل البحث الذكي",
|
"machine_learning_smart_search_enabled": "تفعيل البحث الذكي",
|
||||||
"machine_learning_smart_search_enabled_description": "إذا تم تعطيله، فلن يتم ترميز الصور للبحث الذكي.",
|
"machine_learning_smart_search_enabled_description": "إذا تم تعطيله، فلن يتم ترميز الصور للبحث الذكي.",
|
||||||
"machine_learning_url_description": "عنوان URL لخادم التعلم الآلي. إذا تم توفير أكثر من عنوان URL، فسيتم محاولة الوصول إلى كل خادم على حدة حتى يستجيب أحد الخوادم بنجاح، بالترتيب من الأول إلى الأخير.",
|
"machine_learning_url_description": "عنوان URL لخادم التعلم الآلي. إذا تم توفير أكثر من عنوان URL واحد، سيتم محاولة الاتصال بكل خادم على حدة حتى يستجيب أحدهم بنجاح، بدءًا من الأول إلى الأخير. سيتم تجاهل الخوادم التي لا تستجيب مؤقتًا حتى تعود للعمل.",
|
||||||
"manage_concurrency": "إدارة التزامن",
|
"manage_concurrency": "إدارة التزامن",
|
||||||
"manage_log_settings": "إدارة إعدادات السجلات",
|
"manage_log_settings": "إدارة إعدادات السجلات",
|
||||||
"map_dark_style": "النمط الداكن",
|
"map_dark_style": "النمط الداكن",
|
||||||
@@ -147,6 +148,8 @@
|
|||||||
"map_settings": "الخريطة",
|
"map_settings": "الخريطة",
|
||||||
"map_settings_description": "إدارة إعدادات الخريطة",
|
"map_settings_description": "إدارة إعدادات الخريطة",
|
||||||
"map_style_description": "عنوان URL لسمة الخريطة style.json",
|
"map_style_description": "عنوان URL لسمة الخريطة style.json",
|
||||||
|
"memory_cleanup_job": "تنظيف الذاكرة",
|
||||||
|
"memory_generate_job": "توليد الذاكرة",
|
||||||
"metadata_extraction_job": "استخراج البيانات الوصفية",
|
"metadata_extraction_job": "استخراج البيانات الوصفية",
|
||||||
"metadata_extraction_job_description": "استخراج معلومات البيانات الوصفية من كل أصل، مثل إحداثيات الموقع, الوجوه والدقة",
|
"metadata_extraction_job_description": "استخراج معلومات البيانات الوصفية من كل أصل، مثل إحداثيات الموقع, الوجوه والدقة",
|
||||||
"metadata_faces_import_setting": "تمكين استيراد الوجه",
|
"metadata_faces_import_setting": "تمكين استيراد الوجه",
|
||||||
@@ -391,6 +394,7 @@
|
|||||||
"allow_edits": "إسمح بالتعديل",
|
"allow_edits": "إسمح بالتعديل",
|
||||||
"allow_public_user_to_download": "السماح لأي مستخدم عام بالتنزيل",
|
"allow_public_user_to_download": "السماح لأي مستخدم عام بالتنزيل",
|
||||||
"allow_public_user_to_upload": "السماح للمستخدم العام بالرفع",
|
"allow_public_user_to_upload": "السماح للمستخدم العام بالرفع",
|
||||||
|
"alt_text_qr_code": "صورة رمز الاستجابة السريعة (QR)",
|
||||||
"anti_clockwise": "عكس اتجاه عقارب الساعة",
|
"anti_clockwise": "عكس اتجاه عقارب الساعة",
|
||||||
"api_key": "مفتاح واجهة برمجة التطبيقات",
|
"api_key": "مفتاح واجهة برمجة التطبيقات",
|
||||||
"api_key_description": "سيتم عرض هذه القيمة مرة واحدة فقط. يرجى التأكد من نسخها قبل إغلاق النافذة.",
|
"api_key_description": "سيتم عرض هذه القيمة مرة واحدة فقط. يرجى التأكد من نسخها قبل إغلاق النافذة.",
|
||||||
@@ -481,6 +485,7 @@
|
|||||||
"comments_are_disabled": "التعليقات معطلة",
|
"comments_are_disabled": "التعليقات معطلة",
|
||||||
"confirm": "تأكيد",
|
"confirm": "تأكيد",
|
||||||
"confirm_admin_password": "تأكيد كلمة مرور المسؤول",
|
"confirm_admin_password": "تأكيد كلمة مرور المسؤول",
|
||||||
|
"confirm_delete_face": "هل أنت متأكد من حذف وجه {name} من الأصول؟",
|
||||||
"confirm_delete_shared_link": "هل أنت متأكد أنك تريد حذف هذا الرابط المشترك؟",
|
"confirm_delete_shared_link": "هل أنت متأكد أنك تريد حذف هذا الرابط المشترك؟",
|
||||||
"confirm_keep_this_delete_others": "سيتم حذف جميع الأصول الأخرى في المجموعة باستثناء هذا الأصل. هل أنت متأكد من أنك تريد المتابعة؟",
|
"confirm_keep_this_delete_others": "سيتم حذف جميع الأصول الأخرى في المجموعة باستثناء هذا الأصل. هل أنت متأكد من أنك تريد المتابعة؟",
|
||||||
"confirm_password": "تأكيد كلمة المرور",
|
"confirm_password": "تأكيد كلمة المرور",
|
||||||
@@ -533,6 +538,7 @@
|
|||||||
"delete_album": "حذف الألبوم",
|
"delete_album": "حذف الألبوم",
|
||||||
"delete_api_key_prompt": "هل أنت متأكد أنك تريد حذف مفتاح API هذا؟",
|
"delete_api_key_prompt": "هل أنت متأكد أنك تريد حذف مفتاح API هذا؟",
|
||||||
"delete_duplicates_confirmation": "هل أنت متأكد أنك تريد حذف هذه التكرارات نهائيًا؟",
|
"delete_duplicates_confirmation": "هل أنت متأكد أنك تريد حذف هذه التكرارات نهائيًا؟",
|
||||||
|
"delete_face": "حذف الوجه",
|
||||||
"delete_key": "حذف المفتاح",
|
"delete_key": "حذف المفتاح",
|
||||||
"delete_library": "حذف المكتبة",
|
"delete_library": "حذف المكتبة",
|
||||||
"delete_link": "حذف الرابط",
|
"delete_link": "حذف الرابط",
|
||||||
@@ -600,6 +606,7 @@
|
|||||||
"enabled": "مفعل",
|
"enabled": "مفعل",
|
||||||
"end_date": "تاريخ الإنتهاء",
|
"end_date": "تاريخ الإنتهاء",
|
||||||
"error": "خطأ",
|
"error": "خطأ",
|
||||||
|
"error_delete_face": "حدث خطأ في حذف الوجه من الأصول",
|
||||||
"error_loading_image": "حدث خطأ أثناء تحميل الصورة",
|
"error_loading_image": "حدث خطأ أثناء تحميل الصورة",
|
||||||
"error_title": "خطأ - حدث خللٌ ما",
|
"error_title": "خطأ - حدث خللٌ ما",
|
||||||
"errors": {
|
"errors": {
|
||||||
@@ -769,7 +776,7 @@
|
|||||||
"group_country": "مجموعة البلد",
|
"group_country": "مجموعة البلد",
|
||||||
"group_no": "بدون تجميع",
|
"group_no": "بدون تجميع",
|
||||||
"group_owner": "تجميع حسب المالك",
|
"group_owner": "تجميع حسب المالك",
|
||||||
"group_places_by": "تجميع الأماكن حسب",
|
"group_places_by": "تجميع الأماكن حسب...",
|
||||||
"group_year": "تجميع حسب السنة",
|
"group_year": "تجميع حسب السنة",
|
||||||
"has_quota": "محدد بحصة",
|
"has_quota": "محدد بحصة",
|
||||||
"hi_user": "مرحبا {name} ({email})",
|
"hi_user": "مرحبا {name} ({email})",
|
||||||
@@ -884,6 +891,7 @@
|
|||||||
"month": "شهر",
|
"month": "شهر",
|
||||||
"more": "المزيد",
|
"more": "المزيد",
|
||||||
"moved_to_trash": "تم النقل إلى سلة المهملات",
|
"moved_to_trash": "تم النقل إلى سلة المهملات",
|
||||||
|
"mute_memories": "كتم الذكريات",
|
||||||
"my_albums": "ألبوماتي",
|
"my_albums": "ألبوماتي",
|
||||||
"name": "الاسم",
|
"name": "الاسم",
|
||||||
"name_or_nickname": "الاسم أو اللقب",
|
"name_or_nickname": "الاسم أو اللقب",
|
||||||
@@ -979,6 +987,7 @@
|
|||||||
"permanently_deleted_asset": "تم حذف الأصل بشكل نهائي",
|
"permanently_deleted_asset": "تم حذف الأصل بشكل نهائي",
|
||||||
"permanently_deleted_assets_count": "تم حذف {count, plural, one {# محتوى} other {# المحتويات}} نهائيًا",
|
"permanently_deleted_assets_count": "تم حذف {count, plural, one {# محتوى} other {# المحتويات}} نهائيًا",
|
||||||
"person": "شخص",
|
"person": "شخص",
|
||||||
|
"person_birthdate": "تاريخ الميلاد {التاريخ}",
|
||||||
"person_hidden": "{name}{hidden, select, true { (مخفي)} other {}}",
|
"person_hidden": "{name}{hidden, select, true { (مخفي)} other {}}",
|
||||||
"photo_shared_all_users": "يبدو أنك شاركت صورك مع جميع المستخدمين أو ليس لديك أي مستخدم للمشاركة معه.",
|
"photo_shared_all_users": "يبدو أنك شاركت صورك مع جميع المستخدمين أو ليس لديك أي مستخدم للمشاركة معه.",
|
||||||
"photos": "الصور",
|
"photos": "الصور",
|
||||||
@@ -1070,12 +1079,16 @@
|
|||||||
"remove_from_album": "إزالة من الألبوم",
|
"remove_from_album": "إزالة من الألبوم",
|
||||||
"remove_from_favorites": "إزالة من المفضلة",
|
"remove_from_favorites": "إزالة من المفضلة",
|
||||||
"remove_from_shared_link": "إزالة من الرابط المشترك",
|
"remove_from_shared_link": "إزالة من الرابط المشترك",
|
||||||
|
"remove_memory": "إزالة الذاكرة",
|
||||||
|
"remove_photo_from_memory": "إزالة الصورة من هذه الذكرى",
|
||||||
"remove_url": "إزالة عنوان URL",
|
"remove_url": "إزالة عنوان URL",
|
||||||
"remove_user": "إزالة المستخدم",
|
"remove_user": "إزالة المستخدم",
|
||||||
"removed_api_key": "تم إزالة مفتاح API: {name}",
|
"removed_api_key": "تم إزالة مفتاح API: {name}",
|
||||||
"removed_from_archive": "تمت إزالتها من الأرشيف",
|
"removed_from_archive": "تمت إزالتها من الأرشيف",
|
||||||
"removed_from_favorites": "تمت الإزالة من المفضلة",
|
"removed_from_favorites": "تمت الإزالة من المفضلة",
|
||||||
"removed_from_favorites_count": "{count, plural, other {أُزيلت #}} من التفضيلات",
|
"removed_from_favorites_count": "{count, plural, other {أُزيلت #}} من التفضيلات",
|
||||||
|
"removed_memory": "الذاكرة المحذوفة",
|
||||||
|
"removed_photo_from_memory": "تم إزالة الصورة من الذاكرة",
|
||||||
"removed_tagged_assets": "تمت إزالة العلامة من {count, plural, one {# الأصل} other {# الأصول}}",
|
"removed_tagged_assets": "تمت إزالة العلامة من {count, plural, one {# الأصل} other {# الأصول}}",
|
||||||
"rename": "إعادة تسمية",
|
"rename": "إعادة تسمية",
|
||||||
"repair": "إصلاح",
|
"repair": "إصلاح",
|
||||||
@@ -1084,6 +1097,7 @@
|
|||||||
"repository": "المستودع",
|
"repository": "المستودع",
|
||||||
"require_password": "يتطلب كلمة المرور",
|
"require_password": "يتطلب كلمة المرور",
|
||||||
"require_user_to_change_password_on_first_login": "مطالبة المستخدم بتغيير كلمة المرور عند تسجيل الدخول لأول مرة",
|
"require_user_to_change_password_on_first_login": "مطالبة المستخدم بتغيير كلمة المرور عند تسجيل الدخول لأول مرة",
|
||||||
|
"rescan": "إعادة المسح",
|
||||||
"reset": "إعادة ضبط",
|
"reset": "إعادة ضبط",
|
||||||
"reset_password": "إعادة تعيين كلمة المرور",
|
"reset_password": "إعادة تعيين كلمة المرور",
|
||||||
"reset_people_visibility": "إعادة ضبط ظهور الأشخاص",
|
"reset_people_visibility": "إعادة ضبط ظهور الأشخاص",
|
||||||
@@ -1127,6 +1141,7 @@
|
|||||||
"search_options": "خيارات البحث",
|
"search_options": "خيارات البحث",
|
||||||
"search_people": "البحث عن الأشخاص",
|
"search_people": "البحث عن الأشخاص",
|
||||||
"search_places": "البحث عن الأماكن",
|
"search_places": "البحث عن الأماكن",
|
||||||
|
"search_rating": "البحث حسب التقييم...",
|
||||||
"search_settings": "إعدادات البحث",
|
"search_settings": "إعدادات البحث",
|
||||||
"search_state": "البحث حسب الولاية...",
|
"search_state": "البحث حسب الولاية...",
|
||||||
"search_tags": "البحث عن العلامات...",
|
"search_tags": "البحث عن العلامات...",
|
||||||
@@ -1136,6 +1151,7 @@
|
|||||||
"searching_locales": "جارٍ البحث في اللغات...",
|
"searching_locales": "جارٍ البحث في اللغات...",
|
||||||
"second": "ثانية",
|
"second": "ثانية",
|
||||||
"see_all_people": "عرض جميع الأشخاص",
|
"see_all_people": "عرض جميع الأشخاص",
|
||||||
|
"select": "إختر",
|
||||||
"select_album_cover": "تحديد غلاف الألبوم",
|
"select_album_cover": "تحديد غلاف الألبوم",
|
||||||
"select_all": "تحديد الكل",
|
"select_all": "تحديد الكل",
|
||||||
"select_all_duplicates": "تحديد جميع النسخ المكررة",
|
"select_all_duplicates": "تحديد جميع النسخ المكررة",
|
||||||
@@ -1250,6 +1266,7 @@
|
|||||||
"tag_created": "تم إنشاء العلامة: {tag}",
|
"tag_created": "تم إنشاء العلامة: {tag}",
|
||||||
"tag_feature_description": "تصفح الصور ومقاطع الفيديو المجمعة حسب مواضيع العلامات المنطقية",
|
"tag_feature_description": "تصفح الصور ومقاطع الفيديو المجمعة حسب مواضيع العلامات المنطقية",
|
||||||
"tag_not_found_question": "لا يمكن العثور على علامة؟ <link>قم بإنشاء علامة جديدة.</link>",
|
"tag_not_found_question": "لا يمكن العثور على علامة؟ <link>قم بإنشاء علامة جديدة.</link>",
|
||||||
|
"tag_people": "علِّم الأشخاص",
|
||||||
"tag_updated": "تم تحديث العلامة: {tag}",
|
"tag_updated": "تم تحديث العلامة: {tag}",
|
||||||
"tagged_assets": "تم وضع علامة {count, plural, one {# asset} other {# assets}}",
|
"tagged_assets": "تم وضع علامة {count, plural, one {# asset} other {# assets}}",
|
||||||
"tags": "العلامات",
|
"tags": "العلامات",
|
||||||
@@ -1290,6 +1307,7 @@
|
|||||||
"unlink_motion_video": "إلغاء ربط فيديو الحركة",
|
"unlink_motion_video": "إلغاء ربط فيديو الحركة",
|
||||||
"unlink_oauth": "إلغاء ربط OAuth",
|
"unlink_oauth": "إلغاء ربط OAuth",
|
||||||
"unlinked_oauth_account": "تم إلغاء ربط حساب OAuth",
|
"unlinked_oauth_account": "تم إلغاء ربط حساب OAuth",
|
||||||
|
"unmute_memories": "تشغيل الصوت للذكريات",
|
||||||
"unnamed_album": "ألبوم بلا إسم",
|
"unnamed_album": "ألبوم بلا إسم",
|
||||||
"unnamed_album_delete_confirmation": "هل أنت متأكد أنك تريد حذف هذا الألبوم؟",
|
"unnamed_album_delete_confirmation": "هل أنت متأكد أنك تريد حذف هذا الألبوم؟",
|
||||||
"unnamed_share": "مشاركة بلا إسم",
|
"unnamed_share": "مشاركة بلا إسم",
|
||||||
@@ -1343,6 +1361,7 @@
|
|||||||
"view_all": "عرض الكل",
|
"view_all": "عرض الكل",
|
||||||
"view_all_users": "عرض كافة المستخدمين",
|
"view_all_users": "عرض كافة المستخدمين",
|
||||||
"view_in_timeline": "عرض في الجدول الزمني",
|
"view_in_timeline": "عرض في الجدول الزمني",
|
||||||
|
"view_link": "عرض الرابط",
|
||||||
"view_links": "عرض الروابط",
|
"view_links": "عرض الروابط",
|
||||||
"view_name": "عرض",
|
"view_name": "عرض",
|
||||||
"view_next_asset": "عرض المحتوى التالي",
|
"view_next_asset": "عرض المحتوى التالي",
|
||||||
|
|||||||
48
i18n/bg.json
48
i18n/bg.json
@@ -20,7 +20,7 @@
|
|||||||
"add_partner": "Добавете партньор",
|
"add_partner": "Добавете партньор",
|
||||||
"add_path": "Добави път",
|
"add_path": "Добави път",
|
||||||
"add_photos": "Добавете снимки",
|
"add_photos": "Добавете снимки",
|
||||||
"add_to": "Добави към...",
|
"add_to": "Добави към…",
|
||||||
"add_to_album": "Добави към албум",
|
"add_to_album": "Добави към албум",
|
||||||
"add_to_shared_album": "Добави към споделен албум",
|
"add_to_shared_album": "Добави към споделен албум",
|
||||||
"add_url": "Добави URL",
|
"add_url": "Добави URL",
|
||||||
@@ -41,6 +41,7 @@
|
|||||||
"backup_settings": "Настройка на резервни копия",
|
"backup_settings": "Настройка на резервни копия",
|
||||||
"backup_settings_description": "Управление на настройките за резервно копие на базата данни",
|
"backup_settings_description": "Управление на настройките за резервно копие на базата данни",
|
||||||
"check_all": "Провери всичко",
|
"check_all": "Провери всичко",
|
||||||
|
"cleanup": "Почистване",
|
||||||
"cleared_jobs": "Изчистени задачи от тип: {job}",
|
"cleared_jobs": "Изчистени задачи от тип: {job}",
|
||||||
"config_set_by_file": "Конфигурацията е зададена от файл",
|
"config_set_by_file": "Конфигурацията е зададена от файл",
|
||||||
"confirm_delete_library": "Сигурни ли сте че искате да изтриете библиотеката - {library} ?",
|
"confirm_delete_library": "Сигурни ли сте че искате да изтриете библиотеката - {library} ?",
|
||||||
@@ -96,7 +97,7 @@
|
|||||||
"library_scanning_enable_description": "Включване на периодичното сканиране на библиотеката",
|
"library_scanning_enable_description": "Включване на периодичното сканиране на библиотеката",
|
||||||
"library_settings": "Външна библиотека",
|
"library_settings": "Външна библиотека",
|
||||||
"library_settings_description": "Управление на настройките за външна библиотека",
|
"library_settings_description": "Управление на настройките за външна библиотека",
|
||||||
"library_tasks_description": "Извършване на задачи за библиотеката",
|
"library_tasks_description": "Сканирайте външни библиотеки за нови и/или променени активи",
|
||||||
"library_watching_enable_description": "Наблюдаване за промяна на файловете във външната библиотека",
|
"library_watching_enable_description": "Наблюдаване за промяна на файловете във външната библиотека",
|
||||||
"library_watching_settings": "Наблюдаване на библиотеката (ЕКСПЕРИМЕНТАЛНО)",
|
"library_watching_settings": "Наблюдаване на библиотеката (ЕКСПЕРИМЕНТАЛНО)",
|
||||||
"library_watching_settings_description": "Автоматично наблюдавай за променени файлове",
|
"library_watching_settings_description": "Автоматично наблюдавай за променени файлове",
|
||||||
@@ -131,7 +132,7 @@
|
|||||||
"machine_learning_smart_search_description": "Семантично търсене на изображения с помощта на CLIP вграждания",
|
"machine_learning_smart_search_description": "Семантично търсене на изображения с помощта на CLIP вграждания",
|
||||||
"machine_learning_smart_search_enabled": "Включване на Интелигентно Търсене",
|
"machine_learning_smart_search_enabled": "Включване на Интелигентно Търсене",
|
||||||
"machine_learning_smart_search_enabled_description": "Ако е деактивирано, изображенията няма да бъдат кодирани за Интелигентно Търсене.",
|
"machine_learning_smart_search_enabled_description": "Ако е деактивирано, изображенията няма да бъдат кодирани за Интелигентно Търсене.",
|
||||||
"machine_learning_url_description": "URL на сървъра за машинно обучение. Ако са предоставени повече от един URL, всеки сървър ще бъде опитан един по един, докато един не отговори успешно, в реда от първия до последния.",
|
"machine_learning_url_description": "URL на сървъра за машинно обучение. Ако са предоставени повече от един URL, всеки сървър ще бъде опитан един по един, докато един отговори успешно, в реда от първия до последния. Сървъри, които не отговорят, ще бъдат временно игнорирани, докато не се върнат онлайн.",
|
||||||
"manage_concurrency": "Управление на паралелност",
|
"manage_concurrency": "Управление на паралелност",
|
||||||
"manage_log_settings": "Управление на настройките на записване",
|
"manage_log_settings": "Управление на настройките на записване",
|
||||||
"map_dark_style": "Тъмен стил",
|
"map_dark_style": "Тъмен стил",
|
||||||
@@ -147,6 +148,8 @@
|
|||||||
"map_settings": "Карта",
|
"map_settings": "Карта",
|
||||||
"map_settings_description": "Управление на настройките на картата",
|
"map_settings_description": "Управление на настройките на картата",
|
||||||
"map_style_description": "URL адрес към файл \"style.json\" за задаване на стил на картата",
|
"map_style_description": "URL адрес към файл \"style.json\" за задаване на стил на картата",
|
||||||
|
"memory_cleanup_job": "Почистване на паметта",
|
||||||
|
"memory_generate_job": "Генериране на паметта",
|
||||||
"metadata_extraction_job": "Извличане на метаданни",
|
"metadata_extraction_job": "Извличане на метаданни",
|
||||||
"metadata_extraction_job_description": "Извличане на метаданни от всеки от елемент, като GPS локация, лица и резолюция на файловете",
|
"metadata_extraction_job_description": "Извличане на метаданни от всеки от елемент, като GPS локация, лица и резолюция на файловете",
|
||||||
"metadata_faces_import_setting": "Включи импорт на лице",
|
"metadata_faces_import_setting": "Включи импорт на лице",
|
||||||
@@ -219,7 +222,7 @@
|
|||||||
"reset_settings_to_default": "Възстановяване на настройките по подразбиране",
|
"reset_settings_to_default": "Възстановяване на настройките по подразбиране",
|
||||||
"reset_settings_to_recent_saved": "Възстановяване на настройките до последните запазени настройки",
|
"reset_settings_to_recent_saved": "Възстановяване на настройките до последните запазени настройки",
|
||||||
"scanning_library": "Сканиране на библиотеката",
|
"scanning_library": "Сканиране на библиотеката",
|
||||||
"search_jobs": "Търсене на задачи...",
|
"search_jobs": "Търсене на задачи…",
|
||||||
"send_welcome_email": "Изпращане на имейл за добре дошли",
|
"send_welcome_email": "Изпращане на имейл за добре дошли",
|
||||||
"server_external_domain_settings": "Външен домейн",
|
"server_external_domain_settings": "Външен домейн",
|
||||||
"server_external_domain_settings_description": "Домейн за публични споделени връзки, включително http(s)://",
|
"server_external_domain_settings_description": "Домейн за публични споделени връзки, включително http(s)://",
|
||||||
@@ -299,7 +302,7 @@
|
|||||||
"transcoding_max_b_frames": "Максимални B-фрейма",
|
"transcoding_max_b_frames": "Максимални B-фрейма",
|
||||||
"transcoding_max_b_frames_description": "По-високите стойности подобряват ефективността на компресията, но забавят разкодирането. Може да не е съвместим с хардуерното ускорение на по-стари устройства. 0 деактивира B-фрейма, докато -1 задава тази стойност автоматично.",
|
"transcoding_max_b_frames_description": "По-високите стойности подобряват ефективността на компресията, но забавят разкодирането. Може да не е съвместим с хардуерното ускорение на по-стари устройства. 0 деактивира B-фрейма, докато -1 задава тази стойност автоматично.",
|
||||||
"transcoding_max_bitrate": "Максимален битрейт",
|
"transcoding_max_bitrate": "Максимален битрейт",
|
||||||
"transcoding_max_bitrate_description": "Задаването на максимален битрейт може да направи размерите на файловете по-предвидими при незначителни разлики за качеството. При 720p типичните стойности са 2600k за VP9 или HEVC или 4500k за H.264. Деактивирано, ако е зададено на 0.",
|
"transcoding_max_bitrate_description": "Задаването на максимален битрейт може да направи размерите на файловете по-предвидими при незначителни разлики за качеството. При 720p типичните стойности са 2600 kbit/s за VP9 или HEVC или 4500 kbit/s за H.264. Деактивирано, ако е зададено на 0.",
|
||||||
"transcoding_max_keyframe_interval": "Максимален интервал между ключовите кадри",
|
"transcoding_max_keyframe_interval": "Максимален интервал между ключовите кадри",
|
||||||
"transcoding_max_keyframe_interval_description": "Задава максималното разстояние между ключовите кадри. По-ниските стойности влошават ефективността на компресията, но подобряват времето за търсене и могат да подобрят качеството в сцени с бързо движение. 0 задава тази стойност автоматично.",
|
"transcoding_max_keyframe_interval_description": "Задава максималното разстояние между ключовите кадри. По-ниските стойности влошават ефективността на компресията, но подобряват времето за търсене и могат да подобрят качеството в сцени с бързо движение. 0 задава тази стойност автоматично.",
|
||||||
"transcoding_optimal_description": "Видеоклипове с по-висока от целевата разделителна способност или не в приетия формат",
|
"transcoding_optimal_description": "Видеоклипове с по-висока от целевата разделителна способност или не в приетия формат",
|
||||||
@@ -391,6 +394,7 @@
|
|||||||
"allow_edits": "Позволяване на редакции",
|
"allow_edits": "Позволяване на редакции",
|
||||||
"allow_public_user_to_download": "Позволете на публичен потребител да може да изтегля",
|
"allow_public_user_to_download": "Позволете на публичен потребител да може да изтегля",
|
||||||
"allow_public_user_to_upload": "Позволете на публичния потребител да може да качва",
|
"allow_public_user_to_upload": "Позволете на публичния потребител да може да качва",
|
||||||
|
"alt_text_qr_code": "Изображение на QR код",
|
||||||
"anti_clockwise": "Обратно на часовниковата стрелка",
|
"anti_clockwise": "Обратно на часовниковата стрелка",
|
||||||
"api_key": "API ключ",
|
"api_key": "API ключ",
|
||||||
"api_key_description": "Тази стойност ще бъде показана само веднъж. Моля, не забравяйте да го копирате, преди да затворите прозореца.",
|
"api_key_description": "Тази стойност ще бъде показана само веднъж. Моля, не забравяйте да го копирате, преди да затворите прозореца.",
|
||||||
@@ -406,17 +410,17 @@
|
|||||||
"are_these_the_same_person": "Това едно и също лице ли е?",
|
"are_these_the_same_person": "Това едно и също лице ли е?",
|
||||||
"are_you_sure_to_do_this": "Сигурни ли сте, че искате да направите това?",
|
"are_you_sure_to_do_this": "Сигурни ли сте, че искате да направите това?",
|
||||||
"asset_added_to_album": "Добавено в албум",
|
"asset_added_to_album": "Добавено в албум",
|
||||||
"asset_adding_to_album": "Добавяне в албум...",
|
"asset_adding_to_album": "Добавяне в албум…",
|
||||||
"asset_description_updated": "Описанието на елемента е обновено",
|
"asset_description_updated": "Описанието на елемента е обновено",
|
||||||
"asset_filename_is_offline": "Активът {filename} е офлайн",
|
"asset_filename_is_offline": "Активът {filename} е офлайн",
|
||||||
"asset_has_unassigned_faces": "Елементът има незададени лица",
|
"asset_has_unassigned_faces": "Елементът има незададени лица",
|
||||||
"asset_hashing": "Хеширане...",
|
"asset_hashing": "Хеширане…",
|
||||||
"asset_offline": "Елементът е офлайн",
|
"asset_offline": "Елементът е офлайн",
|
||||||
"asset_offline_description": "Този външен актив вече не се намира на диска. Моля, свържете се с администратора на Immich за помощ.",
|
"asset_offline_description": "Този външен актив вече не се намира на диска. Моля, свържете се с администратора на Immich за помощ.",
|
||||||
"asset_skipped": "Пропуснато",
|
"asset_skipped": "Пропуснато",
|
||||||
"asset_skipped_in_trash": "В кошчето",
|
"asset_skipped_in_trash": "В кошчето",
|
||||||
"asset_uploaded": "Качено",
|
"asset_uploaded": "Качено",
|
||||||
"asset_uploading": "Качване...",
|
"asset_uploading": "Качване…",
|
||||||
"assets": "Елементи",
|
"assets": "Елементи",
|
||||||
"assets_added_count": "Добавено {count, plural, one {# asset} other {# assets}}",
|
"assets_added_count": "Добавено {count, plural, one {# asset} other {# assets}}",
|
||||||
"assets_added_to_album_count": "Добавен(и) са {count, plural, one {# актив} other {# актива}} в албума",
|
"assets_added_to_album_count": "Добавен(и) са {count, plural, one {# актив} other {# актива}} в албума",
|
||||||
@@ -481,6 +485,7 @@
|
|||||||
"comments_are_disabled": "Коментарите са деактивирани",
|
"comments_are_disabled": "Коментарите са деактивирани",
|
||||||
"confirm": "Потвърди",
|
"confirm": "Потвърди",
|
||||||
"confirm_admin_password": "Потвърждаване на паролата на администратора",
|
"confirm_admin_password": "Потвърждаване на паролата на администратора",
|
||||||
|
"confirm_delete_face": "Сигурни ли сте, че искате да изтриете лицето на {name} от актива?",
|
||||||
"confirm_delete_shared_link": "Сигурни ли сте, че искате да изтриете тази споделена връзка?",
|
"confirm_delete_shared_link": "Сигурни ли сте, че искате да изтриете тази споделена връзка?",
|
||||||
"confirm_keep_this_delete_others": "Всички останали файлове в стека ще бъдат изтрити, с изключение на този файл. Сигурни ли сте, че искате да продължите?",
|
"confirm_keep_this_delete_others": "Всички останали файлове в стека ще бъдат изтрити, с изключение на този файл. Сигурни ли сте, че искате да продължите?",
|
||||||
"confirm_password": "Потвърдете паролата",
|
"confirm_password": "Потвърдете паролата",
|
||||||
@@ -533,6 +538,7 @@
|
|||||||
"delete_album": "Изтрий албум",
|
"delete_album": "Изтрий албум",
|
||||||
"delete_api_key_prompt": "Сигурни ли сте, че искате да изтриете този API ключ?",
|
"delete_api_key_prompt": "Сигурни ли сте, че искате да изтриете този API ключ?",
|
||||||
"delete_duplicates_confirmation": "Сигурни ли сте, че искате да изтриете окончателно тези дубликати?",
|
"delete_duplicates_confirmation": "Сигурни ли сте, че искате да изтриете окончателно тези дубликати?",
|
||||||
|
"delete_face": "Изтрий лице",
|
||||||
"delete_key": "Изтрий ключ",
|
"delete_key": "Изтрий ключ",
|
||||||
"delete_library": "Изтрий библиотека",
|
"delete_library": "Изтрий библиотека",
|
||||||
"delete_link": "Изтрий линк",
|
"delete_link": "Изтрий линк",
|
||||||
@@ -600,6 +606,7 @@
|
|||||||
"enabled": "Включено",
|
"enabled": "Включено",
|
||||||
"end_date": "Крайна дата",
|
"end_date": "Крайна дата",
|
||||||
"error": "Грешка",
|
"error": "Грешка",
|
||||||
|
"error_delete_face": "Грешка при изтриване на лице от актива",
|
||||||
"error_loading_image": "Грешка при зареждане на изображението",
|
"error_loading_image": "Грешка при зареждане на изображението",
|
||||||
"error_title": "Грешка - нещо се обърка",
|
"error_title": "Грешка - нещо се обърка",
|
||||||
"errors": {
|
"errors": {
|
||||||
@@ -766,8 +773,10 @@
|
|||||||
"go_to_folder": "Отиди в папката",
|
"go_to_folder": "Отиди в папката",
|
||||||
"go_to_search": "Преминаване към търсене",
|
"go_to_search": "Преминаване към търсене",
|
||||||
"group_albums_by": "Групирай албум по...",
|
"group_albums_by": "Групирай албум по...",
|
||||||
|
"group_country": "Групирай по държава",
|
||||||
"group_no": "Няма група",
|
"group_no": "Няма група",
|
||||||
"group_owner": "Групиране по собственик",
|
"group_owner": "Групиране по собственик",
|
||||||
|
"group_places_by": "Групирай места по…",
|
||||||
"group_year": "Групиране по година",
|
"group_year": "Групиране по година",
|
||||||
"has_quota": "Лимит",
|
"has_quota": "Лимит",
|
||||||
"hi_user": "Здравей, {name} {email}",
|
"hi_user": "Здравей, {name} {email}",
|
||||||
@@ -800,6 +809,7 @@
|
|||||||
"include_shared_albums": "Включване на споделени албуми",
|
"include_shared_albums": "Включване на споделени албуми",
|
||||||
"include_shared_partner_assets": "Включване на споделените с партньор елементи",
|
"include_shared_partner_assets": "Включване на споделените с партньор елементи",
|
||||||
"individual_share": "Индивидуално споделяне",
|
"individual_share": "Индивидуално споделяне",
|
||||||
|
"individual_shares": "Индивидуални споделяния",
|
||||||
"info": "Информация",
|
"info": "Информация",
|
||||||
"interval": {
|
"interval": {
|
||||||
"day_at_onepm": "Всеки ден в 13:00",
|
"day_at_onepm": "Всеки ден в 13:00",
|
||||||
@@ -822,6 +832,7 @@
|
|||||||
"latest_version": "Последна версия",
|
"latest_version": "Последна версия",
|
||||||
"latitude": "Ширина",
|
"latitude": "Ширина",
|
||||||
"leave": "Излез",
|
"leave": "Излез",
|
||||||
|
"lens_model": "Модел леща",
|
||||||
"let_others_respond": "Позволете на другите да отговорят",
|
"let_others_respond": "Позволете на другите да отговорят",
|
||||||
"level": "Ниво",
|
"level": "Ниво",
|
||||||
"library": "Библиотека",
|
"library": "Библиотека",
|
||||||
@@ -880,6 +891,7 @@
|
|||||||
"month": "Месец",
|
"month": "Месец",
|
||||||
"more": "Още",
|
"more": "Още",
|
||||||
"moved_to_trash": "Преместено в кошчето",
|
"moved_to_trash": "Преместено в кошчето",
|
||||||
|
"mute_memories": "Изключване на звука на спомените",
|
||||||
"my_albums": "Мои албуми",
|
"my_albums": "Мои албуми",
|
||||||
"name": "Име",
|
"name": "Име",
|
||||||
"name_or_nickname": "Име или прякор",
|
"name_or_nickname": "Име или прякор",
|
||||||
@@ -984,6 +996,7 @@
|
|||||||
"pick_a_location": "Избери локация",
|
"pick_a_location": "Избери локация",
|
||||||
"place": "Местоположение",
|
"place": "Местоположение",
|
||||||
"places": "Местоположения",
|
"places": "Местоположения",
|
||||||
|
"places_count": "{count, plural, one {{count, number} Място} other {{count, number} Места}}",
|
||||||
"play": "Възпроизвеждане",
|
"play": "Възпроизвеждане",
|
||||||
"play_memories": "Възпроизвеждане на спомени",
|
"play_memories": "Възпроизвеждане на спомени",
|
||||||
"play_motion_photo": "Възпроизведи Motion Photo",
|
"play_motion_photo": "Възпроизведи Motion Photo",
|
||||||
@@ -1067,10 +1080,12 @@
|
|||||||
"remove_from_shared_link": "Премахни от споделения линк",
|
"remove_from_shared_link": "Премахни от споделения линк",
|
||||||
"remove_url": "Премахни URL",
|
"remove_url": "Премахни URL",
|
||||||
"remove_user": "Премахни потребителя",
|
"remove_user": "Премахни потребителя",
|
||||||
"removed_api_key": "Премахни API Key: {name}",
|
"removed_api_key": "Премахат API ключ: {name}",
|
||||||
"removed_from_archive": "Премахни от Архива",
|
"removed_from_archive": "Премахни от архива",
|
||||||
"removed_from_favorites": "Премахнато от Любими",
|
"removed_from_favorites": "Премахнато от любими",
|
||||||
"removed_from_favorites_count": "{count, plural, other {Премахнати #}} от Любими",
|
"removed_from_favorites_count": "{count, plural, other {Премахнати #}} от Любими",
|
||||||
|
"removed_memory": "Премахнат спомен",
|
||||||
|
"removed_photo_from_memory": "Премахната снимка от спомен",
|
||||||
"removed_tagged_assets": "Премахнат е етикетът от {count, plural, one {# елемент} other {# елемента}}",
|
"removed_tagged_assets": "Премахнат е етикетът от {count, plural, one {# елемент} other {# елемента}}",
|
||||||
"rename": "Преименувай",
|
"rename": "Преименувай",
|
||||||
"repair": "Поправи",
|
"repair": "Поправи",
|
||||||
@@ -1079,6 +1094,7 @@
|
|||||||
"repository": "Хранилище",
|
"repository": "Хранилище",
|
||||||
"require_password": "Изискай парола",
|
"require_password": "Изискай парола",
|
||||||
"require_user_to_change_password_on_first_login": "Изисквай потребителят да промени паролата си при първото влизане",
|
"require_user_to_change_password_on_first_login": "Изисквай потребителят да промени паролата си при първото влизане",
|
||||||
|
"rescan": "Пресканиране",
|
||||||
"reset": "Нулиране",
|
"reset": "Нулиране",
|
||||||
"reset_password": "Нулиране на паролата",
|
"reset_password": "Нулиране на паролата",
|
||||||
"reset_people_visibility": "Нулиране на видимостта на хората",
|
"reset_people_visibility": "Нулиране на видимостта на хората",
|
||||||
@@ -1107,18 +1123,22 @@
|
|||||||
"search": "Търсене",
|
"search": "Търсене",
|
||||||
"search_albums": "Търси албуми",
|
"search_albums": "Търси албуми",
|
||||||
"search_by_context": "Търси по контекст",
|
"search_by_context": "Търси по контекст",
|
||||||
|
"search_by_description": "Търси по описание",
|
||||||
|
"search_by_description_example": "Разходка в Сапа",
|
||||||
"search_by_filename": "Търси по име на файла или разширение",
|
"search_by_filename": "Търси по име на файла или разширение",
|
||||||
"search_by_filename_example": "например IMG_1234.JPG или PNG",
|
"search_by_filename_example": "например IMG_1234.JPG или PNG",
|
||||||
"search_camera_make": "Търси производител на камерата...",
|
"search_camera_make": "Търси производител на камерата...",
|
||||||
"search_camera_model": "Търси модел на камерата...",
|
"search_camera_model": "Търси модел на камерата...",
|
||||||
"search_city": "Търси град...",
|
"search_city": "Търси град...",
|
||||||
"search_country": "Търси държава...",
|
"search_country": "Търси държава...",
|
||||||
|
"search_for": "Търси за",
|
||||||
"search_for_existing_person": "Търси съществуващ човек",
|
"search_for_existing_person": "Търси съществуващ човек",
|
||||||
"search_no_people": "Няма хора",
|
"search_no_people": "Няма хора",
|
||||||
"search_no_people_named": "Няма хора на име \"{name}\"",
|
"search_no_people_named": "Няма хора на име \"{name}\"",
|
||||||
"search_options": "Опции за търсене",
|
"search_options": "Опции за търсене",
|
||||||
"search_people": "Търсете на хора",
|
"search_people": "Търсете на хора",
|
||||||
"search_places": "Търсене на места",
|
"search_places": "Търсене на места",
|
||||||
|
"search_rating": "Търси по рейтинг…",
|
||||||
"search_settings": "Търсене на настройки",
|
"search_settings": "Търсене на настройки",
|
||||||
"search_state": "Търсене на щат...",
|
"search_state": "Търсене на щат...",
|
||||||
"search_tags": "Търсене на етикети...",
|
"search_tags": "Търсене на етикети...",
|
||||||
@@ -1165,6 +1185,7 @@
|
|||||||
"shared_from_partner": "Снимки от {partner}",
|
"shared_from_partner": "Снимки от {partner}",
|
||||||
"shared_link_options": "Опции за споделена връзка",
|
"shared_link_options": "Опции за споделена връзка",
|
||||||
"shared_links": "Споделени връзки",
|
"shared_links": "Споделени връзки",
|
||||||
|
"shared_links_description": "Сподели снимки и видеа с линк",
|
||||||
"shared_photos_and_videos_count": "{assetCount, plural, other {# споделени снимки и видеа.}}",
|
"shared_photos_and_videos_count": "{assetCount, plural, other {# споделени снимки и видеа.}}",
|
||||||
"shared_with_partner": "Споделено с {partner}",
|
"shared_with_partner": "Споделено с {partner}",
|
||||||
"sharing": "Споделени",
|
"sharing": "Споделени",
|
||||||
@@ -1187,6 +1208,7 @@
|
|||||||
"show_person_options": "Показване на опции за лица",
|
"show_person_options": "Показване на опции за лица",
|
||||||
"show_progress_bar": "Показване на прогрес бара",
|
"show_progress_bar": "Показване на прогрес бара",
|
||||||
"show_search_options": "Показване на опциите за търсене",
|
"show_search_options": "Показване на опциите за търсене",
|
||||||
|
"show_shared_links": "Покажи споделени линкове",
|
||||||
"show_slideshow_transition": "Покажи прехода на слайдшоуто",
|
"show_slideshow_transition": "Покажи прехода на слайдшоуто",
|
||||||
"show_supporter_badge": "Значка поддръжник",
|
"show_supporter_badge": "Значка поддръжник",
|
||||||
"show_supporter_badge_description": "Покажи значка поддръжник",
|
"show_supporter_badge_description": "Покажи значка поддръжник",
|
||||||
@@ -1240,6 +1262,7 @@
|
|||||||
"tag_created": "Създаден етикет: {tag}",
|
"tag_created": "Създаден етикет: {tag}",
|
||||||
"tag_feature_description": "Разглеждане на снимки и видеоклипове, групирани по теми с логически тагове",
|
"tag_feature_description": "Разглеждане на снимки и видеоклипове, групирани по теми с логически тагове",
|
||||||
"tag_not_found_question": "Не можете да намерите етикет? Създайте такъв <link>тук</link>",
|
"tag_not_found_question": "Не можете да намерите етикет? Създайте такъв <link>тук</link>",
|
||||||
|
"tag_people": "Отбележи Хора",
|
||||||
"tag_updated": "Актуализиран етикет: {tag}",
|
"tag_updated": "Актуализиран етикет: {tag}",
|
||||||
"tagged_assets": "Тагнати {count, plural, one {# елемент} other {# елементи}}",
|
"tagged_assets": "Тагнати {count, plural, one {# елемент} other {# елементи}}",
|
||||||
"tags": "Етикет",
|
"tags": "Етикет",
|
||||||
@@ -1274,11 +1297,13 @@
|
|||||||
"unfavorite": "Премахване от любимите",
|
"unfavorite": "Премахване от любимите",
|
||||||
"unhide_person": "Покажи отново човека",
|
"unhide_person": "Покажи отново човека",
|
||||||
"unknown": "Неизвестно",
|
"unknown": "Неизвестно",
|
||||||
|
"unknown_country": "Непозната Държава",
|
||||||
"unknown_year": "Неизвестна година",
|
"unknown_year": "Неизвестна година",
|
||||||
"unlimited": "Неограничено",
|
"unlimited": "Неограничено",
|
||||||
"unlink_motion_video": "Премахни връзката с видео",
|
"unlink_motion_video": "Премахни връзката с видео",
|
||||||
"unlink_oauth": "Премахни OAuth",
|
"unlink_oauth": "Премахни OAuth",
|
||||||
"unlinked_oauth_account": "Премахни OAuth акаунт",
|
"unlinked_oauth_account": "Премахни OAuth акаунт",
|
||||||
|
"unmute_memories": "Включване на звука на спомените",
|
||||||
"unnamed_album": "Албум без име",
|
"unnamed_album": "Албум без име",
|
||||||
"unnamed_album_delete_confirmation": "Сигурни ли сте, че искате да изтриете този албум?",
|
"unnamed_album_delete_confirmation": "Сигурни ли сте, че искате да изтриете този албум?",
|
||||||
"unnamed_share": "Споделяне без име",
|
"unnamed_share": "Споделяне без име",
|
||||||
@@ -1332,6 +1357,7 @@
|
|||||||
"view_all": "Преглед на всички",
|
"view_all": "Преглед на всички",
|
||||||
"view_all_users": "Преглед на всички потребители",
|
"view_all_users": "Преглед на всички потребители",
|
||||||
"view_in_timeline": "Покажи във времева линия",
|
"view_in_timeline": "Покажи във времева линия",
|
||||||
|
"view_link": "Преглед на връзката",
|
||||||
"view_links": "Преглед на връзките",
|
"view_links": "Преглед на връзките",
|
||||||
"view_name": "Прегледай",
|
"view_name": "Прегледай",
|
||||||
"view_next_asset": "Преглед на следващия файл",
|
"view_next_asset": "Преглед на следващия файл",
|
||||||
|
|||||||
30
i18n/bi.json
30
i18n/bi.json
@@ -1,20 +1,22 @@
|
|||||||
{
|
{
|
||||||
"account": "",
|
"about": "abaot",
|
||||||
"account_settings": "",
|
"account": "Akaont",
|
||||||
"acknowledge": "",
|
"account_settings": "Seting blo Akaont",
|
||||||
|
"acknowledge": "Akcept",
|
||||||
"action": "",
|
"action": "",
|
||||||
"actions": "",
|
"actions": "",
|
||||||
"active": "",
|
"active": "Stap Mekem",
|
||||||
"activity": "",
|
"activity": "Wanem hemi Mekem",
|
||||||
"add": "",
|
"activity_changed": "WAnem hemi Mekem hemi",
|
||||||
"add_a_description": "",
|
"add": "Ad",
|
||||||
"add_a_location": "",
|
"add_a_description": "Putem Description blo hem",
|
||||||
"add_a_name": "",
|
"add_a_location": "Putem place blo hem",
|
||||||
"add_a_title": "",
|
"add_a_name": "Putem nam blo hem",
|
||||||
"add_exclusion_pattern": "",
|
"add_a_title": "Putem wan name blo hem",
|
||||||
"add_import_path": "",
|
"add_exclusion_pattern": "Putem wan paten wae hemi karem aot",
|
||||||
"add_location": "",
|
"add_import_path": "Putem wan pat blo import",
|
||||||
"add_more_users": "",
|
"add_location": "Putem wan place blo hem",
|
||||||
|
"add_more_users": "Putem mor man",
|
||||||
"add_partner": "",
|
"add_partner": "",
|
||||||
"add_path": "",
|
"add_path": "",
|
||||||
"add_photos": "",
|
"add_photos": "",
|
||||||
|
|||||||
36
i18n/ca.json
36
i18n/ca.json
@@ -41,6 +41,7 @@
|
|||||||
"backup_settings": "Ajustes de les còpies de seguretat",
|
"backup_settings": "Ajustes de les còpies de seguretat",
|
||||||
"backup_settings_description": "Gestionar la configuració de la còpia de seguretat de la base de dades",
|
"backup_settings_description": "Gestionar la configuració de la còpia de seguretat de la base de dades",
|
||||||
"check_all": "Marca-ho tot",
|
"check_all": "Marca-ho tot",
|
||||||
|
"cleanup": "Neteja",
|
||||||
"cleared_jobs": "Tasques esborrades per a: {job}",
|
"cleared_jobs": "Tasques esborrades per a: {job}",
|
||||||
"config_set_by_file": "La configuració està definida per un fitxer de configuració",
|
"config_set_by_file": "La configuració està definida per un fitxer de configuració",
|
||||||
"confirm_delete_library": "Esteu segurs que voleu eliminar la llibreria {library}?",
|
"confirm_delete_library": "Esteu segurs que voleu eliminar la llibreria {library}?",
|
||||||
@@ -96,7 +97,7 @@
|
|||||||
"library_scanning_enable_description": "Habilita l'escaneig periòdic de biblioteques",
|
"library_scanning_enable_description": "Habilita l'escaneig periòdic de biblioteques",
|
||||||
"library_settings": "Llibreria externes",
|
"library_settings": "Llibreria externes",
|
||||||
"library_settings_description": "Gestiona la configuració de les llibreries externes",
|
"library_settings_description": "Gestiona la configuració de les llibreries externes",
|
||||||
"library_tasks_description": "Realitza tasques de la bilbioteca",
|
"library_tasks_description": "Escaneja biblioteques externes per arxius nous o canviats",
|
||||||
"library_watching_enable_description": "Consultar llibreries externes per detectar canvis en fitxers",
|
"library_watching_enable_description": "Consultar llibreries externes per detectar canvis en fitxers",
|
||||||
"library_watching_settings": "Monitoratge de la llibreria (EXPERIMENTAL)",
|
"library_watching_settings": "Monitoratge de la llibreria (EXPERIMENTAL)",
|
||||||
"library_watching_settings_description": "Monitorització automàtica de fitxers modificats",
|
"library_watching_settings_description": "Monitorització automàtica de fitxers modificats",
|
||||||
@@ -131,7 +132,7 @@
|
|||||||
"machine_learning_smart_search_description": "Cerca imatges semànticament emprant incrustacions CLIP",
|
"machine_learning_smart_search_description": "Cerca imatges semànticament emprant incrustacions CLIP",
|
||||||
"machine_learning_smart_search_enabled": "Activa la cerca intel·ligent",
|
"machine_learning_smart_search_enabled": "Activa la cerca intel·ligent",
|
||||||
"machine_learning_smart_search_enabled_description": "Si està deshabilitat les imatges no es codificaran per la cerca intel·ligent.",
|
"machine_learning_smart_search_enabled_description": "Si està deshabilitat les imatges no es codificaran per la cerca intel·ligent.",
|
||||||
"machine_learning_url_description": "La URL del servidor d'aprenentatge automàtic. Si es proporciona més d'una URL, s'intentarà accedir a cada servidor en ordre fins que un d'ells respongui correctament.",
|
"machine_learning_url_description": "L'URL del servidor d'aprenentatge automàtic. Si es proporciona més d'un URL, s'intentarà accedir a cada servidor en ordre fins que un d'ells respongui correctament.",
|
||||||
"manage_concurrency": "Gestiona la concurrència",
|
"manage_concurrency": "Gestiona la concurrència",
|
||||||
"manage_log_settings": "Gestiona la configuració del registre",
|
"manage_log_settings": "Gestiona la configuració del registre",
|
||||||
"map_dark_style": "Tema fosc",
|
"map_dark_style": "Tema fosc",
|
||||||
@@ -147,6 +148,8 @@
|
|||||||
"map_settings": "Mapa",
|
"map_settings": "Mapa",
|
||||||
"map_settings_description": "Gestiona la configuració del mapa",
|
"map_settings_description": "Gestiona la configuració del mapa",
|
||||||
"map_style_description": "URL a un tema del mapa style.json",
|
"map_style_description": "URL a un tema del mapa style.json",
|
||||||
|
"memory_cleanup_job": "Neteja de memòries",
|
||||||
|
"memory_generate_job": "Generació de memòries",
|
||||||
"metadata_extraction_job": "Extreure metadades",
|
"metadata_extraction_job": "Extreure metadades",
|
||||||
"metadata_extraction_job_description": "Extreu la informació de metadades de cada element, com per exemple el GPS i la resolució",
|
"metadata_extraction_job_description": "Extreu la informació de metadades de cada element, com per exemple el GPS i la resolució",
|
||||||
"metadata_faces_import_setting": "Activar la importació de cares",
|
"metadata_faces_import_setting": "Activar la importació de cares",
|
||||||
@@ -240,7 +243,7 @@
|
|||||||
"storage_template_hash_verification_enabled_description": "Activa la verificació de hash. No la desactiveu a menys que estigueu segurs de les implicacions",
|
"storage_template_hash_verification_enabled_description": "Activa la verificació de hash. No la desactiveu a menys que estigueu segurs de les implicacions",
|
||||||
"storage_template_migration": "Migració de plantilles d'emmagatzematge",
|
"storage_template_migration": "Migració de plantilles d'emmagatzematge",
|
||||||
"storage_template_migration_description": "Aplica la <link>{template}</link> actual als elements pujats prèviament",
|
"storage_template_migration_description": "Aplica la <link>{template}</link> actual als elements pujats prèviament",
|
||||||
"storage_template_migration_info": "Els canvis de plantilla només s'aplicaran a nous elements. Per aplicar la plantilla rectroactivament a elements pujats prèviament, executeu la <link>{job}</link>.",
|
"storage_template_migration_info": "Les extensions es convertiran a minúscules. Els canvis de plantilla només s'aplicaran a nous elements. Per aplicar la plantilla rectroactivament a elements pujats prèviament, executeu la <link>{job}</link>.",
|
||||||
"storage_template_migration_job": "Tasca de migració de la plantilla d'emmagatzematge",
|
"storage_template_migration_job": "Tasca de migració de la plantilla d'emmagatzematge",
|
||||||
"storage_template_more_details": "Per obtenir més detalls sobre aquesta funció, consulteu la <template-link>Storage Template</template-link> i les seves <implications-link>implications</implications-link>",
|
"storage_template_more_details": "Per obtenir més detalls sobre aquesta funció, consulteu la <template-link>Storage Template</template-link> i les seves <implications-link>implications</implications-link>",
|
||||||
"storage_template_onboarding_description": "Quan està activada, aquesta funció organitzarà automàticament els fitxers en funció d'una plantilla definida per l'usuari. A causa de problemes d'estabilitat, la funció s'ha desactivat de manera predeterminada. Per obtenir més informació, consulteu la <link>documentation</link>.",
|
"storage_template_onboarding_description": "Quan està activada, aquesta funció organitzarà automàticament els fitxers en funció d'una plantilla definida per l'usuari. A causa de problemes d'estabilitat, la funció s'ha desactivat de manera predeterminada. Per obtenir més informació, consulteu la <link>documentation</link>.",
|
||||||
@@ -299,7 +302,7 @@
|
|||||||
"transcoding_max_b_frames": "Nombre màxim de B-frames",
|
"transcoding_max_b_frames": "Nombre màxim de B-frames",
|
||||||
"transcoding_max_b_frames_description": "Els valors més alts milloren l'eficiència de la compressió, però alenteixen la codificació. És possible que no sigui compatible amb l'acceleració de maquinari en dispositius antics. 0 desactiva els B-frames, mentre que -1 estableix aquest valor automàticament.",
|
"transcoding_max_b_frames_description": "Els valors més alts milloren l'eficiència de la compressió, però alenteixen la codificació. És possible que no sigui compatible amb l'acceleració de maquinari en dispositius antics. 0 desactiva els B-frames, mentre que -1 estableix aquest valor automàticament.",
|
||||||
"transcoding_max_bitrate": "Taxa de bits màxima",
|
"transcoding_max_bitrate": "Taxa de bits màxima",
|
||||||
"transcoding_max_bitrate_description": "Establir una taxa de bits màxima pot fer que les mides dels fitxers siguin més previsibles amb un cost menor per a la qualitat. A 720p, els valors típics són 2600k per a VP9 o HEVC, o 4500k per a H.264. Desactivat si s'estableix a 0.",
|
"transcoding_max_bitrate_description": "Establir una taxa de bits màxima pot fer que les mides dels fitxers siguin més previsibles amb un cost menor per a la qualitat. A 720p, els valors típics són 2600 kbit/s per a VP9 o HEVC, o 4500 kbit/s per a H.264. Desactivat si s'estableix a 0.",
|
||||||
"transcoding_max_keyframe_interval": "Interval màxim de fotogrames clau",
|
"transcoding_max_keyframe_interval": "Interval màxim de fotogrames clau",
|
||||||
"transcoding_max_keyframe_interval_description": "Estableix la distància màxima entre fotogrames clau. Els valors més baixos empitjoren l'eficiència de la compressió, però milloren els temps de cerca i poden millorar la qualitat en escenes amb moviment ràpid. 0 estableix aquest valor automàticament.",
|
"transcoding_max_keyframe_interval_description": "Estableix la distància màxima entre fotogrames clau. Els valors més baixos empitjoren l'eficiència de la compressió, però milloren els temps de cerca i poden millorar la qualitat en escenes amb moviment ràpid. 0 estableix aquest valor automàticament.",
|
||||||
"transcoding_optimal_description": "Vídeos superiors a la resolució objectiu o que no tenen un format acceptat",
|
"transcoding_optimal_description": "Vídeos superiors a la resolució objectiu o que no tenen un format acceptat",
|
||||||
@@ -391,6 +394,7 @@
|
|||||||
"allow_edits": "Permet editar",
|
"allow_edits": "Permet editar",
|
||||||
"allow_public_user_to_download": "Permet que l'usuari públic pugui descarregar",
|
"allow_public_user_to_download": "Permet que l'usuari públic pugui descarregar",
|
||||||
"allow_public_user_to_upload": "Permet que l'usuari públic pugui carregar",
|
"allow_public_user_to_upload": "Permet que l'usuari públic pugui carregar",
|
||||||
|
"alt_text_qr_code": "Codi QR",
|
||||||
"anti_clockwise": "En sentit antihorari",
|
"anti_clockwise": "En sentit antihorari",
|
||||||
"api_key": "Clau API",
|
"api_key": "Clau API",
|
||||||
"api_key_description": "Aquest valor només es mostrarà una vegada. Assegureu-vos de copiar-lo abans de tancar la finestra.",
|
"api_key_description": "Aquest valor només es mostrarà una vegada. Assegureu-vos de copiar-lo abans de tancar la finestra.",
|
||||||
@@ -410,7 +414,7 @@
|
|||||||
"asset_description_updated": "La descripció del recurs s'ha actualitzat",
|
"asset_description_updated": "La descripció del recurs s'ha actualitzat",
|
||||||
"asset_filename_is_offline": "L'element {filename} està fora de línia",
|
"asset_filename_is_offline": "L'element {filename} està fora de línia",
|
||||||
"asset_has_unassigned_faces": "L'element té cares no assignades",
|
"asset_has_unassigned_faces": "L'element té cares no assignades",
|
||||||
"asset_hashing": "Hashing…",
|
"asset_hashing": "Hasheant…",
|
||||||
"asset_offline": "Element fora de línia",
|
"asset_offline": "Element fora de línia",
|
||||||
"asset_offline_description": "Aquest recurs extern ja no es troba al disc. Poseu-vos en contacte amb el vostre administrador d'Immich per obtenir ajuda.",
|
"asset_offline_description": "Aquest recurs extern ja no es troba al disc. Poseu-vos en contacte amb el vostre administrador d'Immich per obtenir ajuda.",
|
||||||
"asset_skipped": "Saltat",
|
"asset_skipped": "Saltat",
|
||||||
@@ -464,7 +468,7 @@
|
|||||||
"check_logs": "Comprovar els registres",
|
"check_logs": "Comprovar els registres",
|
||||||
"choose_matching_people_to_merge": "Trieu les persones que coincideixin per combinar-les",
|
"choose_matching_people_to_merge": "Trieu les persones que coincideixin per combinar-les",
|
||||||
"city": "Ciutat",
|
"city": "Ciutat",
|
||||||
"clear": "Neteja",
|
"clear": "Buida",
|
||||||
"clear_all": "Neteja-ho tot",
|
"clear_all": "Neteja-ho tot",
|
||||||
"clear_all_recent_searches": "Esborra totes les cerques recents",
|
"clear_all_recent_searches": "Esborra totes les cerques recents",
|
||||||
"clear_message": "Neteja el missatge",
|
"clear_message": "Neteja el missatge",
|
||||||
@@ -481,6 +485,7 @@
|
|||||||
"comments_are_disabled": "Els comentaris estan desactivats",
|
"comments_are_disabled": "Els comentaris estan desactivats",
|
||||||
"confirm": "Confirmar",
|
"confirm": "Confirmar",
|
||||||
"confirm_admin_password": "Confirmeu la contrasenya d'administrador",
|
"confirm_admin_password": "Confirmeu la contrasenya d'administrador",
|
||||||
|
"confirm_delete_face": "Estàs segur que vols eliminar la cara de {name} de les cares reconegudes?",
|
||||||
"confirm_delete_shared_link": "Esteu segurs que voleu eliminar aquest enllaç compartit?",
|
"confirm_delete_shared_link": "Esteu segurs que voleu eliminar aquest enllaç compartit?",
|
||||||
"confirm_keep_this_delete_others": "Excepte aquest element, tots els altres de la pila se suprimiran. Esteu segur que voleu continuar?",
|
"confirm_keep_this_delete_others": "Excepte aquest element, tots els altres de la pila se suprimiran. Esteu segur que voleu continuar?",
|
||||||
"confirm_password": "Confirmació de contrasenya",
|
"confirm_password": "Confirmació de contrasenya",
|
||||||
@@ -533,6 +538,7 @@
|
|||||||
"delete_album": "Esborra l'àlbum",
|
"delete_album": "Esborra l'àlbum",
|
||||||
"delete_api_key_prompt": "Esteu segurs que voleu eliminar aquesta clau API?",
|
"delete_api_key_prompt": "Esteu segurs que voleu eliminar aquesta clau API?",
|
||||||
"delete_duplicates_confirmation": "Esteu segurs que voleu eliminar aquests duplicats permanentment?",
|
"delete_duplicates_confirmation": "Esteu segurs que voleu eliminar aquests duplicats permanentment?",
|
||||||
|
"delete_face": "Esborrar cara",
|
||||||
"delete_key": "Suprimeix la clau",
|
"delete_key": "Suprimeix la clau",
|
||||||
"delete_library": "Suprimeix la Llibreria",
|
"delete_library": "Suprimeix la Llibreria",
|
||||||
"delete_link": "Esborra l'enllaç",
|
"delete_link": "Esborra l'enllaç",
|
||||||
@@ -600,6 +606,7 @@
|
|||||||
"enabled": "Activat",
|
"enabled": "Activat",
|
||||||
"end_date": "Data final",
|
"end_date": "Data final",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
|
"error_delete_face": "Error esborrant cara de les cares reconegudes",
|
||||||
"error_loading_image": "Error carregant la imatge",
|
"error_loading_image": "Error carregant la imatge",
|
||||||
"error_title": "Error - Quelcom ha anat malament",
|
"error_title": "Error - Quelcom ha anat malament",
|
||||||
"errors": {
|
"errors": {
|
||||||
@@ -766,8 +773,10 @@
|
|||||||
"go_to_folder": "Anar al directori",
|
"go_to_folder": "Anar al directori",
|
||||||
"go_to_search": "Vés a cercar",
|
"go_to_search": "Vés a cercar",
|
||||||
"group_albums_by": "Agrupa àlbums per...",
|
"group_albums_by": "Agrupa àlbums per...",
|
||||||
|
"group_country": "Agrupar per país",
|
||||||
"group_no": "Cap agrupació",
|
"group_no": "Cap agrupació",
|
||||||
"group_owner": "Agrupar per propietari",
|
"group_owner": "Agrupar per propietari",
|
||||||
|
"group_places_by": "Agrupar llocs per...",
|
||||||
"group_year": "Agrupar per any",
|
"group_year": "Agrupar per any",
|
||||||
"has_quota": "Quota",
|
"has_quota": "Quota",
|
||||||
"hi_user": "Hola {name} ({email})",
|
"hi_user": "Hola {name} ({email})",
|
||||||
@@ -800,6 +809,7 @@
|
|||||||
"include_shared_albums": "Inclou àlbums compartits",
|
"include_shared_albums": "Inclou àlbums compartits",
|
||||||
"include_shared_partner_assets": "Incloure elements dels companys",
|
"include_shared_partner_assets": "Incloure elements dels companys",
|
||||||
"individual_share": "Compartit individualment",
|
"individual_share": "Compartit individualment",
|
||||||
|
"individual_shares": "Espais individuals",
|
||||||
"info": "Informació",
|
"info": "Informació",
|
||||||
"interval": {
|
"interval": {
|
||||||
"day_at_onepm": "Cada dia a les 13h",
|
"day_at_onepm": "Cada dia a les 13h",
|
||||||
@@ -881,6 +891,7 @@
|
|||||||
"month": "Mes",
|
"month": "Mes",
|
||||||
"more": "Més",
|
"more": "Més",
|
||||||
"moved_to_trash": "S'ha mogut a la paperera",
|
"moved_to_trash": "S'ha mogut a la paperera",
|
||||||
|
"mute_memories": "Silenciar records",
|
||||||
"my_albums": "Els meus àlbums",
|
"my_albums": "Els meus àlbums",
|
||||||
"name": "Nom",
|
"name": "Nom",
|
||||||
"name_or_nickname": "Nom o sobrenom",
|
"name_or_nickname": "Nom o sobrenom",
|
||||||
@@ -985,6 +996,7 @@
|
|||||||
"pick_a_location": "Triar una ubicació",
|
"pick_a_location": "Triar una ubicació",
|
||||||
"place": "Lloc",
|
"place": "Lloc",
|
||||||
"places": "Llocs",
|
"places": "Llocs",
|
||||||
|
"places_count": "{count, plural, one {{count, number} Lloc} other {{count, number} Llocs}}",
|
||||||
"play": "Reprodueix",
|
"play": "Reprodueix",
|
||||||
"play_memories": "Reproduir records",
|
"play_memories": "Reproduir records",
|
||||||
"play_motion_photo": "Reproduir Fotos en Moviment",
|
"play_motion_photo": "Reproduir Fotos en Moviment",
|
||||||
@@ -1072,6 +1084,8 @@
|
|||||||
"removed_from_archive": "Eliminat de l'arxiu",
|
"removed_from_archive": "Eliminat de l'arxiu",
|
||||||
"removed_from_favorites": "Eliminat dels preferits",
|
"removed_from_favorites": "Eliminat dels preferits",
|
||||||
"removed_from_favorites_count": "{count, plural, other {# eliminats}} dels preferits",
|
"removed_from_favorites_count": "{count, plural, other {# eliminats}} dels preferits",
|
||||||
|
"removed_memory": "Eliminat memòria",
|
||||||
|
"removed_photo_from_memory": "Eliminat foto de memòria",
|
||||||
"removed_tagged_assets": "Etiqueta eliminada de {count, plural, one {# actiu} other {# actius}}",
|
"removed_tagged_assets": "Etiqueta eliminada de {count, plural, one {# actiu} other {# actius}}",
|
||||||
"rename": "Canviar nom",
|
"rename": "Canviar nom",
|
||||||
"repair": "Reparació",
|
"repair": "Reparació",
|
||||||
@@ -1080,6 +1094,7 @@
|
|||||||
"repository": "Repositori",
|
"repository": "Repositori",
|
||||||
"require_password": "Requereix contrasenya",
|
"require_password": "Requereix contrasenya",
|
||||||
"require_user_to_change_password_on_first_login": "Requerir que l'usuari canviï la contrasenya en el primer inici de sessió",
|
"require_user_to_change_password_on_first_login": "Requerir que l'usuari canviï la contrasenya en el primer inici de sessió",
|
||||||
|
"rescan": "Tornar a escanejar",
|
||||||
"reset": "Restablir",
|
"reset": "Restablir",
|
||||||
"reset_password": "Restablir contrasenya",
|
"reset_password": "Restablir contrasenya",
|
||||||
"reset_people_visibility": "Restablir la visibilitat de les persones",
|
"reset_people_visibility": "Restablir la visibilitat de les persones",
|
||||||
@@ -1108,6 +1123,8 @@
|
|||||||
"search": "Cerca",
|
"search": "Cerca",
|
||||||
"search_albums": "Buscar àlbums",
|
"search_albums": "Buscar àlbums",
|
||||||
"search_by_context": "Buscar per context",
|
"search_by_context": "Buscar per context",
|
||||||
|
"search_by_description": "Cercar per descripció",
|
||||||
|
"search_by_description_example": "Jornada de senderisme a Sapa",
|
||||||
"search_by_filename": "Cerca per nom de fitxer o extensió",
|
"search_by_filename": "Cerca per nom de fitxer o extensió",
|
||||||
"search_by_filename_example": "per exemple IMG_1234.JPG o PNG",
|
"search_by_filename_example": "per exemple IMG_1234.JPG o PNG",
|
||||||
"search_camera_make": "Buscar per fabricant de càmara...",
|
"search_camera_make": "Buscar per fabricant de càmara...",
|
||||||
@@ -1121,6 +1138,7 @@
|
|||||||
"search_options": "Opcions de cerca",
|
"search_options": "Opcions de cerca",
|
||||||
"search_people": "Buscar persones",
|
"search_people": "Buscar persones",
|
||||||
"search_places": "Buscar llocs",
|
"search_places": "Buscar llocs",
|
||||||
|
"search_rating": "Buscar per qualificació...",
|
||||||
"search_settings": "Configuració de cerca",
|
"search_settings": "Configuració de cerca",
|
||||||
"search_state": "Buscar per regió...",
|
"search_state": "Buscar per regió...",
|
||||||
"search_tags": "Cercant etiquetes...",
|
"search_tags": "Cercant etiquetes...",
|
||||||
@@ -1167,6 +1185,7 @@
|
|||||||
"shared_from_partner": "Fotos de {partner}",
|
"shared_from_partner": "Fotos de {partner}",
|
||||||
"shared_link_options": "Opcions d'enllaços compartits",
|
"shared_link_options": "Opcions d'enllaços compartits",
|
||||||
"shared_links": "Enllaços compartits",
|
"shared_links": "Enllaços compartits",
|
||||||
|
"shared_links_description": "Comparteix fotos i vídeos amb un enllaç",
|
||||||
"shared_photos_and_videos_count": "{assetCount, plural, other {# fotos i vídeos compartits.}}",
|
"shared_photos_and_videos_count": "{assetCount, plural, other {# fotos i vídeos compartits.}}",
|
||||||
"shared_with_partner": "Compartit amb {partner}",
|
"shared_with_partner": "Compartit amb {partner}",
|
||||||
"sharing": "Compartit",
|
"sharing": "Compartit",
|
||||||
@@ -1189,6 +1208,7 @@
|
|||||||
"show_person_options": "Mostra opcions de la persona",
|
"show_person_options": "Mostra opcions de la persona",
|
||||||
"show_progress_bar": "Mostra barra de progrés",
|
"show_progress_bar": "Mostra barra de progrés",
|
||||||
"show_search_options": "Mostra opcions de cerca",
|
"show_search_options": "Mostra opcions de cerca",
|
||||||
|
"show_shared_links": "Mostra els enllaços compartits",
|
||||||
"show_slideshow_transition": "Mostra la transició de la presentació de diapositives",
|
"show_slideshow_transition": "Mostra la transició de la presentació de diapositives",
|
||||||
"show_supporter_badge": "Insígnia de contribuent",
|
"show_supporter_badge": "Insígnia de contribuent",
|
||||||
"show_supporter_badge_description": "Mostra una insígnia de contributor",
|
"show_supporter_badge_description": "Mostra una insígnia de contributor",
|
||||||
@@ -1242,6 +1262,7 @@
|
|||||||
"tag_created": "Etiqueta creada: {tag}",
|
"tag_created": "Etiqueta creada: {tag}",
|
||||||
"tag_feature_description": "Exploreu fotos i vídeos agrupats per temes d'etiquetes lògiques",
|
"tag_feature_description": "Exploreu fotos i vídeos agrupats per temes d'etiquetes lògiques",
|
||||||
"tag_not_found_question": "No trobeu una etiqueta? <link>Crear una nova etiqueta</link>",
|
"tag_not_found_question": "No trobeu una etiqueta? <link>Crear una nova etiqueta</link>",
|
||||||
|
"tag_people": "Etiquetar personas",
|
||||||
"tag_updated": "Etiqueta actualizada: {tag}",
|
"tag_updated": "Etiqueta actualizada: {tag}",
|
||||||
"tagged_assets": "{count, plural, one {#Etiquetat} other {#Etiquetats}} {count, plural, one {# actiu} other {# actius}}",
|
"tagged_assets": "{count, plural, one {#Etiquetat} other {#Etiquetats}} {count, plural, one {# actiu} other {# actius}}",
|
||||||
"tags": "Etiquetes",
|
"tags": "Etiquetes",
|
||||||
@@ -1276,11 +1297,13 @@
|
|||||||
"unfavorite": "Reverteix preferit",
|
"unfavorite": "Reverteix preferit",
|
||||||
"unhide_person": "Mostra persona",
|
"unhide_person": "Mostra persona",
|
||||||
"unknown": "Desconegut",
|
"unknown": "Desconegut",
|
||||||
|
"unknown_country": "País Desconegut",
|
||||||
"unknown_year": "Any desconegut",
|
"unknown_year": "Any desconegut",
|
||||||
"unlimited": "Il·limitat",
|
"unlimited": "Il·limitat",
|
||||||
"unlink_motion_video": "Desvincular vídeo en moviment",
|
"unlink_motion_video": "Desvincular vídeo en moviment",
|
||||||
"unlink_oauth": "Desvincula OAuth",
|
"unlink_oauth": "Desvincula OAuth",
|
||||||
"unlinked_oauth_account": "Compte Oauth desvinculat",
|
"unlinked_oauth_account": "Compte Oauth desvinculat",
|
||||||
|
"unmute_memories": "Activar el so dels records",
|
||||||
"unnamed_album": "Àlbum sense nom",
|
"unnamed_album": "Àlbum sense nom",
|
||||||
"unnamed_album_delete_confirmation": "Segur que voleu esborrar aquest àlbum?",
|
"unnamed_album_delete_confirmation": "Segur que voleu esborrar aquest àlbum?",
|
||||||
"unnamed_share": "Compartit sense nom",
|
"unnamed_share": "Compartit sense nom",
|
||||||
@@ -1334,6 +1357,7 @@
|
|||||||
"view_all": "Veure tot",
|
"view_all": "Veure tot",
|
||||||
"view_all_users": "Mostra tot els usuaris",
|
"view_all_users": "Mostra tot els usuaris",
|
||||||
"view_in_timeline": "Mostrar a la línia de temps",
|
"view_in_timeline": "Mostrar a la línia de temps",
|
||||||
|
"view_link": "Veure enllaç",
|
||||||
"view_links": "Mostra enllaços",
|
"view_links": "Mostra enllaços",
|
||||||
"view_name": "Veure",
|
"view_name": "Veure",
|
||||||
"view_next_asset": "Mostra el següent element",
|
"view_next_asset": "Mostra el següent element",
|
||||||
|
|||||||
51
i18n/cs.json
51
i18n/cs.json
@@ -41,6 +41,7 @@
|
|||||||
"backup_settings": "Nastavení zálohování",
|
"backup_settings": "Nastavení zálohování",
|
||||||
"backup_settings_description": "Správa nastavení zálohování databáze",
|
"backup_settings_description": "Správa nastavení zálohování databáze",
|
||||||
"check_all": "Vše zkontrolovat",
|
"check_all": "Vše zkontrolovat",
|
||||||
|
"cleanup": "Vyčištění",
|
||||||
"cleared_jobs": "Hotové úlohy pro: {job}",
|
"cleared_jobs": "Hotové úlohy pro: {job}",
|
||||||
"config_set_by_file": "Konfigurace je aktuálně prováděna konfiguračním souborem",
|
"config_set_by_file": "Konfigurace je aktuálně prováděna konfiguračním souborem",
|
||||||
"confirm_delete_library": "Opravdu chcete odstranit knihovnu {library}?",
|
"confirm_delete_library": "Opravdu chcete odstranit knihovnu {library}?",
|
||||||
@@ -65,8 +66,13 @@
|
|||||||
"forcing_refresh_library_files": "Vynucení obnovy všech souborů knihovny",
|
"forcing_refresh_library_files": "Vynucení obnovy všech souborů knihovny",
|
||||||
"image_format": "Formát",
|
"image_format": "Formát",
|
||||||
"image_format_description": "WebP vytváří menší soubory než JPEG, ale je pomalejší při kódování.",
|
"image_format_description": "WebP vytváří menší soubory než JPEG, ale je pomalejší při kódování.",
|
||||||
|
"image_fullsize_description": "Obrázek v plné velikosti s odstraněnými metadaty, použito při přiblížení",
|
||||||
|
"image_fullsize_enabled": "Povolit generování obrázků v plné velikosti",
|
||||||
|
"image_fullsize_enabled_description": "Generovat obrázky v plné velikosti pro formáty, které nejsou vhodné pro web. Pokud je povolena možnost „Preferovat vložený náhled“, budou přímo použity vložené náhledy bez převodu. Neovlivňuje formáty vhodné pro web, jako je JPEG.",
|
||||||
|
"image_fullsize_quality_description": "Kvalita obrázku v plné velikosti od 1 do 100. Vyšší je lepší, ale vytváří větší soubory.",
|
||||||
|
"image_fullsize_title": "Nastavení obrázků v plné velikosti",
|
||||||
"image_prefer_embedded_preview": "Preferovat vložený náhled",
|
"image_prefer_embedded_preview": "Preferovat vložený náhled",
|
||||||
"image_prefer_embedded_preview_setting_description": "Použít vložené náhledy z RAW fotografií jako vstup pro zpracování snímků, pokud jsou k dispozici. U některých snímků tak lze dosáhnout přesnějších barev, ale kvalita náhledu závisí na fotoaparátu a snímek může obsahovat více kompresních artefaktů.",
|
"image_prefer_embedded_preview_setting_description": "Použít vložené náhledy z RAW fotografií jako vstup pro zpracování snímků a pokud jsou k dispozici. U některých snímků tak lze dosáhnout přesnějších barev, ale kvalita náhledu závisí na fotoaparátu a snímek může obsahovat více kompresních artefaktů.",
|
||||||
"image_prefer_wide_gamut": "Preferovat široký gamut",
|
"image_prefer_wide_gamut": "Preferovat široký gamut",
|
||||||
"image_prefer_wide_gamut_setting_description": "Použít Display P3 pro miniatury. To lépe zachovává živost obrázků s širokým barevným prostorem, ale obrázky se mohou na starých zařízeních se starou verzí prohlížeče zobrazovat jinak. sRGB obrázky jsou ponechány jako sRGB, aby se zabránilo posunům barev.",
|
"image_prefer_wide_gamut_setting_description": "Použít Display P3 pro miniatury. To lépe zachovává živost obrázků s širokým barevným prostorem, ale obrázky se mohou na starých zařízeních se starou verzí prohlížeče zobrazovat jinak. sRGB obrázky jsou ponechány jako sRGB, aby se zabránilo posunům barev.",
|
||||||
"image_preview_description": "Středně velký obrázek se zbavenými metadaty, který se používá při prohlížení jedné položky a pro strojové učení",
|
"image_preview_description": "Středně velký obrázek se zbavenými metadaty, který se používá při prohlížení jedné položky a pro strojové učení",
|
||||||
@@ -96,7 +102,7 @@
|
|||||||
"library_scanning_enable_description": "Povolit pravidelné prohledávání knihovny",
|
"library_scanning_enable_description": "Povolit pravidelné prohledávání knihovny",
|
||||||
"library_settings": "Externí knihovna",
|
"library_settings": "Externí knihovna",
|
||||||
"library_settings_description": "Správa nastavení externí knihovny",
|
"library_settings_description": "Správa nastavení externí knihovny",
|
||||||
"library_tasks_description": "Provádění úkolů v knihovně",
|
"library_tasks_description": "Vyhledávání nových nebo změněných položek v externích knihovnách",
|
||||||
"library_watching_enable_description": "Sledovat změny souborů v externích knihovnách",
|
"library_watching_enable_description": "Sledovat změny souborů v externích knihovnách",
|
||||||
"library_watching_settings": "Sledování knihovny (EXPERIMENTÁLNÍ)",
|
"library_watching_settings": "Sledování knihovny (EXPERIMENTÁLNÍ)",
|
||||||
"library_watching_settings_description": "Automatické sledování změněných souborů",
|
"library_watching_settings_description": "Automatické sledování změněných souborů",
|
||||||
@@ -131,7 +137,7 @@
|
|||||||
"machine_learning_smart_search_description": "Sémantické vyhledávání obrázků pomocí CLIP embeddings",
|
"machine_learning_smart_search_description": "Sémantické vyhledávání obrázků pomocí CLIP embeddings",
|
||||||
"machine_learning_smart_search_enabled": "Povolit chytré vyhledávání",
|
"machine_learning_smart_search_enabled": "Povolit chytré vyhledávání",
|
||||||
"machine_learning_smart_search_enabled_description": "Pokud je vypnuto, obrázky nebudou kódovány pro inteligentní vyhledávání.",
|
"machine_learning_smart_search_enabled_description": "Pokud je vypnuto, obrázky nebudou kódovány pro inteligentní vyhledávání.",
|
||||||
"machine_learning_url_description": "URL serveru strojového učení. Pokud je zadáno více URL adres, budou jednotlivé servery zkoušeny postupně, dokud jeden z nich neodpoví úspěšně, a to v pořadí od prvního k poslednímu.",
|
"machine_learning_url_description": "URL serveru strojového učení. Pokud je zadáno více URL adres, budou jednotlivé servery zkoušeny postupně, dokud jeden z nich neodpoví úspěšně, a to v pořadí od prvního k poslednímu. Servery, které neodpoví, budou dočasně ignorovány, dokud nebudou opět online.",
|
||||||
"manage_concurrency": "Správa souběžnosti",
|
"manage_concurrency": "Správa souběžnosti",
|
||||||
"manage_log_settings": "Správa nastavení protokolu",
|
"manage_log_settings": "Správa nastavení protokolu",
|
||||||
"map_dark_style": "Tmavý motiv",
|
"map_dark_style": "Tmavý motiv",
|
||||||
@@ -147,6 +153,8 @@
|
|||||||
"map_settings": "Mapa",
|
"map_settings": "Mapa",
|
||||||
"map_settings_description": "Správa nastavení mapy",
|
"map_settings_description": "Správa nastavení mapy",
|
||||||
"map_style_description": "URL na style.json motivu",
|
"map_style_description": "URL na style.json motivu",
|
||||||
|
"memory_cleanup_job": "Promazání vzpomínek",
|
||||||
|
"memory_generate_job": "Vytvoření vzpomínek",
|
||||||
"metadata_extraction_job": "Extrakce metadat",
|
"metadata_extraction_job": "Extrakce metadat",
|
||||||
"metadata_extraction_job_description": "Získání informací o metadatech z každého snímku, jako je GPS, obličeje a rozlišení",
|
"metadata_extraction_job_description": "Získání informací o metadatech z každého snímku, jako je GPS, obličeje a rozlišení",
|
||||||
"metadata_faces_import_setting": "Povolit import obličeje",
|
"metadata_faces_import_setting": "Povolit import obličeje",
|
||||||
@@ -240,7 +248,7 @@
|
|||||||
"storage_template_hash_verification_enabled_description": "Povolí ověřování hashe, nevypínejte ji, pokud si nejste jisti důsledky",
|
"storage_template_hash_verification_enabled_description": "Povolí ověřování hashe, nevypínejte ji, pokud si nejste jisti důsledky",
|
||||||
"storage_template_migration": "Migrace šablony úložiště",
|
"storage_template_migration": "Migrace šablony úložiště",
|
||||||
"storage_template_migration_description": "Použít aktuální <link>{template}</link> na dříve nahrané položky",
|
"storage_template_migration_description": "Použít aktuální <link>{template}</link> na dříve nahrané položky",
|
||||||
"storage_template_migration_info": "Změny šablon se uplatní pouze u nových položek. Chcete-li šablonu zpětně použít na dříve nahrané položky, spusťte <link>{job}</link>.",
|
"storage_template_migration_info": "Šablona úložiště převede všechny přípony na malá písmena. Změny šablon se uplatní pouze u nových položek. Chcete-li šablonu zpětně použít na dříve nahrané položky, spusťte <link>{job}</link>.",
|
||||||
"storage_template_migration_job": "Úloha migrace šablony úložiště",
|
"storage_template_migration_job": "Úloha migrace šablony úložiště",
|
||||||
"storage_template_more_details": "Další podrobnosti o této funkci naleznete v sekci <template-link>Šablona úložiště</template-link> včetně jejích <implications-link>důsledků</implications-link>",
|
"storage_template_more_details": "Další podrobnosti o této funkci naleznete v sekci <template-link>Šablona úložiště</template-link> včetně jejích <implications-link>důsledků</implications-link>",
|
||||||
"storage_template_onboarding_description": "Je-li tato funkce povolena, automaticky uspořádá soubory na základě uživatelem definované šablony. Z důvodu problémů se stabilitou byla tato funkce ve výchozím nastavení vypnuta. Další informace naleznete v <link>dokumentaci</link>.",
|
"storage_template_onboarding_description": "Je-li tato funkce povolena, automaticky uspořádá soubory na základě uživatelem definované šablony. Z důvodu problémů se stabilitou byla tato funkce ve výchozím nastavení vypnuta. Další informace naleznete v <link>dokumentaci</link>.",
|
||||||
@@ -288,7 +296,7 @@
|
|||||||
"transcoding_constant_quality_mode_description": "ICQ je lepší než CQP, ale některá zařízení pro hardwarovou akceleraci tento režim nepodporují. Nastavením této volby se při použití kódování založeného na kvalitě upřednostní zadaný režim. Ignorováno NVENC, protože nepodporuje ICQ.",
|
"transcoding_constant_quality_mode_description": "ICQ je lepší než CQP, ale některá zařízení pro hardwarovou akceleraci tento režim nepodporují. Nastavením této volby se při použití kódování založeného na kvalitě upřednostní zadaný režim. Ignorováno NVENC, protože nepodporuje ICQ.",
|
||||||
"transcoding_constant_rate_factor": "Faktor konstantní rychlosti (-crf)",
|
"transcoding_constant_rate_factor": "Faktor konstantní rychlosti (-crf)",
|
||||||
"transcoding_constant_rate_factor_description": "Úroveň kvality videa. Typické hodnoty jsou 23 pro H.264, 28 pro HEVC, 31 pro VP9 a 35 pro AV1. Nižší je lepší, ale vytváří větší soubory.",
|
"transcoding_constant_rate_factor_description": "Úroveň kvality videa. Typické hodnoty jsou 23 pro H.264, 28 pro HEVC, 31 pro VP9 a 35 pro AV1. Nižší je lepší, ale vytváří větší soubory.",
|
||||||
"transcoding_disabled_description": "Nepřekódovávejte žádná videa, u některých klientů může dojít k znemožnění přehrávání",
|
"transcoding_disabled_description": "Nepřekódovávat žádná videa, u některých klientů může dojít k znemožnění přehrávání",
|
||||||
"transcoding_encoding_options": "Možnosti kódování",
|
"transcoding_encoding_options": "Možnosti kódování",
|
||||||
"transcoding_encoding_options_description": "Nastavte kodeky, rozlišení, kvalitu a další možnosti pro kódovaná videa",
|
"transcoding_encoding_options_description": "Nastavte kodeky, rozlišení, kvalitu a další možnosti pro kódovaná videa",
|
||||||
"transcoding_hardware_acceleration": "Hardwarová akcelerace",
|
"transcoding_hardware_acceleration": "Hardwarová akcelerace",
|
||||||
@@ -299,7 +307,7 @@
|
|||||||
"transcoding_max_b_frames": "Maximální počet B-snímků",
|
"transcoding_max_b_frames": "Maximální počet B-snímků",
|
||||||
"transcoding_max_b_frames_description": "Vyšší hodnoty zvyšují účinnost komprese, ale zpomalují kódování. Nemusí být kompatibilní s hardwarovou akcelerací na starších zařízeních. Hodnota 0 zakáže B-snímky, zatímco -1 tuto hodnotu nastaví automaticky.",
|
"transcoding_max_b_frames_description": "Vyšší hodnoty zvyšují účinnost komprese, ale zpomalují kódování. Nemusí být kompatibilní s hardwarovou akcelerací na starších zařízeních. Hodnota 0 zakáže B-snímky, zatímco -1 tuto hodnotu nastaví automaticky.",
|
||||||
"transcoding_max_bitrate": "Maximální datový tok",
|
"transcoding_max_bitrate": "Maximální datový tok",
|
||||||
"transcoding_max_bitrate_description": "Nastavení maximálního datového toku může zvýšit předvídatelnost velikosti souborů za cenu menší újmy na kvalitě. Při rozlišení 720p jsou typické hodnoty 2600k pro VP9 nebo HEVC nebo 4500k pro H.264. Je zakázáno, pokud je nastavena hodnota 0.",
|
"transcoding_max_bitrate_description": "Nastavení maximálního datového toku může zvýšit předvídatelnost velikosti souborů za cenu menší újmy na kvalitě. Při rozlišení 720p jsou typické hodnoty 2600 kbit/s pro VP9 nebo HEVC nebo 4500 kbit/s pro H.264. Je zakázáno, pokud je nastavena hodnota 0.",
|
||||||
"transcoding_max_keyframe_interval": "Maximální interval klíčových snímků",
|
"transcoding_max_keyframe_interval": "Maximální interval klíčových snímků",
|
||||||
"transcoding_max_keyframe_interval_description": "Nastavuje maximální vzdálenost mezi klíčovými snímky. Nižší hodnoty zhoršují účinnost komprese, ale zlepšují rychlost při přeskakování a mohou zlepšit kvalitu ve scénách s rychlým pohybem. Hodnota 0 nastavuje tuto hodnotu automaticky.",
|
"transcoding_max_keyframe_interval_description": "Nastavuje maximální vzdálenost mezi klíčovými snímky. Nižší hodnoty zhoršují účinnost komprese, ale zlepšují rychlost při přeskakování a mohou zlepšit kvalitu ve scénách s rychlým pohybem. Hodnota 0 nastavuje tuto hodnotu automaticky.",
|
||||||
"transcoding_optimal_description": "Videa s vyšším než cílovým rozlišením nebo videa, která nejsou v akceptovaném formátu",
|
"transcoding_optimal_description": "Videa s vyšším než cílovým rozlišením nebo videa, která nejsou v akceptovaném formátu",
|
||||||
@@ -391,6 +399,7 @@
|
|||||||
"allow_edits": "Povolit úpravy",
|
"allow_edits": "Povolit úpravy",
|
||||||
"allow_public_user_to_download": "Povolit veřejnosti stahovat",
|
"allow_public_user_to_download": "Povolit veřejnosti stahovat",
|
||||||
"allow_public_user_to_upload": "Povolit veřejnosti nahrávat",
|
"allow_public_user_to_upload": "Povolit veřejnosti nahrávat",
|
||||||
|
"alt_text_qr_code": "Obrázek QR kódu",
|
||||||
"anti_clockwise": "Proti směru hodinových ručiček",
|
"anti_clockwise": "Proti směru hodinových ručiček",
|
||||||
"api_key": "API klíč",
|
"api_key": "API klíč",
|
||||||
"api_key_description": "Tato hodnota se zobrazí pouze jednou. Před zavřením okna ji nezapomeňte zkopírovat.",
|
"api_key_description": "Tato hodnota se zobrazí pouze jednou. Před zavřením okna ji nezapomeňte zkopírovat.",
|
||||||
@@ -464,11 +473,11 @@
|
|||||||
"check_logs": "Zkontrolujte protokoly",
|
"check_logs": "Zkontrolujte protokoly",
|
||||||
"choose_matching_people_to_merge": "Zvolte odpovídající osoby ke sloučení",
|
"choose_matching_people_to_merge": "Zvolte odpovídající osoby ke sloučení",
|
||||||
"city": "Město",
|
"city": "Město",
|
||||||
"clear": "Vyčistit",
|
"clear": "Vymazat",
|
||||||
"clear_all": "Vymazat vše",
|
"clear_all": "Vymazat vše",
|
||||||
"clear_all_recent_searches": "Vymazat všechna nedávná vyhledávání",
|
"clear_all_recent_searches": "Vymazat všechna nedávná vyhledávání",
|
||||||
"clear_message": "Vyčistit zprávu",
|
"clear_message": "Vymazat zprávu",
|
||||||
"clear_value": "Vyčistit hodnotu",
|
"clear_value": "Vymazat hodnotu",
|
||||||
"clockwise": "Po směru hodinových ručiček",
|
"clockwise": "Po směru hodinových ručiček",
|
||||||
"close": "Zavřít",
|
"close": "Zavřít",
|
||||||
"collapse": "Sbalit",
|
"collapse": "Sbalit",
|
||||||
@@ -481,6 +490,7 @@
|
|||||||
"comments_are_disabled": "Komentáře jsou vypnuty",
|
"comments_are_disabled": "Komentáře jsou vypnuty",
|
||||||
"confirm": "Potvrdit",
|
"confirm": "Potvrdit",
|
||||||
"confirm_admin_password": "Potvrzení hesla správce",
|
"confirm_admin_password": "Potvrzení hesla správce",
|
||||||
|
"confirm_delete_face": "Opravdu chcete z položky odstranit obličej osoby {name}?",
|
||||||
"confirm_delete_shared_link": "Opravdu chcete odstranit tento sdílený odkaz?",
|
"confirm_delete_shared_link": "Opravdu chcete odstranit tento sdílený odkaz?",
|
||||||
"confirm_keep_this_delete_others": "Všechny ostatní položky v tomto uskupení mimo této budou odstraněny. Opravdu chcete pokračovat?",
|
"confirm_keep_this_delete_others": "Všechny ostatní položky v tomto uskupení mimo této budou odstraněny. Opravdu chcete pokračovat?",
|
||||||
"confirm_password": "Potvrzení hesla",
|
"confirm_password": "Potvrzení hesla",
|
||||||
@@ -533,6 +543,7 @@
|
|||||||
"delete_album": "Smazat album",
|
"delete_album": "Smazat album",
|
||||||
"delete_api_key_prompt": "Opravdu chcete tento API klíč odstranit?",
|
"delete_api_key_prompt": "Opravdu chcete tento API klíč odstranit?",
|
||||||
"delete_duplicates_confirmation": "Opravdu chcete tyto duplicity trvale odstranit?",
|
"delete_duplicates_confirmation": "Opravdu chcete tyto duplicity trvale odstranit?",
|
||||||
|
"delete_face": "Odstranit obličej",
|
||||||
"delete_key": "Smazat klíč",
|
"delete_key": "Smazat klíč",
|
||||||
"delete_library": "Smazat knihovnu",
|
"delete_library": "Smazat knihovnu",
|
||||||
"delete_link": "Smazat odkaz",
|
"delete_link": "Smazat odkaz",
|
||||||
@@ -600,6 +611,7 @@
|
|||||||
"enabled": "Povoleno",
|
"enabled": "Povoleno",
|
||||||
"end_date": "Konečné datum",
|
"end_date": "Konečné datum",
|
||||||
"error": "Chyba",
|
"error": "Chyba",
|
||||||
|
"error_delete_face": "Chyba při odstraňování obličeje z položky",
|
||||||
"error_loading_image": "Chyba při načítání obrázku",
|
"error_loading_image": "Chyba při načítání obrázku",
|
||||||
"error_title": "Chyba - Něco se pokazilo",
|
"error_title": "Chyba - Něco se pokazilo",
|
||||||
"errors": {
|
"errors": {
|
||||||
@@ -751,8 +763,8 @@
|
|||||||
"features_setting_description": "Správa funkcí aplikace",
|
"features_setting_description": "Správa funkcí aplikace",
|
||||||
"file_name": "Název souboru",
|
"file_name": "Název souboru",
|
||||||
"file_name_or_extension": "Název nebo přípona souboru",
|
"file_name_or_extension": "Název nebo přípona souboru",
|
||||||
"filename": "Filename",
|
"filename": "Název souboru",
|
||||||
"filetype": "Filetype",
|
"filetype": "Typ souboru",
|
||||||
"filter_people": "Filtrovat lidi",
|
"filter_people": "Filtrovat lidi",
|
||||||
"find_them_fast": "Najděte je rychle vyhledáním jejich jména",
|
"find_them_fast": "Najděte je rychle vyhledáním jejich jména",
|
||||||
"fix_incorrect_match": "Opravit nesprávnou shodu",
|
"fix_incorrect_match": "Opravit nesprávnou shodu",
|
||||||
@@ -852,6 +864,7 @@
|
|||||||
"loop_videos": "Videa ve smyčce",
|
"loop_videos": "Videa ve smyčce",
|
||||||
"loop_videos_description": "Povolit automatickou smyčku videa v prohlížeči.",
|
"loop_videos_description": "Povolit automatickou smyčku videa v prohlížeči.",
|
||||||
"main_branch_warning": "Používáte vývojovou verzi; důrazně doporučujeme používat verzi z vydání!",
|
"main_branch_warning": "Používáte vývojovou verzi; důrazně doporučujeme používat verzi z vydání!",
|
||||||
|
"main_menu": "Hlavní nabídka",
|
||||||
"make": "Výrobce",
|
"make": "Výrobce",
|
||||||
"manage_shared_links": "Spravovat sdílené odkazy",
|
"manage_shared_links": "Spravovat sdílené odkazy",
|
||||||
"manage_sharing_with_partners": "Správa sdílení s partnery",
|
"manage_sharing_with_partners": "Správa sdílení s partnery",
|
||||||
@@ -884,6 +897,7 @@
|
|||||||
"month": "Měsíc",
|
"month": "Měsíc",
|
||||||
"more": "Více",
|
"more": "Více",
|
||||||
"moved_to_trash": "Přesunuto do koše",
|
"moved_to_trash": "Přesunuto do koše",
|
||||||
|
"mute_memories": "Ztlumit vzpomínky",
|
||||||
"my_albums": "Moje alba",
|
"my_albums": "Moje alba",
|
||||||
"name": "Jméno",
|
"name": "Jméno",
|
||||||
"name_or_nickname": "Jméno nebo přezdívka",
|
"name_or_nickname": "Jméno nebo přezdívka",
|
||||||
@@ -979,6 +993,7 @@
|
|||||||
"permanently_deleted_asset": "Položka trvale odstraněna",
|
"permanently_deleted_asset": "Položka trvale odstraněna",
|
||||||
"permanently_deleted_assets_count": "{count, plural, one {Položka trvale vymazána} other {Položky trvale vymazány}}",
|
"permanently_deleted_assets_count": "{count, plural, one {Položka trvale vymazána} other {Položky trvale vymazány}}",
|
||||||
"person": "Osoba",
|
"person": "Osoba",
|
||||||
|
"person_birthdate": "Narozen/a {date}",
|
||||||
"person_hidden": "{name}{hidden, select, true { (skryto)} other {}}",
|
"person_hidden": "{name}{hidden, select, true { (skryto)} other {}}",
|
||||||
"photo_shared_all_users": "Vypadá to, že jste fotky sdíleli se všemi uživateli, nebo nemáte žádného uživatele, se kterým byste je mohli sdílet.",
|
"photo_shared_all_users": "Vypadá to, že jste fotky sdíleli se všemi uživateli, nebo nemáte žádného uživatele, se kterým byste je mohli sdílet.",
|
||||||
"photos": "Fotky",
|
"photos": "Fotky",
|
||||||
@@ -1070,12 +1085,16 @@
|
|||||||
"remove_from_album": "Odstranit z alba",
|
"remove_from_album": "Odstranit z alba",
|
||||||
"remove_from_favorites": "Odstranit z oblíbených",
|
"remove_from_favorites": "Odstranit z oblíbených",
|
||||||
"remove_from_shared_link": "Odstranit ze sdíleného odkazu",
|
"remove_from_shared_link": "Odstranit ze sdíleného odkazu",
|
||||||
|
"remove_memory": "Odstranit vzpomínku",
|
||||||
|
"remove_photo_from_memory": "Odstranit fotografii z této vzpomínky",
|
||||||
"remove_url": "Odstranit URL",
|
"remove_url": "Odstranit URL",
|
||||||
"remove_user": "Odebrat uživatele",
|
"remove_user": "Odebrat uživatele",
|
||||||
"removed_api_key": "Odstraněn API klíč: {name}",
|
"removed_api_key": "Odstraněn API klíč: {name}",
|
||||||
"removed_from_archive": "Odstraněno z archivu",
|
"removed_from_archive": "Odstraněno z archivu",
|
||||||
"removed_from_favorites": "Odstraněno z oblíbených",
|
"removed_from_favorites": "Odstraněno z oblíbených",
|
||||||
"removed_from_favorites_count": "{count, plural, one {Odstraněn #} few {Odstraněny #} other {Odstraněno #}} z oblíbených",
|
"removed_from_favorites_count": "{count, plural, one {Odstraněn #} few {Odstraněny #} other {Odstraněno #}} z oblíbených",
|
||||||
|
"removed_memory": "Vzpomínka odstraněna",
|
||||||
|
"removed_photo_from_memory": "Fotografie odstraněna ze vzpomínky",
|
||||||
"removed_tagged_assets": "Odstraněná značka z {count, plural, one {# položky} other {# položek}}",
|
"removed_tagged_assets": "Odstraněná značka z {count, plural, one {# položky} other {# položek}}",
|
||||||
"rename": "Přejmenovat",
|
"rename": "Přejmenovat",
|
||||||
"repair": "Opravy",
|
"repair": "Opravy",
|
||||||
@@ -1084,6 +1103,7 @@
|
|||||||
"repository": "Repozitář",
|
"repository": "Repozitář",
|
||||||
"require_password": "Požadovat heslo",
|
"require_password": "Požadovat heslo",
|
||||||
"require_user_to_change_password_on_first_login": "Požadovat, aby si uživatel při prvním přihlášení změnil heslo",
|
"require_user_to_change_password_on_first_login": "Požadovat, aby si uživatel při prvním přihlášení změnil heslo",
|
||||||
|
"rescan": "Znovu prohledat",
|
||||||
"reset": "Výchozí",
|
"reset": "Výchozí",
|
||||||
"reset_password": "Obnovit heslo",
|
"reset_password": "Obnovit heslo",
|
||||||
"reset_people_visibility": "Obnovit viditelnost lidí",
|
"reset_people_visibility": "Obnovit viditelnost lidí",
|
||||||
@@ -1127,6 +1147,7 @@
|
|||||||
"search_options": "Možnosti vyhledávání",
|
"search_options": "Možnosti vyhledávání",
|
||||||
"search_people": "Vyhledat lidi",
|
"search_people": "Vyhledat lidi",
|
||||||
"search_places": "Vyhledat místa",
|
"search_places": "Vyhledat místa",
|
||||||
|
"search_rating": "Vyhledávání podle hodnocení...",
|
||||||
"search_settings": "Hledat nastavení",
|
"search_settings": "Hledat nastavení",
|
||||||
"search_state": "Vyhledat stát...",
|
"search_state": "Vyhledat stát...",
|
||||||
"search_tags": "Vyhledávat značky...",
|
"search_tags": "Vyhledávat značky...",
|
||||||
@@ -1136,6 +1157,7 @@
|
|||||||
"searching_locales": "Vyhledávání jazyků...",
|
"searching_locales": "Vyhledávání jazyků...",
|
||||||
"second": "Sekunda",
|
"second": "Sekunda",
|
||||||
"see_all_people": "Zobrazit všechny lidi",
|
"see_all_people": "Zobrazit všechny lidi",
|
||||||
|
"select": "Vybrat",
|
||||||
"select_album_cover": "Vybrat obal alba",
|
"select_album_cover": "Vybrat obal alba",
|
||||||
"select_all": "Vybrat vše",
|
"select_all": "Vybrat vše",
|
||||||
"select_all_duplicates": "Vybrat všechny duplicity",
|
"select_all_duplicates": "Vybrat všechny duplicity",
|
||||||
@@ -1218,7 +1240,7 @@
|
|||||||
"sort_oldest": "Nejstarší fotka",
|
"sort_oldest": "Nejstarší fotka",
|
||||||
"sort_people_by_similarity": "Seřadit lidi podle podobnosti",
|
"sort_people_by_similarity": "Seřadit lidi podle podobnosti",
|
||||||
"sort_recent": "Nejnovější fotka",
|
"sort_recent": "Nejnovější fotka",
|
||||||
"sort_title": "Název",
|
"sort_title": "Název alba",
|
||||||
"source": "Zdroj",
|
"source": "Zdroj",
|
||||||
"stack": "Seskupit",
|
"stack": "Seskupit",
|
||||||
"stack_duplicates": "Seskupit duplicity",
|
"stack_duplicates": "Seskupit duplicity",
|
||||||
@@ -1250,6 +1272,7 @@
|
|||||||
"tag_created": "Vytvořena značka: {tag}",
|
"tag_created": "Vytvořena značka: {tag}",
|
||||||
"tag_feature_description": "Procházení fotografií a videí seskupených podle témat logických značek",
|
"tag_feature_description": "Procházení fotografií a videí seskupených podle témat logických značek",
|
||||||
"tag_not_found_question": "Nemůžete najít značku? <link>Vytvořte novou.</link>",
|
"tag_not_found_question": "Nemůžete najít značku? <link>Vytvořte novou.</link>",
|
||||||
|
"tag_people": "Označit lidi",
|
||||||
"tag_updated": "Aktualizována značka: {tag}",
|
"tag_updated": "Aktualizována značka: {tag}",
|
||||||
"tagged_assets": "Přiřazena značka {count, plural, one {# položce} other {# položkám}}",
|
"tagged_assets": "Přiřazena značka {count, plural, one {# položce} other {# položkám}}",
|
||||||
"tags": "Značky",
|
"tags": "Značky",
|
||||||
@@ -1290,6 +1313,7 @@
|
|||||||
"unlink_motion_video": "Odpojit pohyblivé video",
|
"unlink_motion_video": "Odpojit pohyblivé video",
|
||||||
"unlink_oauth": "Zrušit OAuth propojení",
|
"unlink_oauth": "Zrušit OAuth propojení",
|
||||||
"unlinked_oauth_account": "OAuth účet odpojen",
|
"unlinked_oauth_account": "OAuth účet odpojen",
|
||||||
|
"unmute_memories": "Zrušit ztlumení vzpomínek",
|
||||||
"unnamed_album": "Nepojmenované album",
|
"unnamed_album": "Nepojmenované album",
|
||||||
"unnamed_album_delete_confirmation": "Opravdu chcete toto album smazat?",
|
"unnamed_album_delete_confirmation": "Opravdu chcete toto album smazat?",
|
||||||
"unnamed_share": "Nepojmenované sdílení",
|
"unnamed_share": "Nepojmenované sdílení",
|
||||||
@@ -1343,6 +1367,7 @@
|
|||||||
"view_all": "Zobrazit vše",
|
"view_all": "Zobrazit vše",
|
||||||
"view_all_users": "Zobrazit všechny uživatele",
|
"view_all_users": "Zobrazit všechny uživatele",
|
||||||
"view_in_timeline": "Zobrazit na časové ose",
|
"view_in_timeline": "Zobrazit na časové ose",
|
||||||
|
"view_link": "Zobrazit odkaz",
|
||||||
"view_links": "Zobrazit odkazy",
|
"view_links": "Zobrazit odkazy",
|
||||||
"view_name": "Zobrazit",
|
"view_name": "Zobrazit",
|
||||||
"view_next_asset": "Zobrazit další položku",
|
"view_next_asset": "Zobrazit další položku",
|
||||||
@@ -1355,7 +1380,7 @@
|
|||||||
"welcome": "Vítejte",
|
"welcome": "Vítejte",
|
||||||
"welcome_to_immich": "Vítejte v Immichi",
|
"welcome_to_immich": "Vítejte v Immichi",
|
||||||
"year": "Rok",
|
"year": "Rok",
|
||||||
"years_ago": "Před {years, plural, one {# rokem} other {# lety}}",
|
"years_ago": "Před {years, plural, one {rokem} other {# lety}}",
|
||||||
"yes": "Ano",
|
"yes": "Ano",
|
||||||
"you_dont_have_any_shared_links": "Nemáte žádné sdílené odkazy",
|
"you_dont_have_any_shared_links": "Nemáte žádné sdílené odkazy",
|
||||||
"zoom_image": "Zvětšit obrázek"
|
"zoom_image": "Zvětšit obrázek"
|
||||||
|
|||||||
125
i18n/da.json
125
i18n/da.json
@@ -41,6 +41,7 @@
|
|||||||
"backup_settings": "Backup-indstillinger",
|
"backup_settings": "Backup-indstillinger",
|
||||||
"backup_settings_description": "Administrer backupindstillinger for database",
|
"backup_settings_description": "Administrer backupindstillinger for database",
|
||||||
"check_all": "Tjek Alle",
|
"check_all": "Tjek Alle",
|
||||||
|
"cleanup": "Ryd op",
|
||||||
"cleared_jobs": "Ryddet jobs til: {job}",
|
"cleared_jobs": "Ryddet jobs til: {job}",
|
||||||
"config_set_by_file": "konfigurationen er i øjeblikket indstillet af en konfigurations fil",
|
"config_set_by_file": "konfigurationen er i øjeblikket indstillet af en konfigurations fil",
|
||||||
"confirm_delete_library": "Er du sikker på, at du vil slette {library} bibliotek?",
|
"confirm_delete_library": "Er du sikker på, at du vil slette {library} bibliotek?",
|
||||||
@@ -96,7 +97,7 @@
|
|||||||
"library_scanning_enable_description": "Aktiver periodisk biblioteksscanning",
|
"library_scanning_enable_description": "Aktiver periodisk biblioteksscanning",
|
||||||
"library_settings": "Eksternt bibliotek",
|
"library_settings": "Eksternt bibliotek",
|
||||||
"library_settings_description": "Administrer eksterne biblioteksindstillinger",
|
"library_settings_description": "Administrer eksterne biblioteksindstillinger",
|
||||||
"library_tasks_description": "Udfør biblioteksopgaver",
|
"library_tasks_description": "Scan eksterne biblioteker for nye og/eller ændrede mediefiler",
|
||||||
"library_watching_enable_description": "Overvåg eksterne biblioteker for filændringer",
|
"library_watching_enable_description": "Overvåg eksterne biblioteker for filændringer",
|
||||||
"library_watching_settings": "Biblioteks overvågning (EKSPERIMENTEL)",
|
"library_watching_settings": "Biblioteks overvågning (EKSPERIMENTEL)",
|
||||||
"library_watching_settings_description": "Tjek automatisk for ændrede filer",
|
"library_watching_settings_description": "Tjek automatisk for ændrede filer",
|
||||||
@@ -131,7 +132,7 @@
|
|||||||
"machine_learning_smart_search_description": "Søg semantisk efter billeder ved hjælp af CLIP-indlejringer",
|
"machine_learning_smart_search_description": "Søg semantisk efter billeder ved hjælp af CLIP-indlejringer",
|
||||||
"machine_learning_smart_search_enabled": "Aktiver smart søgning",
|
"machine_learning_smart_search_enabled": "Aktiver smart søgning",
|
||||||
"machine_learning_smart_search_enabled_description": "Hvis deaktiveret, vil billeder ikke blive kodet til smart søgning.",
|
"machine_learning_smart_search_enabled_description": "Hvis deaktiveret, vil billeder ikke blive kodet til smart søgning.",
|
||||||
"machine_learning_url_description": "URL på maskinlæringsserveren. Hvis der er angivet mere end én URL, hver server vil blive forsøgt en ad gangen, indtil en reagerer med succes, i rækkefølge fra første til sidste.",
|
"machine_learning_url_description": "URL’en for maskinlæringsserveren. Hvis mere end én URL angives, vil hver server blive forsøgt én ad gangen, indtil en svarer succesfuldt, i rækkefølge fra første til sidste. Servere, der ikke svarer, vil midlertidigt blive ignoreret, indtil de kommer online igen.",
|
||||||
"manage_concurrency": "Administrer antallet af samtidige opgaver",
|
"manage_concurrency": "Administrer antallet af samtidige opgaver",
|
||||||
"manage_log_settings": "Administrer logindstillinger",
|
"manage_log_settings": "Administrer logindstillinger",
|
||||||
"map_dark_style": "Mørk tema",
|
"map_dark_style": "Mørk tema",
|
||||||
@@ -147,6 +148,8 @@
|
|||||||
"map_settings": "Kort",
|
"map_settings": "Kort",
|
||||||
"map_settings_description": "Administrer kortindstillinger",
|
"map_settings_description": "Administrer kortindstillinger",
|
||||||
"map_style_description": "URL til en style.json for et korttema",
|
"map_style_description": "URL til en style.json for et korttema",
|
||||||
|
"memory_cleanup_job": "Mindeoprydning",
|
||||||
|
"memory_generate_job": "Mindegeneration",
|
||||||
"metadata_extraction_job": "Udtræk metadata",
|
"metadata_extraction_job": "Udtræk metadata",
|
||||||
"metadata_extraction_job_description": "Udtræk metadataoplysninger fra hvert Billede/Video, såsom GPS og opløsning",
|
"metadata_extraction_job_description": "Udtræk metadataoplysninger fra hvert Billede/Video, såsom GPS og opløsning",
|
||||||
"metadata_faces_import_setting": "Aktivér for at importere ansigter",
|
"metadata_faces_import_setting": "Aktivér for at importere ansigter",
|
||||||
@@ -240,7 +243,7 @@
|
|||||||
"storage_template_hash_verification_enabled_description": "Slår hash-verifikation til, slå ikke dette fra med mindre du er sikker på dets konsekvenser",
|
"storage_template_hash_verification_enabled_description": "Slår hash-verifikation til, slå ikke dette fra med mindre du er sikker på dets konsekvenser",
|
||||||
"storage_template_migration": "Lagringsskabelonmigration",
|
"storage_template_migration": "Lagringsskabelonmigration",
|
||||||
"storage_template_migration_description": "Anvend den nuværende <link>{template}</link> på tidligere uploadede mediefiler",
|
"storage_template_migration_description": "Anvend den nuværende <link>{template}</link> på tidligere uploadede mediefiler",
|
||||||
"storage_template_migration_info": "Skabelonændringer vil kun gælde for nye mediefiler. For at anvende skabelonen retroaktivt på tidligere uploadede mediefiler skal du køre <link>{job}</link>.",
|
"storage_template_migration_info": "Lager-skabelonen vil konvertere alle filendelser til små bogstaver. Skabelonændringer vil kun gælde for nye mediefiler. For at anvende skabelonen retroaktivt på tidligere uploadede mediefiler skal du køre <link>{job}</link>.",
|
||||||
"storage_template_migration_job": "Lager Skabelon Migreringsjob",
|
"storage_template_migration_job": "Lager Skabelon Migreringsjob",
|
||||||
"storage_template_more_details": "For flere detaljer om denne funktion, referer til <template-link>Lager Skabelonen</template-link> og dens <implications-link>implikationer</implications-link>",
|
"storage_template_more_details": "For flere detaljer om denne funktion, referer til <template-link>Lager Skabelonen</template-link> og dens <implications-link>implikationer</implications-link>",
|
||||||
"storage_template_onboarding_description": "Når denne funktion er aktiveret, vil den automatisk organisere filer baseret på en brugerdefineret skabelon. På grund af stabilitetsproblemer er funktionen som standard slået fra. For mere information, se <link>dokumentation</link>.",
|
"storage_template_onboarding_description": "Når denne funktion er aktiveret, vil den automatisk organisere filer baseret på en brugerdefineret skabelon. På grund af stabilitetsproblemer er funktionen som standard slået fra. For mere information, se <link>dokumentation</link>.",
|
||||||
@@ -299,7 +302,7 @@
|
|||||||
"transcoding_max_b_frames": "Maksimum B-frames",
|
"transcoding_max_b_frames": "Maksimum B-frames",
|
||||||
"transcoding_max_b_frames_description": "Højere værdier forbedrer kompressionseffektivitet, men kan gøre indkodning langsommere. Er måske ikke kompatibelt med hardware-acceleration på ældre enheder. 0 slår B-frames fra, mens -1 sætter denne værdi automatisk.",
|
"transcoding_max_b_frames_description": "Højere værdier forbedrer kompressionseffektivitet, men kan gøre indkodning langsommere. Er måske ikke kompatibelt med hardware-acceleration på ældre enheder. 0 slår B-frames fra, mens -1 sætter denne værdi automatisk.",
|
||||||
"transcoding_max_bitrate": "Maksimal bitrate",
|
"transcoding_max_bitrate": "Maksimal bitrate",
|
||||||
"transcoding_max_bitrate_description": "At sætte en maksmimal bitrate kan gøre filstørrelserne mere forudsigelige med et lille tab i kvalitet. Ved 720p er almindelige værdier 2600k for VP9 eller HEVC, eller 4500K for H.264. Slået fra hvis sat til 0.",
|
"transcoding_max_bitrate_description": "At sætte en maksmimal bitrate kan gøre filstørrelserne mere forudsigelige med et lille tab i kvalitet. Ved 720p er almindelige værdier 2600 kbit/s for VP9 eller HEVC, eller 4500 kbit/s for H.264. Slået fra hvis sat til 0.",
|
||||||
"transcoding_max_keyframe_interval": "Maksimal keyframe-interval",
|
"transcoding_max_keyframe_interval": "Maksimal keyframe-interval",
|
||||||
"transcoding_max_keyframe_interval_description": "Sætter den maksimale frameafstand mellem keyframes. Lavere værdier forringer kompressionseffektiviteten, men forbedrer søgetider og kan forbedre kvaliteten i scener med hurtig bevægelse. 0 sætter denne værdi automatisk.",
|
"transcoding_max_keyframe_interval_description": "Sætter den maksimale frameafstand mellem keyframes. Lavere værdier forringer kompressionseffektiviteten, men forbedrer søgetider og kan forbedre kvaliteten i scener med hurtig bevægelse. 0 sætter denne værdi automatisk.",
|
||||||
"transcoding_optimal_description": "Videoer højere end målopløsningen eller ikke i et godkendt format",
|
"transcoding_optimal_description": "Videoer højere end målopløsningen eller ikke i et godkendt format",
|
||||||
@@ -391,6 +394,7 @@
|
|||||||
"allow_edits": "Tillad redigeringer",
|
"allow_edits": "Tillad redigeringer",
|
||||||
"allow_public_user_to_download": "Tillad offentlige brugere til at hente",
|
"allow_public_user_to_download": "Tillad offentlige brugere til at hente",
|
||||||
"allow_public_user_to_upload": "Tillad offentlige brugere til at uploade",
|
"allow_public_user_to_upload": "Tillad offentlige brugere til at uploade",
|
||||||
|
"alt_text_qr_code": "QR-kode billede",
|
||||||
"anti_clockwise": "Mod uret",
|
"anti_clockwise": "Mod uret",
|
||||||
"api_key": "API-nøgle",
|
"api_key": "API-nøgle",
|
||||||
"api_key_description": "Denne værdi vises kun én gang. Venligst kopiér den før du lukker vinduet.",
|
"api_key_description": "Denne værdi vises kun én gang. Venligst kopiér den før du lukker vinduet.",
|
||||||
@@ -481,6 +485,7 @@
|
|||||||
"comments_are_disabled": "Kommentarer er slået fra",
|
"comments_are_disabled": "Kommentarer er slået fra",
|
||||||
"confirm": "Bekræft",
|
"confirm": "Bekræft",
|
||||||
"confirm_admin_password": "Bekræft administratoradgangskode",
|
"confirm_admin_password": "Bekræft administratoradgangskode",
|
||||||
|
"confirm_delete_face": "Er du sikker på, du vil slette {name}s ansigt fra denne mediefil?",
|
||||||
"confirm_delete_shared_link": "Er du sikker på, at du vil slette dette delte link?",
|
"confirm_delete_shared_link": "Er du sikker på, at du vil slette dette delte link?",
|
||||||
"confirm_keep_this_delete_others": "Alle andre aktiver i stakken vil blive slettet undtagen dette aktiv. Er du sikker på, at du vil fortsætte?",
|
"confirm_keep_this_delete_others": "Alle andre aktiver i stakken vil blive slettet undtagen dette aktiv. Er du sikker på, at du vil fortsætte?",
|
||||||
"confirm_password": "Bekræft adgangskode",
|
"confirm_password": "Bekræft adgangskode",
|
||||||
@@ -504,7 +509,7 @@
|
|||||||
"create_library": "Opret bibliotek",
|
"create_library": "Opret bibliotek",
|
||||||
"create_link": "Opret link",
|
"create_link": "Opret link",
|
||||||
"create_link_to_share": "Opret link for at dele",
|
"create_link_to_share": "Opret link for at dele",
|
||||||
"create_link_to_share_description": "Lad alle med linket se de(t) valgte billede(r)",
|
"create_link_to_share_description": "Tillad alle med linket at se de(t) valgte billede(r)",
|
||||||
"create_new_person": "Opret ny person",
|
"create_new_person": "Opret ny person",
|
||||||
"create_new_person_hint": "Tildel valgte aktiver til en ny person",
|
"create_new_person_hint": "Tildel valgte aktiver til en ny person",
|
||||||
"create_new_user": "Opret ny bruger",
|
"create_new_user": "Opret ny bruger",
|
||||||
@@ -519,20 +524,21 @@
|
|||||||
"date_after": "Dato efter",
|
"date_after": "Dato efter",
|
||||||
"date_and_time": "Dato og klokkeslæt",
|
"date_and_time": "Dato og klokkeslæt",
|
||||||
"date_before": "Dato før",
|
"date_before": "Dato før",
|
||||||
"date_of_birth_saved": "Fødselsdatoen blev gemt",
|
"date_of_birth_saved": "Fødselsdatoen blev gemt korrekt",
|
||||||
"date_range": "Datointerval",
|
"date_range": "Datointerval",
|
||||||
"day": "Dag",
|
"day": "Dag",
|
||||||
"deduplicate_all": "Dedupliker alle",
|
"deduplicate_all": "Kopier alle",
|
||||||
"deduplication_criteria_1": "Billedstørrelse i bytes",
|
"deduplication_criteria_1": "Billedstørrelse i bytes",
|
||||||
"deduplication_criteria_2": "Antal EXIF-data",
|
"deduplication_criteria_2": "Antal EXIF-data",
|
||||||
"deduplication_info": "Deduplikerings info",
|
"deduplication_info": "Deduplikerings info",
|
||||||
"deduplication_info_description": "For automatisk at forudvælge emner og fjerne dubletter i bulk ser vi på:",
|
"deduplication_info_description": "For automatisk at forudvælge emner og fjerne dubletter i bulk ser vi på:",
|
||||||
"default_locale": "Standardlokalitet",
|
"default_locale": "Standardlokalitet",
|
||||||
"default_locale_description": "Formatér datoer og tal",
|
"default_locale_description": "Formatér datoer og tal baseret på din browsers regions indstillinger",
|
||||||
"delete": "Slet",
|
"delete": "Slet",
|
||||||
"delete_album": "Slet album",
|
"delete_album": "Slet album",
|
||||||
"delete_api_key_prompt": "Er du sikker på, at du vil slette denne API-nøgle?",
|
"delete_api_key_prompt": "Er du sikker på, at du vil slette denne API-nøgle?",
|
||||||
"delete_duplicates_confirmation": "Er du sikker på, at du vil slette disse dubletter permanent?",
|
"delete_duplicates_confirmation": "Er du sikker på, at du vil slette disse dubletter permanent?",
|
||||||
|
"delete_face": "Slet ansigt",
|
||||||
"delete_key": "Slet nøgle",
|
"delete_key": "Slet nøgle",
|
||||||
"delete_library": "Slet bibliotek",
|
"delete_library": "Slet bibliotek",
|
||||||
"delete_link": "Slet link",
|
"delete_link": "Slet link",
|
||||||
@@ -565,7 +571,7 @@
|
|||||||
"download_settings": "Download",
|
"download_settings": "Download",
|
||||||
"download_settings_description": "Administrer indstillinger relateret til mediefil-downloads",
|
"download_settings_description": "Administrer indstillinger relateret til mediefil-downloads",
|
||||||
"downloading": "Downloader",
|
"downloading": "Downloader",
|
||||||
"downloading_asset_filename": "Downloader aktiv {filename}",
|
"downloading_asset_filename": "Downloader mediefil {filename}",
|
||||||
"drop_files_to_upload": "Slip filer hvor som helst for at uploade dem",
|
"drop_files_to_upload": "Slip filer hvor som helst for at uploade dem",
|
||||||
"duplicates": "Duplikater",
|
"duplicates": "Duplikater",
|
||||||
"duplicates_description": "Løs hver gruppe ved at angive, hvilke, hvis nogen, er dubletter",
|
"duplicates_description": "Løs hver gruppe ved at angive, hvilke, hvis nogen, er dubletter",
|
||||||
@@ -600,6 +606,7 @@
|
|||||||
"enabled": "Aktiveret",
|
"enabled": "Aktiveret",
|
||||||
"end_date": "Slutdato",
|
"end_date": "Slutdato",
|
||||||
"error": "Fejl",
|
"error": "Fejl",
|
||||||
|
"error_delete_face": "Fejl ved sletning af ansigt fra mediefil",
|
||||||
"error_loading_image": "Fejl ved indlæsning af billede",
|
"error_loading_image": "Fejl ved indlæsning af billede",
|
||||||
"error_title": "Fejl - Noget gik galt",
|
"error_title": "Fejl - Noget gik galt",
|
||||||
"errors": {
|
"errors": {
|
||||||
@@ -607,11 +614,11 @@
|
|||||||
"cannot_navigate_previous_asset": "Kan ikke navigere til forrige mediefil",
|
"cannot_navigate_previous_asset": "Kan ikke navigere til forrige mediefil",
|
||||||
"cant_apply_changes": "Ændringerne kan ikke anvendes",
|
"cant_apply_changes": "Ændringerne kan ikke anvendes",
|
||||||
"cant_change_activity": "Kan ikke {enabled, select, true {disable} other {enable}} aktivitet",
|
"cant_change_activity": "Kan ikke {enabled, select, true {disable} other {enable}} aktivitet",
|
||||||
"cant_change_asset_favorite": "Kan ikke ændre favorit til aktiv",
|
"cant_change_asset_favorite": "Kan ikke ændre favorit til mediefil",
|
||||||
"cant_change_metadata_assets_count": "Kan ikke ændre metadata for {count, plural, one {# objekt} other {# objekter}}",
|
"cant_change_metadata_assets_count": "Kan ikke ændre metadata for {count, plural, one {# objekt} other {# objekter}}",
|
||||||
"cant_get_faces": "Kan ikke hente ansigter",
|
"cant_get_faces": "Kan ikke hente ansigter",
|
||||||
"cant_get_number_of_comments": "Kan ikke få antallet af kommentarer",
|
"cant_get_number_of_comments": "Kan ikke få antallet af kommentarer",
|
||||||
"cant_search_people": "Kan ikke søge efter folk",
|
"cant_search_people": "Kan ikke søge efter personer",
|
||||||
"cant_search_places": "Kan ikke søge efter steder",
|
"cant_search_places": "Kan ikke søge efter steder",
|
||||||
"cleared_jobs": "Ryddede opgaver for: {job}",
|
"cleared_jobs": "Ryddede opgaver for: {job}",
|
||||||
"error_adding_assets_to_album": "Fejl i tilføjelse af mediefiler til album",
|
"error_adding_assets_to_album": "Fejl i tilføjelse af mediefiler til album",
|
||||||
@@ -620,20 +627,20 @@
|
|||||||
"error_downloading": "Fejl i download af {filename}",
|
"error_downloading": "Fejl i download af {filename}",
|
||||||
"error_hiding_buy_button": "Fejl i skjulning af køb-knap",
|
"error_hiding_buy_button": "Fejl i skjulning af køb-knap",
|
||||||
"error_removing_assets_from_album": "Fejl i fjernelse af mediefiler fra album. Tjek konsol for flere detaljer",
|
"error_removing_assets_from_album": "Fejl i fjernelse af mediefiler fra album. Tjek konsol for flere detaljer",
|
||||||
"error_selecting_all_assets": "Fejl ved valg af alle aktiver",
|
"error_selecting_all_assets": "Fejl ved valg af alle mediefiler",
|
||||||
"exclusion_pattern_already_exists": "Denne udelukkelsesmønster findes allerede.",
|
"exclusion_pattern_already_exists": "Denne udelukkelsesmønster findes allerede.",
|
||||||
"failed_job_command": "Kommando {command} slog fejl for opgave: {job}",
|
"failed_job_command": "Kommando {command} slog fejl for opgave: {job}",
|
||||||
"failed_to_create_album": "Oprettelse af album mislykkedes",
|
"failed_to_create_album": "Oprettelse af album mislykkedes",
|
||||||
"failed_to_create_shared_link": "Oprettelse af delt link mislykkedes",
|
"failed_to_create_shared_link": "Oprettelse af delt link mislykkedes",
|
||||||
"failed_to_edit_shared_link": "Redigering af delt link mislykkedes",
|
"failed_to_edit_shared_link": "Redigering af delt link mislykkedes",
|
||||||
"failed_to_get_people": "Det lykkedes ikke at hente folk",
|
"failed_to_get_people": "Det lykkedes ikke at hente personer",
|
||||||
"failed_to_keep_this_delete_others": "Kunne ikke beholde dette aktiv og slette de andre aktiver",
|
"failed_to_keep_this_delete_others": "Kunne ikke beholde denne mediefil og slette de andre mediefiler",
|
||||||
"failed_to_load_asset": "Indlæsning af mediefil mislykkedes",
|
"failed_to_load_asset": "Indlæsning af mediefil mislykkedes",
|
||||||
"failed_to_load_assets": "Indlæsning af mediefiler mislykkedes",
|
"failed_to_load_assets": "Indlæsning af mediefiler mislykkedes",
|
||||||
"failed_to_load_people": "Indlæsning af personer mislykkedes",
|
"failed_to_load_people": "Indlæsning af personer mislykkedes",
|
||||||
"failed_to_remove_product_key": "Fjernelse af produktnøgle mislykkedes",
|
"failed_to_remove_product_key": "Fjernelse af produktnøgle mislykkedes",
|
||||||
"failed_to_stack_assets": "Det lykkedes ikke at stable aktiver",
|
"failed_to_stack_assets": "Det lykkedes ikke at stable mediefiler",
|
||||||
"failed_to_unstack_assets": "Det lykkedes ikke at fjerne stablen af aktiver",
|
"failed_to_unstack_assets": "Det lykkedes ikke at fjerne gruperingen af mediefiler",
|
||||||
"import_path_already_exists": "Denne importsti findes allerede.",
|
"import_path_already_exists": "Denne importsti findes allerede.",
|
||||||
"incorrect_email_or_password": "Forkert email eller kodeord",
|
"incorrect_email_or_password": "Forkert email eller kodeord",
|
||||||
"paths_validation_failed": "{paths, plural, one {# sti} other {# stier}} slog fejl ved validering",
|
"paths_validation_failed": "{paths, plural, one {# sti} other {# stier}} slog fejl ved validering",
|
||||||
@@ -641,7 +648,7 @@
|
|||||||
"quota_higher_than_disk_size": "Du har sat en kvote der er større end disken",
|
"quota_higher_than_disk_size": "Du har sat en kvote der er større end disken",
|
||||||
"repair_unable_to_check_items": "Kunne ikke tjekke {count, select, one {element} other {elementer}}",
|
"repair_unable_to_check_items": "Kunne ikke tjekke {count, select, one {element} other {elementer}}",
|
||||||
"unable_to_add_album_users": "Ikke i stand til at tilføje brugere til album",
|
"unable_to_add_album_users": "Ikke i stand til at tilføje brugere til album",
|
||||||
"unable_to_add_assets_to_shared_link": "Kan ikke tilføje aktiver til delt link",
|
"unable_to_add_assets_to_shared_link": "Kan ikke tilføje mediefiler til det delte link",
|
||||||
"unable_to_add_comment": "Ikke i stand til at tilføje kommentar",
|
"unable_to_add_comment": "Ikke i stand til at tilføje kommentar",
|
||||||
"unable_to_add_exclusion_pattern": "Kunne ikke tilføje udelukkelsesmønster",
|
"unable_to_add_exclusion_pattern": "Kunne ikke tilføje udelukkelsesmønster",
|
||||||
"unable_to_add_import_path": "Kunne ikke tilføje importsti",
|
"unable_to_add_import_path": "Kunne ikke tilføje importsti",
|
||||||
@@ -651,7 +658,7 @@
|
|||||||
"unable_to_archive_unarchive": "Ude af stand til at {archived, select, true {arkivere} other {fjerne fra arkiv}}",
|
"unable_to_archive_unarchive": "Ude af stand til at {archived, select, true {arkivere} other {fjerne fra arkiv}}",
|
||||||
"unable_to_change_album_user_role": "Ikke i stand til at ændre albumbrugerens rolle",
|
"unable_to_change_album_user_role": "Ikke i stand til at ændre albumbrugerens rolle",
|
||||||
"unable_to_change_date": "Ikke i stand til at ændre dato",
|
"unable_to_change_date": "Ikke i stand til at ændre dato",
|
||||||
"unable_to_change_favorite": "Kan ikke ændre favorit for aktiv",
|
"unable_to_change_favorite": "Kan ikke ændre favorit for mediefil",
|
||||||
"unable_to_change_location": "Ikke i stand til at ændre sted",
|
"unable_to_change_location": "Ikke i stand til at ændre sted",
|
||||||
"unable_to_change_password": "Kunne ikke ændre adgangskode",
|
"unable_to_change_password": "Kunne ikke ændre adgangskode",
|
||||||
"unable_to_change_visibility": "Kan ikke ændre synligheden for {count, plural, one {# person} other {# personer}}",
|
"unable_to_change_visibility": "Kan ikke ændre synligheden for {count, plural, one {# person} other {# personer}}",
|
||||||
@@ -698,16 +705,16 @@
|
|||||||
"unable_to_remove_deleted_assets": "Kunne ikke fjerne offlinefiler",
|
"unable_to_remove_deleted_assets": "Kunne ikke fjerne offlinefiler",
|
||||||
"unable_to_remove_library": "Ikke i stand til at fjerne bibliotek",
|
"unable_to_remove_library": "Ikke i stand til at fjerne bibliotek",
|
||||||
"unable_to_remove_partner": "Ikke i stand til at fjerne partner",
|
"unable_to_remove_partner": "Ikke i stand til at fjerne partner",
|
||||||
"unable_to_remove_reaction": "Ikke i stand til at reaktion",
|
"unable_to_remove_reaction": "Ikke i stand til at fjerne reaktion",
|
||||||
"unable_to_repair_items": "Ikke i stand til at reparere ting",
|
"unable_to_repair_items": "Ikke i stand til at reparere ting",
|
||||||
"unable_to_reset_password": "Ikke i stand til at nulstille adgangskode",
|
"unable_to_reset_password": "Ikke i stand til at nulstille adgangskode",
|
||||||
"unable_to_resolve_duplicate": "Kunne ikke opklare duplikat",
|
"unable_to_resolve_duplicate": "Kunne ikke opklare duplikat",
|
||||||
"unable_to_restore_assets": "Kunne ikke genoprette medier",
|
"unable_to_restore_assets": "Kunne ikke gendanne medierfil",
|
||||||
"unable_to_restore_trash": "Ikke i stand til at genoprette skrald",
|
"unable_to_restore_trash": "Ikke i stand til at gendanne fra skraldespanden",
|
||||||
"unable_to_restore_user": "Ikke i stand til at genoprette bruger",
|
"unable_to_restore_user": "Ikke i stand til at gendanne bruger",
|
||||||
"unable_to_save_album": "Ikke i stand til at gemme album",
|
"unable_to_save_album": "Ikke i stand til at gemme album",
|
||||||
"unable_to_save_api_key": "Kunne ikke gemme API-nøgle",
|
"unable_to_save_api_key": "Kunne ikke gemme API-nøgle",
|
||||||
"unable_to_save_date_of_birth": "Kan ikke gemme fødselsdatoen",
|
"unable_to_save_date_of_birth": "Kunne ikke gemme fødselsdatoen",
|
||||||
"unable_to_save_name": "Ikke i stand til at gemme navn",
|
"unable_to_save_name": "Ikke i stand til at gemme navn",
|
||||||
"unable_to_save_profile": "Ikke i stand til at gemme profil",
|
"unable_to_save_profile": "Ikke i stand til at gemme profil",
|
||||||
"unable_to_save_settings": "Ikke i stand til at gemme indstillinger",
|
"unable_to_save_settings": "Ikke i stand til at gemme indstillinger",
|
||||||
@@ -729,7 +736,7 @@
|
|||||||
"unable_to_upload_file": "Filen kunne ikke uploades"
|
"unable_to_upload_file": "Filen kunne ikke uploades"
|
||||||
},
|
},
|
||||||
"exif": "Exif",
|
"exif": "Exif",
|
||||||
"exit_slideshow": "Forlad slideshow",
|
"exit_slideshow": "Afslut slideshow",
|
||||||
"expand_all": "Udvid alle",
|
"expand_all": "Udvid alle",
|
||||||
"expire_after": "Udløb efter",
|
"expire_after": "Udløb efter",
|
||||||
"expired": "Udløbet",
|
"expired": "Udløbet",
|
||||||
@@ -742,7 +749,7 @@
|
|||||||
"external": "Ekstern",
|
"external": "Ekstern",
|
||||||
"external_libraries": "Eksterne biblioteker",
|
"external_libraries": "Eksterne biblioteker",
|
||||||
"face_unassigned": "Ikke tildelt",
|
"face_unassigned": "Ikke tildelt",
|
||||||
"failed_to_load_assets": "Kunne ikke indlæse aktiver",
|
"failed_to_load_assets": "Kunne ikke indlæse mediefiler",
|
||||||
"favorite": "Favorit",
|
"favorite": "Favorit",
|
||||||
"favorite_or_unfavorite_photo": "Tilføj eller fjern fra yndlingsbilleder",
|
"favorite_or_unfavorite_photo": "Tilføj eller fjern fra yndlingsbilleder",
|
||||||
"favorites": "Favoritter",
|
"favorites": "Favoritter",
|
||||||
@@ -774,10 +781,10 @@
|
|||||||
"has_quota": "Har kvote",
|
"has_quota": "Har kvote",
|
||||||
"hi_user": "Hej {name} ({email})",
|
"hi_user": "Hej {name} ({email})",
|
||||||
"hide_all_people": "Skjul alle personer",
|
"hide_all_people": "Skjul alle personer",
|
||||||
"hide_gallery": "Gem galleri",
|
"hide_gallery": "Skjul galleri",
|
||||||
"hide_named_person": "Skjul person {name}",
|
"hide_named_person": "Skjul person {name}",
|
||||||
"hide_password": "Gem adgangskode",
|
"hide_password": "Skjul adgangskode",
|
||||||
"hide_person": "Gem person",
|
"hide_person": "Skjul person",
|
||||||
"hide_unnamed_people": "Skjul unavngivne personer",
|
"hide_unnamed_people": "Skjul unavngivne personer",
|
||||||
"host": "Host",
|
"host": "Host",
|
||||||
"hour": "Time",
|
"hour": "Time",
|
||||||
@@ -817,7 +824,7 @@
|
|||||||
"keep": "Behold",
|
"keep": "Behold",
|
||||||
"keep_all": "Behold alle",
|
"keep_all": "Behold alle",
|
||||||
"keep_this_delete_others": "Behold dette, slet andre",
|
"keep_this_delete_others": "Behold dette, slet andre",
|
||||||
"kept_this_deleted_others": "Beholdt dette aktiv og slettede {count, plural, one {# aktiv} other {# aktiver}}",
|
"kept_this_deleted_others": "Beholdt denne mediefil og slettede {count, plural, one {# aktiv} other {# aktiver}}",
|
||||||
"keyboard_shortcuts": "Tastaturgenveje",
|
"keyboard_shortcuts": "Tastaturgenveje",
|
||||||
"language": "Sprog",
|
"language": "Sprog",
|
||||||
"language_setting_description": "Vælg dit foretrukne sprog",
|
"language_setting_description": "Vælg dit foretrukne sprog",
|
||||||
@@ -837,8 +844,8 @@
|
|||||||
"link_to_oauth": "Link til OAuth",
|
"link_to_oauth": "Link til OAuth",
|
||||||
"linked_oauth_account": "Tilsluttet OAuth-konto",
|
"linked_oauth_account": "Tilsluttet OAuth-konto",
|
||||||
"list": "Liste",
|
"list": "Liste",
|
||||||
"loading": "Loader",
|
"loading": "Indlæser",
|
||||||
"loading_search_results_failed": "At loade søgeresultater slog fejl",
|
"loading_search_results_failed": "Indlæsning af søgeresultater fejlede",
|
||||||
"log_out": "Log ud",
|
"log_out": "Log ud",
|
||||||
"log_out_all_devices": "Log ud af alle enheder",
|
"log_out_all_devices": "Log ud af alle enheder",
|
||||||
"logged_out_all_devices": "Logget ud af alle enheder",
|
"logged_out_all_devices": "Logget ud af alle enheder",
|
||||||
@@ -874,7 +881,7 @@
|
|||||||
"merge": "Sammenflet",
|
"merge": "Sammenflet",
|
||||||
"merge_people": "Sammenflet personer",
|
"merge_people": "Sammenflet personer",
|
||||||
"merge_people_limit": "Du kan kun flette op til 5 ansigter ad gangen",
|
"merge_people_limit": "Du kan kun flette op til 5 ansigter ad gangen",
|
||||||
"merge_people_prompt": "Vil du slå disse mennesker sammen? Denne handling er uigenkaldelig.",
|
"merge_people_prompt": "Vil du flette disse mennesker sammen? Denne handling er uigenkaldelig.",
|
||||||
"merge_people_successfully": "Personer sammenflettet med succes",
|
"merge_people_successfully": "Personer sammenflettet med succes",
|
||||||
"merged_people_count": "{count, plural, one {# person} other {# personer}} lagt sammen",
|
"merged_people_count": "{count, plural, one {# person} other {# personer}} lagt sammen",
|
||||||
"minimize": "Minimér",
|
"minimize": "Minimér",
|
||||||
@@ -884,6 +891,7 @@
|
|||||||
"month": "Måned",
|
"month": "Måned",
|
||||||
"more": "Mere",
|
"more": "Mere",
|
||||||
"moved_to_trash": "Flyttet til skraldespand",
|
"moved_to_trash": "Flyttet til skraldespand",
|
||||||
|
"mute_memories": "Dæmp minder",
|
||||||
"my_albums": "Mine albummer",
|
"my_albums": "Mine albummer",
|
||||||
"name": "Navn",
|
"name": "Navn",
|
||||||
"name_or_nickname": "Navn eller kælenavn",
|
"name_or_nickname": "Navn eller kælenavn",
|
||||||
@@ -901,7 +909,7 @@
|
|||||||
"no_albums_message": "Opret et album for at organisere dine billeder og videoer",
|
"no_albums_message": "Opret et album for at organisere dine billeder og videoer",
|
||||||
"no_albums_with_name_yet": "Det ser ud til, at du ikke har noget album med dette navn endnu.",
|
"no_albums_with_name_yet": "Det ser ud til, at du ikke har noget album med dette navn endnu.",
|
||||||
"no_albums_yet": "Det ser ud til, at du ikke har nogen album endnu.",
|
"no_albums_yet": "Det ser ud til, at du ikke har nogen album endnu.",
|
||||||
"no_archived_assets_message": "Arkivér billeder og fotos for at gemme dem væk fra dit Billed-view",
|
"no_archived_assets_message": "Arkivér billeder og videoer for at gemme dem væk fra din Billede oversigt",
|
||||||
"no_assets_message": "KLIK FOR AT UPLOADE DIT FØRSTE BILLEDE",
|
"no_assets_message": "KLIK FOR AT UPLOADE DIT FØRSTE BILLEDE",
|
||||||
"no_duplicates_found": "Ingen duplikater fundet.",
|
"no_duplicates_found": "Ingen duplikater fundet.",
|
||||||
"no_exif_info_available": "Ingen tilgængelig exif information",
|
"no_exif_info_available": "Ingen tilgængelig exif information",
|
||||||
@@ -948,7 +956,7 @@
|
|||||||
"owner": "Ejer",
|
"owner": "Ejer",
|
||||||
"partner": "Partner",
|
"partner": "Partner",
|
||||||
"partner_can_access": "{partner} kan tilgå",
|
"partner_can_access": "{partner} kan tilgå",
|
||||||
"partner_can_access_assets": "Alle dine billeder og videoer, bortset fra dem i Arkiveret og Slettet",
|
"partner_can_access_assets": "Alle dine billeder og videoer, bortset fra dem i Arkivet og Slettet",
|
||||||
"partner_can_access_location": "Stedet, hvor dine billeder blev taget",
|
"partner_can_access_location": "Stedet, hvor dine billeder blev taget",
|
||||||
"partner_sharing": "Partnerdeling",
|
"partner_sharing": "Partnerdeling",
|
||||||
"partners": "Partnere",
|
"partners": "Partnere",
|
||||||
@@ -979,6 +987,7 @@
|
|||||||
"permanently_deleted_asset": "Permanent slettet medie",
|
"permanently_deleted_asset": "Permanent slettet medie",
|
||||||
"permanently_deleted_assets_count": "{count, plural, one {# aktiv} other {# aktiver}} permanent slettet",
|
"permanently_deleted_assets_count": "{count, plural, one {# aktiv} other {# aktiver}} permanent slettet",
|
||||||
"person": "Person",
|
"person": "Person",
|
||||||
|
"person_birthdate": "Født den {date}",
|
||||||
"person_hidden": "{name}{hidden, select, true { (skjult)} other {}}",
|
"person_hidden": "{name}{hidden, select, true { (skjult)} other {}}",
|
||||||
"photo_shared_all_users": "Det ser ud til, at du har delt dine billeder med alle brugere, eller også har du ikke nogen bruger at dele med.",
|
"photo_shared_all_users": "Det ser ud til, at du har delt dine billeder med alle brugere, eller også har du ikke nogen bruger at dele med.",
|
||||||
"photos": "Billeder",
|
"photos": "Billeder",
|
||||||
@@ -992,7 +1001,7 @@
|
|||||||
"play": "Afspil",
|
"play": "Afspil",
|
||||||
"play_memories": "Afspil minder",
|
"play_memories": "Afspil minder",
|
||||||
"play_motion_photo": "Afspil bevægelsesbillede",
|
"play_motion_photo": "Afspil bevægelsesbillede",
|
||||||
"play_or_pause_video": "Afspil eller paus video",
|
"play_or_pause_video": "Afspil eller pause video",
|
||||||
"port": "Port",
|
"port": "Port",
|
||||||
"preset": "Forudindstilling",
|
"preset": "Forudindstilling",
|
||||||
"preview": "Forhåndsvisning",
|
"preview": "Forhåndsvisning",
|
||||||
@@ -1046,7 +1055,7 @@
|
|||||||
"reassign": "Gentildel",
|
"reassign": "Gentildel",
|
||||||
"reassigned_assets_to_existing_person": "{count, plural, one {# mediefil} other {# mediefiler}} er blevet gentildelt til {name, select, null {en eksisterende person} other {{name}}}",
|
"reassigned_assets_to_existing_person": "{count, plural, one {# mediefil} other {# mediefiler}} er blevet gentildelt til {name, select, null {en eksisterende person} other {{name}}}",
|
||||||
"reassigned_assets_to_new_person": "Gentildelt {count, plural, one {# aktiv} other {# aktiver}} til en ny person",
|
"reassigned_assets_to_new_person": "Gentildelt {count, plural, one {# aktiv} other {# aktiver}} til en ny person",
|
||||||
"reassing_hint": "Tildel valgte aktiver til en eksisterende person",
|
"reassing_hint": "Tildel valgte mediefiler til en eksisterende person",
|
||||||
"recent": "For nylig",
|
"recent": "For nylig",
|
||||||
"recent-albums": "Seneste albums",
|
"recent-albums": "Seneste albums",
|
||||||
"recent_searches": "Seneste søgninger",
|
"recent_searches": "Seneste søgninger",
|
||||||
@@ -1064,26 +1073,31 @@
|
|||||||
"remove": "Fjern",
|
"remove": "Fjern",
|
||||||
"remove_assets_album_confirmation": "Er du sikker på, at du vil fjerne {count, plural, one {# aktiv} other {# aktiver}} fra albummet?",
|
"remove_assets_album_confirmation": "Er du sikker på, at du vil fjerne {count, plural, one {# aktiv} other {# aktiver}} fra albummet?",
|
||||||
"remove_assets_shared_link_confirmation": "Er du sikker på, at du vil fjerne {count, plural, one {# aktiv} other {# aktiver}} fra dette delte link?",
|
"remove_assets_shared_link_confirmation": "Er du sikker på, at du vil fjerne {count, plural, one {# aktiv} other {# aktiver}} fra dette delte link?",
|
||||||
"remove_assets_title": "Fjern aktiver?",
|
"remove_assets_title": "Fjern mediefiler?",
|
||||||
"remove_custom_date_range": "Fjern tilpasset datointerval",
|
"remove_custom_date_range": "Fjern tilpasset datointerval",
|
||||||
"remove_deleted_assets": "Fjern fra offlinefiler",
|
"remove_deleted_assets": "Fjern slettede mediefiler",
|
||||||
"remove_from_album": "Fjern fra album",
|
"remove_from_album": "Fjern fra album",
|
||||||
"remove_from_favorites": "Fjern fra favoritter",
|
"remove_from_favorites": "Fjern fra favoritter",
|
||||||
"remove_from_shared_link": "Fjern fra delt link",
|
"remove_from_shared_link": "Fjern fra delt link",
|
||||||
|
"remove_memory": "Fjern minde",
|
||||||
|
"remove_photo_from_memory": "Fjern foto fra dette minde",
|
||||||
"remove_url": "Fjern URL",
|
"remove_url": "Fjern URL",
|
||||||
"remove_user": "Fjern bruger",
|
"remove_user": "Fjern bruger",
|
||||||
"removed_api_key": "Fjernede API-nøgle: {name}",
|
"removed_api_key": "Fjernede API-nøgle: {name}",
|
||||||
"removed_from_archive": "Fjernet fra arkiv",
|
"removed_from_archive": "Fjernet fra arkiv",
|
||||||
"removed_from_favorites": "Fjernet fra favoritter",
|
"removed_from_favorites": "Fjernet fra favoritter",
|
||||||
"removed_from_favorites_count": "{count, plural, other {Fjernede #}} fra favoritter",
|
"removed_from_favorites_count": "{count, plural, other {Fjernede #}} fra favoritter",
|
||||||
|
"removed_memory": "Fjernede minde",
|
||||||
|
"removed_photo_from_memory": "Fjernede foto fra minde",
|
||||||
"removed_tagged_assets": "Fjernede tag fra {count, plural, one {# aktiv} other {# aktiver}}",
|
"removed_tagged_assets": "Fjernede tag fra {count, plural, one {# aktiv} other {# aktiver}}",
|
||||||
"rename": "Omdøb",
|
"rename": "Omdøb",
|
||||||
"repair": "Reparér",
|
"repair": "Reparér",
|
||||||
"repair_no_results_message": "Utrackede og manglende filer vil blive vist her",
|
"repair_no_results_message": "Usporede og manglende filer vil blive vist her",
|
||||||
"replace_with_upload": "Erstat med upload",
|
"replace_with_upload": "Erstat med upload",
|
||||||
"repository": "Depot",
|
"repository": "Depot",
|
||||||
"require_password": "Kræv adgangskode",
|
"require_password": "Kræv adgangskode",
|
||||||
"require_user_to_change_password_on_first_login": "Kræv at bruger skifter adgangskode ved første login",
|
"require_user_to_change_password_on_first_login": "Kræv at bruger skifter adgangskode ved første login",
|
||||||
|
"rescan": "Genopfrisk",
|
||||||
"reset": "Nulstil",
|
"reset": "Nulstil",
|
||||||
"reset_password": "Nulstil adgangskode",
|
"reset_password": "Nulstil adgangskode",
|
||||||
"reset_people_visibility": "Nulstil personsynlighed",
|
"reset_people_visibility": "Nulstil personsynlighed",
|
||||||
@@ -1093,19 +1107,19 @@
|
|||||||
"restore": "Gendan",
|
"restore": "Gendan",
|
||||||
"restore_all": "Gendan alle",
|
"restore_all": "Gendan alle",
|
||||||
"restore_user": "Gendan bruger",
|
"restore_user": "Gendan bruger",
|
||||||
"restored_asset": "Gendannet aktiv",
|
"restored_asset": "Gendannet mediefilen",
|
||||||
"resume": "Genoptag",
|
"resume": "Genoptag",
|
||||||
"retry_upload": "Forsøg upload igen",
|
"retry_upload": "Forsøg upload igen",
|
||||||
"review_duplicates": "Gennemgå dubletter",
|
"review_duplicates": "Gennemgå dubletter",
|
||||||
"role": "Rolle",
|
"role": "Rolle",
|
||||||
"role_editor": "Editor",
|
"role_editor": "Redaktør",
|
||||||
"role_viewer": "Seer",
|
"role_viewer": "Seer",
|
||||||
"save": "Gem",
|
"save": "Gem",
|
||||||
"saved_api_key": "Gemt API-nøgle",
|
"saved_api_key": "Gemt API-nøgle",
|
||||||
"saved_profile": "Gemte profil",
|
"saved_profile": "Gemte profil",
|
||||||
"saved_settings": "Gemte indstillinger",
|
"saved_settings": "Gemte indstillinger",
|
||||||
"say_something": "Skriv noget",
|
"say_something": "Skriv noget",
|
||||||
"scan_all_libraries": "Skan gennem alle biblioteker",
|
"scan_all_libraries": "Skan alle biblioteker",
|
||||||
"scan_library": "Skan",
|
"scan_library": "Skan",
|
||||||
"scan_settings": "Skanningsindstillinger",
|
"scan_settings": "Skanningsindstillinger",
|
||||||
"scanning_for_album": "Skanner efter albummer...",
|
"scanning_for_album": "Skanner efter albummer...",
|
||||||
@@ -1127,8 +1141,9 @@
|
|||||||
"search_options": "Søgemuligheder",
|
"search_options": "Søgemuligheder",
|
||||||
"search_people": "Søg i personer",
|
"search_people": "Søg i personer",
|
||||||
"search_places": "Søg i steder",
|
"search_places": "Søg i steder",
|
||||||
|
"search_rating": "Søg efter vurdering...",
|
||||||
"search_settings": "søgeindstillinger",
|
"search_settings": "søgeindstillinger",
|
||||||
"search_state": "Søg efter stat...",
|
"search_state": "Søg efter lansdel...",
|
||||||
"search_tags": "Søg tags...",
|
"search_tags": "Søg tags...",
|
||||||
"search_timezone": "Søg i tidszone...",
|
"search_timezone": "Søg i tidszone...",
|
||||||
"search_type": "Søg efter type",
|
"search_type": "Søg efter type",
|
||||||
@@ -1136,6 +1151,7 @@
|
|||||||
"searching_locales": "Søger lokaler...",
|
"searching_locales": "Søger lokaler...",
|
||||||
"second": "Sekund",
|
"second": "Sekund",
|
||||||
"see_all_people": "Se alle personer",
|
"see_all_people": "Se alle personer",
|
||||||
|
"select": "Vælg",
|
||||||
"select_album_cover": "Vælg albumcover",
|
"select_album_cover": "Vælg albumcover",
|
||||||
"select_all": "Vælg alle",
|
"select_all": "Vælg alle",
|
||||||
"select_all_duplicates": "Vælg alle dubletter",
|
"select_all_duplicates": "Vælg alle dubletter",
|
||||||
@@ -1183,15 +1199,15 @@
|
|||||||
"show_album_options": "Vis albumindstillinger",
|
"show_album_options": "Vis albumindstillinger",
|
||||||
"show_albums": "Vis albummer",
|
"show_albums": "Vis albummer",
|
||||||
"show_all_people": "Vis alle personer",
|
"show_all_people": "Vis alle personer",
|
||||||
"show_and_hide_people": "Vis & gem personer",
|
"show_and_hide_people": "Vis & skjul personer",
|
||||||
"show_file_location": "Vis filplacering",
|
"show_file_location": "Vis filplacering",
|
||||||
"show_gallery": "Vis galleri",
|
"show_gallery": "Vis galleri",
|
||||||
"show_hidden_people": "Vis gemte personer",
|
"show_hidden_people": "Vis skjulte personer",
|
||||||
"show_in_timeline": "Vis på tidslinje",
|
"show_in_timeline": "Vis på tidslinje",
|
||||||
"show_in_timeline_setting_description": "Vis billeder og videoer fra denne bruger på din tidslinje",
|
"show_in_timeline_setting_description": "Vis billeder og videoer fra denne bruger på din tidslinje",
|
||||||
"show_keyboard_shortcuts": "Vis tastaturgenveje",
|
"show_keyboard_shortcuts": "Vis tastaturgenveje",
|
||||||
"show_metadata": "Vis metadata",
|
"show_metadata": "Vis metadata",
|
||||||
"show_or_hide_info": "Vis eller gem info",
|
"show_or_hide_info": "Vis eller skjul info",
|
||||||
"show_password": "Vis adgangskode",
|
"show_password": "Vis adgangskode",
|
||||||
"show_person_options": "Vis personindstillinger",
|
"show_person_options": "Vis personindstillinger",
|
||||||
"show_progress_bar": "Vis statuslinje",
|
"show_progress_bar": "Vis statuslinje",
|
||||||
@@ -1216,7 +1232,7 @@
|
|||||||
"sort_items": "Antal genstande",
|
"sort_items": "Antal genstande",
|
||||||
"sort_modified": "Ændret dato",
|
"sort_modified": "Ændret dato",
|
||||||
"sort_oldest": "Ældste foto",
|
"sort_oldest": "Ældste foto",
|
||||||
"sort_people_by_similarity": "Sorter folk efter lighed",
|
"sort_people_by_similarity": "Sorter efter personer der ligner hinanden",
|
||||||
"sort_recent": "Seneste foto",
|
"sort_recent": "Seneste foto",
|
||||||
"sort_title": "Titel",
|
"sort_title": "Titel",
|
||||||
"source": "Kilde",
|
"source": "Kilde",
|
||||||
@@ -1242,14 +1258,15 @@
|
|||||||
"sunrise_on_the_beach": "Solopgang på stranden",
|
"sunrise_on_the_beach": "Solopgang på stranden",
|
||||||
"support": "Support",
|
"support": "Support",
|
||||||
"support_and_feedback": "Support & Feedback",
|
"support_and_feedback": "Support & Feedback",
|
||||||
"support_third_party_description": "Din Immich-installation blev pakket af en tredjepart. Problemer, du oplever, kan være forårsaget af denne pakke, så rejs venligst problemer med dem i første omgang ved at bruge nedenstående links.",
|
"support_third_party_description": "Din Immich-installation blev sammensat af en tredjepart. Problemer, du oplever, kan være forårsaget af denne udvikler, så rejs venligst problemer med dem i første omgang ved at bruge nedenstående links.",
|
||||||
"swap_merge_direction": "Byt retning for sammenfletning",
|
"swap_merge_direction": "Byt retning for sammenfletning",
|
||||||
"sync": "Synkronisér",
|
"sync": "Synkronisér",
|
||||||
"tag": "Tag",
|
"tag": "Tag",
|
||||||
"tag_assets": "Tag aktiver",
|
"tag_assets": "Tag mediefiler",
|
||||||
"tag_created": "Oprettet tag: {tag}",
|
"tag_created": "Oprettet tag: {tag}",
|
||||||
"tag_feature_description": "Gennemse billeder og videoer grupperet efter logiske tag-emner",
|
"tag_feature_description": "Gennemse billeder og videoer grupperet efter logiske tag-emner",
|
||||||
"tag_not_found_question": "Kan du ikke finde et tag? <link>Opret et nyt tag.</link>",
|
"tag_not_found_question": "Kan du ikke finde et tag? <link>Opret et nyt tag.</link>",
|
||||||
|
"tag_people": "Tag personer",
|
||||||
"tag_updated": "Opdateret tag: {tag}",
|
"tag_updated": "Opdateret tag: {tag}",
|
||||||
"tagged_assets": "Tagget {count, plural, one {# aktiv} other {# aktiver}}",
|
"tagged_assets": "Tagget {count, plural, one {# aktiv} other {# aktiver}}",
|
||||||
"tags": "Tags",
|
"tags": "Tags",
|
||||||
@@ -1275,14 +1292,14 @@
|
|||||||
"trash": "Papirkurv",
|
"trash": "Papirkurv",
|
||||||
"trash_all": "Smid alle ud",
|
"trash_all": "Smid alle ud",
|
||||||
"trash_count": "Slet {count, number}",
|
"trash_count": "Slet {count, number}",
|
||||||
"trash_delete_asset": "Papirkurv/slet aktiv",
|
"trash_delete_asset": "Flyt mediefil til Papirkurv",
|
||||||
"trash_no_results_message": "Billeder og videoer markeret til sletning vil blive vist her.",
|
"trash_no_results_message": "Billeder og videoer markeret til sletning vil blive vist her.",
|
||||||
"trashed_items_will_be_permanently_deleted_after": "Mediefiler i skraldespanden vil blive slettet permanent efter {days, plural, one {# dag} other {# dage}}.",
|
"trashed_items_will_be_permanently_deleted_after": "Mediefiler i skraldespanden vil blive slettet permanent efter {days, plural, one {# dag} other {# dage}}.",
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
"unarchive": "Afakivér",
|
"unarchive": "Afakivér",
|
||||||
"unarchived_count": "{count, plural, other {Uarkiveret #}}",
|
"unarchived_count": "{count, plural, other {Uarkiveret #}}",
|
||||||
"unfavorite": "Fjern favorit",
|
"unfavorite": "Fjern favorit",
|
||||||
"unhide_person": "Hold op med at gemme person væk",
|
"unhide_person": "Stop med at skjule person",
|
||||||
"unknown": "Ukendt",
|
"unknown": "Ukendt",
|
||||||
"unknown_country": "Ukendt land",
|
"unknown_country": "Ukendt land",
|
||||||
"unknown_year": "Ukendt år",
|
"unknown_year": "Ukendt år",
|
||||||
@@ -1290,6 +1307,7 @@
|
|||||||
"unlink_motion_video": "Fjern link til bevægelsesvideo",
|
"unlink_motion_video": "Fjern link til bevægelsesvideo",
|
||||||
"unlink_oauth": "Frakobl OAuth",
|
"unlink_oauth": "Frakobl OAuth",
|
||||||
"unlinked_oauth_account": "Frakoblede OAuth-konto",
|
"unlinked_oauth_account": "Frakoblede OAuth-konto",
|
||||||
|
"unmute_memories": "Ophæv dæmpning af minder",
|
||||||
"unnamed_album": "Unavngivet album",
|
"unnamed_album": "Unavngivet album",
|
||||||
"unnamed_album_delete_confirmation": "Er du sikker på, at du vil slette dette album?",
|
"unnamed_album_delete_confirmation": "Er du sikker på, at du vil slette dette album?",
|
||||||
"unnamed_share": "Unavngivet deling",
|
"unnamed_share": "Unavngivet deling",
|
||||||
@@ -1303,7 +1321,7 @@
|
|||||||
"up_next": "Næste",
|
"up_next": "Næste",
|
||||||
"updated_password": "Opdaterede adgangskode",
|
"updated_password": "Opdaterede adgangskode",
|
||||||
"upload": "Upload",
|
"upload": "Upload",
|
||||||
"upload_concurrency": "Uploadsamtidighed",
|
"upload_concurrency": "Upload samtidighed",
|
||||||
"upload_errors": "Upload afsluttet med {count, plural, one {# fejl} other {# fejl}}. Opdater siden for at se nye uploadaktiver.",
|
"upload_errors": "Upload afsluttet med {count, plural, one {# fejl} other {# fejl}}. Opdater siden for at se nye uploadaktiver.",
|
||||||
"upload_progress": "Resterende {remaining, number} - Behandlet {processed, number}/{total, number}",
|
"upload_progress": "Resterende {remaining, number} - Behandlet {processed, number}/{total, number}",
|
||||||
"upload_skipped_duplicates": "Sprang over {count, plural, one {# duplet aktiv} other {# duplikerede aktiver}}",
|
"upload_skipped_duplicates": "Sprang over {count, plural, one {# duplet aktiv} other {# duplikerede aktiver}}",
|
||||||
@@ -1321,7 +1339,7 @@
|
|||||||
"user_purchase_settings_description": "Administrer dit køb",
|
"user_purchase_settings_description": "Administrer dit køb",
|
||||||
"user_role_set": "Indstil {user} som {role}",
|
"user_role_set": "Indstil {user} som {role}",
|
||||||
"user_usage_detail": "Detaljer om brugers forbrug",
|
"user_usage_detail": "Detaljer om brugers forbrug",
|
||||||
"user_usage_stats": "Konto anvendelsesstatistik",
|
"user_usage_stats": "Kontoens anvendelsesstatistik",
|
||||||
"user_usage_stats_description": "Vis konto anvendelsesstatistik",
|
"user_usage_stats_description": "Vis konto anvendelsesstatistik",
|
||||||
"username": "Brugernavn",
|
"username": "Brugernavn",
|
||||||
"users": "Brugere",
|
"users": "Brugere",
|
||||||
@@ -1343,6 +1361,7 @@
|
|||||||
"view_all": "Se alle",
|
"view_all": "Se alle",
|
||||||
"view_all_users": "Se alle brugere",
|
"view_all_users": "Se alle brugere",
|
||||||
"view_in_timeline": "Se på tidslinjen",
|
"view_in_timeline": "Se på tidslinjen",
|
||||||
|
"view_link": "Vis Link",
|
||||||
"view_links": "Vis links",
|
"view_links": "Vis links",
|
||||||
"view_name": "Se",
|
"view_name": "Se",
|
||||||
"view_next_asset": "Se næste medie",
|
"view_next_asset": "Se næste medie",
|
||||||
|
|||||||
33
i18n/de.json
33
i18n/de.json
@@ -41,6 +41,7 @@
|
|||||||
"backup_settings": "Datensicherungs-Einstellungen",
|
"backup_settings": "Datensicherungs-Einstellungen",
|
||||||
"backup_settings_description": "Datensicherungs-Einstellungen verwalten",
|
"backup_settings_description": "Datensicherungs-Einstellungen verwalten",
|
||||||
"check_all": "Alle überprüfen",
|
"check_all": "Alle überprüfen",
|
||||||
|
"cleanup": "Aufräumen",
|
||||||
"cleared_jobs": "Folgende Aufgaben zurückgesetzt: {job}",
|
"cleared_jobs": "Folgende Aufgaben zurückgesetzt: {job}",
|
||||||
"config_set_by_file": "Ist derzeit in einer Konfigurationsdatei festgelegt",
|
"config_set_by_file": "Ist derzeit in einer Konfigurationsdatei festgelegt",
|
||||||
"confirm_delete_library": "Bist du sicher, dass du die Bibliothek {library} löschen willst?",
|
"confirm_delete_library": "Bist du sicher, dass du die Bibliothek {library} löschen willst?",
|
||||||
@@ -65,6 +66,11 @@
|
|||||||
"forcing_refresh_library_files": "Erneutes Laden aller Bibliotheksdateien erzwingen",
|
"forcing_refresh_library_files": "Erneutes Laden aller Bibliotheksdateien erzwingen",
|
||||||
"image_format": "Format",
|
"image_format": "Format",
|
||||||
"image_format_description": "WebP erzeugt kleinere Dateien als JPEG, ist aber etwas langsamer in der Erstellung.",
|
"image_format_description": "WebP erzeugt kleinere Dateien als JPEG, ist aber etwas langsamer in der Erstellung.",
|
||||||
|
"image_fullsize_description": "Hochauflösendes Bild mit entfernten Metadaten, das beim Zoomen verwendet wird",
|
||||||
|
"image_fullsize_enabled": "Hochauflösende Vorschaubilder aktivieren",
|
||||||
|
"image_fullsize_enabled_description": "Generiere Hochauflösende Vorschaubilder in Originalauflösung für nicht web-kompatibel Formate. Wenn \"Eingebettete Vorschau bevorzugen\" aktiviert ist, werden eingebettete Vorschaubilder direkt verwendet. Hat keinen Einfluss auf web-kompatible Formate wie JPEG.",
|
||||||
|
"image_fullsize_quality_description": "Qualität der Hochauflösenden Vorschaubilder von 1-100. Höher ist besser, erzeugt aber größere Dateien.",
|
||||||
|
"image_fullsize_title": "Hochauflösende Vorschaueinstellungen",
|
||||||
"image_prefer_embedded_preview": "Eingebettete Vorschau bevorzugen",
|
"image_prefer_embedded_preview": "Eingebettete Vorschau bevorzugen",
|
||||||
"image_prefer_embedded_preview_setting_description": "Verwende eingebettete Vorschaubilder in RAW-Fotos als Grundlage für die Bildverarbeitung, sofern diese zur Verfügung stehen. Dies kann bei einigen Bildern genauere Farben erzeugen, allerdings ist die Qualität der Vorschau kameraabhängig und das Bild kann mehr Kompressionsartefakte aufweisen.",
|
"image_prefer_embedded_preview_setting_description": "Verwende eingebettete Vorschaubilder in RAW-Fotos als Grundlage für die Bildverarbeitung, sofern diese zur Verfügung stehen. Dies kann bei einigen Bildern genauere Farben erzeugen, allerdings ist die Qualität der Vorschau kameraabhängig und das Bild kann mehr Kompressionsartefakte aufweisen.",
|
||||||
"image_prefer_wide_gamut": "Breites Spektrum bevorzugen",
|
"image_prefer_wide_gamut": "Breites Spektrum bevorzugen",
|
||||||
@@ -96,7 +102,7 @@
|
|||||||
"library_scanning_enable_description": "Regelmäßiges Scannen der Bibliothek aktivieren",
|
"library_scanning_enable_description": "Regelmäßiges Scannen der Bibliothek aktivieren",
|
||||||
"library_settings": "Externe Bibliothek",
|
"library_settings": "Externe Bibliothek",
|
||||||
"library_settings_description": "Einstellungen externer Bibliotheken verwalten",
|
"library_settings_description": "Einstellungen externer Bibliotheken verwalten",
|
||||||
"library_tasks_description": "Diese Aufgabe aktualisiert und überprüft die Bibliotheken",
|
"library_tasks_description": "Überprüfe externe Bibliotheken auf neue oder veränderte Medien",
|
||||||
"library_watching_enable_description": "Überwache externe Bibliotheken auf Dateiänderungen",
|
"library_watching_enable_description": "Überwache externe Bibliotheken auf Dateiänderungen",
|
||||||
"library_watching_settings": "Bibliotheksüberwachung (EXPERIMENTELL)",
|
"library_watching_settings": "Bibliotheksüberwachung (EXPERIMENTELL)",
|
||||||
"library_watching_settings_description": "Automatisch auf geänderte Dateien prüfen",
|
"library_watching_settings_description": "Automatisch auf geänderte Dateien prüfen",
|
||||||
@@ -131,7 +137,7 @@
|
|||||||
"machine_learning_smart_search_description": "Semantische Bildsuche mittels CLIP-Einbettungen",
|
"machine_learning_smart_search_description": "Semantische Bildsuche mittels CLIP-Einbettungen",
|
||||||
"machine_learning_smart_search_enabled": "Intelligente Suche aktivieren",
|
"machine_learning_smart_search_enabled": "Intelligente Suche aktivieren",
|
||||||
"machine_learning_smart_search_enabled_description": "Ist diese Option deaktiviert, werden die Bilder nicht für die intelligente Suche verwendet.",
|
"machine_learning_smart_search_enabled_description": "Ist diese Option deaktiviert, werden die Bilder nicht für die intelligente Suche verwendet.",
|
||||||
"machine_learning_url_description": "Die URL des Servers für maschinelles Lernen. Wenn mehr als eine URL angegeben wird, wird jeder Server einzeln ausprobiert, bis einer erfolgreich antwortet, und zwar in der Reihenfolge vom ersten bis zum letzten.",
|
"machine_learning_url_description": "Die URL des Servers für maschinelles Lernen. Wenn mehr als eine URL angegeben wird, wird jeder Server einzeln ausprobiert, bis einer erfolgreich antwortet, und zwar in der Reihenfolge vom ersten bis zum letzten. Server die nicht antworten werden temporär ignoriert, bis sie wieder verfügbar sind.",
|
||||||
"manage_concurrency": "Gleichzeitige Ausführungen verwalten",
|
"manage_concurrency": "Gleichzeitige Ausführungen verwalten",
|
||||||
"manage_log_settings": "Log-Einstellungen verwalten",
|
"manage_log_settings": "Log-Einstellungen verwalten",
|
||||||
"map_dark_style": "Dunkler Stil",
|
"map_dark_style": "Dunkler Stil",
|
||||||
@@ -147,6 +153,8 @@
|
|||||||
"map_settings": "Karte",
|
"map_settings": "Karte",
|
||||||
"map_settings_description": "Karten- und GPS-Einstellungen verwalten",
|
"map_settings_description": "Karten- und GPS-Einstellungen verwalten",
|
||||||
"map_style_description": "URL zu einem style.json Karten-Theme",
|
"map_style_description": "URL zu einem style.json Karten-Theme",
|
||||||
|
"memory_cleanup_job": "Erinnerungen aufräumen",
|
||||||
|
"memory_generate_job": "Erinnerungen Generierung",
|
||||||
"metadata_extraction_job": "Metadaten extrahieren",
|
"metadata_extraction_job": "Metadaten extrahieren",
|
||||||
"metadata_extraction_job_description": "Extrahieren von Metadaten, wie zum Beispiel GPS, Gesichtern und Auflösung aus jeder Datei",
|
"metadata_extraction_job_description": "Extrahieren von Metadaten, wie zum Beispiel GPS, Gesichtern und Auflösung aus jeder Datei",
|
||||||
"metadata_faces_import_setting": "Import von Gesichtern aktivieren",
|
"metadata_faces_import_setting": "Import von Gesichtern aktivieren",
|
||||||
@@ -240,7 +248,7 @@
|
|||||||
"storage_template_hash_verification_enabled_description": "Aktiviert die Hash-Verifizierung. Deaktiviere diese Option nur, wenn du dir über die damit verbundenen Auswirkungen im Klaren bist",
|
"storage_template_hash_verification_enabled_description": "Aktiviert die Hash-Verifizierung. Deaktiviere diese Option nur, wenn du dir über die damit verbundenen Auswirkungen im Klaren bist",
|
||||||
"storage_template_migration": "Migration von Speichervorlagen",
|
"storage_template_migration": "Migration von Speichervorlagen",
|
||||||
"storage_template_migration_description": "Diese Aufgabe wendet die aktuelle <link>{template}</link> auf zuvor hochgeladene Dateien an",
|
"storage_template_migration_description": "Diese Aufgabe wendet die aktuelle <link>{template}</link> auf zuvor hochgeladene Dateien an",
|
||||||
"storage_template_migration_info": "Vorlagenänderungen gelten nur für neue Dateien. Um die Vorlage rückwirkend auf bereits hochgeladene Assets anzuwenden, führe den <link>{job}</link> aus.",
|
"storage_template_migration_info": "Die Vorlage wird alle Dateierweiterungen in Kleinbuchstaben umwandeln. Vorlagenänderungen gelten nur für neue Dateien. Um die Vorlage rückwirkend auf bereits hochgeladene Assets anzuwenden, führe den <link>{job}</link> aus.",
|
||||||
"storage_template_migration_job": "Speichervorlagenmigrations-Aufgabe",
|
"storage_template_migration_job": "Speichervorlagenmigrations-Aufgabe",
|
||||||
"storage_template_more_details": "Weitere Details zu dieser Funktion findest du unter <template-link>Speichervorlage</template-link> und dessen <implications-link>Implikationen</implications-link>",
|
"storage_template_more_details": "Weitere Details zu dieser Funktion findest du unter <template-link>Speichervorlage</template-link> und dessen <implications-link>Implikationen</implications-link>",
|
||||||
"storage_template_onboarding_description": "Wenn aktiviert, sortiert diese Funktion Dateien automatisch basierend auf einer benutzerdefinierten Vorlage. Aufgrund von Stabilitätsproblemen ist die Funktion standardmäßig deaktiviert. Weitere Informationen findest du in der <link>Dokumentation</link>.",
|
"storage_template_onboarding_description": "Wenn aktiviert, sortiert diese Funktion Dateien automatisch basierend auf einer benutzerdefinierten Vorlage. Aufgrund von Stabilitätsproblemen ist die Funktion standardmäßig deaktiviert. Weitere Informationen findest du in der <link>Dokumentation</link>.",
|
||||||
@@ -299,7 +307,7 @@
|
|||||||
"transcoding_max_b_frames": "Maximale B-Frames",
|
"transcoding_max_b_frames": "Maximale B-Frames",
|
||||||
"transcoding_max_b_frames_description": "Höhere Werte verbessern die Komprimierungseffizienz, verlangsamen aber die Kodierung. Ist möglicherweise nicht mit der Hardware-Beschleunigung älterer Geräte kompatibel. 0 deaktiviert die B-Frames, während -1 diesen Wert automatisch setzt.",
|
"transcoding_max_b_frames_description": "Höhere Werte verbessern die Komprimierungseffizienz, verlangsamen aber die Kodierung. Ist möglicherweise nicht mit der Hardware-Beschleunigung älterer Geräte kompatibel. 0 deaktiviert die B-Frames, während -1 diesen Wert automatisch setzt.",
|
||||||
"transcoding_max_bitrate": "Maximale Bitrate",
|
"transcoding_max_bitrate": "Maximale Bitrate",
|
||||||
"transcoding_max_bitrate_description": "Die Festlegung einer maximalen Bitrate kann die Dateigrößen vorhersagbarer machen, ohne dass die Qualität darunter leidet. Bei 720p sind typische Werte 2600k für VP9 oder HEVC oder 4500k für H.264. Deaktiviert, wenn der Wert auf 0 gesetzt ist.",
|
"transcoding_max_bitrate_description": "Die Festlegung einer maximalen Bitrate kann die Dateigrößen vorhersagbarer machen, ohne dass die Qualität darunter leidet. Bei 720p sind typische Werte 2600 kbit/s für VP9 oder HEVC oder 4500 kbit/s für H.264. Deaktiviert, wenn der Wert auf 0 gesetzt ist.",
|
||||||
"transcoding_max_keyframe_interval": "Maximales Keyframe-Intervall",
|
"transcoding_max_keyframe_interval": "Maximales Keyframe-Intervall",
|
||||||
"transcoding_max_keyframe_interval_description": "Legt den maximalen Frame-Abstand zwischen Keyframes fest. Niedrigere Werte verschlechtern die Komprimierungseffizienz, verbessern aber die Suchzeiten und können die Qualität in Szenen mit schnellen Bewegungen verbessern. Bei 0 wird dieser Wert automatisch eingestellt.",
|
"transcoding_max_keyframe_interval_description": "Legt den maximalen Frame-Abstand zwischen Keyframes fest. Niedrigere Werte verschlechtern die Komprimierungseffizienz, verbessern aber die Suchzeiten und können die Qualität in Szenen mit schnellen Bewegungen verbessern. Bei 0 wird dieser Wert automatisch eingestellt.",
|
||||||
"transcoding_optimal_description": "Videos mit einer höheren Auflösung als der Zielauflösung oder in einem nicht akzeptierten Format",
|
"transcoding_optimal_description": "Videos mit einer höheren Auflösung als der Zielauflösung oder in einem nicht akzeptierten Format",
|
||||||
@@ -391,6 +399,7 @@
|
|||||||
"allow_edits": "Bearbeiten erlauben",
|
"allow_edits": "Bearbeiten erlauben",
|
||||||
"allow_public_user_to_download": "Erlaube öffentlichen Benutzern, herunterzuladen",
|
"allow_public_user_to_download": "Erlaube öffentlichen Benutzern, herunterzuladen",
|
||||||
"allow_public_user_to_upload": "Erlaube öffentlichen Benutzern, hochzuladen",
|
"allow_public_user_to_upload": "Erlaube öffentlichen Benutzern, hochzuladen",
|
||||||
|
"alt_text_qr_code": "QR-Code Bild",
|
||||||
"anti_clockwise": "Gegen den Uhrzeigersinn",
|
"anti_clockwise": "Gegen den Uhrzeigersinn",
|
||||||
"api_key": "API-Schlüssel",
|
"api_key": "API-Schlüssel",
|
||||||
"api_key_description": "Dieser Wert wird nur einmal angezeigt. Bitte kopiere ihn, bevor du das Fenster schließt.",
|
"api_key_description": "Dieser Wert wird nur einmal angezeigt. Bitte kopiere ihn, bevor du das Fenster schließt.",
|
||||||
@@ -481,6 +490,7 @@
|
|||||||
"comments_are_disabled": "Kommentare sind deaktiviert",
|
"comments_are_disabled": "Kommentare sind deaktiviert",
|
||||||
"confirm": "Bestätigen",
|
"confirm": "Bestätigen",
|
||||||
"confirm_admin_password": "Administrator Passwort bestätigen",
|
"confirm_admin_password": "Administrator Passwort bestätigen",
|
||||||
|
"confirm_delete_face": "Bist du sicher dass du das Gesicht von {name} aus der Datei entfernen willst?",
|
||||||
"confirm_delete_shared_link": "Bist du sicher, dass du diesen geteilten Link löschen willst?",
|
"confirm_delete_shared_link": "Bist du sicher, dass du diesen geteilten Link löschen willst?",
|
||||||
"confirm_keep_this_delete_others": "Alle anderen Dateien im Stapel bis auf diese werden gelöscht. Bist du sicher, dass du fortfahren möchten?",
|
"confirm_keep_this_delete_others": "Alle anderen Dateien im Stapel bis auf diese werden gelöscht. Bist du sicher, dass du fortfahren möchten?",
|
||||||
"confirm_password": "Passwort bestätigen",
|
"confirm_password": "Passwort bestätigen",
|
||||||
@@ -533,6 +543,7 @@
|
|||||||
"delete_album": "Album löschen",
|
"delete_album": "Album löschen",
|
||||||
"delete_api_key_prompt": "Bist du sicher, dass du diesen API-Schlüssel löschen willst?",
|
"delete_api_key_prompt": "Bist du sicher, dass du diesen API-Schlüssel löschen willst?",
|
||||||
"delete_duplicates_confirmation": "Bist du sicher, dass du diese Duplikate endgültig löschen willst?",
|
"delete_duplicates_confirmation": "Bist du sicher, dass du diese Duplikate endgültig löschen willst?",
|
||||||
|
"delete_face": "Gesicht löschen",
|
||||||
"delete_key": "Schlüssel löschen",
|
"delete_key": "Schlüssel löschen",
|
||||||
"delete_library": "Bibliothek löschen",
|
"delete_library": "Bibliothek löschen",
|
||||||
"delete_link": "Link löschen",
|
"delete_link": "Link löschen",
|
||||||
@@ -600,6 +611,7 @@
|
|||||||
"enabled": "Aktiviert",
|
"enabled": "Aktiviert",
|
||||||
"end_date": "Enddatum",
|
"end_date": "Enddatum",
|
||||||
"error": "Fehler",
|
"error": "Fehler",
|
||||||
|
"error_delete_face": "Fehler beim Löschen des Gesichts",
|
||||||
"error_loading_image": "Fehler beim Laden des Bildes",
|
"error_loading_image": "Fehler beim Laden des Bildes",
|
||||||
"error_title": "Fehler - Etwas ist schief gelaufen",
|
"error_title": "Fehler - Etwas ist schief gelaufen",
|
||||||
"errors": {
|
"errors": {
|
||||||
@@ -852,6 +864,7 @@
|
|||||||
"loop_videos": "Loop-Videos",
|
"loop_videos": "Loop-Videos",
|
||||||
"loop_videos_description": "Aktiviere diese Option, um eine automatische Videoschleife in der Detailansicht zu erstellen.",
|
"loop_videos_description": "Aktiviere diese Option, um eine automatische Videoschleife in der Detailansicht zu erstellen.",
|
||||||
"main_branch_warning": "Du benutzt eine Entwicklungsversion. Wir empfehlen dringend, eine Release-Version zu verwenden!",
|
"main_branch_warning": "Du benutzt eine Entwicklungsversion. Wir empfehlen dringend, eine Release-Version zu verwenden!",
|
||||||
|
"main_menu": "Hauptmenü",
|
||||||
"make": "Marke",
|
"make": "Marke",
|
||||||
"manage_shared_links": "Freigegebene Links verwalten",
|
"manage_shared_links": "Freigegebene Links verwalten",
|
||||||
"manage_sharing_with_partners": "Gemeinsame Nutzung mit Partnern verwalten",
|
"manage_sharing_with_partners": "Gemeinsame Nutzung mit Partnern verwalten",
|
||||||
@@ -884,6 +897,7 @@
|
|||||||
"month": "Monat",
|
"month": "Monat",
|
||||||
"more": "Mehr",
|
"more": "Mehr",
|
||||||
"moved_to_trash": "In den Papierkorb verschoben",
|
"moved_to_trash": "In den Papierkorb verschoben",
|
||||||
|
"mute_memories": "Erinnerungen stumm schalten",
|
||||||
"my_albums": "Meine Alben",
|
"my_albums": "Meine Alben",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"name_or_nickname": "Name oder Nickname",
|
"name_or_nickname": "Name oder Nickname",
|
||||||
@@ -979,6 +993,7 @@
|
|||||||
"permanently_deleted_asset": "Endgültig gelöschtes Objekt",
|
"permanently_deleted_asset": "Endgültig gelöschtes Objekt",
|
||||||
"permanently_deleted_assets_count": "{count, plural, one {# Datei} other {# Dateien}} endgültig gelöscht",
|
"permanently_deleted_assets_count": "{count, plural, one {# Datei} other {# Dateien}} endgültig gelöscht",
|
||||||
"person": "Person",
|
"person": "Person",
|
||||||
|
"person_birthdate": "Geboren am {date}",
|
||||||
"person_hidden": "{name}{hidden, select, true { (verborgen)} other {}}",
|
"person_hidden": "{name}{hidden, select, true { (verborgen)} other {}}",
|
||||||
"photo_shared_all_users": "Es sieht so aus, als hättest du deine Fotos mit allen Benutzern geteilt oder du hast keine Benutzer, mit denen du teilen kannst.",
|
"photo_shared_all_users": "Es sieht so aus, als hättest du deine Fotos mit allen Benutzern geteilt oder du hast keine Benutzer, mit denen du teilen kannst.",
|
||||||
"photos": "Fotos",
|
"photos": "Fotos",
|
||||||
@@ -1070,12 +1085,16 @@
|
|||||||
"remove_from_album": "Aus Album entfernen",
|
"remove_from_album": "Aus Album entfernen",
|
||||||
"remove_from_favorites": "Aus Favoriten entfernen",
|
"remove_from_favorites": "Aus Favoriten entfernen",
|
||||||
"remove_from_shared_link": "Aus geteiltem Link entfernen",
|
"remove_from_shared_link": "Aus geteiltem Link entfernen",
|
||||||
|
"remove_memory": "Erinnerung entfernen",
|
||||||
|
"remove_photo_from_memory": "Foto aus dieser Erinnerung entfernen",
|
||||||
"remove_url": "URL entfernen",
|
"remove_url": "URL entfernen",
|
||||||
"remove_user": "Nutzer entfernen",
|
"remove_user": "Nutzer entfernen",
|
||||||
"removed_api_key": "API-Schlüssel {name} wurde entfernt",
|
"removed_api_key": "API-Schlüssel {name} wurde entfernt",
|
||||||
"removed_from_archive": "Aus dem Archiv entfernt",
|
"removed_from_archive": "Aus dem Archiv entfernt",
|
||||||
"removed_from_favorites": "Aus den Favoriten entfernt",
|
"removed_from_favorites": "Aus den Favoriten entfernt",
|
||||||
"removed_from_favorites_count": "{count, plural, other {#}} aus den Favoriten entfernt",
|
"removed_from_favorites_count": "{count, plural, other {#}} aus den Favoriten entfernt",
|
||||||
|
"removed_memory": "Erinnerung entfernt",
|
||||||
|
"removed_photo_from_memory": "Foto aus Erinnerung entfernt",
|
||||||
"removed_tagged_assets": "Tag von {count, plural, one {# Datei} other {# Dateien}} entfernt",
|
"removed_tagged_assets": "Tag von {count, plural, one {# Datei} other {# Dateien}} entfernt",
|
||||||
"rename": "Umbenennen",
|
"rename": "Umbenennen",
|
||||||
"repair": "Reparatur",
|
"repair": "Reparatur",
|
||||||
@@ -1084,6 +1103,7 @@
|
|||||||
"repository": "Repository",
|
"repository": "Repository",
|
||||||
"require_password": "Passwort erforderlich",
|
"require_password": "Passwort erforderlich",
|
||||||
"require_user_to_change_password_on_first_login": "Benutzer muss das Passwort beim ersten Login ändern",
|
"require_user_to_change_password_on_first_login": "Benutzer muss das Passwort beim ersten Login ändern",
|
||||||
|
"rescan": "Erneut scannen",
|
||||||
"reset": "Zurücksetzen",
|
"reset": "Zurücksetzen",
|
||||||
"reset_password": "Passwort zurücksetzen",
|
"reset_password": "Passwort zurücksetzen",
|
||||||
"reset_people_visibility": "Sichtbarkeit von Personen zurücksetzen",
|
"reset_people_visibility": "Sichtbarkeit von Personen zurücksetzen",
|
||||||
@@ -1127,6 +1147,7 @@
|
|||||||
"search_options": "Suchoptionen",
|
"search_options": "Suchoptionen",
|
||||||
"search_people": "Suche nach Personen",
|
"search_people": "Suche nach Personen",
|
||||||
"search_places": "Suche nach Orten",
|
"search_places": "Suche nach Orten",
|
||||||
|
"search_rating": "Suche nach Bewertung...",
|
||||||
"search_settings": "Suche nach Einstellungen",
|
"search_settings": "Suche nach Einstellungen",
|
||||||
"search_state": "Suche nach Bundesland / Provinz...",
|
"search_state": "Suche nach Bundesland / Provinz...",
|
||||||
"search_tags": "Sache nach Tags...",
|
"search_tags": "Sache nach Tags...",
|
||||||
@@ -1136,6 +1157,7 @@
|
|||||||
"searching_locales": "Suche nach Orten...",
|
"searching_locales": "Suche nach Orten...",
|
||||||
"second": "Sekunde",
|
"second": "Sekunde",
|
||||||
"see_all_people": "Alle Personen anzeigen",
|
"see_all_people": "Alle Personen anzeigen",
|
||||||
|
"select": "Auswählen",
|
||||||
"select_album_cover": "Album-Cover auswählen",
|
"select_album_cover": "Album-Cover auswählen",
|
||||||
"select_all": "Alles auswählen",
|
"select_all": "Alles auswählen",
|
||||||
"select_all_duplicates": "Alle Duplikate auswählen",
|
"select_all_duplicates": "Alle Duplikate auswählen",
|
||||||
@@ -1250,6 +1272,7 @@
|
|||||||
"tag_created": "Tag erstellt: {tag}",
|
"tag_created": "Tag erstellt: {tag}",
|
||||||
"tag_feature_description": "Durchsuchen von Fotos und Videos, gruppiert nach logischen Tag-Themen",
|
"tag_feature_description": "Durchsuchen von Fotos und Videos, gruppiert nach logischen Tag-Themen",
|
||||||
"tag_not_found_question": "Kein Tag zu finden? <link>Erstelle einen neuen Tag.</link>",
|
"tag_not_found_question": "Kein Tag zu finden? <link>Erstelle einen neuen Tag.</link>",
|
||||||
|
"tag_people": "Personen taggen",
|
||||||
"tag_updated": "Tag aktualisiert: {tag}",
|
"tag_updated": "Tag aktualisiert: {tag}",
|
||||||
"tagged_assets": "{count, plural, one {# Datei} other {# Dateien}} getagged",
|
"tagged_assets": "{count, plural, one {# Datei} other {# Dateien}} getagged",
|
||||||
"tags": "Tags",
|
"tags": "Tags",
|
||||||
@@ -1290,6 +1313,7 @@
|
|||||||
"unlink_motion_video": "Verknüpfung zum Bewegungsvideo aufheben",
|
"unlink_motion_video": "Verknüpfung zum Bewegungsvideo aufheben",
|
||||||
"unlink_oauth": "OAuth entfernen",
|
"unlink_oauth": "OAuth entfernen",
|
||||||
"unlinked_oauth_account": "OAuth-Konto entfernt",
|
"unlinked_oauth_account": "OAuth-Konto entfernt",
|
||||||
|
"unmute_memories": "Stummschaltung für Erinnerungen aufheben",
|
||||||
"unnamed_album": "Unbenanntes Album",
|
"unnamed_album": "Unbenanntes Album",
|
||||||
"unnamed_album_delete_confirmation": "Bist du sicher, dass du dieses Album löschen willst?",
|
"unnamed_album_delete_confirmation": "Bist du sicher, dass du dieses Album löschen willst?",
|
||||||
"unnamed_share": "Unbenannte Freigabe",
|
"unnamed_share": "Unbenannte Freigabe",
|
||||||
@@ -1343,6 +1367,7 @@
|
|||||||
"view_all": "Alles anzeigen",
|
"view_all": "Alles anzeigen",
|
||||||
"view_all_users": "Alle Nutzer anzeigen",
|
"view_all_users": "Alle Nutzer anzeigen",
|
||||||
"view_in_timeline": "In Zeitleiste anzeigen",
|
"view_in_timeline": "In Zeitleiste anzeigen",
|
||||||
|
"view_link": "Link anzeigen",
|
||||||
"view_links": "Links anzeigen",
|
"view_links": "Links anzeigen",
|
||||||
"view_name": "Ansicht",
|
"view_name": "Ansicht",
|
||||||
"view_next_asset": "Nächste Datei anzeigen",
|
"view_next_asset": "Nächste Datei anzeigen",
|
||||||
|
|||||||
28
i18n/el.json
28
i18n/el.json
@@ -41,6 +41,7 @@
|
|||||||
"backup_settings": "Ρυθμίσεις Αντιγράφων Ασφαλείας",
|
"backup_settings": "Ρυθμίσεις Αντιγράφων Ασφαλείας",
|
||||||
"backup_settings_description": "Διαχείρηση ρυθμίσεων των αντιγράφων ασφαλείας της βάσης δεδομένων",
|
"backup_settings_description": "Διαχείρηση ρυθμίσεων των αντιγράφων ασφαλείας της βάσης δεδομένων",
|
||||||
"check_all": "Έλεγχος Όλων",
|
"check_all": "Έλεγχος Όλων",
|
||||||
|
"cleanup": "Εκκαθάριση",
|
||||||
"cleared_jobs": "Εκκαθαρίστηκαν οι εργασίες για: {job}",
|
"cleared_jobs": "Εκκαθαρίστηκαν οι εργασίες για: {job}",
|
||||||
"config_set_by_file": "Η παραμετροποίηση γίνεται, προς το παρόν, μέσω ενός αρχείου παραμέτρων",
|
"config_set_by_file": "Η παραμετροποίηση γίνεται, προς το παρόν, μέσω ενός αρχείου παραμέτρων",
|
||||||
"confirm_delete_library": "Είστε βέβαιοι ότι θέλετε να διαγράψετε τη βιβλιοθήκη {library};",
|
"confirm_delete_library": "Είστε βέβαιοι ότι θέλετε να διαγράψετε τη βιβλιοθήκη {library};",
|
||||||
@@ -131,7 +132,7 @@
|
|||||||
"machine_learning_smart_search_description": "Αναζητήστε εικόνες σημασιολογικά χρησιμοποιώντας ενσωματώσεις CLIP",
|
"machine_learning_smart_search_description": "Αναζητήστε εικόνες σημασιολογικά χρησιμοποιώντας ενσωματώσεις CLIP",
|
||||||
"machine_learning_smart_search_enabled": "Ενεργοποίηση έξυπνης αναζήτησης",
|
"machine_learning_smart_search_enabled": "Ενεργοποίηση έξυπνης αναζήτησης",
|
||||||
"machine_learning_smart_search_enabled_description": "Αν απενεργοποιηθεί, οι εικόνες δεν θα κωδικοποιούνται για έξυπνη αναζήτηση.",
|
"machine_learning_smart_search_enabled_description": "Αν απενεργοποιηθεί, οι εικόνες δεν θα κωδικοποιούνται για έξυπνη αναζήτηση.",
|
||||||
"machine_learning_url_description": "Η διεύθυνση URL του διακομιστή μηχανικής μάθησης. Αν παρέχονται περισσότερες από μία διευθύνσεις, τότε θα γίνει προσπάθεια σύνδεσης σε κάθε μια διαδοχικά από την πρώτη μέχρι την τελευταία, έως ότου κάποια να είναι επιτυχής.",
|
"machine_learning_url_description": "Η διεύθυνση URL του διακομιστή μηχανικής μάθησης. Αν δοθούν περισσότερες από μία διευθύνσεις URL, κάθε διακομιστής θα δοκιμάζεται διαδοχικά μέχρι να ανταποκριθεί ένας με επιτυχία, με τη σειρά από την πρώτη έως την τελευταία. Οι διακομιστές που δεν ανταποκρίνονται θα αγνοούνται προσωρινά μέχρι να επανέλθουν σε λειτουργία.",
|
||||||
"manage_concurrency": "Διαχείριση ταυτόχρονη εκτέλεσης",
|
"manage_concurrency": "Διαχείριση ταυτόχρονη εκτέλεσης",
|
||||||
"manage_log_settings": "Διαχείριση ρυθμίσεων αρχείου καταγραφής",
|
"manage_log_settings": "Διαχείριση ρυθμίσεων αρχείου καταγραφής",
|
||||||
"map_dark_style": "Σκούρο Θέμα",
|
"map_dark_style": "Σκούρο Θέμα",
|
||||||
@@ -147,6 +148,8 @@
|
|||||||
"map_settings": "Χάρτης",
|
"map_settings": "Χάρτης",
|
||||||
"map_settings_description": "Διαχείριση ρυθμίσεων χάρτη",
|
"map_settings_description": "Διαχείριση ρυθμίσεων χάρτη",
|
||||||
"map_style_description": "URL προς αρχείο θέματος του χάρτη style.json",
|
"map_style_description": "URL προς αρχείο θέματος του χάρτη style.json",
|
||||||
|
"memory_cleanup_job": "Καθαρισμός μνήμης",
|
||||||
|
"memory_generate_job": "Δημιουργία μνήμης",
|
||||||
"metadata_extraction_job": "Εξαγωγή μεταδεδομένων",
|
"metadata_extraction_job": "Εξαγωγή μεταδεδομένων",
|
||||||
"metadata_extraction_job_description": "Εξαγωγή μεταδεδομένων από κάθε αρχείο, όπως τοποθεσία, πρόσωπα και ανάλυση",
|
"metadata_extraction_job_description": "Εξαγωγή μεταδεδομένων από κάθε αρχείο, όπως τοποθεσία, πρόσωπα και ανάλυση",
|
||||||
"metadata_faces_import_setting": "Ενεργοποίηση εισαγωγής προσώπων",
|
"metadata_faces_import_setting": "Ενεργοποίηση εισαγωγής προσώπων",
|
||||||
@@ -299,7 +302,7 @@
|
|||||||
"transcoding_max_b_frames": "Μέγιστος αριθμός B-frames(Bidirectional Predictive Frames)",
|
"transcoding_max_b_frames": "Μέγιστος αριθμός B-frames(Bidirectional Predictive Frames)",
|
||||||
"transcoding_max_b_frames_description": "Οι υψηλότερες τιμές βελτιώνουν την αποδοτικότητα της συμπίεσης, αλλά επιβραδύνουν την κωδικοποίηση. Ενδέχεται να μην είναι συμβατές με την επιτάχυνση υλικού σε παλαιότερες συσκευές. Η τιμή 0 απενεργοποιεί τα B-frames, ενώ η -1, τη ρυθμίζει αυτόματα.",
|
"transcoding_max_b_frames_description": "Οι υψηλότερες τιμές βελτιώνουν την αποδοτικότητα της συμπίεσης, αλλά επιβραδύνουν την κωδικοποίηση. Ενδέχεται να μην είναι συμβατές με την επιτάχυνση υλικού σε παλαιότερες συσκευές. Η τιμή 0 απενεργοποιεί τα B-frames, ενώ η -1, τη ρυθμίζει αυτόματα.",
|
||||||
"transcoding_max_bitrate": "Μέγιστος ρυθμός μετάδοσης (bitrate)",
|
"transcoding_max_bitrate": "Μέγιστος ρυθμός μετάδοσης (bitrate)",
|
||||||
"transcoding_max_bitrate_description": "Η ρύθμιση ενός μέγιστου ρυθμού μετάδοσης(bitrate) μπορεί να κάνει το μέγεθος των αρχείων πιο προβλέψιμο, αλλά με ένα μικρό κόστος στην ποιότητα. Στην ανάλυση των 720p, οι τυπικές τιμές είναι 2600k για VP9 ή HEVC, ή 4500k για H.264. Απενεργοποιείται εάν οριστεί σε 0.",
|
"transcoding_max_bitrate_description": "Η ρύθμιση ενός μέγιστου ρυθμού μετάδοσης(bitrate) μπορεί να κάνει το μέγεθος των αρχείων πιο προβλέψιμο, αλλά με ένα μικρό κόστος στην ποιότητα. Στην ανάλυση των 720p, οι τυπικές τιμές είναι 2600 kbit/s για VP9 ή HEVC, ή 4500 kbit/s για H.264. Απενεργοποιείται εάν οριστεί σε 0.",
|
||||||
"transcoding_max_keyframe_interval": "Μέγιστο χρονικό διάστημα μεταξύ των καρέ αναφοράς (keyframe)",
|
"transcoding_max_keyframe_interval": "Μέγιστο χρονικό διάστημα μεταξύ των καρέ αναφοράς (keyframe)",
|
||||||
"transcoding_max_keyframe_interval_description": "Ορίζει το μέγιστο διάστημα μεταξύ των καρέ αναφοράς. Χαμηλότερες τιμές μειώνουν την αποδοτικότητα συμπίεσης, αλλά βελτιώνουν τον χρόνο αναζήτησης και μπορεί να βελτιώσουν την ποιότητα σε σκηνές με γρήγορη κίνηση. Η τιμή 0 ρυθμίζει αυτό το διάστημα αυτόματα.",
|
"transcoding_max_keyframe_interval_description": "Ορίζει το μέγιστο διάστημα μεταξύ των καρέ αναφοράς. Χαμηλότερες τιμές μειώνουν την αποδοτικότητα συμπίεσης, αλλά βελτιώνουν τον χρόνο αναζήτησης και μπορεί να βελτιώσουν την ποιότητα σε σκηνές με γρήγορη κίνηση. Η τιμή 0 ρυθμίζει αυτό το διάστημα αυτόματα.",
|
||||||
"transcoding_optimal_description": "Βίντεο με ανώτερη ανάλυση από την επιθυμητή ή σε μη αποδεκτή μορφή",
|
"transcoding_optimal_description": "Βίντεο με ανώτερη ανάλυση από την επιθυμητή ή σε μη αποδεκτή μορφή",
|
||||||
@@ -385,12 +388,13 @@
|
|||||||
"albums_count": "{count, plural, one {{count, number} Άλμπουμ} other {{count, number} Άλμπουμ}}",
|
"albums_count": "{count, plural, one {{count, number} Άλμπουμ} other {{count, number} Άλμπουμ}}",
|
||||||
"all": "Όλα",
|
"all": "Όλα",
|
||||||
"all_albums": "Όλα τα άλμπουμ",
|
"all_albums": "Όλα τα άλμπουμ",
|
||||||
"all_people": "Όλοι οι άνθρωποι",
|
"all_people": "Όλα τα άτομα",
|
||||||
"all_videos": "Όλα τα βίντεο",
|
"all_videos": "Όλα τα βίντεο",
|
||||||
"allow_dark_mode": "Επιτρέψτε τη σκοτεινή λειτουργία",
|
"allow_dark_mode": "Επιτρέψτε τη σκοτεινή λειτουργία",
|
||||||
"allow_edits": "Επιτρέψτε τις τροποποιήσεις",
|
"allow_edits": "Επιτρέψτε τις τροποποιήσεις",
|
||||||
"allow_public_user_to_download": "Επιτρέψτε σε δημόσιο χρήστη να κατεβάσει",
|
"allow_public_user_to_download": "Επιτρέψτε σε δημόσιο χρήστη να κατεβάσει",
|
||||||
"allow_public_user_to_upload": "Επιτρέψτε στον δημόσιο χρήστη να ανεβάσει",
|
"allow_public_user_to_upload": "Επιτρέψτε στον δημόσιο χρήστη να ανεβάσει",
|
||||||
|
"alt_text_qr_code": "Εικόνα κωδικού QR",
|
||||||
"anti_clockwise": "Αντίθετα με τη φορά του ρολογιού",
|
"anti_clockwise": "Αντίθετα με τη φορά του ρολογιού",
|
||||||
"api_key": "Κλειδί API",
|
"api_key": "Κλειδί API",
|
||||||
"api_key_description": "Αυτή η τιμή θα εμφανιστεί μόνο μία φορά. Παρακαλώ βεβαιωθείτε ότι την έχετε αντιγράψει πριν κλείσετε το παράθυρο.",
|
"api_key_description": "Αυτή η τιμή θα εμφανιστεί μόνο μία φορά. Παρακαλώ βεβαιωθείτε ότι την έχετε αντιγράψει πριν κλείσετε το παράθυρο.",
|
||||||
@@ -448,7 +452,7 @@
|
|||||||
"camera_model": "Μοντέλο κάμερας",
|
"camera_model": "Μοντέλο κάμερας",
|
||||||
"cancel": "Ακύρωση",
|
"cancel": "Ακύρωση",
|
||||||
"cancel_search": "Ακύρωση αναζήτησης",
|
"cancel_search": "Ακύρωση αναζήτησης",
|
||||||
"cannot_merge_people": "Αδύνατη η συγχώνευση προσώπων",
|
"cannot_merge_people": "Αδύνατη η συγχώνευση ατόμων",
|
||||||
"cannot_undo_this_action": "Δεν μπορείτε να αναιρέσετε αυτήν την ενέργεια!",
|
"cannot_undo_this_action": "Δεν μπορείτε να αναιρέσετε αυτήν την ενέργεια!",
|
||||||
"cannot_update_the_description": "Αδύνατη η ενημέρωση της περιγραφής",
|
"cannot_update_the_description": "Αδύνατη η ενημέρωση της περιγραφής",
|
||||||
"change_date": "Αλλαγή ημερομηνίας",
|
"change_date": "Αλλαγή ημερομηνίας",
|
||||||
@@ -481,6 +485,7 @@
|
|||||||
"comments_are_disabled": "Τα σχόλια είναι απενεργοποιημένα",
|
"comments_are_disabled": "Τα σχόλια είναι απενεργοποιημένα",
|
||||||
"confirm": "Επιβεβαίωση",
|
"confirm": "Επιβεβαίωση",
|
||||||
"confirm_admin_password": "Επιβεβαίωση κωδικού Διαχειριστή",
|
"confirm_admin_password": "Επιβεβαίωση κωδικού Διαχειριστή",
|
||||||
|
"confirm_delete_face": "Είστε σίγουροι ότι θέλετε να διαγράψετε το πρόσωπο του/της {name} από το στοιχείο;",
|
||||||
"confirm_delete_shared_link": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτόν τον κοινόχρηστο σύνδεσμο;",
|
"confirm_delete_shared_link": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτόν τον κοινόχρηστο σύνδεσμο;",
|
||||||
"confirm_keep_this_delete_others": "Όλα τα άλλα στοιχεία της στοίβας θα διαγραφούν, εκτός από αυτό το στοιχείο. Είστε σίγουροι ότι θέλετε να συνεχίσετε;",
|
"confirm_keep_this_delete_others": "Όλα τα άλλα στοιχεία της στοίβας θα διαγραφούν, εκτός από αυτό το στοιχείο. Είστε σίγουροι ότι θέλετε να συνεχίσετε;",
|
||||||
"confirm_password": "Επιβεβαίωση κωδικού",
|
"confirm_password": "Επιβεβαίωση κωδικού",
|
||||||
@@ -533,6 +538,7 @@
|
|||||||
"delete_album": "Διαγραφή άλμπουμ",
|
"delete_album": "Διαγραφή άλμπουμ",
|
||||||
"delete_api_key_prompt": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτό κλειδί API;",
|
"delete_api_key_prompt": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτό κλειδί API;",
|
||||||
"delete_duplicates_confirmation": "Είστε σίγουροι ότι επιθυμείτε τη μόνιμη διαγραφή αυτών των διπλότυπων;",
|
"delete_duplicates_confirmation": "Είστε σίγουροι ότι επιθυμείτε τη μόνιμη διαγραφή αυτών των διπλότυπων;",
|
||||||
|
"delete_face": "Διαγραφή προσώπου",
|
||||||
"delete_key": "Διαγραφή κλειδιού",
|
"delete_key": "Διαγραφή κλειδιού",
|
||||||
"delete_library": "Διαγραφή Βιβλιοθήκης",
|
"delete_library": "Διαγραφή Βιβλιοθήκης",
|
||||||
"delete_link": "Διαγραφή συνδέσμου",
|
"delete_link": "Διαγραφή συνδέσμου",
|
||||||
@@ -600,6 +606,7 @@
|
|||||||
"enabled": "Ενεργοποιημένο",
|
"enabled": "Ενεργοποιημένο",
|
||||||
"end_date": "Τελική ημερομηνία",
|
"end_date": "Τελική ημερομηνία",
|
||||||
"error": "Σφάλμα",
|
"error": "Σφάλμα",
|
||||||
|
"error_delete_face": "Σφάλμα διαγραφής προσώπου από το στοιχείο",
|
||||||
"error_loading_image": "Σφάλμα κατά τη φόρτωση της εικόνας",
|
"error_loading_image": "Σφάλμα κατά τη φόρτωση της εικόνας",
|
||||||
"error_title": "Σφάλμα - Κάτι πήγε στραβά",
|
"error_title": "Σφάλμα - Κάτι πήγε στραβά",
|
||||||
"errors": {
|
"errors": {
|
||||||
@@ -611,7 +618,7 @@
|
|||||||
"cant_change_metadata_assets_count": "Δεν μπορείτε να αλλάξετε τα μεταδεδομένα του {count, plural, one {# αρχείου} other {# αρχείων}}",
|
"cant_change_metadata_assets_count": "Δεν μπορείτε να αλλάξετε τα μεταδεδομένα του {count, plural, one {# αρχείου} other {# αρχείων}}",
|
||||||
"cant_get_faces": "Δεν είναι δυνατή η ανάκτηση προσώπων",
|
"cant_get_faces": "Δεν είναι δυνατή η ανάκτηση προσώπων",
|
||||||
"cant_get_number_of_comments": "Δεν είναι δυνατή η ανάκτηση του αριθμού των σχολίων",
|
"cant_get_number_of_comments": "Δεν είναι δυνατή η ανάκτηση του αριθμού των σχολίων",
|
||||||
"cant_search_people": "Δεν μπορείτε να αναζητήσετε άτομα",
|
"cant_search_people": "Αδύνατη η αναζήτηση ατόμων",
|
||||||
"cant_search_places": "Δεν μπορείτε να αναζητήσετε τοποθεσίες",
|
"cant_search_places": "Δεν μπορείτε να αναζητήσετε τοποθεσίες",
|
||||||
"cleared_jobs": "Εκκαθαρισμένες εργασίες για: {job}",
|
"cleared_jobs": "Εκκαθαρισμένες εργασίες για: {job}",
|
||||||
"error_adding_assets_to_album": "Σφάλμα κατά την προσθήκη στοιχείων στο άλμπουμ",
|
"error_adding_assets_to_album": "Σφάλμα κατά την προσθήκη στοιχείων στο άλμπουμ",
|
||||||
@@ -884,6 +891,7 @@
|
|||||||
"month": "Μήνας",
|
"month": "Μήνας",
|
||||||
"more": "Περισσότερα",
|
"more": "Περισσότερα",
|
||||||
"moved_to_trash": "Μετακινήθηκε στον κάδο απορριμμάτων",
|
"moved_to_trash": "Μετακινήθηκε στον κάδο απορριμμάτων",
|
||||||
|
"mute_memories": "Σίγαση Αναμνήσεων",
|
||||||
"my_albums": "Τα άλμπουμ μου",
|
"my_albums": "Τα άλμπουμ μου",
|
||||||
"name": "Όνομα",
|
"name": "Όνομα",
|
||||||
"name_or_nickname": "Όνομα ή ψευδώνυμο",
|
"name_or_nickname": "Όνομα ή ψευδώνυμο",
|
||||||
@@ -979,6 +987,7 @@
|
|||||||
"permanently_deleted_asset": "Οριστικά διαγραμμένο στοιχείο",
|
"permanently_deleted_asset": "Οριστικά διαγραμμένο στοιχείο",
|
||||||
"permanently_deleted_assets_count": "Οριστική διαγραφή {count, plural, one {# στοιχείου} other {# στοιχείων}}",
|
"permanently_deleted_assets_count": "Οριστική διαγραφή {count, plural, one {# στοιχείου} other {# στοιχείων}}",
|
||||||
"person": "Άτομο",
|
"person": "Άτομο",
|
||||||
|
"person_birthdate": "Γεννηθείς στις {date}",
|
||||||
"person_hidden": "{name}{hidden, select, true { (κρυφό)} other {}}",
|
"person_hidden": "{name}{hidden, select, true { (κρυφό)} other {}}",
|
||||||
"photo_shared_all_users": "Φαίνεται ότι μοιραστήκατε τις φωτογραφίες σας με όλους τους χρήστες ή δεν έχετε κανέναν χρήστη για κοινή χρήση.",
|
"photo_shared_all_users": "Φαίνεται ότι μοιραστήκατε τις φωτογραφίες σας με όλους τους χρήστες ή δεν έχετε κανέναν χρήστη για κοινή χρήση.",
|
||||||
"photos": "Φωτογραφίες",
|
"photos": "Φωτογραφίες",
|
||||||
@@ -1070,12 +1079,16 @@
|
|||||||
"remove_from_album": "Αφαίρεση από το άλμπουμ",
|
"remove_from_album": "Αφαίρεση από το άλμπουμ",
|
||||||
"remove_from_favorites": "Αφαίρεση από τα αγαπημένα",
|
"remove_from_favorites": "Αφαίρεση από τα αγαπημένα",
|
||||||
"remove_from_shared_link": "Αφαίρεση από τον κοινόχρηστο σύνδεσμο",
|
"remove_from_shared_link": "Αφαίρεση από τον κοινόχρηστο σύνδεσμο",
|
||||||
|
"remove_memory": "Αφαίρεση ανάμνησης",
|
||||||
|
"remove_photo_from_memory": "Αφαίρεση φωτογραφίας από την ανάμνηση",
|
||||||
"remove_url": "Αφαίρεση Συνδέσμου",
|
"remove_url": "Αφαίρεση Συνδέσμου",
|
||||||
"remove_user": "Αφαίρεση χρήστη",
|
"remove_user": "Αφαίρεση χρήστη",
|
||||||
"removed_api_key": "Αφαιρέθηκε το API Key: {name}",
|
"removed_api_key": "Αφαιρέθηκε το API Key: {name}",
|
||||||
"removed_from_archive": "Αφαιρέθηκε/καν από το Αρχείο",
|
"removed_from_archive": "Αφαιρέθηκε/καν από το Αρχείο",
|
||||||
"removed_from_favorites": "Αφαιρέθηκε από τα αγαπημένα",
|
"removed_from_favorites": "Αφαιρέθηκε από τα αγαπημένα",
|
||||||
"removed_from_favorites_count": "Αφαιρέθηκαν {count, plural, other {#}} από τα αγαπημένα",
|
"removed_from_favorites_count": "Αφαιρέθηκαν {count, plural, other {#}} από τα αγαπημένα",
|
||||||
|
"removed_memory": "Διαγραμμένη μνήμη",
|
||||||
|
"removed_photo_from_memory": "Διαγραμμένη φωτογραφία από τη μνήμη",
|
||||||
"removed_tagged_assets": "Αφαιρέθηκε η ετικέτα από {count, plural, one {# στοιχείο} other {# στοιχεία}}",
|
"removed_tagged_assets": "Αφαιρέθηκε η ετικέτα από {count, plural, one {# στοιχείο} other {# στοιχεία}}",
|
||||||
"rename": "Μετονομασία",
|
"rename": "Μετονομασία",
|
||||||
"repair": "Επισκευή",
|
"repair": "Επισκευή",
|
||||||
@@ -1084,6 +1097,7 @@
|
|||||||
"repository": "Αποθετήριο",
|
"repository": "Αποθετήριο",
|
||||||
"require_password": "Απαιτείται κωδικός πρόσβασης",
|
"require_password": "Απαιτείται κωδικός πρόσβασης",
|
||||||
"require_user_to_change_password_on_first_login": "Ο χρήστης απαιτείται να αλλάξει τον κωδικό πρόσβασής του κατά την πρώτη σύνδεση",
|
"require_user_to_change_password_on_first_login": "Ο χρήστης απαιτείται να αλλάξει τον κωδικό πρόσβασής του κατά την πρώτη σύνδεση",
|
||||||
|
"rescan": "Εκ νέου σάρωση",
|
||||||
"reset": "Επαναφορά",
|
"reset": "Επαναφορά",
|
||||||
"reset_password": "Επαναφορά κωδικού πρόσβασης",
|
"reset_password": "Επαναφορά κωδικού πρόσβασης",
|
||||||
"reset_people_visibility": "Επαναφορά προβολής ατόμων",
|
"reset_people_visibility": "Επαναφορά προβολής ατόμων",
|
||||||
@@ -1127,6 +1141,7 @@
|
|||||||
"search_options": "Επιλογές αναζήτησης",
|
"search_options": "Επιλογές αναζήτησης",
|
||||||
"search_people": "Αναζήτηση ατόμων",
|
"search_people": "Αναζήτηση ατόμων",
|
||||||
"search_places": "Αναζήτηση τοποθεσιών",
|
"search_places": "Αναζήτηση τοποθεσιών",
|
||||||
|
"search_rating": "Αναζήτηση κατά βαθμολογία...",
|
||||||
"search_settings": "Ρυθμίσεις αναζήτησης",
|
"search_settings": "Ρυθμίσεις αναζήτησης",
|
||||||
"search_state": "Αναζήτηση νομού...",
|
"search_state": "Αναζήτηση νομού...",
|
||||||
"search_tags": "Αναζήτηση ετικετών...",
|
"search_tags": "Αναζήτηση ετικετών...",
|
||||||
@@ -1136,6 +1151,7 @@
|
|||||||
"searching_locales": "Αναζήτηση τοποθεσιών...",
|
"searching_locales": "Αναζήτηση τοποθεσιών...",
|
||||||
"second": "Δευτερόλεπτο",
|
"second": "Δευτερόλεπτο",
|
||||||
"see_all_people": "Προβολή όλων των ατόμων",
|
"see_all_people": "Προβολή όλων των ατόμων",
|
||||||
|
"select": "Επιλογή",
|
||||||
"select_album_cover": "Επιλέξτε εξώφυλλο άλμπουμ",
|
"select_album_cover": "Επιλέξτε εξώφυλλο άλμπουμ",
|
||||||
"select_all": "Επιλογή όλων",
|
"select_all": "Επιλογή όλων",
|
||||||
"select_all_duplicates": "Επιλογή όλων των διπλότυπων",
|
"select_all_duplicates": "Επιλογή όλων των διπλότυπων",
|
||||||
@@ -1250,6 +1266,7 @@
|
|||||||
"tag_created": "Δημιουργήθηκε ετικέτα: {tag}",
|
"tag_created": "Δημιουργήθηκε ετικέτα: {tag}",
|
||||||
"tag_feature_description": "Περιήγηση σε φωτογραφίες και βίντεο που είναι οργανωμένα σύμφωνα με λογικά θέματα ετικετών",
|
"tag_feature_description": "Περιήγηση σε φωτογραφίες και βίντεο που είναι οργανωμένα σύμφωνα με λογικά θέματα ετικετών",
|
||||||
"tag_not_found_question": "Δεν μπορείτε να βρείτε μια ετικέτα; <link>Δημιουργήστε μια νέα ετικέτα.</link>",
|
"tag_not_found_question": "Δεν μπορείτε να βρείτε μια ετικέτα; <link>Δημιουργήστε μια νέα ετικέτα.</link>",
|
||||||
|
"tag_people": "Επισήμανση ατόμων",
|
||||||
"tag_updated": "Ενημερώθηκε η ετικέτα: {tag}",
|
"tag_updated": "Ενημερώθηκε η ετικέτα: {tag}",
|
||||||
"tagged_assets": "Ετικετοποιημένο/α {count, plural, one {# στοιχείο} other {# στοιχεία}}",
|
"tagged_assets": "Ετικετοποιημένο/α {count, plural, one {# στοιχείο} other {# στοιχεία}}",
|
||||||
"tags": "Ετικέτες",
|
"tags": "Ετικέτες",
|
||||||
@@ -1343,6 +1360,7 @@
|
|||||||
"view_all": "Προβολή Όλων",
|
"view_all": "Προβολή Όλων",
|
||||||
"view_all_users": "Προβολή όλων των χρηστών",
|
"view_all_users": "Προβολή όλων των χρηστών",
|
||||||
"view_in_timeline": "Προβολή στο χρονοδιάγραμμα",
|
"view_in_timeline": "Προβολή στο χρονοδιάγραμμα",
|
||||||
|
"view_link": "Προβολή σύνδεσμου",
|
||||||
"view_links": "Προβολή συνδέσμων",
|
"view_links": "Προβολή συνδέσμων",
|
||||||
"view_name": "Προβολή",
|
"view_name": "Προβολή",
|
||||||
"view_next_asset": "Προβολή επόμενου στοιχείου",
|
"view_next_asset": "Προβολή επόμενου στοιχείου",
|
||||||
|
|||||||
26
i18n/en.json
26
i18n/en.json
@@ -65,8 +65,13 @@
|
|||||||
"forcing_refresh_library_files": "Forcing refresh of all library files",
|
"forcing_refresh_library_files": "Forcing refresh of all library files",
|
||||||
"image_format": "Format",
|
"image_format": "Format",
|
||||||
"image_format_description": "WebP produces smaller files than JPEG, but is slower to encode.",
|
"image_format_description": "WebP produces smaller files than JPEG, but is slower to encode.",
|
||||||
|
"image_fullsize_enabled": "Enable full-size image generation",
|
||||||
|
"image_fullsize_enabled_description": "Generate full-size image for non-web-friendly formats. When \"Prefer embedded preview\" is enabled, embedded previews are used directly without conversion. Does not affect web-friendly formats like JPEG.",
|
||||||
|
"image_fullsize_quality_description": "Full-size image quality from 1-100. Higher is better, but produces larger files.",
|
||||||
|
"image_fullsize_title": "Full-size Image Settings",
|
||||||
|
"image_fullsize_description": "Full-size image with stripped metadata, used when zoomed in",
|
||||||
"image_prefer_embedded_preview": "Prefer embedded preview",
|
"image_prefer_embedded_preview": "Prefer embedded preview",
|
||||||
"image_prefer_embedded_preview_setting_description": "Use embedded previews in RAW photos as the input to image processing when available. This can produce more accurate colors for some images, but the quality of the preview is camera-dependent and the image may have more compression artifacts.",
|
"image_prefer_embedded_preview_setting_description": "Use embedded previews in RAW photos as the input to image processing and when available. This can produce more accurate colors for some images, but the quality of the preview is camera-dependent and the image may have more compression artifacts.",
|
||||||
"image_prefer_wide_gamut": "Prefer wide gamut",
|
"image_prefer_wide_gamut": "Prefer wide gamut",
|
||||||
"image_prefer_wide_gamut_setting_description": "Use Display P3 for thumbnails. This better preserves the vibrance of images with wide colorspaces, but images may appear differently on old devices with an old browser version. sRGB images are kept as sRGB to avoid color shifts.",
|
"image_prefer_wide_gamut_setting_description": "Use Display P3 for thumbnails. This better preserves the vibrance of images with wide colorspaces, but images may appear differently on old devices with an old browser version. sRGB images are kept as sRGB to avoid color shifts.",
|
||||||
"image_preview_description": "Medium-size image with stripped metadata, used when viewing a single asset and for machine learning",
|
"image_preview_description": "Medium-size image with stripped metadata, used when viewing a single asset and for machine learning",
|
||||||
@@ -96,7 +101,7 @@
|
|||||||
"library_scanning_enable_description": "Enable periodic library scanning",
|
"library_scanning_enable_description": "Enable periodic library scanning",
|
||||||
"library_settings": "External Library",
|
"library_settings": "External Library",
|
||||||
"library_settings_description": "Manage external library settings",
|
"library_settings_description": "Manage external library settings",
|
||||||
"library_tasks_description": "Perform library tasks",
|
"library_tasks_description": "Scan external libraries for new and/or changed assets",
|
||||||
"library_watching_enable_description": "Watch external libraries for file changes",
|
"library_watching_enable_description": "Watch external libraries for file changes",
|
||||||
"library_watching_settings": "Library watching (EXPERIMENTAL)",
|
"library_watching_settings": "Library watching (EXPERIMENTAL)",
|
||||||
"library_watching_settings_description": "Automatically watch for changed files",
|
"library_watching_settings_description": "Automatically watch for changed files",
|
||||||
@@ -159,7 +164,6 @@
|
|||||||
"no_pattern_added": "No pattern added",
|
"no_pattern_added": "No pattern added",
|
||||||
"note_apply_storage_label_previous_assets": "Note: To apply the Storage Label to previously uploaded assets, run the",
|
"note_apply_storage_label_previous_assets": "Note: To apply the Storage Label to previously uploaded assets, run the",
|
||||||
"note_cannot_be_changed_later": "NOTE: This cannot be changed later!",
|
"note_cannot_be_changed_later": "NOTE: This cannot be changed later!",
|
||||||
"note_unlimited_quota": "Note: Enter 0 for unlimited quota",
|
|
||||||
"notification_email_from_address": "From address",
|
"notification_email_from_address": "From address",
|
||||||
"notification_email_from_address_description": "Sender email address, for example: \"Immich Photo Server <noreply@example.com>\"",
|
"notification_email_from_address_description": "Sender email address, for example: \"Immich Photo Server <noreply@example.com>\"",
|
||||||
"notification_email_host_description": "Host of the email server (e.g. smtp.immich.app)",
|
"notification_email_host_description": "Host of the email server (e.g. smtp.immich.app)",
|
||||||
@@ -240,7 +244,7 @@
|
|||||||
"storage_template_hash_verification_enabled_description": "Enables hash verification, don't disable this unless you're certain of the implications",
|
"storage_template_hash_verification_enabled_description": "Enables hash verification, don't disable this unless you're certain of the implications",
|
||||||
"storage_template_migration": "Storage template migration",
|
"storage_template_migration": "Storage template migration",
|
||||||
"storage_template_migration_description": "Apply the current <link>{template}</link> to previously uploaded assets",
|
"storage_template_migration_description": "Apply the current <link>{template}</link> to previously uploaded assets",
|
||||||
"storage_template_migration_info": "Template changes will only apply to new assets. To retroactively apply the template to previously uploaded assets, run the <link>{job}</link>.",
|
"storage_template_migration_info": "The storage template will convert all extensions to lowercase. Template changes will only apply to new assets. To retroactively apply the template to previously uploaded assets, run the <link>{job}</link>.",
|
||||||
"storage_template_migration_job": "Storage Template Migration Job",
|
"storage_template_migration_job": "Storage Template Migration Job",
|
||||||
"storage_template_more_details": "For more details about this feature, refer to the <template-link>Storage Template</template-link> and its <implications-link>implications</implications-link>",
|
"storage_template_more_details": "For more details about this feature, refer to the <template-link>Storage Template</template-link> and its <implications-link>implications</implications-link>",
|
||||||
"storage_template_onboarding_description": "When enabled, this feature will auto-organize files based on a user-defined template. Due to stability issues the feature has been turned off by default. For more information, please see the <link>documentation</link>.",
|
"storage_template_onboarding_description": "When enabled, this feature will auto-organize files based on a user-defined template. Due to stability issues the feature has been turned off by default. For more information, please see the <link>documentation</link>.",
|
||||||
@@ -299,7 +303,7 @@
|
|||||||
"transcoding_max_b_frames": "Maximum B-frames",
|
"transcoding_max_b_frames": "Maximum B-frames",
|
||||||
"transcoding_max_b_frames_description": "Higher values improve compression efficiency, but slow down encoding. May not be compatible with hardware acceleration on older devices. 0 disables B-frames, while -1 sets this value automatically.",
|
"transcoding_max_b_frames_description": "Higher values improve compression efficiency, but slow down encoding. May not be compatible with hardware acceleration on older devices. 0 disables B-frames, while -1 sets this value automatically.",
|
||||||
"transcoding_max_bitrate": "Maximum bitrate",
|
"transcoding_max_bitrate": "Maximum bitrate",
|
||||||
"transcoding_max_bitrate_description": "Setting a max bitrate can make file sizes more predictable at a minor cost to quality. At 720p, typical values are 2600k for VP9 or HEVC, or 4500k for H.264. Disabled if set to 0.",
|
"transcoding_max_bitrate_description": "Setting a max bitrate can make file sizes more predictable at a minor cost to quality. At 720p, typical values are 2600 kbit/s for VP9 or HEVC, or 4500 kbit/s for H.264. Disabled if set to 0.",
|
||||||
"transcoding_max_keyframe_interval": "Maximum keyframe interval",
|
"transcoding_max_keyframe_interval": "Maximum keyframe interval",
|
||||||
"transcoding_max_keyframe_interval_description": "Sets the maximum frame distance between keyframes. Lower values worsen compression efficiency, but improve seek times and may improve quality in scenes with fast movement. 0 sets this value automatically.",
|
"transcoding_max_keyframe_interval_description": "Sets the maximum frame distance between keyframes. Lower values worsen compression efficiency, but improve seek times and may improve quality in scenes with fast movement. 0 sets this value automatically.",
|
||||||
"transcoding_optimal_description": "Videos higher than target resolution or not in an accepted format",
|
"transcoding_optimal_description": "Videos higher than target resolution or not in an accepted format",
|
||||||
@@ -336,6 +340,7 @@
|
|||||||
"untracked_files": "Untracked Files",
|
"untracked_files": "Untracked Files",
|
||||||
"untracked_files_description": "These files are not tracked by the application. They can be the results of failed moves, interrupted uploads, or left behind due to a bug",
|
"untracked_files_description": "These files are not tracked by the application. They can be the results of failed moves, interrupted uploads, or left behind due to a bug",
|
||||||
"user_cleanup_job": "User cleanup",
|
"user_cleanup_job": "User cleanup",
|
||||||
|
"cleanup": "Cleanup",
|
||||||
"user_delete_delay": "<b>{user}</b>'s account and assets will be scheduled for permanent deletion in {delay, plural, one {# day} other {# days}}.",
|
"user_delete_delay": "<b>{user}</b>'s account and assets will be scheduled for permanent deletion in {delay, plural, one {# day} other {# days}}.",
|
||||||
"user_delete_delay_settings": "Delete delay",
|
"user_delete_delay_settings": "Delete delay",
|
||||||
"user_delete_delay_settings_description": "Number of days after removal to permanently delete a user's account and assets. The user deletion job runs at midnight to check for users that are ready for deletion. Changes to this setting will be evaluated at the next execution.",
|
"user_delete_delay_settings_description": "Number of days after removal to permanently delete a user's account and assets. The user deletion job runs at midnight to check for users that are ready for deletion. Changes to this setting will be evaluated at the next execution.",
|
||||||
@@ -393,6 +398,7 @@
|
|||||||
"allow_edits": "Allow edits",
|
"allow_edits": "Allow edits",
|
||||||
"allow_public_user_to_download": "Allow public user to download",
|
"allow_public_user_to_download": "Allow public user to download",
|
||||||
"allow_public_user_to_upload": "Allow public user to upload",
|
"allow_public_user_to_upload": "Allow public user to upload",
|
||||||
|
"alt_text_qr_code": "QR code image",
|
||||||
"anti_clockwise": "Anti-clockwise",
|
"anti_clockwise": "Anti-clockwise",
|
||||||
"api_key": "API Key",
|
"api_key": "API Key",
|
||||||
"api_key_description": "This value will only be shown once. Please be sure to copy it before closing the window.",
|
"api_key_description": "This value will only be shown once. Please be sure to copy it before closing the window.",
|
||||||
@@ -857,6 +863,7 @@
|
|||||||
"loop_videos": "Loop videos",
|
"loop_videos": "Loop videos",
|
||||||
"loop_videos_description": "Enable to automatically loop a video in the detail viewer.",
|
"loop_videos_description": "Enable to automatically loop a video in the detail viewer.",
|
||||||
"main_branch_warning": "You’re using a development version; we strongly recommend using a release version!",
|
"main_branch_warning": "You’re using a development version; we strongly recommend using a release version!",
|
||||||
|
"main_menu": "Main menu",
|
||||||
"make": "Make",
|
"make": "Make",
|
||||||
"manage_shared_links": "Manage shared links",
|
"manage_shared_links": "Manage shared links",
|
||||||
"manage_sharing_with_partners": "Manage sharing with partners",
|
"manage_sharing_with_partners": "Manage sharing with partners",
|
||||||
@@ -889,6 +896,7 @@
|
|||||||
"month": "Month",
|
"month": "Month",
|
||||||
"more": "More",
|
"more": "More",
|
||||||
"moved_to_trash": "Moved to trash",
|
"moved_to_trash": "Moved to trash",
|
||||||
|
"mute_memories": "Mute Memories",
|
||||||
"my_albums": "My albums",
|
"my_albums": "My albums",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"name_or_nickname": "Name or nickname",
|
"name_or_nickname": "Name or nickname",
|
||||||
@@ -920,7 +928,6 @@
|
|||||||
"no_shared_albums_message": "Create an album to share photos and videos with people in your network",
|
"no_shared_albums_message": "Create an album to share photos and videos with people in your network",
|
||||||
"not_in_any_album": "Not in any album",
|
"not_in_any_album": "Not in any album",
|
||||||
"note_apply_storage_label_to_previously_uploaded assets": "Note: To apply the Storage Label to previously uploaded assets, run the",
|
"note_apply_storage_label_to_previously_uploaded assets": "Note: To apply the Storage Label to previously uploaded assets, run the",
|
||||||
"note_unlimited_quota": "Note: Enter 0 for unlimited quota",
|
|
||||||
"notes": "Notes",
|
"notes": "Notes",
|
||||||
"notification_toggle_setting_description": "Enable email notifications",
|
"notification_toggle_setting_description": "Enable email notifications",
|
||||||
"notifications": "Notifications",
|
"notifications": "Notifications",
|
||||||
@@ -984,6 +991,7 @@
|
|||||||
"permanently_deleted_asset": "Permanently deleted asset",
|
"permanently_deleted_asset": "Permanently deleted asset",
|
||||||
"permanently_deleted_assets_count": "Permanently deleted {count, plural, one {# asset} other {# assets}}",
|
"permanently_deleted_assets_count": "Permanently deleted {count, plural, one {# asset} other {# assets}}",
|
||||||
"person": "Person",
|
"person": "Person",
|
||||||
|
"person_birthdate": "Born on {date}",
|
||||||
"person_hidden": "{name}{hidden, select, true { (hidden)} other {}}",
|
"person_hidden": "{name}{hidden, select, true { (hidden)} other {}}",
|
||||||
"photo_shared_all_users": "Looks like you shared your photos with all users or you don't have any user to share with.",
|
"photo_shared_all_users": "Looks like you shared your photos with all users or you don't have any user to share with.",
|
||||||
"photos": "Photos",
|
"photos": "Photos",
|
||||||
@@ -1078,7 +1086,9 @@
|
|||||||
"remove_url": "Remove URL",
|
"remove_url": "Remove URL",
|
||||||
"remove_user": "Remove user",
|
"remove_user": "Remove user",
|
||||||
"removed_api_key": "Removed API Key: {name}",
|
"removed_api_key": "Removed API Key: {name}",
|
||||||
|
"remove_memory": "Remove memory",
|
||||||
"removed_memory": "Removed memory",
|
"removed_memory": "Removed memory",
|
||||||
|
"remove_photo_from_memory": "Remove photo from this memory",
|
||||||
"removed_photo_from_memory": "Removed photo from memory",
|
"removed_photo_from_memory": "Removed photo from memory",
|
||||||
"removed_from_archive": "Removed from archive",
|
"removed_from_archive": "Removed from archive",
|
||||||
"removed_from_favorites": "Removed from favorites",
|
"removed_from_favorites": "Removed from favorites",
|
||||||
@@ -1114,6 +1124,7 @@
|
|||||||
"say_something": "Say something",
|
"say_something": "Say something",
|
||||||
"scan_all_libraries": "Scan All Libraries",
|
"scan_all_libraries": "Scan All Libraries",
|
||||||
"scan_library": "Scan",
|
"scan_library": "Scan",
|
||||||
|
"rescan": "Rescan",
|
||||||
"scan_settings": "Scan Settings",
|
"scan_settings": "Scan Settings",
|
||||||
"scanning_for_album": "Scanning for album...",
|
"scanning_for_album": "Scanning for album...",
|
||||||
"search": "Search",
|
"search": "Search",
|
||||||
@@ -1145,6 +1156,7 @@
|
|||||||
"second": "Second",
|
"second": "Second",
|
||||||
"see_all_people": "See all people",
|
"see_all_people": "See all people",
|
||||||
"select_album_cover": "Select album cover",
|
"select_album_cover": "Select album cover",
|
||||||
|
"select": "Select",
|
||||||
"select_all": "Select all",
|
"select_all": "Select all",
|
||||||
"select_all_duplicates": "Select all duplicates",
|
"select_all_duplicates": "Select all duplicates",
|
||||||
"select_avatar_color": "Select avatar color",
|
"select_avatar_color": "Select avatar color",
|
||||||
@@ -1302,6 +1314,7 @@
|
|||||||
"unnamed_album": "Unnamed Album",
|
"unnamed_album": "Unnamed Album",
|
||||||
"unnamed_album_delete_confirmation": "Are you sure you want to delete this album?",
|
"unnamed_album_delete_confirmation": "Are you sure you want to delete this album?",
|
||||||
"unnamed_share": "Unnamed Share",
|
"unnamed_share": "Unnamed Share",
|
||||||
|
"unmute_memories": "Unmute Memories",
|
||||||
"unsaved_change": "Unsaved change",
|
"unsaved_change": "Unsaved change",
|
||||||
"unselect_all": "Unselect all",
|
"unselect_all": "Unselect all",
|
||||||
"unselect_all_duplicates": "Unselect all duplicates",
|
"unselect_all_duplicates": "Unselect all duplicates",
|
||||||
@@ -1352,6 +1365,7 @@
|
|||||||
"view_all": "View All",
|
"view_all": "View All",
|
||||||
"view_all_users": "View all users",
|
"view_all_users": "View all users",
|
||||||
"view_in_timeline": "View in timeline",
|
"view_in_timeline": "View in timeline",
|
||||||
|
"view_link": "View link",
|
||||||
"view_links": "View links",
|
"view_links": "View links",
|
||||||
"view_name": "View",
|
"view_name": "View",
|
||||||
"view_next_asset": "View next asset",
|
"view_next_asset": "View next asset",
|
||||||
|
|||||||
47
i18n/es.json
47
i18n/es.json
@@ -7,7 +7,7 @@
|
|||||||
"actions": "Acciones",
|
"actions": "Acciones",
|
||||||
"active": "Activo",
|
"active": "Activo",
|
||||||
"activity": "Actividad",
|
"activity": "Actividad",
|
||||||
"activity_changed": "La actividad está {enabled, select, true {activada} other {desactivada}}",
|
"activity_changed": "La actividad está {enabled, select, true {habilitada} other {deshabilitada}}",
|
||||||
"add": "Agregar",
|
"add": "Agregar",
|
||||||
"add_a_description": "Agregar descripción",
|
"add_a_description": "Agregar descripción",
|
||||||
"add_a_location": "Agregar ubicación",
|
"add_a_location": "Agregar ubicación",
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"add_to_album": "Incluir en álbum",
|
"add_to_album": "Incluir en álbum",
|
||||||
"add_to_shared_album": "Incluir en álbum compartido",
|
"add_to_shared_album": "Incluir en álbum compartido",
|
||||||
"add_url": "Añadir URL",
|
"add_url": "Añadir URL",
|
||||||
"added_to_archive": "Archivado",
|
"added_to_archive": "Agregado al Archivado",
|
||||||
"added_to_favorites": "Agregado a favoritos",
|
"added_to_favorites": "Agregado a favoritos",
|
||||||
"added_to_favorites_count": "Agregado {count, number} a favoritos",
|
"added_to_favorites_count": "Agregado {count, number} a favoritos",
|
||||||
"admin": {
|
"admin": {
|
||||||
@@ -41,6 +41,7 @@
|
|||||||
"backup_settings": "Ajustes de respaldo",
|
"backup_settings": "Ajustes de respaldo",
|
||||||
"backup_settings_description": "Administrar configuración de respaldo de base de datos",
|
"backup_settings_description": "Administrar configuración de respaldo de base de datos",
|
||||||
"check_all": "Verificar todo",
|
"check_all": "Verificar todo",
|
||||||
|
"cleanup": "Limpieza",
|
||||||
"cleared_jobs": "Trabajos borrados para: {job}",
|
"cleared_jobs": "Trabajos borrados para: {job}",
|
||||||
"config_set_by_file": "La configuración está definida por un archivo de configuración",
|
"config_set_by_file": "La configuración está definida por un archivo de configuración",
|
||||||
"confirm_delete_library": "¿Estás seguro de que quieres eliminar la biblioteca {library}?",
|
"confirm_delete_library": "¿Estás seguro de que quieres eliminar la biblioteca {library}?",
|
||||||
@@ -65,8 +66,13 @@
|
|||||||
"forcing_refresh_library_files": "Forzando la recarga de todos los elementos en la biblioteca",
|
"forcing_refresh_library_files": "Forzando la recarga de todos los elementos en la biblioteca",
|
||||||
"image_format": "Formato",
|
"image_format": "Formato",
|
||||||
"image_format_description": "WebP genera archivos más pequeños que JPEG, pero es más lento al codificarlos.",
|
"image_format_description": "WebP genera archivos más pequeños que JPEG, pero es más lento al codificarlos.",
|
||||||
|
"image_fullsize_description": "Imagen de tamaño completo con metadatos removidos, usado cuando se hace zoom",
|
||||||
|
"image_fullsize_enabled": "Activar generación de imágenes a tamaño completo",
|
||||||
|
"image_fullsize_enabled_description": "Generar imágenes a tamaño completo para formatos no aptos para web. Cuando \"Preferir vista previa incrustada\" está activada, las vistas previas incrustadas se utilizan directamente sin conversión. No afecta a los formatos aptos para la web, como JPEG.",
|
||||||
|
"image_fullsize_quality_description": "De 1 a 100, calidad de imágenes de tamaño completo. Mientras más alto es mejor, pero genera archivos de mayor tamaño.",
|
||||||
|
"image_fullsize_title": "Configuraciones de imágenes de tamaño completo",
|
||||||
"image_prefer_embedded_preview": "Preferir vista previa embebida",
|
"image_prefer_embedded_preview": "Preferir vista previa embebida",
|
||||||
"image_prefer_embedded_preview_setting_description": "Usar vistas previas embebidas en fotos RAW como entrada para el procesamiento de imágenes cuando estén disponibles. Esto puede producir colores más precisos en algunas imágenes, pero la calidad de la vista previa depende de la cámara y la imagen puede tener más artefactos de compresión.",
|
"image_prefer_embedded_preview_setting_description": "Usar vistas previas embebidas en fotos RAW como entrada para el procesamiento de imágenes y cuando estén disponibles. Esto puede producir colores más precisos en algunas imágenes, pero la calidad de la vista previa depende de la cámara y la imagen puede tener más artefactos de compresión.",
|
||||||
"image_prefer_wide_gamut": "Preferir 'gamut' amplio",
|
"image_prefer_wide_gamut": "Preferir 'gamut' amplio",
|
||||||
"image_prefer_wide_gamut_setting_description": "Usar \"Display P3\" para las miniaturas. Preserva mejor la vivacidad de las imágenes con espacios de color amplios pero las imágenes pueden aparecer de manera diferente en dispositivos antiguos con una versión antigua del navegador. Las imágenes sRGB se mantienen como sRGB para evitar cambios de color.",
|
"image_prefer_wide_gamut_setting_description": "Usar \"Display P3\" para las miniaturas. Preserva mejor la vivacidad de las imágenes con espacios de color amplios pero las imágenes pueden aparecer de manera diferente en dispositivos antiguos con una versión antigua del navegador. Las imágenes sRGB se mantienen como sRGB para evitar cambios de color.",
|
||||||
"image_preview_description": "Imagen de tamaño mediano con metadatos eliminados. Es utilizado al visualizar un solo activo y para el aprendizaje automático",
|
"image_preview_description": "Imagen de tamaño mediano con metadatos eliminados. Es utilizado al visualizar un solo activo y para el aprendizaje automático",
|
||||||
@@ -96,7 +102,7 @@
|
|||||||
"library_scanning_enable_description": "Activar el escaneo periódico de la biblioteca",
|
"library_scanning_enable_description": "Activar el escaneo periódico de la biblioteca",
|
||||||
"library_settings": "Biblioteca externa",
|
"library_settings": "Biblioteca externa",
|
||||||
"library_settings_description": "Administrar configuración biblioteca externa",
|
"library_settings_description": "Administrar configuración biblioteca externa",
|
||||||
"library_tasks_description": "Realizar tareas de biblioteca",
|
"library_tasks_description": "Buscar archivos nuevos o modificados en bibliotecas externas",
|
||||||
"library_watching_enable_description": "Vigilar las bibliotecas externas para detectar cambios en los archivos",
|
"library_watching_enable_description": "Vigilar las bibliotecas externas para detectar cambios en los archivos",
|
||||||
"library_watching_settings": "Vigilancia de la biblioteca (EXPERIMENTAL)",
|
"library_watching_settings": "Vigilancia de la biblioteca (EXPERIMENTAL)",
|
||||||
"library_watching_settings_description": "Vigilar automaticamente en busca de archivos modificados",
|
"library_watching_settings_description": "Vigilar automaticamente en busca de archivos modificados",
|
||||||
@@ -131,7 +137,7 @@
|
|||||||
"machine_learning_smart_search_description": "Busque imágenes semánticamente utilizando incrustaciones CLIP (Contrastive Language-Image Pre-Training)",
|
"machine_learning_smart_search_description": "Busque imágenes semánticamente utilizando incrustaciones CLIP (Contrastive Language-Image Pre-Training)",
|
||||||
"machine_learning_smart_search_enabled": "Habilitar búsqueda inteligente",
|
"machine_learning_smart_search_enabled": "Habilitar búsqueda inteligente",
|
||||||
"machine_learning_smart_search_enabled_description": "Al desactivarlo las imágenes no se procesarán para usar la búsqueda inteligente.",
|
"machine_learning_smart_search_enabled_description": "Al desactivarlo las imágenes no se procesarán para usar la búsqueda inteligente.",
|
||||||
"machine_learning_url_description": "La URL del servidor de aprendizaje automático. Si se proporciona más de una URL se intentará acceder a cada servidor sucesivamente hasta que uno responda correctamente en el orden especificado.",
|
"machine_learning_url_description": "La URL del servidor de aprendizaje automático. Si se proporciona más de una URL se intentará acceder a cada servidor sucesivamente hasta que uno responda correctamente en el orden especificado. Los servidores que no respondan serán ignorados temporalmente hasta que vuelvan a estar en línea.",
|
||||||
"manage_concurrency": "Ajustes de concurrencia",
|
"manage_concurrency": "Ajustes de concurrencia",
|
||||||
"manage_log_settings": "Administrar la configuración de los registros",
|
"manage_log_settings": "Administrar la configuración de los registros",
|
||||||
"map_dark_style": "Estilo oscuro",
|
"map_dark_style": "Estilo oscuro",
|
||||||
@@ -147,6 +153,8 @@
|
|||||||
"map_settings": "Mapa",
|
"map_settings": "Mapa",
|
||||||
"map_settings_description": "Administrar la configuración del mapa",
|
"map_settings_description": "Administrar la configuración del mapa",
|
||||||
"map_style_description": "Dirección URL a un tema de mapa (style.json)",
|
"map_style_description": "Dirección URL a un tema de mapa (style.json)",
|
||||||
|
"memory_cleanup_job": "Limpieza de memoria",
|
||||||
|
"memory_generate_job": "Generación de memoria",
|
||||||
"metadata_extraction_job": "Extracción de metadatos",
|
"metadata_extraction_job": "Extracción de metadatos",
|
||||||
"metadata_extraction_job_description": "Extraer información de metadatos de cada activo, como GPS, caras y resolución",
|
"metadata_extraction_job_description": "Extraer información de metadatos de cada activo, como GPS, caras y resolución",
|
||||||
"metadata_faces_import_setting": "Activar importación de caras",
|
"metadata_faces_import_setting": "Activar importación de caras",
|
||||||
@@ -240,7 +248,7 @@
|
|||||||
"storage_template_hash_verification_enabled_description": "Habilita la verificación de hash, no la desactive a menos que esté seguro de las implicaciones",
|
"storage_template_hash_verification_enabled_description": "Habilita la verificación de hash, no la desactive a menos que esté seguro de las implicaciones",
|
||||||
"storage_template_migration": "Migración de plantillas de almacenamiento",
|
"storage_template_migration": "Migración de plantillas de almacenamiento",
|
||||||
"storage_template_migration_description": "Aplicar la <link>{template}</link> actual a los elementos subidos previamente",
|
"storage_template_migration_description": "Aplicar la <link>{template}</link> actual a los elementos subidos previamente",
|
||||||
"storage_template_migration_info": "Los cambios en las plantillas solo se aplican a los elementos nuevos. Para aplicarlos retroactivamente a los elementos subidos previamente ejecute la <link>{job}</link>.",
|
"storage_template_migration_info": "La plantilla de almacenamiento convertirá todas las extensiones a minúscula. Los cambios en las plantillas solo se aplican a los elementos nuevos. Para aplicarlos retroactivamente a los elementos subidos previamente ejecute la <link>{job}</link>.",
|
||||||
"storage_template_migration_job": "Migración de la plantilla de almacenamiento",
|
"storage_template_migration_job": "Migración de la plantilla de almacenamiento",
|
||||||
"storage_template_more_details": "Para obtener más detalles sobre esta función, consulte la <template-link>Plantilla de almacenamiento</template-link> y sus <implications-link>implicaciones</implications-link>",
|
"storage_template_more_details": "Para obtener más detalles sobre esta función, consulte la <template-link>Plantilla de almacenamiento</template-link> y sus <implications-link>implicaciones</implications-link>",
|
||||||
"storage_template_onboarding_description": "Cuando está habilitada, esta función organizará automáticamente los archivos según una plantilla definida por el usuario. Debido a problemas de estabilidad, la función se ha desactivado de forma predeterminada. Para obtener más información, consulte la <link>documentación</link>.",
|
"storage_template_onboarding_description": "Cuando está habilitada, esta función organizará automáticamente los archivos según una plantilla definida por el usuario. Debido a problemas de estabilidad, la función se ha desactivado de forma predeterminada. Para obtener más información, consulte la <link>documentación</link>.",
|
||||||
@@ -299,7 +307,7 @@
|
|||||||
"transcoding_max_b_frames": "Maximos B-frames",
|
"transcoding_max_b_frames": "Maximos B-frames",
|
||||||
"transcoding_max_b_frames_description": "Los valores más altos mejoran la eficiencia de la compresión, pero ralentizan la codificación. Puede que no sea compatible con la aceleración de hardware en dispositivos más antiguos. 0 desactiva los fotogramas B, mientras que -1 establece este valor automáticamente.",
|
"transcoding_max_b_frames_description": "Los valores más altos mejoran la eficiencia de la compresión, pero ralentizan la codificación. Puede que no sea compatible con la aceleración de hardware en dispositivos más antiguos. 0 desactiva los fotogramas B, mientras que -1 establece este valor automáticamente.",
|
||||||
"transcoding_max_bitrate": "Máxima tasa de bits",
|
"transcoding_max_bitrate": "Máxima tasa de bits",
|
||||||
"transcoding_max_bitrate_description": "Establecer una tasa de bits máxima puede hacer que los tamaños de archivos sean más predecibles con un costo menor para la calidad. A 720p, los valores típicos son 2600k para VP9 o HEVC, o 4500k para H.264. Deshabilitado si se establece en 0.",
|
"transcoding_max_bitrate_description": "Establecer una tasa de bits máxima puede hacer que los tamaños de archivos sean más predecibles con un costo menor para la calidad. A 720p, los valores típicos son 2600 kbit/s para VP9 o HEVC, o 4500 kbit/s para H.264. Deshabilitado si se establece en 0.",
|
||||||
"transcoding_max_keyframe_interval": "Intervalo máximo de fotogramas clave",
|
"transcoding_max_keyframe_interval": "Intervalo máximo de fotogramas clave",
|
||||||
"transcoding_max_keyframe_interval_description": "Establece la distancia máxima de fotograma entre fotogramas clave. Los valores más bajos empeoran la eficiencia de la compresión, pero mejoran los tiempos de búsqueda y pueden mejorar la calidad en escenas con movimientos rápidos. 0 establece este valor automáticamente.",
|
"transcoding_max_keyframe_interval_description": "Establece la distancia máxima de fotograma entre fotogramas clave. Los valores más bajos empeoran la eficiencia de la compresión, pero mejoran los tiempos de búsqueda y pueden mejorar la calidad en escenas con movimientos rápidos. 0 establece este valor automáticamente.",
|
||||||
"transcoding_optimal_description": "Vídeos con una resolución superior a la fijada o que no están en un formato aceptado",
|
"transcoding_optimal_description": "Vídeos con una resolución superior a la fijada o que no están en un formato aceptado",
|
||||||
@@ -391,6 +399,7 @@
|
|||||||
"allow_edits": "Permitir edición",
|
"allow_edits": "Permitir edición",
|
||||||
"allow_public_user_to_download": "Permitir descargar al usuario público",
|
"allow_public_user_to_download": "Permitir descargar al usuario público",
|
||||||
"allow_public_user_to_upload": "Permitir cargar al usuario publico",
|
"allow_public_user_to_upload": "Permitir cargar al usuario publico",
|
||||||
|
"alt_text_qr_code": "Código QR",
|
||||||
"anti_clockwise": "En sentido antihorario",
|
"anti_clockwise": "En sentido antihorario",
|
||||||
"api_key": "Clave API",
|
"api_key": "Clave API",
|
||||||
"api_key_description": "Este valor sólo se mostrará una vez. Asegúrese de copiarlo antes de cerrar la ventana.",
|
"api_key_description": "Este valor sólo se mostrará una vez. Asegúrese de copiarlo antes de cerrar la ventana.",
|
||||||
@@ -410,7 +419,7 @@
|
|||||||
"asset_description_updated": "La descripción del elemento ha sido actualizada",
|
"asset_description_updated": "La descripción del elemento ha sido actualizada",
|
||||||
"asset_filename_is_offline": "El archivo {filename} está offline",
|
"asset_filename_is_offline": "El archivo {filename} está offline",
|
||||||
"asset_has_unassigned_faces": "El archivo no tiene rostros asignados",
|
"asset_has_unassigned_faces": "El archivo no tiene rostros asignados",
|
||||||
"asset_hashing": "Hashing…",
|
"asset_hashing": "Calculando hash…",
|
||||||
"asset_offline": "Archivos sin conexión",
|
"asset_offline": "Archivos sin conexión",
|
||||||
"asset_offline_description": "Este activo externo ya no se encuentra en el disco. Por favor, póngase en contacto con su administrador de Immich para obtener ayuda.",
|
"asset_offline_description": "Este activo externo ya no se encuentra en el disco. Por favor, póngase en contacto con su administrador de Immich para obtener ayuda.",
|
||||||
"asset_skipped": "Omitido",
|
"asset_skipped": "Omitido",
|
||||||
@@ -438,7 +447,7 @@
|
|||||||
"blurred_background": "Fondo borroso",
|
"blurred_background": "Fondo borroso",
|
||||||
"bugs_and_feature_requests": "Errores y solicitudes de funciones",
|
"bugs_and_feature_requests": "Errores y solicitudes de funciones",
|
||||||
"build": "Compilación",
|
"build": "Compilación",
|
||||||
"build_image": "Construir imagen",
|
"build_image": "Imagen de compilación",
|
||||||
"bulk_delete_duplicates_confirmation": "¿Estás seguro de que deseas eliminar de forma masiva {count, plural, one {# elemento duplicado} other {# elementos duplicados}}? Esto mantendrá el activo más grande de cada grupo y eliminará permanentemente todos los demás duplicados. ¡Esta acción no se puede deshacer!",
|
"bulk_delete_duplicates_confirmation": "¿Estás seguro de que deseas eliminar de forma masiva {count, plural, one {# elemento duplicado} other {# elementos duplicados}}? Esto mantendrá el activo más grande de cada grupo y eliminará permanentemente todos los demás duplicados. ¡Esta acción no se puede deshacer!",
|
||||||
"bulk_keep_duplicates_confirmation": "¿Estas seguro de que desea mantener {count, plural, one {# duplicate asset} other {# duplicate assets}} archivos duplicados? Esto resolverá todos los grupos duplicados sin borrar nada.",
|
"bulk_keep_duplicates_confirmation": "¿Estas seguro de que desea mantener {count, plural, one {# duplicate asset} other {# duplicate assets}} archivos duplicados? Esto resolverá todos los grupos duplicados sin borrar nada.",
|
||||||
"bulk_trash_duplicates_confirmation": "¿Estas seguro de que desea eliminar masivamente {count, plural, one {# duplicate asset} other {# duplicate assets}} archivos duplicados? Esto mantendrá el archivo más grande de cada grupo y eliminará todos los demás duplicados.",
|
"bulk_trash_duplicates_confirmation": "¿Estas seguro de que desea eliminar masivamente {count, plural, one {# duplicate asset} other {# duplicate assets}} archivos duplicados? Esto mantendrá el archivo más grande de cada grupo y eliminará todos los demás duplicados.",
|
||||||
@@ -481,6 +490,7 @@
|
|||||||
"comments_are_disabled": "Los comentarios están deshabilitados",
|
"comments_are_disabled": "Los comentarios están deshabilitados",
|
||||||
"confirm": "Confirmar",
|
"confirm": "Confirmar",
|
||||||
"confirm_admin_password": "Confirmar Contraseña de Administrador",
|
"confirm_admin_password": "Confirmar Contraseña de Administrador",
|
||||||
|
"confirm_delete_face": "¿Estás seguro que deseas eliminar la cara de {name} del archivo?",
|
||||||
"confirm_delete_shared_link": "¿Estás seguro de que deseas eliminar este enlace compartido?",
|
"confirm_delete_shared_link": "¿Estás seguro de que deseas eliminar este enlace compartido?",
|
||||||
"confirm_keep_this_delete_others": "Todos los demás activos de la pila se eliminarán excepto este activo. ¿Está seguro de que quiere continuar?",
|
"confirm_keep_this_delete_others": "Todos los demás activos de la pila se eliminarán excepto este activo. ¿Está seguro de que quiere continuar?",
|
||||||
"confirm_password": "Confirmar contraseña",
|
"confirm_password": "Confirmar contraseña",
|
||||||
@@ -533,6 +543,7 @@
|
|||||||
"delete_album": "Eliminar álbum",
|
"delete_album": "Eliminar álbum",
|
||||||
"delete_api_key_prompt": "¿Está seguro de que desea eliminar esta clave API?",
|
"delete_api_key_prompt": "¿Está seguro de que desea eliminar esta clave API?",
|
||||||
"delete_duplicates_confirmation": "¿Está seguro de que desea eliminar permanentemente estos duplicados?",
|
"delete_duplicates_confirmation": "¿Está seguro de que desea eliminar permanentemente estos duplicados?",
|
||||||
|
"delete_face": "Eliminar cara",
|
||||||
"delete_key": "Eliminar clave",
|
"delete_key": "Eliminar clave",
|
||||||
"delete_library": "Eliminar biblioteca",
|
"delete_library": "Eliminar biblioteca",
|
||||||
"delete_link": "Eliminar enlace",
|
"delete_link": "Eliminar enlace",
|
||||||
@@ -600,6 +611,7 @@
|
|||||||
"enabled": "Habilitado",
|
"enabled": "Habilitado",
|
||||||
"end_date": "Fecha final",
|
"end_date": "Fecha final",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
|
"error_delete_face": "Error al eliminar la cara del archivo",
|
||||||
"error_loading_image": "Error al cargar la imagen",
|
"error_loading_image": "Error al cargar la imagen",
|
||||||
"error_title": "Error: algo salió mal",
|
"error_title": "Error: algo salió mal",
|
||||||
"errors": {
|
"errors": {
|
||||||
@@ -852,6 +864,7 @@
|
|||||||
"loop_videos": "Vídeos en bucle",
|
"loop_videos": "Vídeos en bucle",
|
||||||
"loop_videos_description": "Habilite la reproducción automática de un video en el visor de detalles.",
|
"loop_videos_description": "Habilite la reproducción automática de un video en el visor de detalles.",
|
||||||
"main_branch_warning": "Estás ejecutando una compilación desde la rama principal. ¡Recomendamos encarecidamente usar una versión de lanzamiento!",
|
"main_branch_warning": "Estás ejecutando una compilación desde la rama principal. ¡Recomendamos encarecidamente usar una versión de lanzamiento!",
|
||||||
|
"main_menu": "Menú principal",
|
||||||
"make": "Marca",
|
"make": "Marca",
|
||||||
"manage_shared_links": "Administrar enlaces compartidos",
|
"manage_shared_links": "Administrar enlaces compartidos",
|
||||||
"manage_sharing_with_partners": "Administrar el uso compartido con invitados",
|
"manage_sharing_with_partners": "Administrar el uso compartido con invitados",
|
||||||
@@ -884,6 +897,7 @@
|
|||||||
"month": "Mes",
|
"month": "Mes",
|
||||||
"more": "Mas",
|
"more": "Mas",
|
||||||
"moved_to_trash": "Movido a la papelera",
|
"moved_to_trash": "Movido a la papelera",
|
||||||
|
"mute_memories": "Silenciar Recuerdos",
|
||||||
"my_albums": "Mis albums",
|
"my_albums": "Mis albums",
|
||||||
"name": "Nombre",
|
"name": "Nombre",
|
||||||
"name_or_nickname": "Nombre o apodo",
|
"name_or_nickname": "Nombre o apodo",
|
||||||
@@ -979,6 +993,7 @@
|
|||||||
"permanently_deleted_asset": "Archivo eliminado permanentemente",
|
"permanently_deleted_asset": "Archivo eliminado permanentemente",
|
||||||
"permanently_deleted_assets_count": "Eliminado permanentemente {count, plural, one {# elemento} other {# elementos}}",
|
"permanently_deleted_assets_count": "Eliminado permanentemente {count, plural, one {# elemento} other {# elementos}}",
|
||||||
"person": "Persona",
|
"person": "Persona",
|
||||||
|
"person_birthdate": "Nacido el {date}",
|
||||||
"person_hidden": "{name}{hidden, select, true { (oculto)} other {}}",
|
"person_hidden": "{name}{hidden, select, true { (oculto)} other {}}",
|
||||||
"photo_shared_all_users": "Parece que compartiste tus fotos con todos los usuarios o no tienes ningún usuario con quien compartirlas.",
|
"photo_shared_all_users": "Parece que compartiste tus fotos con todos los usuarios o no tienes ningún usuario con quien compartirlas.",
|
||||||
"photos": "Fotos",
|
"photos": "Fotos",
|
||||||
@@ -1070,12 +1085,16 @@
|
|||||||
"remove_from_album": "Eliminar del álbum",
|
"remove_from_album": "Eliminar del álbum",
|
||||||
"remove_from_favorites": "Quitar de favoritos",
|
"remove_from_favorites": "Quitar de favoritos",
|
||||||
"remove_from_shared_link": "Eliminar desde enlace compartido",
|
"remove_from_shared_link": "Eliminar desde enlace compartido",
|
||||||
|
"remove_memory": "Quitar memoria",
|
||||||
|
"remove_photo_from_memory": "Quitar foto de esta memoria",
|
||||||
"remove_url": "Eliminar URL",
|
"remove_url": "Eliminar URL",
|
||||||
"remove_user": "Eliminar usuario",
|
"remove_user": "Eliminar usuario",
|
||||||
"removed_api_key": "Clave API eliminada: {name}",
|
"removed_api_key": "Clave API eliminada: {name}",
|
||||||
"removed_from_archive": "Eliminado del archivo",
|
"removed_from_archive": "Eliminado del archivo",
|
||||||
"removed_from_favorites": "Eliminado de favoritos",
|
"removed_from_favorites": "Eliminado de favoritos",
|
||||||
"removed_from_favorites_count": "{count, plural, other {Eliminados #}} de favoritos",
|
"removed_from_favorites_count": "{count, plural, other {Eliminados #}} de favoritos",
|
||||||
|
"removed_memory": "Memoria eliminada",
|
||||||
|
"removed_photo_from_memory": "Se ha eliminado la foto de la memoria",
|
||||||
"removed_tagged_assets": "Etiqueta eliminada de {count, plural, one {# activo} other {# activos}}",
|
"removed_tagged_assets": "Etiqueta eliminada de {count, plural, one {# activo} other {# activos}}",
|
||||||
"rename": "Renombrar",
|
"rename": "Renombrar",
|
||||||
"repair": "Reparar",
|
"repair": "Reparar",
|
||||||
@@ -1084,6 +1103,7 @@
|
|||||||
"repository": "Repositorio",
|
"repository": "Repositorio",
|
||||||
"require_password": "Contraseña requerida",
|
"require_password": "Contraseña requerida",
|
||||||
"require_user_to_change_password_on_first_login": "Requerir que el usuario cambie la contraseña en el primer inicio de sesión",
|
"require_user_to_change_password_on_first_login": "Requerir que el usuario cambie la contraseña en el primer inicio de sesión",
|
||||||
|
"rescan": "Volver a escanear",
|
||||||
"reset": "Reiniciar",
|
"reset": "Reiniciar",
|
||||||
"reset_password": "Restablecer la contraseña",
|
"reset_password": "Restablecer la contraseña",
|
||||||
"reset_people_visibility": "Restablecer la visibilidad de las personas",
|
"reset_people_visibility": "Restablecer la visibilidad de las personas",
|
||||||
@@ -1127,6 +1147,7 @@
|
|||||||
"search_options": "Opciones de búsqueda",
|
"search_options": "Opciones de búsqueda",
|
||||||
"search_people": "Buscar personas",
|
"search_people": "Buscar personas",
|
||||||
"search_places": "Buscar lugar",
|
"search_places": "Buscar lugar",
|
||||||
|
"search_rating": "Buscar por calificación...",
|
||||||
"search_settings": "Ajustes de la búsqueda",
|
"search_settings": "Ajustes de la búsqueda",
|
||||||
"search_state": "Buscar región/estado...",
|
"search_state": "Buscar región/estado...",
|
||||||
"search_tags": "Buscando etiquetas...",
|
"search_tags": "Buscando etiquetas...",
|
||||||
@@ -1136,6 +1157,7 @@
|
|||||||
"searching_locales": "Buscando sitios...",
|
"searching_locales": "Buscando sitios...",
|
||||||
"second": "Segundo",
|
"second": "Segundo",
|
||||||
"see_all_people": "Ver todas las personas",
|
"see_all_people": "Ver todas las personas",
|
||||||
|
"select": "Selecciona",
|
||||||
"select_album_cover": "Seleccionar portada del álbum",
|
"select_album_cover": "Seleccionar portada del álbum",
|
||||||
"select_all": "Seleccionar todo",
|
"select_all": "Seleccionar todo",
|
||||||
"select_all_duplicates": "Seleccionar todos los duplicados",
|
"select_all_duplicates": "Seleccionar todos los duplicados",
|
||||||
@@ -1225,7 +1247,7 @@
|
|||||||
"stack_select_one_photo": "Selecciona una imagen principal para la pila",
|
"stack_select_one_photo": "Selecciona una imagen principal para la pila",
|
||||||
"stack_selected_photos": "Apilar fotos seleccionadas",
|
"stack_selected_photos": "Apilar fotos seleccionadas",
|
||||||
"stacked_assets_count": "Apilado(s) {count, plural, one {# activo} other {# activos}}",
|
"stacked_assets_count": "Apilado(s) {count, plural, one {# activo} other {# activos}}",
|
||||||
"stacktrace": "Stacktrace",
|
"stacktrace": "Seguimiento de pila",
|
||||||
"start": "Inicio",
|
"start": "Inicio",
|
||||||
"start_date": "Fecha de inicio",
|
"start_date": "Fecha de inicio",
|
||||||
"state": "Estado",
|
"state": "Estado",
|
||||||
@@ -1250,6 +1272,7 @@
|
|||||||
"tag_created": "Etiqueta creada: {tag}",
|
"tag_created": "Etiqueta creada: {tag}",
|
||||||
"tag_feature_description": "Explore fotos y videos agrupados por temas de etiquetas lógicas",
|
"tag_feature_description": "Explore fotos y videos agrupados por temas de etiquetas lógicas",
|
||||||
"tag_not_found_question": "¿No encuentra una etiqueta? <link>Crea una nueva etiqueta.</link>",
|
"tag_not_found_question": "¿No encuentra una etiqueta? <link>Crea una nueva etiqueta.</link>",
|
||||||
|
"tag_people": "Etiquetar personas",
|
||||||
"tag_updated": "Etiqueta actualizada: {tag}",
|
"tag_updated": "Etiqueta actualizada: {tag}",
|
||||||
"tagged_assets": "Etiquetado(s) {count, plural, one {# activo} other {# activos}}",
|
"tagged_assets": "Etiquetado(s) {count, plural, one {# activo} other {# activos}}",
|
||||||
"tags": "Etiquetas",
|
"tags": "Etiquetas",
|
||||||
@@ -1290,6 +1313,7 @@
|
|||||||
"unlink_motion_video": "Desvincular vídeo en movimiento",
|
"unlink_motion_video": "Desvincular vídeo en movimiento",
|
||||||
"unlink_oauth": "Desvincular OAuth",
|
"unlink_oauth": "Desvincular OAuth",
|
||||||
"unlinked_oauth_account": "Cuenta OAuth desconectada",
|
"unlinked_oauth_account": "Cuenta OAuth desconectada",
|
||||||
|
"unmute_memories": "Habilitar sonido recuerdos",
|
||||||
"unnamed_album": "Album sin nombre",
|
"unnamed_album": "Album sin nombre",
|
||||||
"unnamed_album_delete_confirmation": "¿Seguro que quieres borrar este álbum?",
|
"unnamed_album_delete_confirmation": "¿Seguro que quieres borrar este álbum?",
|
||||||
"unnamed_share": "Compartido sin nombre",
|
"unnamed_share": "Compartido sin nombre",
|
||||||
@@ -1332,7 +1356,7 @@
|
|||||||
"version_announcement_closing": "Tu amigo, Alex",
|
"version_announcement_closing": "Tu amigo, Alex",
|
||||||
"version_announcement_message": "¡Hola! Hay una nueva versión de Immich disponible. Tómese un tiempo para leer las <link> notas de la versión </link> para asegurarse de que su configuración esté actualizada y evitar errores de configuración, especialmente si utiliza WatchTower o cualquier mecanismo que se encargue de actualizar su instancia de Immich automáticamente.",
|
"version_announcement_message": "¡Hola! Hay una nueva versión de Immich disponible. Tómese un tiempo para leer las <link> notas de la versión </link> para asegurarse de que su configuración esté actualizada y evitar errores de configuración, especialmente si utiliza WatchTower o cualquier mecanismo que se encargue de actualizar su instancia de Immich automáticamente.",
|
||||||
"version_history": "Historial de versiones",
|
"version_history": "Historial de versiones",
|
||||||
"version_history_item": "Instalada la {version} el {date}",
|
"version_history_item": "Instalada {version} el {date}",
|
||||||
"video": "Vídeo",
|
"video": "Vídeo",
|
||||||
"video_hover_setting": "Iniciar vídeo al pasar por encima",
|
"video_hover_setting": "Iniciar vídeo al pasar por encima",
|
||||||
"video_hover_setting_description": "Reproducir el vídeo cuando el ratón está encima de un vídeo. Aunque esté desactivado, se iniciará cuando el cursor del ratón esté sobre el icono de \"reproducir\".",
|
"video_hover_setting_description": "Reproducir el vídeo cuando el ratón está encima de un vídeo. Aunque esté desactivado, se iniciará cuando el cursor del ratón esté sobre el icono de \"reproducir\".",
|
||||||
@@ -1343,6 +1367,7 @@
|
|||||||
"view_all": "Ver todas",
|
"view_all": "Ver todas",
|
||||||
"view_all_users": "Mostrar todos los usuarios",
|
"view_all_users": "Mostrar todos los usuarios",
|
||||||
"view_in_timeline": "Mostrar en la línea de tiempo",
|
"view_in_timeline": "Mostrar en la línea de tiempo",
|
||||||
|
"view_link": "Ver enlace",
|
||||||
"view_links": "Mostrar enlaces",
|
"view_links": "Mostrar enlaces",
|
||||||
"view_name": "Ver",
|
"view_name": "Ver",
|
||||||
"view_next_asset": "Mostrar siguiente elemento",
|
"view_next_asset": "Mostrar siguiente elemento",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user