Compare commits
383 Commits
postgres-q
...
chore/oxli
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01cca7328d | ||
|
|
cfe7fc675d | ||
|
|
229ce04118 | ||
|
|
5cd186d3d4 | ||
|
|
144cc8ab6d | ||
|
|
0322a8b1d9 | ||
|
|
94e9adf625 | ||
|
|
24edf23bc8 | ||
|
|
d784c7737a | ||
|
|
fdc7a154c0 | ||
|
|
e5219f1f31 | ||
|
|
22eef5f3c5 | ||
|
|
5179c5badf | ||
|
|
4c5cd14270 | ||
|
|
38ad15af4c | ||
|
|
7a001d27a5 | ||
|
|
08e2b22db8 | ||
|
|
5dd3a6e13f | ||
|
|
bedcf50196 | ||
|
|
c03e72c1da | ||
|
|
b50d9fa448 | ||
|
|
4b4ee5abf3 | ||
|
|
6499057b4c | ||
|
|
e88bd74fd2 | ||
|
|
16745e77d4 | ||
|
|
c0ed2210b4 | ||
|
|
f9ed314b37 | ||
|
|
160ca28253 | ||
|
|
9380625762 | ||
|
|
ade7cd258d | ||
|
|
63996f4dd3 | ||
|
|
360f68b86b | ||
|
|
bf212bf235 | ||
|
|
b890440f6b | ||
|
|
16f83c0aa9 | ||
|
|
2572413b2b | ||
|
|
14d785cec9 | ||
|
|
242817c49a | ||
|
|
74f79cae69 | ||
|
|
ac0e94c003 | ||
|
|
047c7821a3 | ||
|
|
ccb0e711f0 | ||
|
|
3fb2c3a7bf | ||
|
|
197a1886c3 | ||
|
|
adac30c9a1 | ||
|
|
ae04a62030 | ||
|
|
02246cdd1f | ||
|
|
2d05a5482f | ||
|
|
7b2237b86b | ||
|
|
84024f6cdc | ||
|
|
5574b2dd39 | ||
|
|
e88eb44aba | ||
|
|
75c24f0023 | ||
|
|
e376366b7b | ||
|
|
48e16f0a5a | ||
|
|
e8ba9dd208 | ||
|
|
a932cbae38 | ||
|
|
526206b2a5 | ||
|
|
de2115d11e | ||
|
|
e0ac588ca8 | ||
|
|
0c965ae2ea | ||
|
|
28e05537bd | ||
|
|
acca040524 | ||
|
|
b0a0ae6cd3 | ||
|
|
fb4be6e231 | ||
|
|
ecb16d9907 | ||
|
|
737fedd527 | ||
|
|
b557f3b7f2 | ||
|
|
ce6631f7e0 | ||
|
|
b46e066cc2 | ||
|
|
55f4e93456 | ||
|
|
81423420c8 | ||
|
|
a9bd651692 | ||
|
|
afda7b9525 | ||
|
|
86f64fd0bf | ||
|
|
19013af58f | ||
|
|
e746d27f5e | ||
|
|
90c8fdba96 | ||
|
|
e2ffc9d5a1 | ||
|
|
f64a3003af | ||
|
|
a26d703335 | ||
|
|
5d0ad853f4 | ||
|
|
19ff39c2b9 | ||
|
|
8733d1e554 | ||
|
|
1fb8861e35 | ||
|
|
70b9a4c8f1 | ||
|
|
2da94439c7 | ||
|
|
3d3e5dc547 | ||
|
|
daf1bee7ac | ||
|
|
6b4d5e3beb | ||
|
|
6b9233c71a | ||
|
|
b4a798c39f | ||
|
|
edae9c2d3d | ||
|
|
246d593c9d | ||
|
|
e4322ae0a2 | ||
|
|
e506c7fb19 | ||
|
|
393e8d50b2 | ||
|
|
74438f5bd8 | ||
|
|
e7d7886f44 | ||
|
|
97e86e409a | ||
|
|
72401aa6b1 | ||
|
|
fb94fd3132 | ||
|
|
a02e1f5e7c | ||
|
|
d544053c67 | ||
|
|
df927dd3ce | ||
|
|
d48702f943 | ||
|
|
fa22e865a4 | ||
|
|
b5c3a675b2 | ||
|
|
5589616921 | ||
|
|
a53d033622 | ||
|
|
36506250c4 | ||
|
|
31af44dd2a | ||
|
|
c89ac5b5e5 | ||
|
|
daf1a48b54 | ||
|
|
091a101f39 | ||
|
|
d118b46c3f | ||
|
|
ad3f58bcda | ||
|
|
0711a9006f | ||
|
|
9c18fef9b2 | ||
|
|
d00c872dc1 | ||
|
|
3a5fed99e1 | ||
|
|
e2defbc49a | ||
|
|
f4e4e6628e | ||
|
|
9d04853b34 | ||
|
|
97503d11c5 | ||
|
|
cbf68b006e | ||
|
|
4b9a7b2ce0 | ||
|
|
b854a3dd47 | ||
|
|
aebd68e24e | ||
|
|
0f42babb6b | ||
|
|
dbdb64f6c5 | ||
|
|
2b1b20ab0b | ||
|
|
44d49b9671 | ||
|
|
0e81c20cbb | ||
|
|
1f18a09061 | ||
|
|
0257f1a743 | ||
|
|
6f39a706b2 | ||
|
|
10181defb1 | ||
|
|
8ea40973a7 | ||
|
|
be247395db | ||
|
|
78224961d1 | ||
|
|
b054e9dc2c | ||
|
|
f0d881b4f8 | ||
|
|
9677eb37e1 | ||
|
|
dc23bc4d55 | ||
|
|
e9f8d68f62 | ||
|
|
3f08768854 | ||
|
|
f029910dc7 | ||
|
|
b5593823a2 | ||
|
|
a40d35555f | ||
|
|
0205e89e34 | ||
|
|
a231d7be64 | ||
|
|
219f5b25a4 | ||
|
|
486bb47ddb | ||
|
|
58ae77ec92 | ||
|
|
4794a1a092 | ||
|
|
6abcfaef99 | ||
|
|
f6903696cb | ||
|
|
724a081bb5 | ||
|
|
4e332db2fb | ||
|
|
0712183a18 | ||
|
|
d004c03990 | ||
|
|
fff651f8a5 | ||
|
|
e2720e85bb | ||
|
|
5fdc8c9481 | ||
|
|
a3404cf420 | ||
|
|
5268dc4ee2 | ||
|
|
ef060e97b6 | ||
|
|
a9851df8d1 | ||
|
|
099a1e4210 | ||
|
|
79d760ccd7 | ||
|
|
369d3dfa38 | ||
|
|
93e53f6d74 | ||
|
|
d8f0a69dc8 | ||
|
|
09d9fa9755 | ||
|
|
118dc8cf5a | ||
|
|
9557395991 | ||
|
|
a5d63d6953 | ||
|
|
5ee4a43e74 | ||
|
|
c3aeb6c497 | ||
|
|
d22fb2d5db | ||
|
|
c4df96bd72 | ||
|
|
40e7b58ba4 | ||
|
|
4743a085f1 | ||
|
|
911c877e72 | ||
|
|
806000e671 | ||
|
|
54bafccbf9 | ||
|
|
e61c575b01 | ||
|
|
e12c67742c | ||
|
|
4878c500a5 | ||
|
|
2fa7a40996 | ||
|
|
963dd3210a | ||
|
|
4fdf75311c | ||
|
|
529359de2d | ||
|
|
2d7377a5e9 | ||
|
|
8fcf47e5cb | ||
|
|
c7dc31151d | ||
|
|
065f7c7d5d | ||
|
|
15877ddf1f | ||
|
|
1b8fa51315 | ||
|
|
1f84cbe7e5 | ||
|
|
b194aee754 | ||
|
|
91b961642a | ||
|
|
c61ea483ba | ||
|
|
c278bb0e5b | ||
|
|
bc8e08f5e8 | ||
|
|
0b8fc7b493 | ||
|
|
7bb25a5c8d | ||
|
|
58c1b92816 | ||
|
|
55adc136c8 | ||
|
|
cd288533a1 | ||
|
|
58af574241 | ||
|
|
6954b11be1 | ||
|
|
bc906f7343 | ||
|
|
760b08506a | ||
|
|
6b31e333bb | ||
|
|
493b9b7a54 | ||
|
|
188188a844 | ||
|
|
b2ef8ea7dd | ||
|
|
a6c4bd1555 | ||
|
|
a02fe89ec9 | ||
|
|
98e998e814 | ||
|
|
b83b28cd73 | ||
|
|
9771e48049 | ||
|
|
86db0aafe5 | ||
|
|
12b7a079c1 | ||
|
|
53420b7c02 | ||
|
|
c05aa445d8 | ||
|
|
bdf19ce331 | ||
|
|
895e0eacfe | ||
|
|
e7b60a9278 | ||
|
|
4e2fc9f017 | ||
|
|
d1e6682df0 | ||
|
|
965498d19b | ||
|
|
62f24a79f4 | ||
|
|
495a959879 | ||
|
|
a6a4dfcfd3 | ||
|
|
0d773af6c3 | ||
|
|
fe71894308 | ||
|
|
397808dd1a | ||
|
|
e7edbcdf04 | ||
|
|
59f666b115 | ||
|
|
dc8962f2bc | ||
|
|
00a77c2d6a | ||
|
|
c8641d24f6 | ||
|
|
2431e04a09 | ||
|
|
9e47093501 | ||
|
|
230c286b97 | ||
|
|
14970c5539 | ||
|
|
adb17c4d58 | ||
|
|
56156b97e7 | ||
|
|
c411c1472a | ||
|
|
0bbe70e6a3 | ||
|
|
a65c905621 | ||
|
|
61d784f4e7 | ||
|
|
b63d6cdcd6 | ||
|
|
fa45a26cff | ||
|
|
8f045bc602 | ||
|
|
5353658114 | ||
|
|
21880aec14 | ||
|
|
48d746d9d5 | ||
|
|
8ab5040351 | ||
|
|
1219fd82a0 | ||
|
|
28d8357cc5 | ||
|
|
a9e7d0388b | ||
|
|
86d64f3483 | ||
|
|
c1150fe7e3 | ||
|
|
ecb66fdb2c | ||
|
|
c046651f23 | ||
|
|
6117329057 | ||
|
|
585997d46f | ||
|
|
7146ec99b1 | ||
|
|
b7b0b9b6d8 | ||
|
|
4935f3e0bb | ||
|
|
709a7b70aa | ||
|
|
6a4d21205f | ||
|
|
3a0ddfb92d | ||
|
|
cd03d0c0f2 | ||
|
|
117b263887 | ||
|
|
f357f3324f | ||
|
|
7d95bad5cb | ||
|
|
77b0505006 | ||
|
|
fac1beb7d8 | ||
|
|
3944f5d73b | ||
|
|
4445288758 | ||
|
|
4efc41d5d9 | ||
|
|
c9d45eee86 | ||
|
|
b3b774cfe5 | ||
|
|
15e894b9b5 | ||
|
|
ca06d0aa83 | ||
|
|
0cd51ae9c5 | ||
|
|
68f6111b77 | ||
|
|
668288ca20 | ||
|
|
3fdc1df89c | ||
|
|
989d9dbe51 | ||
|
|
48112d84a3 | ||
|
|
80dfe7a5e9 | ||
|
|
ce90a2ec1a | ||
|
|
dccbe0b3ed | ||
|
|
c0ad12f279 | ||
|
|
9c484b23a9 | ||
|
|
eed014482d | ||
|
|
d271e6a3ae | ||
|
|
60c43081ed | ||
|
|
81d959a27e | ||
|
|
bb775110ef | ||
|
|
7280331b76 | ||
|
|
93ee6ee0a5 | ||
|
|
7544a678ec | ||
|
|
3066c8198c | ||
|
|
eb8dfa283e | ||
|
|
41a127e2ab | ||
|
|
feb475561e | ||
|
|
4c4c67f0d2 | ||
|
|
381b66bf70 | ||
|
|
a89f3ad97c | ||
|
|
c473511133 | ||
|
|
0d66a6b51f | ||
|
|
66400b2e8e | ||
|
|
87cdf0ebd9 | ||
|
|
3f719bd8d7 | ||
|
|
55af925ab3 | ||
|
|
ff63b0fa8f | ||
|
|
f21fe8716c | ||
|
|
6a69dafd31 | ||
|
|
47b1938f17 | ||
|
|
2ffcfe06f3 | ||
|
|
89551edee5 | ||
|
|
cb6c541ae1 | ||
|
|
b1e1362246 | ||
|
|
ccc2b191dd | ||
|
|
bb7010b2bb | ||
|
|
8db666bc38 | ||
|
|
eace0f716d | ||
|
|
96743b6c33 | ||
|
|
ff181cf346 | ||
|
|
0cd5960007 | ||
|
|
698592c1b0 | ||
|
|
f75d853e9a | ||
|
|
3a1e3e82e7 | ||
|
|
0beb3ac4c1 | ||
|
|
894545aeed | ||
|
|
5250269fa4 | ||
|
|
a169fb6a79 | ||
|
|
09ced9a171 | ||
|
|
a6e5e4f625 | ||
|
|
bbd8de177b | ||
|
|
867f6e64f9 | ||
|
|
ec6379b0b2 | ||
|
|
2a80251dc3 | ||
|
|
d33ce13561 | ||
|
|
016d7a6ceb | ||
|
|
8ff25a4f7a | ||
|
|
61a3eba1bd | ||
|
|
7072e48cbe | ||
|
|
ece977d9ca | ||
|
|
2af8095880 | ||
|
|
30822fcd10 | ||
|
|
c578273e7a | ||
|
|
118a3fc9db | ||
|
|
1138f6dcce | ||
|
|
33f3751b72 | ||
|
|
b8509e6411 | ||
|
|
bd43edbcd7 | ||
|
|
c8b4a7e1f1 | ||
|
|
f34f83e164 | ||
|
|
df2cf5d106 | ||
|
|
52975eadb3 | ||
|
|
12610e4a9f | ||
|
|
2b3efa02d8 | ||
|
|
a21a997f21 | ||
|
|
7d61ed7ce4 | ||
|
|
8f7baf8336 | ||
|
|
44923acfd6 | ||
|
|
ab95881ebb | ||
|
|
8801ae5821 | ||
|
|
ea9f11bf39 | ||
|
|
62fc5b3c7d | ||
|
|
15d431ba6a | ||
|
|
5d21ba3166 | ||
|
|
da7a81b752 | ||
|
|
becdc3dcf5 | ||
|
|
84b51e3cbb |
@@ -1,10 +1,10 @@
|
|||||||
ARG BASEIMAGE=mcr.microsoft.com/devcontainers/typescript-node:22@sha256:a20b8a3538313487ac9266875bbf733e544c1aa2091df2bb99ab592a6d4f7399
|
ARG BASEIMAGE=mcr.microsoft.com/devcontainers/typescript-node:22@sha256:7c2e711a4f7b02f32d2da16192d5e05aa7c95279be4ce889cff5df316f251c1d
|
||||||
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.29.1"
|
ENV FLUTTER_VERSION="3.29.3"
|
||||||
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
@@ -9,6 +9,9 @@ mobile/lib/**/*.g.dart linguist-generated=true
|
|||||||
mobile/lib/**/*.drift.dart -diff -merge
|
mobile/lib/**/*.drift.dart -diff -merge
|
||||||
mobile/lib/**/*.drift.dart linguist-generated=true
|
mobile/lib/**/*.drift.dart linguist-generated=true
|
||||||
|
|
||||||
|
mobile/drift_schemas/main/drift_schema_*.json -diff -merge
|
||||||
|
mobile/drift_schemas/main/drift_schema_*.json 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
|
||||||
|
|
||||||
|
|||||||
2
.github/.nvmrc
vendored
2
.github/.nvmrc
vendored
@@ -1 +1 @@
|
|||||||
22.14.0
|
22.16.0
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ body:
|
|||||||
label: I have searched the existing feature requests, both open and closed, 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
|
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: feature
|
id: feature
|
||||||
|
|||||||
1
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
1
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -6,7 +6,6 @@ body:
|
|||||||
label: I have searched the existing issues, both open and closed, to make sure this is not a duplicate report.
|
label: I have searched the existing issues, both open and closed, to make sure this is not a duplicate report.
|
||||||
options:
|
options:
|
||||||
- label: 'Yes'
|
- label: 'Yes'
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
16
.github/workflows/build-mobile.yml
vendored
16
.github/workflows/build-mobile.yml
vendored
@@ -35,12 +35,12 @@ 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
mobile:
|
mobile:
|
||||||
@@ -61,19 +61,19 @@ jobs:
|
|||||||
runs-on: macos-14
|
runs-on: macos-14
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
ref: ${{ inputs.ref || github.sha }}
|
ref: ${{ inputs.ref || github.sha }}
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4
|
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
|
||||||
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@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2
|
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
flutter-version-file: ./mobile/pubspec.yaml
|
||||||
@@ -93,6 +93,10 @@ jobs:
|
|||||||
run: make translation
|
run: make translation
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
|
|
||||||
|
- name: Generate platform APIs
|
||||||
|
run: make pigeon
|
||||||
|
working-directory: ./mobile
|
||||||
|
|
||||||
- name: Build Android App Bundle
|
- name: Build Android App Bundle
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
env:
|
env:
|
||||||
@@ -104,7 +108,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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
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
@@ -19,7 +19,7 @@ jobs:
|
|||||||
actions: write
|
actions: write
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
|
|||||||
12
.github/workflows/cli.yml
vendored
12
.github/workflows/cli.yml
vendored
@@ -29,12 +29,12 @@ jobs:
|
|||||||
working-directory: ./cli
|
working-directory: ./cli
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
# Setup .npmrc file to publish to npm
|
# Setup .npmrc file to publish to npm
|
||||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
@@ -59,7 +59,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.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@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
@@ -85,7 +85,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Generate docker image tags
|
- name: Generate docker image tags
|
||||||
id: metadata
|
id: metadata
|
||||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||||
with:
|
with:
|
||||||
flavor: |
|
flavor: |
|
||||||
latest=false
|
latest=false
|
||||||
@@ -96,7 +96,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@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
|
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0
|
||||||
with:
|
with:
|
||||||
file: cli/Dockerfile
|
file: cli/Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
|
|||||||
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
@@ -44,13 +44,13 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@28deaeda66b76a05916b6923827895f2b14ab387 # v3
|
uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
||||||
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.
|
||||||
@@ -63,7 +63,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# 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)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@28deaeda66b76a05916b6923827895f2b14ab387 # v3
|
uses: github/codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
@@ -76,6 +76,6 @@ jobs:
|
|||||||
# ./location_of_script_within_repo/buildscript.sh
|
# ./location_of_script_within_repo/buildscript.sh
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@28deaeda66b76a05916b6923827895f2b14ab387 # v3
|
uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
||||||
with:
|
with:
|
||||||
category: '/language:${{matrix.language}}'
|
category: '/language:${{matrix.language}}'
|
||||||
|
|||||||
483
.github/workflows/docker.yml
vendored
483
.github/workflows/docker.yml
vendored
@@ -24,11 +24,11 @@ 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
server:
|
server:
|
||||||
@@ -40,6 +40,8 @@ jobs:
|
|||||||
- 'machine-learning/**'
|
- 'machine-learning/**'
|
||||||
workflow:
|
workflow:
|
||||||
- '.github/workflows/docker.yml'
|
- '.github/workflows/docker.yml'
|
||||||
|
- '.github/workflows/multi-runner-build.yml'
|
||||||
|
- '.github/actions/image-build'
|
||||||
|
|
||||||
- name: Check if we should force jobs to run
|
- name: Check if we should force jobs to run
|
||||||
id: should_force
|
id: should_force
|
||||||
@@ -58,7 +60,7 @@ jobs:
|
|||||||
suffix: ['', '-cuda', '-rocm', '-openvino', '-armnn', '-rknn']
|
suffix: ['', '-cuda', '-rocm', '-openvino', '-armnn', '-rknn']
|
||||||
steps:
|
steps:
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
@@ -87,7 +89,7 @@ jobs:
|
|||||||
suffix: ['']
|
suffix: ['']
|
||||||
steps:
|
steps:
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
@@ -103,452 +105,89 @@ jobs:
|
|||||||
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}"
|
||||||
|
|
||||||
build_and_push_ml:
|
machine-learning:
|
||||||
name: Build and Push ML
|
name: Build and Push ML
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' }}
|
||||||
runs-on: ${{ matrix.runner }}
|
|
||||||
env:
|
|
||||||
image: immich-machine-learning
|
|
||||||
context: machine-learning
|
|
||||||
file: machine-learning/Dockerfile
|
|
||||||
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-machine-learning
|
|
||||||
strategy:
|
strategy:
|
||||||
# Prevent a failure in one image from stopping the other builds
|
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: ubuntu-latest
|
|
||||||
device: cpu
|
|
||||||
|
|
||||||
- platform: linux/arm64
|
|
||||||
runner: ubuntu-24.04-arm
|
|
||||||
device: cpu
|
|
||||||
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: ubuntu-latest
|
|
||||||
device: cuda
|
|
||||||
suffix: -cuda
|
|
||||||
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: mich
|
|
||||||
device: rocm
|
|
||||||
suffix: -rocm
|
|
||||||
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: ubuntu-latest
|
|
||||||
device: openvino
|
|
||||||
suffix: -openvino
|
|
||||||
|
|
||||||
- platform: linux/arm64
|
|
||||||
runner: ubuntu-24.04-arm
|
|
||||||
device: armnn
|
|
||||||
suffix: -armnn
|
|
||||||
|
|
||||||
- platform: linux/arm64
|
|
||||||
runner: ubuntu-24.04-arm
|
|
||||||
device: rknn
|
|
||||||
suffix: -rknn
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Prepare
|
|
||||||
run: |
|
|
||||||
platform=${{ matrix.platform }}
|
|
||||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Generate cache key suffix
|
|
||||||
env:
|
|
||||||
REF: ${{ github.ref_name }}
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
|
||||||
echo "CACHE_KEY_SUFFIX=pr-${{ github.event.number }}" >> $GITHUB_ENV
|
|
||||||
else
|
|
||||||
SUFFIX=$(echo "${REF}" | sed 's/[^a-zA-Z0-9]/-/g')
|
|
||||||
echo "CACHE_KEY_SUFFIX=${SUFFIX}" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Generate cache target
|
|
||||||
id: cache-target
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
|
|
||||||
# Essentially just ignore the cache output (forks can't write to registry cache)
|
|
||||||
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "cache-to=type=registry,ref=${GHCR_REPO}-build-cache:${PLATFORM_PAIR}-${{ matrix.device }}-${CACHE_KEY_SUFFIX},mode=max,compression=zstd" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Generate docker image tags
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
|
||||||
env:
|
|
||||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
|
||||||
|
|
||||||
- name: Build and push image
|
|
||||||
id: build
|
|
||||||
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
|
|
||||||
with:
|
|
||||||
context: ${{ env.context }}
|
|
||||||
file: ${{ env.file }}
|
|
||||||
platforms: ${{ matrix.platforms }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
|
||||||
cache-from: |
|
|
||||||
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-${{ env.CACHE_KEY_SUFFIX }}
|
|
||||||
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-main
|
|
||||||
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=${{ !github.event.pull_request.head.repo.fork }}
|
|
||||||
build-args: |
|
|
||||||
DEVICE=${{ matrix.device }}
|
|
||||||
BUILD_ID=${{ github.run_id }}
|
|
||||||
BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.metadata.outputs.tags }}
|
|
||||||
BUILD_SOURCE_REF=${{ github.ref_name }}
|
|
||||||
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
|
||||||
|
|
||||||
- name: Export digest
|
|
||||||
run: | # zizmor: ignore[template-injection]
|
|
||||||
mkdir -p ${{ runner.temp }}/digests
|
|
||||||
digest="${{ steps.build.outputs.digest }}"
|
|
||||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
|
||||||
|
|
||||||
- name: Upload digest
|
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
||||||
with:
|
|
||||||
name: ml-digests-${{ matrix.device }}-${{ env.PLATFORM_PAIR }}
|
|
||||||
path: ${{ runner.temp }}/digests/*
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
merge_ml:
|
|
||||||
name: Merge & Push ML
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
actions: read
|
|
||||||
packages: write
|
|
||||||
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' && !github.event.pull_request.head.repo.fork }}
|
|
||||||
env:
|
|
||||||
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-machine-learning
|
|
||||||
DOCKER_REPO: altran1502/immich-machine-learning
|
|
||||||
strategy:
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- device: cpu
|
- device: cpu
|
||||||
|
tag-suffix: ''
|
||||||
- device: cuda
|
- device: cuda
|
||||||
suffix: -cuda
|
tag-suffix: '-cuda'
|
||||||
- device: rocm
|
platforms: linux/amd64
|
||||||
suffix: -rocm
|
|
||||||
- device: openvino
|
- device: openvino
|
||||||
suffix: -openvino
|
tag-suffix: '-openvino'
|
||||||
|
platforms: linux/amd64
|
||||||
- device: armnn
|
- device: armnn
|
||||||
suffix: -armnn
|
tag-suffix: '-armnn'
|
||||||
|
platforms: linux/arm64
|
||||||
- device: rknn
|
- device: rknn
|
||||||
suffix: -rknn
|
tag-suffix: '-rknn'
|
||||||
needs:
|
platforms: linux/arm64
|
||||||
- build_and_push_ml
|
- device: rocm
|
||||||
steps:
|
tag-suffix: '-rocm'
|
||||||
- name: Download digests
|
platforms: linux/amd64
|
||||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
runner-mapping: '{"linux/amd64": "mich"}'
|
||||||
with:
|
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@094bfb927b8cd75b343abaac27b3241be0fccfe9 # multi-runner-build-workflow-0.1.0
|
||||||
path: ${{ runner.temp }}/digests
|
|
||||||
pattern: ml-digests-${{ matrix.device }}-*
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
if: ${{ github.event_name == 'release' }}
|
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Login to GHCR
|
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
|
||||||
|
|
||||||
- name: Generate docker image tags
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
|
||||||
env:
|
|
||||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
|
||||||
with:
|
|
||||||
flavor: |
|
|
||||||
# Disable latest tag
|
|
||||||
latest=false
|
|
||||||
suffix=${{ matrix.suffix }}
|
|
||||||
images: |
|
|
||||||
name=${{ env.GHCR_REPO }}
|
|
||||||
name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }}
|
|
||||||
tags: |
|
|
||||||
# Tag with branch name
|
|
||||||
type=ref,event=branch
|
|
||||||
# Tag with pr-number
|
|
||||||
type=ref,event=pr
|
|
||||||
# Tag with long commit sha hash
|
|
||||||
type=sha,format=long,prefix=commit-
|
|
||||||
# Tag with git tag on release
|
|
||||||
type=ref,event=tag
|
|
||||||
type=raw,value=release,enable=${{ github.event_name == 'release' }}
|
|
||||||
|
|
||||||
- name: Create manifest list and push
|
|
||||||
working-directory: ${{ runner.temp }}/digests
|
|
||||||
run: |
|
|
||||||
# Process annotations
|
|
||||||
declare -a ANNOTATIONS=()
|
|
||||||
if [[ -n "$DOCKER_METADATA_OUTPUT_JSON" ]]; then
|
|
||||||
while IFS= read -r annotation; do
|
|
||||||
# Extract key and value by removing the manifest: prefix
|
|
||||||
if [[ "$annotation" =~ ^manifest:(.+)=(.+)$ ]]; then
|
|
||||||
key="${BASH_REMATCH[1]}"
|
|
||||||
value="${BASH_REMATCH[2]}"
|
|
||||||
# Use array to properly handle arguments with spaces
|
|
||||||
ANNOTATIONS+=(--annotation "index:$key=$value")
|
|
||||||
fi
|
|
||||||
done < <(jq -r '.annotations[]' <<< "$DOCKER_METADATA_OUTPUT_JSON")
|
|
||||||
fi
|
|
||||||
|
|
||||||
TAGS=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
|
||||||
SOURCE_ARGS=$(printf "${GHCR_REPO}@sha256:%s " *)
|
|
||||||
|
|
||||||
docker buildx imagetools create $TAGS "${ANNOTATIONS[@]}" $SOURCE_ARGS
|
|
||||||
|
|
||||||
build_and_push_server:
|
|
||||||
name: Build and Push Server
|
|
||||||
runs-on: ${{ matrix.runner }}
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
needs: pre-job
|
|
||||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
|
||||||
env:
|
|
||||||
image: immich-server
|
|
||||||
context: .
|
|
||||||
file: server/Dockerfile
|
|
||||||
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-server
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: ubuntu-latest
|
|
||||||
- platform: linux/arm64
|
|
||||||
runner: ubuntu-24.04-arm
|
|
||||||
steps:
|
|
||||||
- name: Prepare
|
|
||||||
run: |
|
|
||||||
platform=${{ matrix.platform }}
|
|
||||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Generate cache key suffix
|
|
||||||
env:
|
|
||||||
REF: ${{ github.ref_name }}
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
|
||||||
echo "CACHE_KEY_SUFFIX=pr-${{ github.event.number }}" >> $GITHUB_ENV
|
|
||||||
else
|
|
||||||
SUFFIX=$(echo "${REF}" | sed 's/[^a-zA-Z0-9]/-/g')
|
|
||||||
echo "CACHE_KEY_SUFFIX=${SUFFIX}" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Generate cache target
|
|
||||||
id: cache-target
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
|
|
||||||
# Essentially just ignore the cache output (forks can't write to registry cache)
|
|
||||||
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "cache-to=type=registry,ref=${GHCR_REPO}-build-cache:${PLATFORM_PAIR}-${CACHE_KEY_SUFFIX},mode=max,compression=zstd" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Generate docker image tags
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
|
||||||
env:
|
|
||||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
|
||||||
|
|
||||||
- name: Build and push image
|
|
||||||
id: build
|
|
||||||
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
|
|
||||||
with:
|
|
||||||
context: ${{ env.context }}
|
|
||||||
file: ${{ env.file }}
|
|
||||||
platforms: ${{ matrix.platform }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
|
||||||
cache-from: |
|
|
||||||
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ env.CACHE_KEY_SUFFIX }}
|
|
||||||
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-main
|
|
||||||
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=${{ !github.event.pull_request.head.repo.fork }}
|
|
||||||
build-args: |
|
|
||||||
DEVICE=cpu
|
|
||||||
BUILD_ID=${{ github.run_id }}
|
|
||||||
BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.metadata.outputs.tags }}
|
|
||||||
BUILD_SOURCE_REF=${{ github.ref_name }}
|
|
||||||
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
|
||||||
|
|
||||||
- name: Export digest
|
|
||||||
run: | # zizmor: ignore[template-injection]
|
|
||||||
mkdir -p ${{ runner.temp }}/digests
|
|
||||||
digest="${{ steps.build.outputs.digest }}"
|
|
||||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
|
||||||
|
|
||||||
- name: Upload digest
|
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
||||||
with:
|
|
||||||
name: server-digests-${{ env.PLATFORM_PAIR }}
|
|
||||||
path: ${{ runner.temp }}/digests/*
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
merge_server:
|
|
||||||
name: Merge & Push Server
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
actions: read
|
actions: read
|
||||||
packages: write
|
packages: write
|
||||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' && !github.event.pull_request.head.repo.fork }}
|
secrets:
|
||||||
env:
|
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-server
|
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
DOCKER_REPO: altran1502/immich-server
|
with:
|
||||||
needs:
|
image: immich-machine-learning
|
||||||
- build_and_push_server
|
context: machine-learning
|
||||||
steps:
|
dockerfile: machine-learning/Dockerfile
|
||||||
- name: Download digests
|
platforms: ${{ matrix.platforms }}
|
||||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
runner-mapping: ${{ matrix.runner-mapping }}
|
||||||
with:
|
tag-suffix: ${{ matrix.tag-suffix }}
|
||||||
path: ${{ runner.temp }}/digests
|
dockerhub-push: ${{ github.event_name == 'release' }}
|
||||||
pattern: server-digests-*
|
build-args: |
|
||||||
merge-multiple: true
|
DEVICE=${{ matrix.device }}
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
server:
|
||||||
if: ${{ github.event_name == 'release' }}
|
name: Build and Push Server
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
needs: pre-job
|
||||||
with:
|
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@094bfb927b8cd75b343abaac27b3241be0fccfe9 # multi-runner-build-workflow-0.1.0
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
permissions:
|
||||||
|
contents: read
|
||||||
- name: Login to GHCR
|
actions: read
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
packages: write
|
||||||
with:
|
secrets:
|
||||||
registry: ghcr.io
|
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
username: ${{ github.repository_owner }}
|
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
with:
|
||||||
|
image: immich-server
|
||||||
- name: Set up Docker Buildx
|
context: .
|
||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
dockerfile: server/Dockerfile
|
||||||
|
dockerhub-push: ${{ github.event_name == 'release' }}
|
||||||
- name: Generate docker image tags
|
build-args: |
|
||||||
id: meta
|
DEVICE=cpu
|
||||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
|
||||||
env:
|
|
||||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
|
||||||
with:
|
|
||||||
flavor: |
|
|
||||||
# Disable latest tag
|
|
||||||
latest=false
|
|
||||||
suffix=${{ matrix.suffix }}
|
|
||||||
images: |
|
|
||||||
name=${{ env.GHCR_REPO }}
|
|
||||||
name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }}
|
|
||||||
tags: |
|
|
||||||
# Tag with branch name
|
|
||||||
type=ref,event=branch
|
|
||||||
# Tag with pr-number
|
|
||||||
type=ref,event=pr
|
|
||||||
# Tag with long commit sha hash
|
|
||||||
type=sha,format=long,prefix=commit-
|
|
||||||
# Tag with git tag on release
|
|
||||||
type=ref,event=tag
|
|
||||||
type=raw,value=release,enable=${{ github.event_name == 'release' }}
|
|
||||||
|
|
||||||
- name: Create manifest list and push
|
|
||||||
working-directory: ${{ runner.temp }}/digests
|
|
||||||
run: |
|
|
||||||
# Process annotations
|
|
||||||
declare -a ANNOTATIONS=()
|
|
||||||
if [[ -n "$DOCKER_METADATA_OUTPUT_JSON" ]]; then
|
|
||||||
while IFS= read -r annotation; do
|
|
||||||
# Extract key and value by removing the manifest: prefix
|
|
||||||
if [[ "$annotation" =~ ^manifest:(.+)=(.+)$ ]]; then
|
|
||||||
key="${BASH_REMATCH[1]}"
|
|
||||||
value="${BASH_REMATCH[2]}"
|
|
||||||
# Use array to properly handle arguments with spaces
|
|
||||||
ANNOTATIONS+=(--annotation "index:$key=$value")
|
|
||||||
fi
|
|
||||||
done < <(jq -r '.annotations[]' <<< "$DOCKER_METADATA_OUTPUT_JSON")
|
|
||||||
fi
|
|
||||||
|
|
||||||
TAGS=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
|
||||||
SOURCE_ARGS=$(printf "${GHCR_REPO}@sha256:%s " *)
|
|
||||||
|
|
||||||
docker buildx imagetools create $TAGS "${ANNOTATIONS[@]}" $SOURCE_ARGS
|
|
||||||
|
|
||||||
success-check-server:
|
success-check-server:
|
||||||
name: Docker Build & Push Server Success
|
name: Docker Build & Push Server Success
|
||||||
needs: [merge_server, retag_server]
|
needs: [server, retag_server]
|
||||||
permissions: {}
|
permissions: {}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- name: Any jobs failed?
|
- uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
|
||||||
if: ${{ contains(needs.*.result, 'failure') }}
|
with:
|
||||||
run: exit 1
|
needs: ${{ toJSON(needs) }}
|
||||||
- name: All jobs passed or skipped
|
|
||||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
|
||||||
# zizmor: ignore[template-injection]
|
|
||||||
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
|
||||||
|
|
||||||
success-check-ml:
|
success-check-ml:
|
||||||
name: Docker Build & Push ML Success
|
name: Docker Build & Push ML Success
|
||||||
needs: [merge_ml, retag_ml]
|
needs: [machine-learning, retag_ml]
|
||||||
permissions: {}
|
permissions: {}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- name: Any jobs failed?
|
- uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
|
||||||
if: ${{ contains(needs.*.result, 'failure') }}
|
with:
|
||||||
run: exit 1
|
needs: ${{ toJSON(needs) }}
|
||||||
- name: All jobs passed or skipped
|
|
||||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
|
||||||
# zizmor: ignore[template-injection]
|
|
||||||
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
|
||||||
|
|||||||
10
.github/workflows/docs-build.yml
vendored
10
.github/workflows/docs-build.yml
vendored
@@ -21,11 +21,11 @@ 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
docs:
|
docs:
|
||||||
@@ -49,12 +49,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './docs/.nvmrc'
|
node-version-file: './docs/.nvmrc'
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ jobs:
|
|||||||
run: npm run build
|
run: npm run build
|
||||||
|
|
||||||
- name: Upload build output
|
- name: Upload build output
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: docs-build-output
|
name: docs-build-output
|
||||||
path: docs/build/
|
path: docs/build/
|
||||||
|
|||||||
21
.github/workflows/docs-deploy.yml
vendored
21
.github/workflows/docs-deploy.yml
vendored
@@ -20,7 +20,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@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||||
@@ -38,7 +38,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@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||||
env:
|
env:
|
||||||
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
|
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
|
||||||
with:
|
with:
|
||||||
@@ -108,13 +108,13 @@ 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Load parameters
|
- name: Load parameters
|
||||||
id: parameters
|
id: parameters
|
||||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||||
env:
|
env:
|
||||||
PARAM_JSON: ${{ needs.checks.outputs.parameters }}
|
PARAM_JSON: ${{ needs.checks.outputs.parameters }}
|
||||||
with:
|
with:
|
||||||
@@ -125,7 +125,7 @@ jobs:
|
|||||||
core.setOutput("shouldDeploy", parameters.shouldDeploy);
|
core.setOutput("shouldDeploy", parameters.shouldDeploy);
|
||||||
|
|
||||||
- name: Download artifact
|
- name: Download artifact
|
||||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||||
env:
|
env:
|
||||||
ARTIFACT_JSON: ${{ needs.checks.outputs.artifact }}
|
ARTIFACT_JSON: ${{ needs.checks.outputs.artifact }}
|
||||||
with:
|
with:
|
||||||
@@ -150,7 +150,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@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2
|
uses: gruntwork-io/terragrunt-action@aee21a7df999be8b471c2a8564c6cd853cb674e1 # v2.1.8
|
||||||
with:
|
with:
|
||||||
tg_version: '0.58.12'
|
tg_version: '0.58.12'
|
||||||
tofu_version: '1.7.1'
|
tofu_version: '1.7.1'
|
||||||
@@ -165,7 +165,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@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2
|
uses: gruntwork-io/terragrunt-action@aee21a7df999be8b471c2a8564c6cd853cb674e1 # v2.1.8
|
||||||
with:
|
with:
|
||||||
tg_version: '0.58.12'
|
tg_version: '0.58.12'
|
||||||
tofu_version: '1.7.1'
|
tofu_version: '1.7.1'
|
||||||
@@ -181,7 +181,8 @@ jobs:
|
|||||||
echo "output=$CLEANED" >> $GITHUB_OUTPUT
|
echo "output=$CLEANED" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Publish to Cloudflare Pages
|
- name: Publish to Cloudflare Pages
|
||||||
uses: cloudflare/pages-action@f0a1cd58cd66095dee69bfa18fa5efd1dde93bca # v1
|
# TODO: Action is deprecated
|
||||||
|
uses: cloudflare/pages-action@f0a1cd58cd66095dee69bfa18fa5efd1dde93bca # v1.5.0
|
||||||
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 }}
|
||||||
@@ -198,7 +199,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@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2
|
uses: gruntwork-io/terragrunt-action@aee21a7df999be8b471c2a8564c6cd853cb674e1 # v2.1.8
|
||||||
with:
|
with:
|
||||||
tg_version: '0.58.12'
|
tg_version: '0.58.12'
|
||||||
tofu_version: '1.7.1'
|
tofu_version: '1.7.1'
|
||||||
@@ -206,7 +207,7 @@ jobs:
|
|||||||
tg_command: 'apply'
|
tg_command: 'apply'
|
||||||
|
|
||||||
- name: Comment
|
- name: Comment
|
||||||
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3
|
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3.2.0
|
||||||
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 }}
|
||||||
|
|||||||
6
.github/workflows/docs-destroy.yml
vendored
6
.github/workflows/docs-destroy.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
@@ -25,7 +25,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@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2
|
uses: gruntwork-io/terragrunt-action@aee21a7df999be8b471c2a8564c6cd853cb674e1 # v2.1.8
|
||||||
with:
|
with:
|
||||||
tg_version: '0.58.12'
|
tg_version: '0.58.12'
|
||||||
tofu_version: '1.7.1'
|
tofu_version: '1.7.1'
|
||||||
@@ -33,7 +33,7 @@ jobs:
|
|||||||
tg_command: 'destroy -refresh=false'
|
tg_command: 'destroy -refresh=false'
|
||||||
|
|
||||||
- name: Comment
|
- name: Comment
|
||||||
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3
|
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3.2.0
|
||||||
with:
|
with:
|
||||||
number: ${{ github.event.number }}
|
number: ${{ github.event.number }}
|
||||||
delete: true
|
delete: true
|
||||||
|
|||||||
10
.github/workflows/fix-format.yml
vendored
10
.github/workflows/fix-format.yml
vendored
@@ -16,20 +16,20 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2
|
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
|
||||||
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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
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 }}
|
||||||
persist-credentials: true
|
persist-credentials: true
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
|
||||||
@@ -37,13 +37,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@a94899bca583c204427a224a7af87c02f9b325d5 # v9
|
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4
|
||||||
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@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
|
|||||||
2
.github/workflows/pr-label-validation.yml
vendored
2
.github/workflows/pr-label-validation.yml
vendored
@@ -14,7 +14,7 @@ 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@388fd6af37b34cdfe5a23b37060e763217e58b03 # v5
|
uses: mheap/github-action-required-labels@fb29a14a076b0f74099f6198f77750e8fc236016 # v5.5.0
|
||||||
with:
|
with:
|
||||||
mode: exactly
|
mode: exactly
|
||||||
count: 1
|
count: 1
|
||||||
|
|||||||
2
.github/workflows/pr-labeler.yml
vendored
2
.github/workflows/pr-labeler.yml
vendored
@@ -11,4 +11,4 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5
|
- uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0
|
||||||
|
|||||||
16
.github/workflows/prepare-release.yml
vendored
16
.github/workflows/prepare-release.yml
vendored
@@ -32,19 +32,19 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2
|
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
|
||||||
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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
persist-credentials: true
|
persist-credentials: true
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
|
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||||
|
|
||||||
- name: Bump version
|
- name: Bump version
|
||||||
env:
|
env:
|
||||||
@@ -54,7 +54,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Commit and tag
|
- name: Commit and tag
|
||||||
id: push-tag
|
id: push-tag
|
||||||
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9
|
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4
|
||||||
with:
|
with:
|
||||||
default_author: github_actions
|
default_author: github_actions
|
||||||
message: 'chore: version ${{ env.IMMICH_VERSION }}'
|
message: 'chore: version ${{ env.IMMICH_VERSION }}'
|
||||||
@@ -83,24 +83,24 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2
|
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
|
||||||
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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Download APK
|
- name: Download APK
|
||||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||||
with:
|
with:
|
||||||
name: release-apk-signed
|
name: release-apk-signed
|
||||||
|
|
||||||
- name: Create draft release
|
- name: Create draft release
|
||||||
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2
|
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
tag_name: ${{ env.IMMICH_VERSION }}
|
tag_name: ${{ env.IMMICH_VERSION }}
|
||||||
|
|||||||
4
.github/workflows/preview-label.yaml
vendored
4
.github/workflows/preview-label.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2
|
- uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2
|
||||||
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/'
|
||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
github.rest.issues.removeLabel({
|
github.rest.issues.removeLabel({
|
||||||
|
|||||||
4
.github/workflows/sdk.yml
vendored
4
.github/workflows/sdk.yml
vendored
@@ -16,12 +16,12 @@ jobs:
|
|||||||
run:
|
run:
|
||||||
working-directory: ./open-api/typescript-sdk
|
working-directory: ./open-api/typescript-sdk
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
# Setup .npmrc file to publish to npm
|
# Setup .npmrc file to publish to npm
|
||||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
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'
|
||||||
|
|||||||
34
.github/workflows/static_analysis.yml
vendored
34
.github/workflows/static_analysis.yml
vendored
@@ -20,11 +20,11 @@ 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
mobile:
|
mobile:
|
||||||
@@ -44,12 +44,12 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2
|
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
flutter-version-file: ./mobile/pubspec.yaml
|
||||||
@@ -58,16 +58,28 @@ jobs:
|
|||||||
run: dart pub get
|
run: dart pub get
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
|
|
||||||
|
- name: Install DCM
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
wget -qO- https://dcm.dev/pgp-key.public | sudo gpg --dearmor -o /usr/share/keyrings/dcm.gpg
|
||||||
|
echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install dcm
|
||||||
|
|
||||||
- name: Generate translation file
|
- name: Generate translation file
|
||||||
run: make translation; dart format lib/generated/codegen_loader.g.dart
|
run: make translation
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
|
|
||||||
- name: Run Build Runner
|
- name: Run Build Runner
|
||||||
run: make build
|
run: make build
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
|
|
||||||
|
- name: Generate platform API
|
||||||
|
run: make pigeon
|
||||||
|
working-directory: ./mobile
|
||||||
|
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||||
id: verify-changed-files
|
id: verify-changed-files
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
@@ -96,6 +108,10 @@ jobs:
|
|||||||
run: dart run custom_lint
|
run: dart run custom_lint
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
|
|
||||||
|
- name: Run DCM
|
||||||
|
run: dcm analyze lib
|
||||||
|
working-directory: ./mobile
|
||||||
|
|
||||||
zizmor:
|
zizmor:
|
||||||
name: zizmor
|
name: zizmor
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -105,12 +121,12 @@ jobs:
|
|||||||
actions: read
|
actions: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Install the latest version of uv
|
- name: Install the latest version of uv
|
||||||
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
|
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||||
|
|
||||||
- name: Run zizmor 🌈
|
- name: Run zizmor 🌈
|
||||||
run: uvx zizmor --format=sarif . > results.sarif
|
run: uvx zizmor --format=sarif . > results.sarif
|
||||||
@@ -118,7 +134,7 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Upload SARIF file
|
- name: Upload SARIF file
|
||||||
uses: github/codeql-action/upload-sarif@28deaeda66b76a05916b6923827895f2b14ab387 # v3
|
uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
||||||
with:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
category: zizmor
|
category: zizmor
|
||||||
|
|||||||
140
.github/workflows/test.yml
vendored
140
.github/workflows/test.yml
vendored
@@ -17,6 +17,7 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
outputs:
|
outputs:
|
||||||
|
should_run_i18n: ${{ steps.found_paths.outputs.i18n == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
should_run_web: ${{ steps.found_paths.outputs.web == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
should_run_web: ${{ steps.found_paths.outputs.web == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
should_run_server: ${{ steps.found_paths.outputs.server == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
should_run_server: ${{ steps.found_paths.outputs.server == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
should_run_cli: ${{ steps.found_paths.outputs.cli == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
should_run_cli: ${{ steps.found_paths.outputs.cli == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
@@ -28,14 +29,16 @@ jobs:
|
|||||||
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
|
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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
|
i18n:
|
||||||
|
- 'i18n/**'
|
||||||
web:
|
web:
|
||||||
- 'web/**'
|
- 'web/**'
|
||||||
- 'i18n/**'
|
- 'i18n/**'
|
||||||
@@ -73,12 +76,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
|
||||||
@@ -114,12 +117,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
|
|
||||||
@@ -159,12 +162,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
|
|
||||||
@@ -197,12 +200,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './web/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
|
|
||||||
@@ -238,12 +241,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './web/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
|
|
||||||
@@ -262,6 +265,46 @@ jobs:
|
|||||||
run: npm run test:cov
|
run: npm run test:cov
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
|
i18n-tests:
|
||||||
|
name: Test i18n
|
||||||
|
needs: pre-job
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run_i18n == 'true' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
|
with:
|
||||||
|
node-version-file: './web/.nvmrc'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm --prefix=web ci
|
||||||
|
|
||||||
|
- name: Format
|
||||||
|
run: npm --prefix=web run format:i18n
|
||||||
|
|
||||||
|
- name: Find file changes
|
||||||
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||||
|
id: verify-changed-files
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
i18n/**
|
||||||
|
|
||||||
|
- name: Verify files have not changed
|
||||||
|
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
||||||
|
env:
|
||||||
|
CHANGED_FILES: ${{ steps.verify-changed-files.outputs.changed_files }}
|
||||||
|
run: |
|
||||||
|
echo "ERROR: i18n files not up to date!"
|
||||||
|
echo "Changed files: ${CHANGED_FILES}"
|
||||||
|
exit 1
|
||||||
|
|
||||||
e2e-tests-lint:
|
e2e-tests-lint:
|
||||||
name: End-to-End Lint
|
name: End-to-End Lint
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
@@ -275,12 +318,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
|
||||||
@@ -318,12 +361,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
|
||||||
@@ -346,17 +389,17 @@ jobs:
|
|||||||
working-directory: ./e2e
|
working-directory: ./e2e
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
runner: [mich, ubuntu-24.04-arm]
|
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
|
||||||
@@ -394,17 +437,17 @@ jobs:
|
|||||||
working-directory: ./e2e
|
working-directory: ./e2e
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
runner: [mich, ubuntu-24.04-arm]
|
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
|
||||||
@@ -436,13 +479,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- name: Any jobs failed?
|
- uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
|
||||||
if: ${{ contains(needs.*.result, 'failure') }}
|
with:
|
||||||
run: exit 1
|
needs: ${{ toJSON(needs) }}
|
||||||
- name: All jobs passed or skipped
|
|
||||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
|
||||||
# zizmor: ignore[template-injection]
|
|
||||||
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
|
||||||
|
|
||||||
mobile-unit-tests:
|
mobile-unit-tests:
|
||||||
name: Unit Test Mobile
|
name: Unit Test Mobile
|
||||||
@@ -452,15 +491,20 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2
|
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
flutter-version-file: ./mobile/pubspec.yaml
|
||||||
|
|
||||||
|
- name: Generate translation file
|
||||||
|
run: make translation
|
||||||
|
working-directory: ./mobile
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
run: flutter test -j 1
|
run: flutter test -j 1
|
||||||
@@ -476,13 +520,13 @@ jobs:
|
|||||||
run:
|
run:
|
||||||
working-directory: ./machine-learning
|
working-directory: ./machine-learning
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
|
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||||
# TODO: add caching when supported (https://github.com/actions/setup-python/pull/818)
|
# TODO: add caching when supported (https://github.com/actions/setup-python/pull/818)
|
||||||
# with:
|
# with:
|
||||||
# python-version: 3.11
|
# python-version: 3.11
|
||||||
@@ -516,12 +560,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './.github/.nvmrc'
|
node-version-file: './.github/.nvmrc'
|
||||||
|
|
||||||
@@ -538,7 +582,7 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
@@ -557,12 +601,12 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
|
||||||
@@ -576,7 +620,7 @@ jobs:
|
|||||||
run: make open-api
|
run: make open-api
|
||||||
|
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||||
id: verify-changed-files
|
id: verify-changed-files
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
@@ -593,14 +637,14 @@ jobs:
|
|||||||
echo "Changed files: ${CHANGED_FILES}"
|
echo "Changed files: ${CHANGED_FILES}"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
generated-typeorm-migrations-up-to-date:
|
sql-schema-up-to-date:
|
||||||
name: TypeORM Checks
|
name: SQL Schema Checks
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1
|
||||||
env:
|
env:
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
@@ -618,12 +662,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
|
||||||
@@ -641,10 +685,10 @@ jobs:
|
|||||||
|
|
||||||
- name: Generate new migrations
|
- name: Generate new migrations
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: npm run migrations:generate TestMigration
|
run: npm run migrations:generate src/TestMigration
|
||||||
|
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||||
id: verify-changed-files
|
id: verify-changed-files
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
@@ -665,7 +709,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@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||||
id: verify-changed-sql-files
|
id: verify-changed-sql-files
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
|
|||||||
16
.github/workflows/weblate-lock.yml
vendored
16
.github/workflows/weblate-lock.yml
vendored
@@ -15,11 +15,11 @@ jobs:
|
|||||||
should_run: ${{ steps.found_paths.outputs.i18n == 'true' && github.head_ref != 'chore/translations'}}
|
should_run: ${{ steps.found_paths.outputs.i18n == 'true' && github.head_ref != 'chore/translations'}}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
i18n:
|
i18n:
|
||||||
@@ -38,7 +38,7 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
- name: Find Pull Request
|
- name: Find Pull Request
|
||||||
uses: juliangruber/find-pull-request-action@48b6133aa6c826f267ebd33aa2d29470f9d9e7d0 # v1
|
uses: juliangruber/find-pull-request-action@48b6133aa6c826f267ebd33aa2d29470f9d9e7d0 # v1.9.0
|
||||||
id: find-pr
|
id: find-pr
|
||||||
with:
|
with:
|
||||||
branch: chore/translations
|
branch: chore/translations
|
||||||
@@ -52,10 +52,6 @@ jobs:
|
|||||||
permissions: {}
|
permissions: {}
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- name: Any jobs failed?
|
- uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
|
||||||
if: ${{ contains(needs.*.result, 'failure') }}
|
with:
|
||||||
run: exit 1
|
needs: ${{ toJSON(needs) }}
|
||||||
- name: All jobs passed or skipped
|
|
||||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
|
||||||
# zizmor: ignore[template-injection]
|
|
||||||
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,6 +3,7 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
.vscode/*
|
.vscode/*
|
||||||
!.vscode/launch.json
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
docker/upload
|
docker/upload
|
||||||
|
|||||||
10
.vscode/extensions.json
vendored
Normal file
10
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"svelte.svelte-vscode",
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"dart-code.flutter",
|
||||||
|
"dart-code.dart-code",
|
||||||
|
"dcmdev.dcm-vscode-extension"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1 +1 @@
|
|||||||
22.14.0
|
22.16.0
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:22.15.0-alpine3.20@sha256:686b8892b69879ef5bfd6047589666933508f9a5451c67320df3070ba0e9807b AS core
|
FROM node:22.16.0-alpine3.20@sha256:2289fb1fba0f4633b08ec47b94a89c7e20b829fc5679f9b7b298eaa2f1ed8b7e 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 ./
|
||||||
|
|||||||
576
cli/package-lock.json
generated
576
cli/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.65",
|
"version": "2.2.68",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.65",
|
"version": "2.2.68",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^4.0.3",
|
"chokidar": "^4.0.3",
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/micromatch": "^4.0.9",
|
"@types/micromatch": "^4.0.9",
|
||||||
"@types/mock-fs": "^4.13.1",
|
"@types/mock-fs": "^4.13.1",
|
||||||
"@types/node": "^22.14.1",
|
"@types/node": "^22.15.29",
|
||||||
"@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",
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
"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": "^57.0.0",
|
"eslint-plugin-unicorn": "^59.0.0",
|
||||||
"globals": "^16.0.0",
|
"globals": "^16.0.0",
|
||||||
"mock-fs": "^5.2.0",
|
"mock-fs": "^5.2.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
@@ -54,14 +54,14 @@
|
|||||||
},
|
},
|
||||||
"../open-api/typescript-sdk": {
|
"../open-api/typescript-sdk": {
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.132.3",
|
"version": "1.134.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oazapfts/runtime": "^1.0.2"
|
"@oazapfts/runtime": "^1.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.14.1",
|
"@types/node": "^22.15.29",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -79,21 +79,6 @@
|
|||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
|
||||||
"version": "7.26.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
|
|
||||||
"integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/helper-validator-identifier": "^7.25.9",
|
|
||||||
"js-tokens": "^4.0.0",
|
|
||||||
"picocolors": "^1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/helper-string-parser": {
|
"node_modules/@babel/helper-string-parser": {
|
||||||
"version": "7.25.9",
|
"version": "7.25.9",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
|
||||||
@@ -580,9 +565,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint-community/eslint-utils": {
|
"node_modules/@eslint-community/eslint-utils": {
|
||||||
"version": "4.6.0",
|
"version": "4.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
|
||||||
"integrity": "sha512-WhCn7Z7TauhBtmzhvKpoQs0Wwb/kBcy4CwpuI0/eEIr2Lx2auxmulAzLr91wVZJaz47iUZdkXOK7WlAfxGKCnA==",
|
"integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -647,9 +632,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/core": {
|
"node_modules/@eslint/core": {
|
||||||
"version": "0.13.0",
|
"version": "0.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz",
|
||||||
"integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
|
"integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -697,13 +682,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "9.25.1",
|
"version": "9.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.1.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz",
|
||||||
"integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==",
|
"integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://eslint.org/donate"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/object-schema": {
|
"node_modules/@eslint/object-schema": {
|
||||||
@@ -717,13 +705,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/plugin-kit": {
|
"node_modules/@eslint/plugin-kit": {
|
||||||
"version": "0.2.8",
|
"version": "0.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz",
|
||||||
"integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==",
|
"integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint/core": "^0.13.0",
|
"@eslint/core": "^0.14.0",
|
||||||
"levn": "^0.4.1"
|
"levn": "^0.4.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1350,38 +1338,31 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "22.14.1",
|
"version": "22.15.30",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.30.tgz",
|
||||||
"integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==",
|
"integrity": "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.21.0"
|
"undici-types": "~6.21.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/normalize-package-data": {
|
|
||||||
"version": "2.4.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz",
|
|
||||||
"integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.31.0",
|
"version": "8.32.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz",
|
||||||
"integrity": "sha512-evaQJZ/J/S4wisevDvC1KFZkPzRetH8kYZbkgcTRyql3mcKsf+ZFDV1BVWUGTCAW5pQHoqn5gK5b8kn7ou9aFQ==",
|
"integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/regexpp": "^4.10.0",
|
"@eslint-community/regexpp": "^4.10.0",
|
||||||
"@typescript-eslint/scope-manager": "8.31.0",
|
"@typescript-eslint/scope-manager": "8.32.1",
|
||||||
"@typescript-eslint/type-utils": "8.31.0",
|
"@typescript-eslint/type-utils": "8.32.1",
|
||||||
"@typescript-eslint/utils": "8.31.0",
|
"@typescript-eslint/utils": "8.32.1",
|
||||||
"@typescript-eslint/visitor-keys": "8.31.0",
|
"@typescript-eslint/visitor-keys": "8.32.1",
|
||||||
"graphemer": "^1.4.0",
|
"graphemer": "^1.4.0",
|
||||||
"ignore": "^5.3.1",
|
"ignore": "^7.0.0",
|
||||||
"natural-compare": "^1.4.0",
|
"natural-compare": "^1.4.0",
|
||||||
"ts-api-utils": "^2.0.1"
|
"ts-api-utils": "^2.1.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -1396,17 +1377,27 @@
|
|||||||
"typescript": ">=4.8.4 <5.9.0"
|
"typescript": ">=4.8.4 <5.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
|
||||||
|
"version": "7.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz",
|
||||||
|
"integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "8.31.0",
|
"version": "8.32.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.31.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz",
|
||||||
"integrity": "sha512-67kYYShjBR0jNI5vsf/c3WG4u+zDnCTHTPqVMQguffaWWFs7artgwKmfwdifl+r6XyM5LYLas/dInj2T0SgJyw==",
|
"integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.31.0",
|
"@typescript-eslint/scope-manager": "8.32.1",
|
||||||
"@typescript-eslint/types": "8.31.0",
|
"@typescript-eslint/types": "8.32.1",
|
||||||
"@typescript-eslint/typescript-estree": "8.31.0",
|
"@typescript-eslint/typescript-estree": "8.32.1",
|
||||||
"@typescript-eslint/visitor-keys": "8.31.0",
|
"@typescript-eslint/visitor-keys": "8.32.1",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1422,14 +1413,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/scope-manager": {
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
"version": "8.31.0",
|
"version": "8.32.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.31.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz",
|
||||||
"integrity": "sha512-knO8UyF78Nt8O/B64i7TlGXod69ko7z6vJD9uhSlm0qkAbGeRUSudcm0+K/4CrRjrpiHfBCjMWlc08Vav1xwcw==",
|
"integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.31.0",
|
"@typescript-eslint/types": "8.32.1",
|
||||||
"@typescript-eslint/visitor-keys": "8.31.0"
|
"@typescript-eslint/visitor-keys": "8.32.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -1440,16 +1431,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/type-utils": {
|
"node_modules/@typescript-eslint/type-utils": {
|
||||||
"version": "8.31.0",
|
"version": "8.32.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.31.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz",
|
||||||
"integrity": "sha512-DJ1N1GdjI7IS7uRlzJuEDCgDQix3ZVYVtgeWEyhyn4iaoitpMBX6Ndd488mXSx0xah/cONAkEaYyylDyAeHMHg==",
|
"integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/typescript-estree": "8.31.0",
|
"@typescript-eslint/typescript-estree": "8.32.1",
|
||||||
"@typescript-eslint/utils": "8.31.0",
|
"@typescript-eslint/utils": "8.32.1",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"ts-api-utils": "^2.0.1"
|
"ts-api-utils": "^2.1.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -1464,9 +1455,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/types": {
|
"node_modules/@typescript-eslint/types": {
|
||||||
"version": "8.31.0",
|
"version": "8.32.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.31.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz",
|
||||||
"integrity": "sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ==",
|
"integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1478,20 +1469,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree": {
|
"node_modules/@typescript-eslint/typescript-estree": {
|
||||||
"version": "8.31.0",
|
"version": "8.32.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz",
|
||||||
"integrity": "sha512-xLmgn4Yl46xi6aDSZ9KkyfhhtnYI15/CvHbpOy/eR5NWhK/BK8wc709KKwhAR0m4ZKRP7h07bm4BWUYOCuRpQQ==",
|
"integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.31.0",
|
"@typescript-eslint/types": "8.32.1",
|
||||||
"@typescript-eslint/visitor-keys": "8.31.0",
|
"@typescript-eslint/visitor-keys": "8.32.1",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"fast-glob": "^3.3.2",
|
"fast-glob": "^3.3.2",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
"minimatch": "^9.0.4",
|
"minimatch": "^9.0.4",
|
||||||
"semver": "^7.6.0",
|
"semver": "^7.6.0",
|
||||||
"ts-api-utils": "^2.0.1"
|
"ts-api-utils": "^2.1.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -1531,16 +1522,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/utils": {
|
"node_modules/@typescript-eslint/utils": {
|
||||||
"version": "8.31.0",
|
"version": "8.32.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.31.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz",
|
||||||
"integrity": "sha512-qi6uPLt9cjTFxAb1zGNgTob4x9ur7xC6mHQJ8GwEzGMGE9tYniublmJaowOJ9V2jUzxrltTPfdG2nKlWsq0+Ww==",
|
"integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.4.0",
|
"@eslint-community/eslint-utils": "^4.7.0",
|
||||||
"@typescript-eslint/scope-manager": "8.31.0",
|
"@typescript-eslint/scope-manager": "8.32.1",
|
||||||
"@typescript-eslint/types": "8.31.0",
|
"@typescript-eslint/types": "8.32.1",
|
||||||
"@typescript-eslint/typescript-estree": "8.31.0"
|
"@typescript-eslint/typescript-estree": "8.32.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -1555,13 +1546,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/visitor-keys": {
|
"node_modules/@typescript-eslint/visitor-keys": {
|
||||||
"version": "8.31.0",
|
"version": "8.32.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz",
|
||||||
"integrity": "sha512-QcGHmlRHWOl93o64ZUMNewCdwKGU6WItOU52H0djgNmn1EOrhVudrDzXz4OycCRSCPwFCDrE2iIt5vmuUdHxuQ==",
|
"integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.31.0",
|
"@typescript-eslint/types": "8.32.1",
|
||||||
"eslint-visitor-keys": "^4.2.0"
|
"eslint-visitor-keys": "^4.2.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1573,9 +1564,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/coverage-v8": {
|
"node_modules/@vitest/coverage-v8": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.4.tgz",
|
||||||
"integrity": "sha512-XDdaDOeaTMAMYW7N63AqoK32sYUWbXnTkC6tEbVcu3RlU1bB9of32T+PGf8KZvxqLNqeXhafDFqCkwpf2+dyaQ==",
|
"integrity": "sha512-G4p6OtioySL+hPV7Y6JHlhpsODbJzt1ndwHAFkyk6vVjpK03PFsKnauZIzcd0PrK4zAbc5lc+jeZ+eNGiMA+iw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1596,8 +1587,8 @@
|
|||||||
"url": "https://opencollective.com/vitest"
|
"url": "https://opencollective.com/vitest"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@vitest/browser": "3.1.2",
|
"@vitest/browser": "3.1.4",
|
||||||
"vitest": "3.1.2"
|
"vitest": "3.1.4"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@vitest/browser": {
|
"@vitest/browser": {
|
||||||
@@ -1606,14 +1597,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/expect": {
|
"node_modules/@vitest/expect": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.4.tgz",
|
||||||
"integrity": "sha512-O8hJgr+zREopCAqWl3uCVaOdqJwZ9qaDwUP7vy3Xigad0phZe9APxKhPcDNqYYi0rX5oMvwJMSCAXY2afqeTSA==",
|
"integrity": "sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/spy": "3.1.2",
|
"@vitest/spy": "3.1.4",
|
||||||
"@vitest/utils": "3.1.2",
|
"@vitest/utils": "3.1.4",
|
||||||
"chai": "^5.2.0",
|
"chai": "^5.2.0",
|
||||||
"tinyrainbow": "^2.0.0"
|
"tinyrainbow": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -1622,13 +1613,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/mocker": {
|
"node_modules/@vitest/mocker": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.4.tgz",
|
||||||
"integrity": "sha512-kOtd6K2lc7SQ0mBqYv/wdGedlqPdM/B38paPY+OwJ1XiNi44w3Fpog82UfOibmHaV9Wod18A09I9SCKLyDMqgw==",
|
"integrity": "sha512-8IJ3CvwtSw/EFXqWFL8aCMu+YyYXG2WUSrQbViOZkWTKTVicVwZ/YiEZDSqD00kX+v/+W+OnxhNWoeVKorHygA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/spy": "3.1.2",
|
"@vitest/spy": "3.1.4",
|
||||||
"estree-walker": "^3.0.3",
|
"estree-walker": "^3.0.3",
|
||||||
"magic-string": "^0.30.17"
|
"magic-string": "^0.30.17"
|
||||||
},
|
},
|
||||||
@@ -1649,9 +1640,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/pretty-format": {
|
"node_modules/@vitest/pretty-format": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.4.tgz",
|
||||||
"integrity": "sha512-R0xAiHuWeDjTSB3kQ3OQpT8Rx3yhdOAIm/JM4axXxnG7Q/fS8XUwggv/A4xzbQA+drYRjzkMnpYnOGAc4oeq8w==",
|
"integrity": "sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1662,13 +1653,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/runner": {
|
"node_modules/@vitest/runner": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.4.tgz",
|
||||||
"integrity": "sha512-bhLib9l4xb4sUMPXnThbnhX2Yi8OutBMA8Yahxa7yavQsFDtwY/jrUZwpKp2XH9DhRFJIeytlyGpXCqZ65nR+g==",
|
"integrity": "sha512-djTeF1/vt985I/wpKVFBMWUlk/I7mb5hmD5oP8K9ACRmVXgKTae3TUOtXAEBfslNKPzUQvnKhNd34nnRSYgLNQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/utils": "3.1.2",
|
"@vitest/utils": "3.1.4",
|
||||||
"pathe": "^2.0.3"
|
"pathe": "^2.0.3"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -1676,13 +1667,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/snapshot": {
|
"node_modules/@vitest/snapshot": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.4.tgz",
|
||||||
"integrity": "sha512-Q1qkpazSF/p4ApZg1vfZSQ5Yw6OCQxVMVrLjslbLFA1hMDrT2uxtqMaw8Tc/jy5DLka1sNs1Y7rBcftMiaSH/Q==",
|
"integrity": "sha512-JPHf68DvuO7vilmvwdPr9TS0SuuIzHvxeaCkxYcCD4jTk67XwL45ZhEHFKIuCm8CYstgI6LZ4XbwD6ANrwMpFg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/pretty-format": "3.1.2",
|
"@vitest/pretty-format": "3.1.4",
|
||||||
"magic-string": "^0.30.17",
|
"magic-string": "^0.30.17",
|
||||||
"pathe": "^2.0.3"
|
"pathe": "^2.0.3"
|
||||||
},
|
},
|
||||||
@@ -1691,9 +1682,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/spy": {
|
"node_modules/@vitest/spy": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.4.tgz",
|
||||||
"integrity": "sha512-OEc5fSXMws6sHVe4kOFyDSj/+4MSwst0ib4un0DlcYgQvRuYQ0+M2HyqGaauUMnjq87tmUaMNDxKQx7wNfVqPA==",
|
"integrity": "sha512-Xg1bXhu+vtPXIodYN369M86K8shGLouNjoVI78g8iAq2rFoHFdajNvJJ5A/9bPMFcfQqdaCpOgWKEoMQg/s0Yg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1704,13 +1695,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/utils": {
|
"node_modules/@vitest/utils": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.4.tgz",
|
||||||
"integrity": "sha512-5GGd0ytZ7BH3H6JTj9Kw7Prn1Nbg0wZVrIvou+UWxm54d+WoXXgAgjFJ8wn3LdagWLFSEfpPeyYrByZaGEZHLg==",
|
"integrity": "sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/pretty-format": "3.1.2",
|
"@vitest/pretty-format": "3.1.4",
|
||||||
"loupe": "^3.1.3",
|
"loupe": "^3.1.3",
|
||||||
"tinyrainbow": "^2.0.0"
|
"tinyrainbow": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -1865,9 +1856,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/builtin-modules": {
|
"node_modules/builtin-modules": {
|
||||||
"version": "4.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.0.0.tgz",
|
||||||
"integrity": "sha512-p1n8zyCkt1BVrKNFymOHjcDSAl7oq/gUvfgULv2EblgpPVQlQr9yHnWjg9IJ2MhfwPqiYqMMrr01OY7yQoK2yA==",
|
"integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -2241,9 +2232,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "9.25.1",
|
"version": "9.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz",
|
||||||
"integrity": "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==",
|
"integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -2251,10 +2242,10 @@
|
|||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
"@eslint/config-array": "^0.20.0",
|
"@eslint/config-array": "^0.20.0",
|
||||||
"@eslint/config-helpers": "^0.2.1",
|
"@eslint/config-helpers": "^0.2.1",
|
||||||
"@eslint/core": "^0.13.0",
|
"@eslint/core": "^0.14.0",
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@eslint/js": "9.25.1",
|
"@eslint/js": "9.27.0",
|
||||||
"@eslint/plugin-kit": "^0.2.8",
|
"@eslint/plugin-kit": "^0.3.1",
|
||||||
"@humanfs/node": "^0.16.6",
|
"@humanfs/node": "^0.16.6",
|
||||||
"@humanwhocodes/module-importer": "^1.0.1",
|
"@humanwhocodes/module-importer": "^1.0.1",
|
||||||
"@humanwhocodes/retry": "^0.4.2",
|
"@humanwhocodes/retry": "^0.4.2",
|
||||||
@@ -2302,22 +2293,25 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-config-prettier": {
|
"node_modules/eslint-config-prettier": {
|
||||||
"version": "10.1.2",
|
"version": "10.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz",
|
||||||
"integrity": "sha512-Epgp/EofAUeEpIdZkW60MHKvPyru1ruQJxPL+WIycnaPApuseK0Zpkrh/FwL9oIpQvIhJwV7ptOy0DWUjTlCiA==",
|
"integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"eslint-config-prettier": "bin/cli.js"
|
"eslint-config-prettier": "bin/cli.js"
|
||||||
},
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/eslint-config-prettier"
|
||||||
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"eslint": ">=7.0.0"
|
"eslint": ">=7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-prettier": {
|
"node_modules/eslint-plugin-prettier": {
|
||||||
"version": "5.2.6",
|
"version": "5.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.0.tgz",
|
||||||
"integrity": "sha512-mUcf7QG2Tjk7H055Jk0lGBjbgDnfrvqjhXh9t2xLMSCjZVcw9Rb1V6sVNXO0th3jgeO7zllWPTNRil3JW94TnQ==",
|
"integrity": "sha512-BvQOvUhkVQM1i63iMETK9Hjud9QhqBnbtT1Zc642p9ynzBuCe5pybkOnvqZIBypXmMlsGcnU4HZ8sCTPfpAexA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -2346,50 +2340,65 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-unicorn": {
|
"node_modules/eslint-plugin-unicorn": {
|
||||||
"version": "57.0.0",
|
"version": "59.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-57.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-59.0.1.tgz",
|
||||||
"integrity": "sha512-zUYYa6zfNdTeG9BISWDlcLmz16c+2Ck2o5ZDHh0UzXJz3DEP7xjmlVDTzbyV0W+XksgZ0q37WEWzN2D2Ze+g9Q==",
|
"integrity": "sha512-EtNXYuWPUmkgSU2E7Ttn57LbRREQesIP1BiLn7OZLKodopKfDXfBUkC/0j6mpw2JExwf43Uf3qLSvrSvppgy8Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-validator-identifier": "^7.25.9",
|
"@babel/helper-validator-identifier": "^7.25.9",
|
||||||
"@eslint-community/eslint-utils": "^4.4.1",
|
"@eslint-community/eslint-utils": "^4.5.1",
|
||||||
"ci-info": "^4.1.0",
|
"@eslint/plugin-kit": "^0.2.7",
|
||||||
|
"ci-info": "^4.2.0",
|
||||||
"clean-regexp": "^1.0.0",
|
"clean-regexp": "^1.0.0",
|
||||||
"core-js-compat": "^3.40.0",
|
"core-js-compat": "^3.41.0",
|
||||||
"esquery": "^1.6.0",
|
"esquery": "^1.6.0",
|
||||||
"globals": "^15.15.0",
|
"find-up-simple": "^1.0.1",
|
||||||
|
"globals": "^16.0.0",
|
||||||
"indent-string": "^5.0.0",
|
"indent-string": "^5.0.0",
|
||||||
"is-builtin-module": "^4.0.0",
|
"is-builtin-module": "^5.0.0",
|
||||||
"jsesc": "^3.1.0",
|
"jsesc": "^3.1.0",
|
||||||
"pluralize": "^8.0.0",
|
"pluralize": "^8.0.0",
|
||||||
"read-package-up": "^11.0.0",
|
|
||||||
"regexp-tree": "^0.1.27",
|
"regexp-tree": "^0.1.27",
|
||||||
"regjsparser": "^0.12.0",
|
"regjsparser": "^0.12.0",
|
||||||
"semver": "^7.7.1",
|
"semver": "^7.7.1",
|
||||||
"strip-indent": "^4.0.0"
|
"strip-indent": "^4.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.18"
|
"node": "^18.20.0 || ^20.10.0 || >=21.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1"
|
"url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"eslint": ">=9.20.0"
|
"eslint": ">=9.22.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-unicorn/node_modules/globals": {
|
"node_modules/eslint-plugin-unicorn/node_modules/@eslint/core": {
|
||||||
"version": "15.15.0",
|
"version": "0.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
|
||||||
"integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
|
"integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "Apache-2.0",
|
||||||
"engines": {
|
"dependencies": {
|
||||||
"node": ">=18"
|
"@types/json-schema": "^7.0.15"
|
||||||
},
|
},
|
||||||
"funding": {
|
"engines": {
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/eslint-plugin-unicorn/node_modules/@eslint/plugin-kit": {
|
||||||
|
"version": "0.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz",
|
||||||
|
"integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@eslint/core": "^0.13.0",
|
||||||
|
"levn": "^0.4.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-scope": {
|
"node_modules/eslint-scope": {
|
||||||
@@ -2740,9 +2749,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/globals": {
|
"node_modules/globals": {
|
||||||
"version": "16.0.0",
|
"version": "16.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-16.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz",
|
||||||
"integrity": "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==",
|
"integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -2776,19 +2785,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/hosted-git-info": {
|
|
||||||
"version": "7.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz",
|
|
||||||
"integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"lru-cache": "^10.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^16.14.0 || >=18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/html-escaper": {
|
"node_modules/html-escaper": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
|
||||||
@@ -2846,27 +2842,14 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/index-to-position": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/is-builtin-module": {
|
"node_modules/is-builtin-module": {
|
||||||
"version": "4.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-5.0.0.tgz",
|
||||||
"integrity": "sha512-rWP3AMAalQSesXO8gleROyL2iKU73SX5Er66losQn9rWOWL4Gef0a/xOEOVqjWGMuR2vHG3FJ8UUmT700O8oFg==",
|
"integrity": "sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"builtin-modules": "^4.0.0"
|
"builtin-modules": "^5.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.20"
|
"node": ">=18.20"
|
||||||
@@ -2992,13 +2975,6 @@
|
|||||||
"@pkgjs/parseargs": "^0.11.0"
|
"@pkgjs/parseargs": "^0.11.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/js-tokens": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||||
@@ -3256,21 +3232,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/normalize-package-data": {
|
|
||||||
"version": "6.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz",
|
|
||||||
"integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-2-Clause",
|
|
||||||
"dependencies": {
|
|
||||||
"hosted-git-info": "^7.0.0",
|
|
||||||
"semver": "^7.3.5",
|
|
||||||
"validate-npm-package-license": "^3.0.4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^16.14.0 || >=18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/optionator": {
|
"node_modules/optionator": {
|
||||||
"version": "0.9.4",
|
"version": "0.9.4",
|
||||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||||
@@ -3341,24 +3302,6 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/parse-json": {
|
|
||||||
"version": "8.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz",
|
|
||||||
"integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/code-frame": "^7.26.2",
|
|
||||||
"index-to-position": "^1.1.0",
|
|
||||||
"type-fest": "^4.39.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/path-exists": {
|
"node_modules/path-exists": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
@@ -3557,44 +3500,6 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/read-package-up": {
|
|
||||||
"version": "11.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz",
|
|
||||||
"integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"find-up-simple": "^1.0.0",
|
|
||||||
"read-pkg": "^9.0.0",
|
|
||||||
"type-fest": "^4.6.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/read-pkg": {
|
|
||||||
"version": "9.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz",
|
|
||||||
"integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/normalize-package-data": "^2.4.3",
|
|
||||||
"normalize-package-data": "^6.0.0",
|
|
||||||
"parse-json": "^8.0.0",
|
|
||||||
"type-fest": "^4.6.0",
|
|
||||||
"unicorn-magic": "^0.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/readdirp": {
|
"node_modules/readdirp": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
|
||||||
@@ -3793,42 +3698,6 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/spdx-correct": {
|
|
||||||
"version": "3.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
|
|
||||||
"integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"spdx-expression-parse": "^3.0.0",
|
|
||||||
"spdx-license-ids": "^3.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/spdx-exceptions": {
|
|
||||||
"version": "2.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
|
|
||||||
"integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "CC-BY-3.0"
|
|
||||||
},
|
|
||||||
"node_modules/spdx-expression-parse": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"spdx-exceptions": "^2.1.0",
|
|
||||||
"spdx-license-ids": "^3.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/spdx-license-ids": {
|
|
||||||
"version": "3.0.21",
|
|
||||||
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz",
|
|
||||||
"integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "CC0-1.0"
|
|
||||||
},
|
|
||||||
"node_modules/stackback": {
|
"node_modules/stackback": {
|
||||||
"version": "0.0.2",
|
"version": "0.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
|
||||||
@@ -4156,19 +4025,6 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/type-fest": {
|
|
||||||
"version": "4.40.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.40.0.tgz",
|
|
||||||
"integrity": "sha512-ABHZ2/tS2JkvH1PEjxFDTUWC8dB5OsIGZP4IFLhR293GqT5Y5qB1WwL2kMPYhQW9DVgVD8Hd7I8gjwPIf5GFkw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "(MIT OR CC0-1.0)",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.8.3",
|
"version": "5.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||||
@@ -4184,15 +4040,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript-eslint": {
|
"node_modules/typescript-eslint": {
|
||||||
"version": "8.31.0",
|
"version": "8.32.1",
|
||||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.31.0.tgz",
|
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.32.1.tgz",
|
||||||
"integrity": "sha512-u+93F0sB0An8WEAPtwxVhFby573E8ckdjwUUQUj9QA4v8JAvgtoDdIyYR3XFwFHq2W1KJ1AurwJCO+w+Y1ixyQ==",
|
"integrity": "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "8.31.0",
|
"@typescript-eslint/eslint-plugin": "8.32.1",
|
||||||
"@typescript-eslint/parser": "8.31.0",
|
"@typescript-eslint/parser": "8.32.1",
|
||||||
"@typescript-eslint/utils": "8.31.0"
|
"@typescript-eslint/utils": "8.32.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -4213,19 +4069,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/unicorn-magic": {
|
|
||||||
"version": "0.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz",
|
|
||||||
"integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/update-browserslist-db": {
|
"node_modules/update-browserslist-db": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
|
||||||
@@ -4267,21 +4110,10 @@
|
|||||||
"punycode": "^2.1.0"
|
"punycode": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/validate-npm-package-license": {
|
|
||||||
"version": "3.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
|
|
||||||
"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"spdx-correct": "^3.0.0",
|
|
||||||
"spdx-expression-parse": "^3.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "6.3.3",
|
"version": "6.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
|
||||||
"integrity": "sha512-5nXH+QsELbFKhsEfWLkHrvgRpTdGJzqOZ+utSdmPTvwHmvU6ITTm3xx+mRusihkcI8GeC7lCDyn3kDtiki9scw==",
|
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -4354,15 +4186,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite-node": {
|
"node_modules/vite-node": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.4.tgz",
|
||||||
"integrity": "sha512-/8iMryv46J3aK13iUXsei5G/A3CUlW4665THCPS+K8xAaqrVWiGB4RfXMQXCLjpK9P2eK//BczrVkn5JLAk6DA==",
|
"integrity": "sha512-6enNwYnpyDo4hEgytbmc6mYWHXDHYEn0D1/rw4Q+tnHUGtKTJsn8T1YkX6Q18wI5LCrS8CTYlBaiCqxOy2kvUA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cac": "^6.7.14",
|
"cac": "^6.7.14",
|
||||||
"debug": "^4.4.0",
|
"debug": "^4.4.0",
|
||||||
"es-module-lexer": "^1.6.0",
|
"es-module-lexer": "^1.7.0",
|
||||||
"pathe": "^2.0.3",
|
"pathe": "^2.0.3",
|
||||||
"vite": "^5.0.0 || ^6.0.0"
|
"vite": "^5.0.0 || ^6.0.0"
|
||||||
},
|
},
|
||||||
@@ -4425,19 +4257,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vitest": {
|
"node_modules/vitest": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.4.tgz",
|
||||||
"integrity": "sha512-WaxpJe092ID1C0mr+LH9MmNrhfzi8I65EX/NRU/Ld016KqQNRgxSOlGNP1hHN+a/F8L15Mh8klwaF77zR3GeDQ==",
|
"integrity": "sha512-Ta56rT7uWxCSJXlBtKgIlApJnT6e6IGmTYxYcmxjJ4ujuZDI59GUQgVDObXXJujOmPDBYXHK1qmaGtneu6TNIQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/expect": "3.1.2",
|
"@vitest/expect": "3.1.4",
|
||||||
"@vitest/mocker": "3.1.2",
|
"@vitest/mocker": "3.1.4",
|
||||||
"@vitest/pretty-format": "^3.1.2",
|
"@vitest/pretty-format": "^3.1.4",
|
||||||
"@vitest/runner": "3.1.2",
|
"@vitest/runner": "3.1.4",
|
||||||
"@vitest/snapshot": "3.1.2",
|
"@vitest/snapshot": "3.1.4",
|
||||||
"@vitest/spy": "3.1.2",
|
"@vitest/spy": "3.1.4",
|
||||||
"@vitest/utils": "3.1.2",
|
"@vitest/utils": "3.1.4",
|
||||||
"chai": "^5.2.0",
|
"chai": "^5.2.0",
|
||||||
"debug": "^4.4.0",
|
"debug": "^4.4.0",
|
||||||
"expect-type": "^1.2.1",
|
"expect-type": "^1.2.1",
|
||||||
@@ -4450,7 +4282,7 @@
|
|||||||
"tinypool": "^1.0.2",
|
"tinypool": "^1.0.2",
|
||||||
"tinyrainbow": "^2.0.0",
|
"tinyrainbow": "^2.0.0",
|
||||||
"vite": "^5.0.0 || ^6.0.0",
|
"vite": "^5.0.0 || ^6.0.0",
|
||||||
"vite-node": "3.1.2",
|
"vite-node": "3.1.4",
|
||||||
"why-is-node-running": "^2.3.0"
|
"why-is-node-running": "^2.3.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -4466,8 +4298,8 @@
|
|||||||
"@edge-runtime/vm": "*",
|
"@edge-runtime/vm": "*",
|
||||||
"@types/debug": "^4.1.12",
|
"@types/debug": "^4.1.12",
|
||||||
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
||||||
"@vitest/browser": "3.1.2",
|
"@vitest/browser": "3.1.4",
|
||||||
"@vitest/ui": "3.1.2",
|
"@vitest/ui": "3.1.4",
|
||||||
"happy-dom": "*",
|
"happy-dom": "*",
|
||||||
"jsdom": "*"
|
"jsdom": "*"
|
||||||
},
|
},
|
||||||
@@ -4656,16 +4488,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/yaml": {
|
"node_modules/yaml": {
|
||||||
"version": "2.7.1",
|
"version": "2.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz",
|
||||||
"integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==",
|
"integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bin": {
|
"bin": {
|
||||||
"yaml": "bin.mjs"
|
"yaml": "bin.mjs"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 14"
|
"node": ">= 14.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/yocto-queue": {
|
"node_modules/yocto-queue": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.65",
|
"version": "2.2.68",
|
||||||
"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",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/micromatch": "^4.0.9",
|
"@types/micromatch": "^4.0.9",
|
||||||
"@types/mock-fs": "^4.13.1",
|
"@types/mock-fs": "^4.13.1",
|
||||||
"@types/node": "^22.14.1",
|
"@types/node": "^22.15.29",
|
||||||
"@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",
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
"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": "^57.0.0",
|
"eslint-plugin-unicorn": "^59.0.0",
|
||||||
"globals": "^16.0.0",
|
"globals": "^16.0.0",
|
||||||
"mock-fs": "^5.2.0",
|
"mock-fs": "^5.2.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
@@ -69,6 +69,6 @@
|
|||||||
"micromatch": "^4.0.8"
|
"micromatch": "^4.0.8"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "22.14.0"
|
"node": "22.16.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export interface UploadOptionsDto {
|
|||||||
concurrency: number;
|
concurrency: number;
|
||||||
progress?: boolean;
|
progress?: boolean;
|
||||||
watch?: boolean;
|
watch?: boolean;
|
||||||
|
jsonOutput?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class UploadFile extends File {
|
class UploadFile extends File {
|
||||||
@@ -65,6 +66,9 @@ class UploadFile extends File {
|
|||||||
const uploadBatch = async (files: string[], options: UploadOptionsDto) => {
|
const uploadBatch = async (files: string[], options: UploadOptionsDto) => {
|
||||||
const { newFiles, duplicates } = await checkForDuplicates(files, options);
|
const { newFiles, duplicates } = await checkForDuplicates(files, options);
|
||||||
const newAssets = await uploadFiles(newFiles, options);
|
const newAssets = await uploadFiles(newFiles, options);
|
||||||
|
if (options.jsonOutput) {
|
||||||
|
console.log(JSON.stringify({ newFiles, duplicates, newAssets }, undefined, 4));
|
||||||
|
}
|
||||||
await updateAlbums([...newAssets, ...duplicates], options);
|
await updateAlbums([...newAssets, ...duplicates], options);
|
||||||
await deleteFiles(newFiles, options);
|
await deleteFiles(newFiles, options);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -68,6 +68,11 @@ program
|
|||||||
.env('IMMICH_UPLOAD_CONCURRENCY')
|
.env('IMMICH_UPLOAD_CONCURRENCY')
|
||||||
.default(4),
|
.default(4),
|
||||||
)
|
)
|
||||||
|
.addOption(
|
||||||
|
new Option('-j, --json-output', 'Output detailed information in json format')
|
||||||
|
.env('IMMICH_JSON_OUTPUT')
|
||||||
|
.default(false),
|
||||||
|
)
|
||||||
.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('--no-progress', 'Hide progress bars').env('IMMICH_PROGRESS_BAR').default(true))
|
||||||
.addOption(
|
.addOption(
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ name: immich-dev
|
|||||||
services:
|
services:
|
||||||
immich-server:
|
immich-server:
|
||||||
container_name: immich_server
|
container_name: immich_server
|
||||||
command: ['/usr/src/app/bin/immich-dev']
|
command: [ '/usr/src/app/bin/immich-dev' ]
|
||||||
image: immich-server-dev:latest
|
image: immich-server-dev:latest
|
||||||
# extends:
|
# extends:
|
||||||
# file: hwaccel.transcoding.yml
|
# file: hwaccel.transcoding.yml
|
||||||
@@ -48,7 +48,7 @@ services:
|
|||||||
IMMICH_THIRD_PARTY_SOURCE_URL: https://github.com/immich-app/immich/
|
IMMICH_THIRD_PARTY_SOURCE_URL: https://github.com/immich-app/immich/
|
||||||
IMMICH_THIRD_PARTY_BUG_FEATURE_URL: https://github.com/immich-app/immich/issues
|
IMMICH_THIRD_PARTY_BUG_FEATURE_URL: https://github.com/immich-app/immich/issues
|
||||||
IMMICH_THIRD_PARTY_DOCUMENTATION_URL: https://immich.app/docs
|
IMMICH_THIRD_PARTY_DOCUMENTATION_URL: https://immich.app/docs
|
||||||
IMMICH_THIRD_PARTY_SUPPORT_URL: https://immich.app/docs/third-party
|
IMMICH_THIRD_PARTY_SUPPORT_URL: https://immich.app/docs/community-guides
|
||||||
ulimits:
|
ulimits:
|
||||||
nofile:
|
nofile:
|
||||||
soft: 1048576
|
soft: 1048576
|
||||||
@@ -70,7 +70,7 @@ services:
|
|||||||
# user: 0:0
|
# user: 0:0
|
||||||
build:
|
build:
|
||||||
context: ../web
|
context: ../web
|
||||||
command: ['/usr/src/app/bin/immich-web']
|
command: [ '/usr/src/app/bin/immich-web' ]
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
ports:
|
ports:
|
||||||
@@ -116,13 +116,13 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:8-bookworm@sha256:c855f98e09d558a0d7cc1a4e56473231206a4c54c0114ada9c485b47aeb92ec8
|
image: docker.io/valkey/valkey:8-bookworm@sha256:a19bebed6a91bd5e6e2106fef015f9602a3392deeb7c9ed47548378dcee3dfc2
|
||||||
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:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
environment:
|
environment:
|
||||||
@@ -134,25 +134,6 @@ services:
|
|||||||
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
|
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
healthcheck:
|
|
||||||
test: >-
|
|
||||||
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
|
|
||||||
interval: 5m
|
|
||||||
start_interval: 30s
|
|
||||||
start_period: 5m
|
|
||||||
command: >-
|
|
||||||
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
|
|
||||||
|
|
||||||
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
|
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
|
||||||
# immich-prometheus:
|
# immich-prometheus:
|
||||||
# container_name: immich_prometheus
|
# container_name: immich_prometheus
|
||||||
|
|||||||
@@ -56,14 +56,14 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:8-bookworm@sha256:c855f98e09d558a0d7cc1a4e56473231206a4c54c0114ada9c485b47aeb92ec8
|
image: docker.io/valkey/valkey:8-bookworm@sha256:a19bebed6a91bd5e6e2106fef015f9602a3392deeb7c9ed47548378dcee3dfc2
|
||||||
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:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
environment:
|
environment:
|
||||||
@@ -75,14 +75,6 @@ services:
|
|||||||
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
|
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
healthcheck:
|
|
||||||
test: >-
|
|
||||||
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
|
|
||||||
interval: 5m
|
|
||||||
start_interval: 30s
|
|
||||||
start_period: 5m
|
|
||||||
command: >-
|
|
||||||
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
|
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
|
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
|
||||||
@@ -90,7 +82,7 @@ services:
|
|||||||
container_name: immich_prometheus
|
container_name: immich_prometheus
|
||||||
ports:
|
ports:
|
||||||
- 9090:9090
|
- 9090:9090
|
||||||
image: prom/prometheus@sha256:339ce86a59413be18d0e445472891d022725b4803fab609069110205e79fb2f1
|
image: prom/prometheus@sha256:9abc6cf6aea7710d163dbb28d8eeb7dc5baef01e38fa4cd146a406dd9f07f70d
|
||||||
volumes:
|
volumes:
|
||||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||||
- prometheus-data:/prometheus
|
- prometheus-data:/prometheus
|
||||||
@@ -102,7 +94,7 @@ services:
|
|||||||
command: [ './run.sh', '-disable-reporting' ]
|
command: [ './run.sh', '-disable-reporting' ]
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
image: grafana/grafana:11.6.1-ubuntu@sha256:6fc273288470ef499dd3c6b36aeade093170d4f608f864c5dd3a7fabeae77b50
|
image: grafana/grafana:12.0.1-ubuntu@sha256:65575bb9c761335e2ff30e364f21d38632e3b2e75f5f81d83cc92f44b9bbc055
|
||||||
volumes:
|
volumes:
|
||||||
- grafana-data:/var/lib/grafana
|
- grafana-data:/var/lib/grafana
|
||||||
|
|
||||||
|
|||||||
@@ -49,30 +49,24 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:8-bookworm@sha256:c855f98e09d558a0d7cc1a4e56473231206a4c54c0114ada9c485b47aeb92ec8
|
image: docker.io/valkey/valkey:8-bookworm@sha256:a19bebed6a91bd5e6e2106fef015f9602a3392deeb7c9ed47548378dcee3dfc2
|
||||||
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:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||||
POSTGRES_USER: ${DB_USERNAME}
|
POSTGRES_USER: ${DB_USERNAME}
|
||||||
POSTGRES_DB: ${DB_DATABASE_NAME}
|
POSTGRES_DB: ${DB_DATABASE_NAME}
|
||||||
POSTGRES_INITDB_ARGS: '--data-checksums'
|
POSTGRES_INITDB_ARGS: '--data-checksums'
|
||||||
|
# Uncomment the DB_STORAGE_TYPE: 'HDD' var if your database isn't stored on SSDs
|
||||||
|
# DB_STORAGE_TYPE: 'HDD'
|
||||||
volumes:
|
volumes:
|
||||||
# Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file
|
# Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file
|
||||||
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
|
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
|
||||||
healthcheck:
|
|
||||||
test: >-
|
|
||||||
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
|
|
||||||
interval: 5m
|
|
||||||
start_interval: 30s
|
|
||||||
start_period: 5m
|
|
||||||
command: >-
|
|
||||||
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
|
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
22.14.0
|
22.16.0
|
||||||
|
|||||||
@@ -219,3 +219,10 @@ When you turn off the storage template engine, it will leave the assets in `UPLO
|
|||||||
Do not touch the files inside these folders under any circumstances except taking a backup. Changing or removing an asset can cause untracked and missing files.
|
Do not touch the files inside these folders under any circumstances except taking a backup. Changing or removing an asset can cause untracked and missing files.
|
||||||
You can think of it as App-Which-Must-Not-Be-Named, the only access to viewing, changing and deleting assets is only through the mobile or browser interface.
|
You can think of it as App-Which-Must-Not-Be-Named, the only access to viewing, changing and deleting assets is only through the mobile or browser interface.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
## Backup ordering
|
||||||
|
|
||||||
|
A backup of Immich should contain both the database and the asset files. When backing these up it's possible for them to get out of sync, potentially resulting in broken assets after you restore.
|
||||||
|
The best way of dealing with this is to stop the immich-server container while you take a backup. If nothing is changing then the backup will always be in sync.
|
||||||
|
|
||||||
|
If stopping the container is not an option, then the recommended order is to back up the database first, and the filesystem second. This way, the worst case scenario is that there are files on the filesystem that the database doesn't know about. If necessary, these can be (re)uploaded manually after a restore. If the backup is done the other way around, with the filesystem first and the database second, it's possible for the restored database to reference files that aren't in the filesystem backup, thus resulting in broken assets.
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ The `.well-known/openid-configuration` part of the url is optional and will be a
|
|||||||
## Auto Launch
|
## Auto Launch
|
||||||
|
|
||||||
When Auto Launch is enabled, the login page will automatically redirect the user to the OAuth authorization url, to login with OAuth. To access the login screen again, use the browser's back button, or navigate directly to `/auth/login?autoLaunch=0`.
|
When Auto Launch is enabled, the login page will automatically redirect the user to the OAuth authorization url, to login with OAuth. To access the login screen again, use the browser's back button, or navigate directly to `/auth/login?autoLaunch=0`.
|
||||||
|
Auto Launch can also be enabled on a per-request basis by navigating to `/auth/login?authLaunch=1`, this can be useful in situations where Immich is called from e.g. Nextcloud using the _External sites_ app and the _oidc_ app so as to enable users to directly interact with a logged-in instance of Immich.
|
||||||
|
|
||||||
## Mobile Redirect URI
|
## Mobile Redirect URI
|
||||||
|
|
||||||
|
|||||||
@@ -10,12 +10,16 @@ Running with a pre-existing Postgres server can unlock powerful administrative f
|
|||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
You must install pgvecto.rs into your instance of Postgres using their [instructions][vectors-install]. After installation, add `shared_preload_libraries = 'vectors.so'` to your `postgresql.conf`. If you already have some `shared_preload_libraries` set, you can separate each extension with a comma. For example, `shared_preload_libraries = 'pg_stat_statements, vectors.so'`.
|
You must install `pgvector` (`>= 0.7.0, < 1.0.0`), as it is a prerequisite for `vchord`.
|
||||||
|
The easiest way to do this on Debian/Ubuntu is by adding the [PostgreSQL Apt repository][pg-apt] and then
|
||||||
|
running `apt install postgresql-NN-pgvector`, where `NN` is your Postgres version (e.g., `16`).
|
||||||
|
|
||||||
|
You must install VectorChord into your instance of Postgres using their [instructions][vchord-install]. After installation, add `shared_preload_libraries = 'vchord.so'` to your `postgresql.conf`. If you already have some `shared_preload_libraries` set, you can separate each extension with a comma. For example, `shared_preload_libraries = 'pg_stat_statements, vchord.so'`.
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
Immich is known to work with Postgres versions 14, 15, and 16. Earlier versions are unsupported. Postgres 17 is nominally compatible, but pgvecto.rs does not have prebuilt images or packages for it as of writing.
|
Immich is known to work with Postgres versions `>= 14, < 18`.
|
||||||
|
|
||||||
Make sure the installed version of pgvecto.rs is compatible with your version of Immich. The current accepted range for pgvecto.rs is `>= 0.2.0, < 0.4.0`.
|
Make sure the installed version of VectorChord is compatible with your version of Immich. The current accepted range for VectorChord is `>= 0.3.0, < 0.5.0`.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## Specifying the connection URL
|
## Specifying the connection URL
|
||||||
@@ -53,21 +57,81 @@ CREATE DATABASE <immichdatabasename>;
|
|||||||
\c <immichdatabasename>
|
\c <immichdatabasename>
|
||||||
BEGIN;
|
BEGIN;
|
||||||
ALTER DATABASE <immichdatabasename> OWNER TO <immichdbusername>;
|
ALTER DATABASE <immichdatabasename> OWNER TO <immichdbusername>;
|
||||||
CREATE EXTENSION vectors;
|
CREATE EXTENSION vchord CASCADE;
|
||||||
CREATE EXTENSION earthdistance CASCADE;
|
CREATE EXTENSION earthdistance CASCADE;
|
||||||
ALTER DATABASE <immichdatabasename> SET search_path TO "$user", public, vectors;
|
|
||||||
ALTER SCHEMA vectors OWNER TO <immichdbusername>;
|
|
||||||
COMMIT;
|
COMMIT;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Updating pgvecto.rs
|
### Updating VectorChord
|
||||||
|
|
||||||
When installing a new version of pgvecto.rs, you will need to manually update the extension by connecting to the Immich database and running `ALTER EXTENSION vectors UPDATE;`.
|
When installing a new version of VectorChord, you will need to manually update the extension by connecting to the Immich database and running `ALTER EXTENSION vchord UPDATE;`.
|
||||||
|
|
||||||
### Common errors
|
## Migrating to VectorChord
|
||||||
|
|
||||||
#### Permission denied for view
|
VectorChord is the successor extension to pgvecto.rs, allowing for higher performance, lower memory usage and higher quality results for smart search and facial recognition.
|
||||||
|
|
||||||
If you get the error `driverError: error: permission denied for view pg_vector_index_stat`, you can fix this by connecting to the Immich database and running `GRANT SELECT ON TABLE pg_vector_index_stat TO <immichdbusername>;`.
|
### Migrating from pgvecto.rs
|
||||||
|
|
||||||
[vectors-install]: https://docs.vectorchord.ai/getting-started/installation.html
|
Support for pgvecto.rs will be dropped in a later release, hence we recommend all users currently using pgvecto.rs to migrate to VectorChord at their convenience. There are two primary approaches to do so.
|
||||||
|
|
||||||
|
The easiest option is to have both extensions installed during the migration:
|
||||||
|
|
||||||
|
1. Ensure you still have pgvecto.rs installed
|
||||||
|
2. Install `pgvector` (`>= 0.7.0, < 1.0.0`). The easiest way to do this is on Debian/Ubuntu by adding the [PostgreSQL Apt repository][pg-apt] and then running `apt install postgresql-NN-pgvector`, where `NN` is your Postgres version (e.g., `16`)
|
||||||
|
3. [Install VectorChord][vchord-install]
|
||||||
|
4. Add `shared_preload_libraries= 'vchord.so, vectors.so'` to your `postgresql.conf`, making sure to include _both_ `vchord.so` and `vectors.so`. You may include other libraries here as well if needed
|
||||||
|
5. Restart the Postgres database
|
||||||
|
6. If Immich does not have superuser permissions, run the SQL command `CREATE EXTENSION vchord CASCADE;` using psql or your choice of database client
|
||||||
|
7. Start Immich and wait for the logs `Reindexed face_index` and `Reindexed clip_index` to be output
|
||||||
|
8. If Immich does not have superuser permissions, run the SQL command `DROP EXTENSION vectors;`
|
||||||
|
9. Drop the old schema by running `DROP SCHEMA vectors;`
|
||||||
|
10. Remove the `vectors.so` entry from the `shared_preload_libraries` setting
|
||||||
|
11. Restart the Postgres database
|
||||||
|
12. Uninstall pgvecto.rs (e.g. `apt-get purge vectors-pg14` on Debian-based environments, replacing `pg14` as appropriate). `pgvector` must remain installed as it provides the data types used by `vchord`
|
||||||
|
|
||||||
|
If it is not possible to have both VectorChord and pgvecto.rs installed at the same time, you can perform the migration with more manual steps:
|
||||||
|
|
||||||
|
1. While pgvecto.rs is still installed, run the following SQL command using psql or your choice of database client. Take note of the number outputted by this command as you will need it later
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT atttypmod as dimsize
|
||||||
|
FROM pg_attribute f
|
||||||
|
JOIN pg_class c ON c.oid = f.attrelid
|
||||||
|
WHERE c.relkind = 'r'::char
|
||||||
|
AND f.attnum > 0
|
||||||
|
AND c.relname = 'smart_search'::text
|
||||||
|
AND f.attname = 'embedding'::text;
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Remove references to pgvecto.rs using the below SQL commands
|
||||||
|
|
||||||
|
```sql
|
||||||
|
DROP INDEX IF EXISTS clip_index;
|
||||||
|
DROP INDEX IF EXISTS face_index;
|
||||||
|
ALTER TABLE smart_search ALTER COLUMN embedding SET DATA TYPE real[];
|
||||||
|
ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE real[];
|
||||||
|
```
|
||||||
|
|
||||||
|
3. [Install VectorChord][vchord-install]
|
||||||
|
4. Change the columns back to the appropriate vector types, replacing `<number>` with the number from step 1
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE EXTENSION IF NOT EXISTS vchord CASCADE;
|
||||||
|
ALTER TABLE smart_search ALTER COLUMN embedding SET DATA TYPE vector(<number>);
|
||||||
|
ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE vector(512);
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Start Immich and let it create new indices using VectorChord
|
||||||
|
|
||||||
|
### Migrating from pgvector
|
||||||
|
|
||||||
|
1. Ensure you have at least 0.7.0 of pgvector installed. If it is below that, please upgrade it and run the SQL command `ALTER EXTENSION vector UPDATE;` using psql or your choice of database client
|
||||||
|
2. Follow the Prerequisites to install VectorChord
|
||||||
|
3. If Immich does not have superuser permissions, run the SQL command `CREATE EXTENSION vchord CASCADE;`
|
||||||
|
4. Remove the `DB_VECTOR_EXTENSION=pgvector` environmental variable as it will make Immich still use pgvector if set
|
||||||
|
5. Start Immich and let it create new indices using VectorChord
|
||||||
|
|
||||||
|
Note that VectorChord itself uses pgvector types, so you should not uninstall pgvector after following these steps.
|
||||||
|
|
||||||
|
[vchord-install]: https://docs.vectorchord.ai/vectorchord/getting-started/installation.html
|
||||||
|
[pg-apt]: https://www.postgresql.org/download/linux/#generic
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ server {
|
|||||||
client_max_body_size 50000M;
|
client_max_body_size 50000M;
|
||||||
|
|
||||||
# Set headers
|
# Set headers
|
||||||
proxy_set_header Host $http_host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|||||||
@@ -75,11 +75,12 @@ npm run dev
|
|||||||
To see local changes to `@immich/ui` in Immich, do the following:
|
To see local changes to `@immich/ui` in Immich, do the following:
|
||||||
|
|
||||||
1. Install `@immich/ui` as a sibling to `immich/`, for example `/home/user/immich` and `/home/user/ui`
|
1. Install `@immich/ui` as a sibling to `immich/`, for example `/home/user/immich` and `/home/user/ui`
|
||||||
1. Build the `@immich/ui` project via `npm run build`
|
2. Build the `@immich/ui` project via `npm run build`
|
||||||
1. Uncomment the corresponding volume in web service of the `docker/docker-compose.dev.yaml` file (`../../ui:/usr/ui`)
|
3. Uncomment the corresponding volume in web service of the `docker/docker-compose.dev.yaml` file (`../../ui:/usr/ui`)
|
||||||
1. Uncomment the corresponding alias in the `web/vite.config.js` file (`'@immich/ui': path.resolve(\_\_dirname, '../../ui')`)
|
4. Uncomment the corresponding alias in the `web/vite.config.js` file (`'@immich/ui': path.resolve(\_\_dirname, '../../ui')`)
|
||||||
1. Start up the stack via `make dev`
|
5. Uncomment the import statement in `web/src/app.css` file `@import '/usr/ui/dist/theme/default.css';` and comment out `@import '@immich/ui/theme/default.css';`
|
||||||
1. After making changes in `@immich/ui`, rebuild it (`npm run build`)
|
6. Start up the stack via `make dev`
|
||||||
|
7. After making changes in `@immich/ui`, rebuild it (`npm run build`)
|
||||||
|
|
||||||
### Mobile app
|
### Mobile app
|
||||||
|
|
||||||
@@ -114,32 +115,72 @@ Note: Activating the license is not required.
|
|||||||
|
|
||||||
### VSCode
|
### VSCode
|
||||||
|
|
||||||
Install `Flutter`, `DCM`, `Prettier`, `ESLint` and `Svelte` extensions.
|
Install `Flutter`, `DCM`, `Prettier`, `ESLint` and `Svelte` extensions. These extensions are listed in the `extensions.json` file under `.vscode/` and should appear as workspace recommendations.
|
||||||
|
|
||||||
in User `settings.json` (`cmd + shift + p` and search for `Open User Settings JSON`) add the following:
|
Here are the settings we use, they should be active as workspace settings (`settings.json`):
|
||||||
|
|
||||||
```json title="settings.json"
|
```json title="settings.json"
|
||||||
{
|
{
|
||||||
"editor.formatOnSave": true,
|
"[css]": {
|
||||||
"[javascript][typescript][css]": {
|
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.tabSize": 2,
|
"editor.formatOnSave": true,
|
||||||
"editor.formatOnSave": true
|
|
||||||
},
|
|
||||||
"[svelte]": {
|
|
||||||
"editor.defaultFormatter": "svelte.svelte-vscode",
|
|
||||||
"editor.tabSize": 2
|
"editor.tabSize": 2
|
||||||
},
|
},
|
||||||
"svelte.enable-ts-plugin": true,
|
|
||||||
"eslint.validate": ["javascript", "svelte"],
|
|
||||||
"[dart]": {
|
"[dart]": {
|
||||||
|
"editor.defaultFormatter": "Dart-Code.dart-code",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.selectionHighlight": false,
|
"editor.selectionHighlight": false,
|
||||||
"editor.suggest.snippetsPreventQuickSuggestions": false,
|
"editor.suggest.snippetsPreventQuickSuggestions": false,
|
||||||
"editor.suggestSelection": "first",
|
"editor.suggestSelection": "first",
|
||||||
"editor.tabCompletion": "onlySnippets",
|
"editor.tabCompletion": "onlySnippets",
|
||||||
"editor.wordBasedSuggestions": "off",
|
"editor.wordBasedSuggestions": "off"
|
||||||
"editor.defaultFormatter": "Dart-Code.dart-code"
|
},
|
||||||
}
|
"[javascript]": {
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.organizeImports": "explicit",
|
||||||
|
"source.removeUnusedImports": "explicit"
|
||||||
|
},
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.tabSize": 2
|
||||||
|
},
|
||||||
|
"[json]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.tabSize": 2
|
||||||
|
},
|
||||||
|
"[jsonc]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.tabSize": 2
|
||||||
|
},
|
||||||
|
"[svelte]": {
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.organizeImports": "explicit",
|
||||||
|
"source.removeUnusedImports": "explicit"
|
||||||
|
},
|
||||||
|
"editor.defaultFormatter": "svelte.svelte-vscode",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.tabSize": 2
|
||||||
|
},
|
||||||
|
"[typescript]": {
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.organizeImports": "explicit",
|
||||||
|
"source.removeUnusedImports": "explicit"
|
||||||
|
},
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.tabSize": 2
|
||||||
|
},
|
||||||
|
"cSpell.words": ["immich"],
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"eslint.validate": ["javascript", "svelte"],
|
||||||
|
"explorer.fileNesting.enabled": true,
|
||||||
|
"explorer.fileNesting.patterns": {
|
||||||
|
"*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart",
|
||||||
|
"*.ts": "${capture}.spec.ts,${capture}.mock.ts"
|
||||||
|
},
|
||||||
|
"svelte.enable-ts-plugin": true,
|
||||||
|
"typescript.preferences.importModuleSpecifier": "non-relative"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
19
docs/docs/features/casting.md
Normal file
19
docs/docs/features/casting.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Chromecast support
|
||||||
|
|
||||||
|
Immich supports the Google's Cast protocol so that photos and videos can be cast to devices such as a Chromecast and a Nest Hub. This feature is considered experimental and has several important limitations listed below. Currently, this feature is only supported by the web client, support on Android and iOS is planned for the future.
|
||||||
|
|
||||||
|
## Enable Google Cast Support
|
||||||
|
|
||||||
|
Google Cast support is disabled by default. The web UI uses Google-provided scripts and must retreive them from Google servers when the page loads. This is a privacy concern for some and is thus opt-in.
|
||||||
|
|
||||||
|
You can enable Google Cast support through `Account Settings > Features > Cast > Google Cast`
|
||||||
|
|
||||||
|
<img src={require('./img/gcast-enable.webp').default} width="70%" title='Enable Google Cast Support' />
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
|
||||||
|
To use casting with Immich, there are a few prerequisites:
|
||||||
|
|
||||||
|
1. Your instance must be accessed via an HTTPS connection in order for the casting menu to show.
|
||||||
|
2. Your instance must be publicly accessible via HTTPS and a DNS record for the server must be accessible via Google's DNS servers (`8.8.8.8` and `8.8.4.4`)
|
||||||
|
3. Videos must be in a format that is compatible with Google Cast. For more info, check out [Google's documentation](https://developers.google.com/cast/docs/media)
|
||||||
@@ -90,19 +90,22 @@ Usage: immich upload [paths...] [options]
|
|||||||
Upload assets
|
Upload assets
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
paths One or more paths to assets to be uploaded
|
paths One or more paths to assets to be uploaded
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-r, --recursive Recursive (default: false, env: IMMICH_RECURSIVE)
|
-r, --recursive Recursive (default: false, env: IMMICH_RECURSIVE)
|
||||||
-i, --ignore [paths...] Paths to ignore (default: [], env: IMMICH_IGNORE_PATHS)
|
-i, --ignore <pattern> Pattern to ignore (env: IMMICH_IGNORE_PATHS)
|
||||||
-h, --skip-hash Don't hash files before upload (default: false, env: IMMICH_SKIP_HASH)
|
-h, --skip-hash Don't hash files before upload (default: false, env: IMMICH_SKIP_HASH)
|
||||||
-H, --include-hidden Include hidden folders (default: false, env: IMMICH_INCLUDE_HIDDEN)
|
-H, --include-hidden Include hidden folders (default: false, env: IMMICH_INCLUDE_HIDDEN)
|
||||||
-a, --album Automatically create albums based on folder name (default: false, env: IMMICH_AUTO_CREATE_ALBUM)
|
-a, --album Automatically create albums based on folder name (default: false, env: IMMICH_AUTO_CREATE_ALBUM)
|
||||||
-A, --album-name <name> Add all assets to specified album (env: IMMICH_ALBUM_NAME)
|
-A, --album-name <name> Add all assets to specified album (env: IMMICH_ALBUM_NAME)
|
||||||
-n, --dry-run Don't perform any actions, just show what will be done (default: false, env: IMMICH_DRY_RUN)
|
-n, --dry-run Don't perform any actions, just show what will be done (default: false, env: IMMICH_DRY_RUN)
|
||||||
-c, --concurrency <number> Number of assets to upload at the same time (default: 4, env: IMMICH_UPLOAD_CONCURRENCY)
|
-c, --concurrency <number> Number of assets to upload at the same time (default: 4, env: IMMICH_UPLOAD_CONCURRENCY)
|
||||||
--delete Delete local assets after upload (env: IMMICH_DELETE_ASSETS)
|
-j, --json-output Output detailed information in json format (default: false, env: IMMICH_JSON_OUTPUT)
|
||||||
--help display help for command
|
--delete Delete local assets after upload (env: IMMICH_DELETE_ASSETS)
|
||||||
|
--no-progress Hide progress bars (env: IMMICH_PROGRESS_BAR)
|
||||||
|
--watch Watch for changes and upload automatically (default: false, env: IMMICH_WATCH_CHANGES)
|
||||||
|
--help display help for command
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
@@ -172,6 +175,16 @@ By default, hidden files are skipped. If you want to include hidden files, use t
|
|||||||
immich upload --include-hidden --recursive directory/
|
immich upload --include-hidden --recursive directory/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can use the `--json-output` option to get a json printed which includes
|
||||||
|
three keys: `newFiles`, `duplicates` and `newAssets`. Due to some logging
|
||||||
|
output you will need to strip the first three lines of output to get the json.
|
||||||
|
For example to get a list of files that would be uploaded for further
|
||||||
|
processing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
immich upload --dry-run . | tail -n +4 | jq .newFiles[]
|
||||||
|
```
|
||||||
|
|
||||||
### Obtain the API Key
|
### Obtain the API Key
|
||||||
|
|
||||||
The API key can be obtained in the user setting panel on the web interface.
|
The API key can be obtained in the user setting panel on the web interface.
|
||||||
|
|||||||
@@ -121,6 +121,6 @@ Once this is done, you can continue to step 3 of "Basic Setup".
|
|||||||
|
|
||||||
[hw-file]: https://github.com/immich-app/immich/releases/latest/download/hwaccel.transcoding.yml
|
[hw-file]: https://github.com/immich-app/immich/releases/latest/download/hwaccel.transcoding.yml
|
||||||
[nvct]: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html
|
[nvct]: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html
|
||||||
[jellyfin-lp]: https://jellyfin.org/docs/general/administration/hardware-acceleration/intel/#configure-and-verify-lp-mode-on-linux
|
[jellyfin-lp]: https://jellyfin.org/docs/general/post-install/transcoding/hardware-acceleration/intel#low-power-encoding
|
||||||
[jellyfin-kernel-bug]: https://jellyfin.org/docs/general/administration/hardware-acceleration/intel/#known-issues-and-limitations
|
[jellyfin-kernel-bug]: https://jellyfin.org/docs/general/post-install/transcoding/hardware-acceleration/intel#known-issues-and-limitations-on-linux
|
||||||
[libmali-rockchip]: https://github.com/tsukumijima/libmali-rockchip/releases
|
[libmali-rockchip]: https://github.com/tsukumijima/libmali-rockchip/releases
|
||||||
|
|||||||
BIN
docs/docs/features/img/gcast-enable.webp
Normal file
BIN
docs/docs/features/img/gcast-enable.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
@@ -72,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. It is possible to trigger the cleanup by clicking "Scan all libraries" in the library managment page.
|
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 management page.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -112,12 +112,15 @@ _Remember to run `docker compose up -d` to register the changes. Make sure you c
|
|||||||
|
|
||||||
These actions must be performed by the Immich administrator.
|
These actions must be performed by the Immich administrator.
|
||||||
|
|
||||||
- Click on Administration -> Libraries
|
- Click on your avatar on the upper right corner
|
||||||
- Click on Create External Library
|
- Click on Administration -> External Libraries
|
||||||
|
- Click on Create an external library…
|
||||||
- Select which user owns the library, this can not be changed later
|
- Select which user owns the library, this can not be changed later
|
||||||
- Enter `/mnt/media/christmas-trip` then click Add
|
- Enter `/mnt/media/christmas-trip` then click Add
|
||||||
- Click on Save
|
- Click on Save
|
||||||
- Click the drop-down menu on the newly created library
|
- Click the drop-down menu on the newly created library
|
||||||
|
- Click on Scan
|
||||||
|
- Click the drop-down menu on the newly created library
|
||||||
- Click on Rename Library and rename it to "Christmas Trip"
|
- Click on Rename Library and rename it to "Christmas Trip"
|
||||||
|
|
||||||
NOTE: We have to use the `/mnt/media/christmas-trip` path and not the `/mnt/nas/christmas-trip` path since all paths have to be what the Docker containers see.
|
NOTE: We have to use the `/mnt/media/christmas-trip` path and not the `/mnt/nas/christmas-trip` path since all paths have to be what the Docker containers see.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import TabItem from '@theme/TabItem';
|
|||||||
|
|
||||||
Immich uses Postgres as its search database for both metadata and contextual CLIP search.
|
Immich uses Postgres as its search database for both metadata and contextual CLIP search.
|
||||||
|
|
||||||
Contextual CLIP search is powered by the [pgvecto.rs](https://github.com/tensorchord/pgvecto.rs) extension, utilizing machine learning models like [CLIP](https://openai.com/research/clip) to provide relevant search results. This allows for freeform searches without requiring specific keywords in the image or video metadata.
|
Contextual CLIP search is powered by the [VectorChord](https://github.com/tensorchord/VectorChord) extension, utilizing machine learning models like [CLIP](https://openai.com/research/clip) to provide relevant search results. This allows for freeform searches without requiring specific keywords in the image or video metadata.
|
||||||
|
|
||||||
## Advanced Search Filters
|
## Advanced Search Filters
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ Memory and execution time estimates were obtained without acceleration on a 7800
|
|||||||
|
|
||||||
**Execution Time (ms)**: After warming up the model with one pass, the mean execution time of 100 passes with the same input.
|
**Execution Time (ms)**: After warming up the model with one pass, the mean execution time of 100 passes with the same input.
|
||||||
|
|
||||||
**Memory (MiB)**: The peak RSS usage of the process afer performing the above timing benchmark. Does not include image decoding, concurrent processing, the web server, etc., which are relatively constant factors.
|
**Memory (MiB)**: The peak RSS usage of the process after performing the above timing benchmark. Does not include image decoding, concurrent processing, the web server, etc., which are relatively constant factors.
|
||||||
|
|
||||||
**Recall (%)**: Evaluated on Crossmodal-3600, the average of the recall@1, recall@5 and recall@10 results for zeroshot image retrieval. Chinese (Simplified), English, French, German, Italian, Japanese, Korean, Polish, Russian, Spanish and Turkish are additionally tested on XTD-10. Chinese (Simplified) and English are additionally tested on Flickr30k. The recall metrics are the average across all tested datasets.
|
**Recall (%)**: Evaluated on Crossmodal-3600, the average of the recall@1, recall@5 and recall@10 results for zeroshot image retrieval. Chinese (Simplified), English, French, German, Italian, Japanese, Korean, Polish, Russian, Spanish and Turkish are additionally tested on XTD-10. Chinese (Simplified) and English are additionally tested on Flickr30k. The recall metrics are the average across all tested datasets.
|
||||||
|
|
||||||
|
|||||||
@@ -2,53 +2,13 @@
|
|||||||
sidebar_position: 30
|
sidebar_position: 30
|
||||||
---
|
---
|
||||||
|
|
||||||
import CodeBlock from '@theme/CodeBlock';
|
|
||||||
import ExampleEnv from '!!raw-loader!../../../docker/example.env';
|
|
||||||
|
|
||||||
# Docker Compose [Recommended]
|
# Docker Compose [Recommended]
|
||||||
|
|
||||||
Docker Compose is the recommended method to run Immich in production. Below are the steps to deploy Immich with Docker Compose.
|
Docker Compose is the recommended method to run Immich in production. Below are the steps to deploy Immich with Docker Compose.
|
||||||
|
|
||||||
## Step 1 - Download the required files
|
import DockerComposeSteps from '/docs/partials/_docker-compose-install-steps.mdx';
|
||||||
|
|
||||||
Create a directory of your choice (e.g. `./immich-app`) to hold the `docker-compose.yml` and `.env` files.
|
<DockerComposeSteps />
|
||||||
|
|
||||||
```bash title="Move to the directory you created"
|
|
||||||
mkdir ./immich-app
|
|
||||||
cd ./immich-app
|
|
||||||
```
|
|
||||||
|
|
||||||
Download [`docker-compose.yml`][compose-file] and [`example.env`][env-file] by running the following commands:
|
|
||||||
|
|
||||||
```bash title="Get docker-compose.yml file"
|
|
||||||
wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash title="Get .env file"
|
|
||||||
wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.env
|
|
||||||
```
|
|
||||||
|
|
||||||
You can alternatively download these two files from your browser and move them to the directory that you created, in which case ensure that you rename `example.env` to `.env`.
|
|
||||||
|
|
||||||
## Step 2 - Populate the .env file with custom values
|
|
||||||
|
|
||||||
<CodeBlock language="bash" title="Default environmental variable content">
|
|
||||||
{ExampleEnv}
|
|
||||||
</CodeBlock>
|
|
||||||
|
|
||||||
- Populate `UPLOAD_LOCATION` with your preferred location for storing backup assets. It should be a new directory on the server with enough free space.
|
|
||||||
- Consider changing `DB_PASSWORD` to a custom value. Postgres is not publicly exposed, so this password is only used for local authentication.
|
|
||||||
To avoid issues with Docker parsing this value, it is best to use only the characters `A-Za-z0-9`. `pwgen` is a handy utility for this.
|
|
||||||
- Set your timezone by uncommenting the `TZ=` line.
|
|
||||||
- Populate custom database information if necessary.
|
|
||||||
|
|
||||||
## Step 3 - Start the containers
|
|
||||||
|
|
||||||
From the directory you created in Step 1 (which should now contain your customized `docker-compose.yml` and `.env` files), run the following command to start Immich as a background service:
|
|
||||||
|
|
||||||
```bash title="Start the containers"
|
|
||||||
docker compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
:::info Docker version
|
:::info Docker version
|
||||||
If you get an error such as `unknown shorthand flag: 'd' in -d` or `open <location of your .env file>: permission denied`, you are probably running the wrong Docker version. (This happens, for example, with the docker.io package in Ubuntu 22.04.3 LTS.) You can correct the problem by following the complete [Docker Engine install](https://docs.docker.com/engine/install/) procedure for your distribution, crucially the "Uninstall old versions" and "Install using the apt/rpm repository" sections. These replace the distro's Docker packages with Docker's official ones.
|
If you get an error such as `unknown shorthand flag: 'd' in -d` or `open <location of your .env file>: permission denied`, you are probably running the wrong Docker version. (This happens, for example, with the docker.io package in Ubuntu 22.04.3 LTS.) You can correct the problem by following the complete [Docker Engine install](https://docs.docker.com/engine/install/) procedure for your distribution, crucially the "Uninstall old versions" and "Install using the apt/rpm repository" sections. These replace the distro's Docker packages with Docker's official ones.
|
||||||
@@ -70,6 +30,3 @@ 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 and [upgrade instructions](/docs/install/upgrading.md).
|
Read the [Post Installation](/docs/install/post-install.mdx) steps and [upgrade instructions](/docs/install/upgrading.md).
|
||||||
|
|
||||||
[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
|
|
||||||
|
|||||||
@@ -72,20 +72,21 @@ Information on the current workers can be found [here](/docs/administration/jobs
|
|||||||
|
|
||||||
## Database
|
## Database
|
||||||
|
|
||||||
| 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_SSL_MODE` | Database SSL mode | | server |
|
||||||
| `DB_SKIP_MIGRATIONS` | Whether to skip running migrations on startup (one of [`true`, `false`]) | `false` | server |
|
| `DB_VECTOR_EXTENSION`<sup>\*2</sup> | Database vector extension (one of [`vectorchord`, `pgvector`, `pgvecto.rs`]) | | 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`.
|
||||||
|
|
||||||
\*2: This setting cannot be changed after the server has successfully started up.
|
\*2: If not provided, the appropriate extension to use is auto-detected at startup by introspecting the database. When multiple extensions are installed, the order of preference is VectorChord, pgvecto.rs, pgvector.
|
||||||
|
|
||||||
:::info
|
:::info
|
||||||
|
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ When you're all done, you should have the following:
|
|||||||
- `./docker/immich-app/postgres`
|
- `./docker/immich-app/postgres`
|
||||||
- `./docker/immich-app/library`
|
- `./docker/immich-app/library`
|
||||||
|
|
||||||
Download [`docker-compose.yml`](https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml) and [`example.env`](https://github.com/immich-app/immich/releases/latest/download/example.env) to your computer. Upload the files to the `./docker/immich-app` directory.
|
Download [`docker-compose.yml`](https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml) and [`example.env`](https://github.com/immich-app/immich/releases/latest/download/example.env) to your computer. Upload the files to the `./docker/immich-app` directory, and rename `example.env` to `.env`.
|
||||||
|
|
||||||
## Step 2 - Populate the .env file with custom values
|
## Step 2 - Populate the .env file with custom values
|
||||||
|
|
||||||
Follow [Step 2 in Docker Compose](./docker-compose#step-2---populate-the-env-file-with-custom-values) for instructions on customizing the `.env` file, and then return back to this guide to continue.
|
Follow [Step 2 in Docker Compose](/docs/install/docker-compose#step-2---populate-the-env-file-with-custom-values) for instructions on customizing the `.env` file, and then return back to this guide to continue.
|
||||||
|
|
||||||
## Step 3 - Create a new project in Container Manager
|
## Step 3 - Create a new project in Container Manager
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
sidebar_position: 80
|
sidebar_position: 80
|
||||||
---
|
---
|
||||||
|
|
||||||
# TrueNAS SCALE [Community]
|
# TrueNAS [Community]
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
This is a community contribution and not officially supported by the Immich team, but included here for convenience.
|
This is a community contribution and not officially supported by the Immich team, but included here for convenience.
|
||||||
@@ -12,17 +12,17 @@ Community support can be found in the dedicated channel on the [Discord Server](
|
|||||||
**Please report app issues to the corresponding [Github Repository](https://github.com/truenas/charts/tree/master/community/immich).**
|
**Please report app issues to the corresponding [Github Repository](https://github.com/truenas/charts/tree/master/community/immich).**
|
||||||
:::
|
:::
|
||||||
|
|
||||||
Immich can easily be installed on TrueNAS SCALE via the **Community** train application.
|
Immich can easily be installed on TrueNAS Community Edition via the **Community** train application.
|
||||||
Consider reviewing the TrueNAS [Apps tutorial](https://www.truenas.com/docs/scale/scaletutorials/apps/) if you have not previously configured applications on your system.
|
Consider reviewing the TrueNAS [Apps resources](https://apps.truenas.com/getting-started/) if you have not previously configured applications on your system.
|
||||||
|
|
||||||
TrueNAS SCALE makes installing and updating Immich easy, but you must use the Immich web portal and mobile app to configure accounts and access libraries.
|
TrueNAS Community Edition makes installing and updating Immich easy, but you must use the Immich web portal and mobile app to configure accounts and access libraries.
|
||||||
|
|
||||||
## First Steps
|
## First Steps
|
||||||
|
|
||||||
The Immich app in TrueNAS SCALE installs, completes the initial configuration, then starts the Immich web portal.
|
The Immich app in TrueNAS Community Edition installs, completes the initial configuration, then starts the Immich web portal.
|
||||||
When updates become available, SCALE alerts and provides easy updates.
|
When updates become available, TrueNAS alerts and provides easy updates.
|
||||||
|
|
||||||
Before installing the Immich app in SCALE, review the [Environment Variables](#environment-variables) documentation to see if you want to configure any during installation.
|
Before installing the Immich app in TrueNAS, review the [Environment Variables](#environment-variables) documentation to see if you want to configure any during installation.
|
||||||
You may also configure environment variables at any time after deploying the application.
|
You may also configure environment variables at any time after deploying the application.
|
||||||
|
|
||||||
### Setting up Storage Datasets
|
### Setting up Storage Datasets
|
||||||
@@ -126,9 +126,9 @@ className="border rounded-xl"
|
|||||||
|
|
||||||
Accept the default port `30041` in **WebUI Port** or enter a custom port number.
|
Accept the default port `30041` in **WebUI Port** or enter a custom port number.
|
||||||
:::info Allowed Port Numbers
|
:::info Allowed Port Numbers
|
||||||
Only numbers within the range 9000-65535 may be used on SCALE versions below TrueNAS Scale 24.10 Electric Eel.
|
Only numbers within the range 9000-65535 may be used on TrueNAS versions below TrueNAS Community Edition 24.10 Electric Eel.
|
||||||
|
|
||||||
Regardless of version, to avoid port conflicts, don't use [ports on this list](https://www.truenas.com/docs/references/defaultports/).
|
Regardless of version, to avoid port conflicts, don't use [ports on this list](https://www.truenas.com/docs/solutions/optimizations/security/#truenas-default-ports).
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### Storage Configuration
|
### Storage Configuration
|
||||||
@@ -173,7 +173,7 @@ className="border rounded-xl"
|
|||||||
|
|
||||||
You may configure [External Libraries](/docs/features/libraries) by mounting them using **Additional Storage**.
|
You may configure [External Libraries](/docs/features/libraries) by mounting them using **Additional Storage**.
|
||||||
The **Mount Path** is the location you will need to copy and paste into the External Library settings within Immich.
|
The **Mount Path** is the location you will need to copy and paste into the External Library settings within Immich.
|
||||||
The **Host Path** is the location on the TrueNAS SCALE server where your external library is located.
|
The **Host Path** is the location on the TrueNAS Community Edition server where your external library is located.
|
||||||
|
|
||||||
<!-- A section for Labels would go here but I don't know what they do. -->
|
<!-- A section for Labels would go here but I don't know what they do. -->
|
||||||
|
|
||||||
@@ -188,17 +188,17 @@ className="border rounded-xl"
|
|||||||
|
|
||||||
Accept the default **CPU** limit of `2` threads or specify the number of threads (CPUs with Multi-/Hyper-threading have 2 threads per core).
|
Accept the default **CPU** limit of `2` threads or specify the number of threads (CPUs with Multi-/Hyper-threading have 2 threads per core).
|
||||||
|
|
||||||
Accept the default **Memory** limit of `4096` MB or specify the number of MB of RAM. If you're using Machine Learning you should probably set this above 8000 MB.
|
Specify the **Memory** limit in MB of RAM. Immich recommends at least 6000 MB (6GB). If you selected **Enable Machine Learning** in **Immich Configuration**, you should probably set this above 8000 MB.
|
||||||
|
|
||||||
:::info Older SCALE Versions
|
:::info Older TrueNAS Versions
|
||||||
Before TrueNAS SCALE version 24.10 Electric Eel:
|
Before TrueNAS Community Edition version 24.10 Electric Eel:
|
||||||
|
|
||||||
The **CPU** value was specified in a different format with a default of `4000m` which is 4 threads.
|
The **CPU** value was specified in a different format with a default of `4000m` which is 4 threads.
|
||||||
|
|
||||||
The **Memory** value was specified in a different format with a default of `8Gi` which is 8 GiB of RAM. The value was specified in bytes or a number with a measurement suffix. Examples: `129M`, `123Mi`, `1000000000`
|
The **Memory** value was specified in a different format with a default of `8Gi` which is 8 GiB of RAM. The value was specified in bytes or a number with a measurement suffix. Examples: `129M`, `123Mi`, `1000000000`
|
||||||
:::
|
:::
|
||||||
|
|
||||||
Enable **GPU Configuration** options if you have a GPU that you will use for [Hardware Transcoding](/docs/features/hardware-transcoding) and/or [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md). More info: [GPU Passthrough Docs for TrueNAS Apps](https://www.truenas.com/docs/truenasapps/#gpu-passthrough)
|
Enable **GPU Configuration** options if you have a GPU that you will use for [Hardware Transcoding](/docs/features/hardware-transcoding) and/or [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md). More info: [GPU Passthrough Docs for TrueNAS Apps](https://apps.truenas.com/managing-apps/installing-apps/#gpu-passthrough)
|
||||||
|
|
||||||
### Install
|
### Install
|
||||||
|
|
||||||
@@ -240,7 +240,7 @@ className="border rounded-xl"
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
:::info
|
:::info
|
||||||
Some Environment Variables are not available for the TrueNAS SCALE app. This is mainly because they can be configured through GUI options in the [Edit Immich screen](#edit-app-settings).
|
Some Environment Variables are not available for the TrueNAS Community Edition app. This is mainly because they can be configured through GUI options in the [Edit Immich screen](#edit-app-settings).
|
||||||
|
|
||||||
Some examples are: `IMMICH_VERSION`, `UPLOAD_LOCATION`, `DB_DATA_LOCATION`, `TZ`, `IMMICH_LOG_LEVEL`, `DB_PASSWORD`, `REDIS_PASSWORD`.
|
Some examples are: `IMMICH_VERSION`, `UPLOAD_LOCATION`, `DB_DATA_LOCATION`, `TZ`, `IMMICH_LOG_LEVEL`, `DB_PASSWORD`, `REDIS_PASSWORD`.
|
||||||
:::
|
:::
|
||||||
@@ -251,7 +251,7 @@ Some examples are: `IMMICH_VERSION`, `UPLOAD_LOCATION`, `DB_DATA_LOCATION`, `TZ`
|
|||||||
Make sure to read the general [upgrade instructions](/docs/install/upgrading.md).
|
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, TrueNAS alerts and provides easy updates.
|
||||||
To update the app to the latest version:
|
To update the app to the latest version:
|
||||||
|
|
||||||
- Go to the **Installed Applications** screen and select Immich from the list of installed applications.
|
- Go to the **Installed Applications** screen and select Immich from the list of installed applications.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
sidebar_position: 2
|
sidebar_position: 3
|
||||||
---
|
---
|
||||||
|
|
||||||
# Comparison
|
# Comparison
|
||||||
|
|||||||
BIN
docs/docs/overview/img/social-preview-light.webp
Normal file
BIN
docs/docs/overview/img/social-preview-light.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 233 KiB |
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
sidebar_position: 3
|
sidebar_position: 2
|
||||||
---
|
---
|
||||||
|
|
||||||
# Quick start
|
# Quick start
|
||||||
@@ -10,11 +10,20 @@ to install and use it.
|
|||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
Check the [requirements page](/docs/install/requirements) to get started.
|
- A system with at least 4GB of RAM and 2 CPU cores.
|
||||||
|
- [Docker](https://docs.docker.com/engine/install/)
|
||||||
|
|
||||||
|
> For a more detailed list of requirements, see the [requirements page](/docs/install/requirements).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Set up the server
|
## Set up the server
|
||||||
|
|
||||||
Follow the [Docker Compose (Recommended)](/docs/install/docker-compose) instructions to install the server.
|
import DockerComposeSteps from '/docs/partials/_docker-compose-install-steps.mdx';
|
||||||
|
|
||||||
|
<DockerComposeSteps />
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Try the web app
|
## Try the web app
|
||||||
|
|
||||||
@@ -26,6 +35,8 @@ Try uploading a picture from your browser.
|
|||||||
|
|
||||||
<img src={require('./img/upload-button.webp').default} title="Upload button" />
|
<img src={require('./img/upload-button.webp').default} title="Upload button" />
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Try the mobile app
|
## Try the mobile app
|
||||||
|
|
||||||
### Download the Mobile App
|
### Download the Mobile App
|
||||||
@@ -56,6 +67,8 @@ You can select the **Jobs** tab to see Immich processing your photos.
|
|||||||
|
|
||||||
<img src={require('/docs/guides/img/jobs-tab.webp').default} title="Jobs tab" width={300} />
|
<img src={require('/docs/guides/img/jobs-tab.webp').default} title="Jobs tab" width={300} />
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Review the database backup and restore process
|
## Review the database backup and restore process
|
||||||
|
|
||||||
Immich has built-in database backups. You can refer to the
|
Immich has built-in database backups. You can refer to the
|
||||||
@@ -65,6 +78,8 @@ Immich has built-in database backups. You can refer to the
|
|||||||
The database only contains metadata and user information. You must setup manual backups of the images and videos stored in `UPLOAD_LOCATION`.
|
The database only contains metadata and user information. You must setup manual backups of the images and videos stored in `UPLOAD_LOCATION`.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Where to go from here?
|
## Where to go from here?
|
||||||
|
|
||||||
You may decide you'd like to install the server a different way; the Install category on the left menu provides many options.
|
You may decide you'd like to install the server a different way; the Install category on the left menu provides many options.
|
||||||
|
|||||||
@@ -2,9 +2,13 @@
|
|||||||
sidebar_position: 1
|
sidebar_position: 1
|
||||||
---
|
---
|
||||||
|
|
||||||
# Introduction
|
# Welcome to Immich
|
||||||
|
|
||||||
<img src={require('./img/feature-panel.webp').default} alt="Immich - Self-hosted photos and videos backup tool" />
|
<img
|
||||||
|
src={require('./img/social-preview-light.webp').default}
|
||||||
|
alt="Immich - Self-hosted photos and videos backup tool"
|
||||||
|
data-theme="light"
|
||||||
|
/>
|
||||||
|
|
||||||
## Welcome!
|
## Welcome!
|
||||||
|
|
||||||
43
docs/docs/partials/_docker-compose-install-steps.mdx
Normal file
43
docs/docs/partials/_docker-compose-install-steps.mdx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import CodeBlock from '@theme/CodeBlock';
|
||||||
|
import ExampleEnv from '!!raw-loader!../../../docker/example.env';
|
||||||
|
|
||||||
|
### Step 1 - Download the required files
|
||||||
|
|
||||||
|
Create a directory of your choice (e.g. `./immich-app`) to hold the `docker-compose.yml` and `.env` files.
|
||||||
|
|
||||||
|
```bash title="Move to the directory you created"
|
||||||
|
mkdir ./immich-app
|
||||||
|
cd ./immich-app
|
||||||
|
```
|
||||||
|
|
||||||
|
Download [`docker-compose.yml`](https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml) and [`example.env`](https://github.com/immich-app/immich/releases/latest/download/example.env) by running the following commands:
|
||||||
|
|
||||||
|
```bash title="Get docker-compose.yml file"
|
||||||
|
wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash title="Get .env file"
|
||||||
|
wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.env
|
||||||
|
```
|
||||||
|
|
||||||
|
You can alternatively download these two files from your browser and move them to the directory that you created, in which case ensure that you rename `example.env` to `.env`.
|
||||||
|
|
||||||
|
### Step 2 - Populate the .env file with custom values
|
||||||
|
|
||||||
|
<CodeBlock language="bash" title="Default environmental variable content">
|
||||||
|
{ExampleEnv}
|
||||||
|
</CodeBlock>
|
||||||
|
|
||||||
|
- Populate `UPLOAD_LOCATION` with your preferred location for storing backup assets. It should be a new directory on the server with enough free space.
|
||||||
|
- Consider changing `DB_PASSWORD` to a custom value. Postgres is not publicly exposed, so this password is only used for local authentication.
|
||||||
|
To avoid issues with Docker parsing this value, it is best to use only the characters `A-Za-z0-9`. `pwgen` is a handy utility for this.
|
||||||
|
- Set your timezone by uncommenting the `TZ=` line.
|
||||||
|
- Populate custom database information if necessary.
|
||||||
|
|
||||||
|
### Step 3 - Start the containers
|
||||||
|
|
||||||
|
From the directory you created in Step 1 (which should now contain your customized `docker-compose.yml` and `.env` files), run the following command to start Immich as a background service:
|
||||||
|
|
||||||
|
```bash title="Start the containers"
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
@@ -95,7 +95,7 @@ const config = {
|
|||||||
position: 'right',
|
position: 'right',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
to: '/docs/overview/introduction',
|
to: '/docs/overview/welcome',
|
||||||
position: 'right',
|
position: 'right',
|
||||||
label: 'Docs',
|
label: 'Docs',
|
||||||
},
|
},
|
||||||
@@ -124,6 +124,12 @@ const config = {
|
|||||||
label: 'Discord',
|
label: 'Discord',
|
||||||
position: 'right',
|
position: 'right',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'html',
|
||||||
|
position: 'right',
|
||||||
|
value:
|
||||||
|
'<a href="https://buy.immich.app" target="_blank" class="no-underline hover:no-underline"><button class="buy-button bg-immich-primary dark:bg-immich-dark-primary text-white dark:text-black rounded-xl">Buy Immich</button></a>',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
@@ -134,7 +140,7 @@ const config = {
|
|||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
label: 'Welcome',
|
label: 'Welcome',
|
||||||
to: '/docs/overview/introduction',
|
to: '/docs/overview/welcome',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Installation',
|
label: 'Installation',
|
||||||
|
|||||||
@@ -57,6 +57,6 @@
|
|||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "22.14.0"
|
"node": "22.16.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,11 +44,6 @@ const projects: CommunityProjectProps[] = [
|
|||||||
'Lightroom plugin to publish, export photos from Lightroom to Immich. Import from Immich to Lightroom is also supported.',
|
'Lightroom plugin to publish, export photos from Lightroom to Immich. Import from Immich to Lightroom is also supported.',
|
||||||
url: 'https://blog.fokuspunk.de/lrc-immich-plugin/',
|
url: 'https://blog.fokuspunk.de/lrc-immich-plugin/',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: 'Immich Duplicate Finder',
|
|
||||||
description: 'Webapp that uses machine learning to identify near-duplicate images.',
|
|
||||||
url: 'https://github.com/vale46n1/immich_duplicate_finder',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: 'Immich-Tiktok-Remover',
|
title: 'Immich-Tiktok-Remover',
|
||||||
description: 'Script to search for and remove TikTok videos from your Immich library.',
|
description: 'Script to search for and remove TikTok videos from your Immich library.',
|
||||||
|
|||||||
@@ -7,14 +7,22 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Be+Vietnam+Pro:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
|
@font-face {
|
||||||
|
font-family: 'Overpass';
|
||||||
body {
|
src: url('/fonts/overpass/Overpass.ttf') format('truetype-variations');
|
||||||
font-family: 'Be Vietnam Pro', serif;
|
font-weight: 1 999;
|
||||||
font-optical-sizing: auto;
|
font-style: normal;
|
||||||
/* font-size: 1.125rem;
|
|
||||||
ascent-override: 106.25%;
|
ascent-override: 106.25%;
|
||||||
size-adjust: 106.25%; */
|
size-adjust: 106.25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Overpass Mono';
|
||||||
|
src: url('/fonts/overpass/OverpassMono.ttf') format('truetype-variations');
|
||||||
|
font-weight: 1 999;
|
||||||
|
font-style: normal;
|
||||||
|
ascent-override: 106.25%;
|
||||||
|
size-adjust: 106.25%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumbs__link {
|
.breadcrumbs__link {
|
||||||
@@ -29,6 +37,7 @@ img {
|
|||||||
|
|
||||||
/* You can override the default Infima variables here. */
|
/* You can override the default Infima variables here. */
|
||||||
:root {
|
:root {
|
||||||
|
font-family: 'Overpass', sans-serif;
|
||||||
--ifm-color-primary: #4250af;
|
--ifm-color-primary: #4250af;
|
||||||
--ifm-color-primary-dark: #4250af;
|
--ifm-color-primary-dark: #4250af;
|
||||||
--ifm-color-primary-darker: #4250af;
|
--ifm-color-primary-darker: #4250af;
|
||||||
@@ -59,14 +68,12 @@ div[class^='announcementBar_'] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu__link {
|
.menu__link {
|
||||||
padding: 10px;
|
padding: 10px 10px 10px 16px;
|
||||||
padding-left: 16px;
|
|
||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu__list-item-collapsible {
|
.menu__list-item-collapsible {
|
||||||
border-radius: 10px;
|
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
}
|
}
|
||||||
@@ -83,3 +90,12 @@ div[class*='navbar__items'] > li:has(a[class*='version-switcher-34ab39']) {
|
|||||||
code {
|
code {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.buy-button {
|
||||||
|
padding: 8px 14px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
font-family: 'Overpass', sans-serif;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 0 5px 2px rgba(181, 206, 254, 0.4);
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {
|
|||||||
mdiBug,
|
mdiBug,
|
||||||
mdiCalendarToday,
|
mdiCalendarToday,
|
||||||
mdiCrosshairsOff,
|
mdiCrosshairsOff,
|
||||||
|
mdiCrop,
|
||||||
mdiDatabase,
|
mdiDatabase,
|
||||||
mdiLeadPencil,
|
mdiLeadPencil,
|
||||||
mdiLockOff,
|
mdiLockOff,
|
||||||
@@ -12,6 +13,9 @@ import {
|
|||||||
mdiTrashCan,
|
mdiTrashCan,
|
||||||
mdiWeb,
|
mdiWeb,
|
||||||
mdiWrap,
|
mdiWrap,
|
||||||
|
mdiCloudKeyOutline,
|
||||||
|
mdiRegex,
|
||||||
|
mdiCodeJson,
|
||||||
} from '@mdi/js';
|
} from '@mdi/js';
|
||||||
import Layout from '@theme/Layout';
|
import Layout from '@theme/Layout';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@@ -22,6 +26,53 @@ const withLanguage = (date: Date) => (language: string) => date.toLocaleDateStri
|
|||||||
type Item = Omit<TimelineItem, 'done' | 'getDateLabel'> & { date: Date };
|
type Item = Omit<TimelineItem, 'done' | 'getDateLabel'> & { date: Date };
|
||||||
|
|
||||||
const items: Item[] = [
|
const items: Item[] = [
|
||||||
|
{
|
||||||
|
icon: mdiRegex,
|
||||||
|
iconColor: 'purple',
|
||||||
|
title: 'Zitadel Actions are cursed',
|
||||||
|
description:
|
||||||
|
"Zitadel is cursed because its custom scripting feature is executed with a JS engine that doesn't support regex named capture groups.",
|
||||||
|
link: {
|
||||||
|
url: 'https://github.com/dop251/goja',
|
||||||
|
text: 'Go JS engine',
|
||||||
|
},
|
||||||
|
date: new Date(2025, 5, 4),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: mdiCloudKeyOutline,
|
||||||
|
iconColor: '#0078d4',
|
||||||
|
title: 'Entra is cursed',
|
||||||
|
description:
|
||||||
|
"Microsoft Entra supports PKCE, but doesn't include it in its OpenID discovery document. This leads to clients thinking PKCE isn't available.",
|
||||||
|
link: {
|
||||||
|
url: 'https://github.com/immich-app/immich/pull/18725',
|
||||||
|
text: '#18725',
|
||||||
|
},
|
||||||
|
date: new Date(2025, 4, 30),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: mdiCrop,
|
||||||
|
iconColor: 'tomato',
|
||||||
|
title: 'Image dimensions in EXIF metadata are cursed',
|
||||||
|
description:
|
||||||
|
'The dimensions in EXIF metadata can be different from the actual dimensions of the image, causing issues with cropping and resizing.',
|
||||||
|
link: {
|
||||||
|
url: 'https://github.com/immich-app/immich/pull/17974',
|
||||||
|
text: '#17974',
|
||||||
|
},
|
||||||
|
date: new Date(2025, 4, 5),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: mdiCodeJson,
|
||||||
|
iconColor: 'yellow',
|
||||||
|
title: 'YAML whitespace is cursed',
|
||||||
|
description: 'YAML whitespaces are often handled in unintuitive ways.',
|
||||||
|
link: {
|
||||||
|
url: 'https://github.com/immich-app/immich/pull/17309',
|
||||||
|
text: '#17309',
|
||||||
|
},
|
||||||
|
date: new Date(2025, 3, 1),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: mdiMicrosoftWindows,
|
icon: mdiMicrosoftWindows,
|
||||||
iconColor: '#357EC7',
|
iconColor: '#357EC7',
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import Layout from '@theme/Layout';
|
|||||||
import { discordPath, discordViewBox } from '@site/src/components/svg-paths';
|
import { discordPath, discordViewBox } from '@site/src/components/svg-paths';
|
||||||
import ThemedImage from '@theme/ThemedImage';
|
import ThemedImage from '@theme/ThemedImage';
|
||||||
import Icon from '@mdi/react';
|
import Icon from '@mdi/react';
|
||||||
import { mdiAndroid } from '@mdi/js';
|
|
||||||
function HomepageHeader() {
|
function HomepageHeader() {
|
||||||
return (
|
return (
|
||||||
<header>
|
<header>
|
||||||
@@ -13,11 +13,14 @@ 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">
|
||||||
<ThemedImage
|
<a href="https://futo.org" target="_blank" rel="noopener noreferrer">
|
||||||
sources={{ dark: 'img/logomark-dark.svg', light: 'img/logomark-light.svg' }}
|
<ThemedImage
|
||||||
className="h-[115px] w-[115px] mb-2 antialiased rounded-none"
|
sources={{ dark: 'img/logomark-dark-with-futo.svg', light: 'img/logomark-light-with-futo.svg' }}
|
||||||
alt="Immich logo"
|
className="h-[125px] w-[125px] mb-2 antialiased rounded-none"
|
||||||
/>
|
alt="Immich logo"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
|
||||||
<div className="mt-8">
|
<div className="mt-8">
|
||||||
<p className="text-3xl md:text-5xl sm:leading-tight mb-1 font-extrabold text-black/90 dark:text-white px-4">
|
<p className="text-3xl md:text-5xl sm:leading-tight mb-1 font-extrabold text-black/90 dark:text-white px-4">
|
||||||
Self-hosted{' '}
|
Self-hosted{' '}
|
||||||
@@ -28,7 +31,7 @@ function HomepageHeader() {
|
|||||||
solution<span className="block"></span>
|
solution<span className="block"></span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className="max-w-1/4 m-auto mt-4 px-4">
|
<p className="max-w-1/4 m-auto mt-4 px-4 text-lg text-gray-700 dark:text-gray-100">
|
||||||
Easily back up, organize, and manage your photos on your own server. Immich helps you
|
Easily back up, organize, and manage your photos on your own server. Immich helps you
|
||||||
<span className="sm:block"></span> browse, search and organize your photos and videos with ease, without
|
<span className="sm:block"></span> browse, search and organize your photos and videos with ease, without
|
||||||
sacrificing your privacy.
|
sacrificing your privacy.
|
||||||
@@ -36,27 +39,21 @@ function HomepageHeader() {
|
|||||||
</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"
|
||||||
to="docs/overview/introduction"
|
to="docs/overview/quick-start"
|
||||||
>
|
>
|
||||||
Get started
|
Get Started
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
className="flex place-items-center place-content-center py-3 px-8 border bg-immich-primary/10 dark:bg-gray-300 rounded-xl hover:no-underline text-immich-primary dark:text-immich-dark-bg font-bold uppercase"
|
className="flex place-items-center place-content-center py-3 px-8 border bg-white/90 dark:bg-gray-300 rounded-xl hover:no-underline text-immich-primary dark:text-immich-dark-bg font-bold"
|
||||||
to="https://demo.immich.app/"
|
to="https://demo.immich.app/"
|
||||||
>
|
>
|
||||||
Demo
|
Open Demo
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Link
|
|
||||||
className="flex place-items-center place-content-center py-3 px-8 border bg-immich-primary/10 dark:bg-gray-300 rounded-xl hover:no-underline text-immich-primary dark:text-immich-dark-bg font-bold uppercase"
|
|
||||||
to="https://immich.store"
|
|
||||||
>
|
|
||||||
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-8 flex gap-1 font-medium place-items-center place-content-center text-immich-primary dark:text-immich-dark-primary">
|
||||||
<Icon
|
<Icon
|
||||||
path={discordPath}
|
path={discordPath}
|
||||||
viewBox={discordViewBox} /* viewBox may show an error in your IDE but it is normal. */
|
viewBox={discordViewBox} /* viewBox may show an error in your IDE but it is normal. */
|
||||||
@@ -119,7 +116,7 @@ export default function Home(): JSX.Element {
|
|||||||
<HomepageHeader />
|
<HomepageHeader />
|
||||||
<div className="flex flex-col place-items-center text-center place-content-center dark:bg-immich-dark-bg py-8">
|
<div className="flex flex-col place-items-center text-center place-content-center dark:bg-immich-dark-bg py-8">
|
||||||
<p>This project is available under GNU AGPL v3 license.</p>
|
<p>This project is available under GNU AGPL v3 license.</p>
|
||||||
<p className="text-xs">Privacy should not be a luxury</p>
|
<p className="text-sm">Privacy should not be a luxury</p>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Link from '@docusaurus/Link';
|
|
||||||
import Layout from '@theme/Layout';
|
import Layout from '@theme/Layout';
|
||||||
function HomepageHeader() {
|
function HomepageHeader() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -78,12 +78,14 @@ import {
|
|||||||
mdiLinkEdit,
|
mdiLinkEdit,
|
||||||
mdiTagFaces,
|
mdiTagFaces,
|
||||||
mdiMovieOpenPlayOutline,
|
mdiMovieOpenPlayOutline,
|
||||||
|
mdiCast,
|
||||||
} from '@mdi/js';
|
} from '@mdi/js';
|
||||||
import Layout from '@theme/Layout';
|
import Layout from '@theme/Layout';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Item, Timeline } from '../components/timeline';
|
import { Item, Timeline } from '../components/timeline';
|
||||||
|
|
||||||
const releases = {
|
const releases = {
|
||||||
|
'v1.133.0': new Date(2025, 4, 21),
|
||||||
'v1.130.0': new Date(2025, 2, 25),
|
'v1.130.0': new Date(2025, 2, 25),
|
||||||
'v1.127.0': new Date(2025, 1, 26),
|
'v1.127.0': new Date(2025, 1, 26),
|
||||||
'v1.122.0': new Date(2024, 11, 5),
|
'v1.122.0': new Date(2024, 11, 5),
|
||||||
@@ -216,14 +218,6 @@ const roadmap: Item[] = [
|
|||||||
iconColor: 'indianred',
|
iconColor: 'indianred',
|
||||||
title: 'Stable release',
|
title: 'Stable release',
|
||||||
description: 'Immich goes stable',
|
description: 'Immich goes stable',
|
||||||
getDateLabel: () => 'Planned for early 2025',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
done: false,
|
|
||||||
icon: mdiLockOutline,
|
|
||||||
iconColor: 'sandybrown',
|
|
||||||
title: 'Private/locked photos',
|
|
||||||
description: 'Private assets with extra protections',
|
|
||||||
getDateLabel: () => 'Planned for 2025',
|
getDateLabel: () => 'Planned for 2025',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -245,6 +239,20 @@ const roadmap: Item[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const milestones: Item[] = [
|
const milestones: Item[] = [
|
||||||
|
withRelease({
|
||||||
|
icon: mdiCast,
|
||||||
|
iconColor: 'aqua',
|
||||||
|
title: 'Google Cast (web)',
|
||||||
|
description: 'Cast assets to Google Cast/Chromecast compatible devices',
|
||||||
|
release: 'v1.133.0',
|
||||||
|
}),
|
||||||
|
withRelease({
|
||||||
|
icon: mdiLockOutline,
|
||||||
|
iconColor: 'sandybrown',
|
||||||
|
title: 'Private/locked photos',
|
||||||
|
description: 'Private assets with extra protections',
|
||||||
|
release: 'v1.133.0',
|
||||||
|
}),
|
||||||
withRelease({
|
withRelease({
|
||||||
icon: mdiFolderMultiple,
|
icon: mdiFolderMultiple,
|
||||||
iconColor: 'brown',
|
iconColor: 'brown',
|
||||||
|
|||||||
1
docs/static/_redirects
vendored
1
docs/static/_redirects
vendored
@@ -30,3 +30,4 @@
|
|||||||
/docs/guides/api-album-sync /docs/community-projects 307
|
/docs/guides/api-album-sync /docs/community-projects 307
|
||||||
/docs/guides/remove-offline-files /docs/community-projects 307
|
/docs/guides/remove-offline-files /docs/community-projects 307
|
||||||
/milestones /roadmap 307
|
/milestones /roadmap 307
|
||||||
|
/docs/overview/introduction /docs/overview/welcome 307
|
||||||
176
docs/static/archived-versions.json
vendored
176
docs/static/archived-versions.json
vendored
@@ -1,52 +1,28 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"label": "v1.134.0",
|
||||||
|
"url": "https://v1.134.0.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.133.1",
|
||||||
|
"url": "https://v1.133.1.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.133.0",
|
||||||
|
"url": "https://v1.133.0.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.132.3",
|
"label": "v1.132.3",
|
||||||
"url": "https://v1.132.3.archive.immich.app"
|
"url": "https://v1.132.3.archive.immich.app"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "v1.132.2",
|
|
||||||
"url": "https://v1.132.2.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "v1.132.1",
|
|
||||||
"url": "https://v1.132.1.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "v1.132.0",
|
|
||||||
"url": "https://v1.132.0.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.131.3",
|
"label": "v1.131.3",
|
||||||
"url": "https://v1.131.3.archive.immich.app"
|
"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",
|
"label": "v1.130.3",
|
||||||
"url": "https://v1.130.3.archive.immich.app"
|
"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",
|
"label": "v1.129.0",
|
||||||
"url": "https://v1.129.0.archive.immich.app"
|
"url": "https://v1.129.0.archive.immich.app"
|
||||||
@@ -63,46 +39,14 @@
|
|||||||
"label": "v1.126.1",
|
"label": "v1.126.1",
|
||||||
"url": "https://v1.126.1.archive.immich.app"
|
"url": "https://v1.126.1.archive.immich.app"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "v1.126.0",
|
|
||||||
"url": "https://v1.126.0.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.125.7",
|
"label": "v1.125.7",
|
||||||
"url": "https://v1.125.7.archive.immich.app"
|
"url": "https://v1.125.7.archive.immich.app"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "v1.125.6",
|
|
||||||
"url": "https://v1.125.6.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "v1.125.5",
|
|
||||||
"url": "https://v1.125.5.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "v1.125.3",
|
|
||||||
"url": "https://v1.125.3.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "v1.125.2",
|
|
||||||
"url": "https://v1.125.2.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "v1.125.1",
|
|
||||||
"url": "https://v1.125.1.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.124.2",
|
"label": "v1.124.2",
|
||||||
"url": "https://v1.124.2.archive.immich.app"
|
"url": "https://v1.124.2.archive.immich.app"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "v1.124.1",
|
|
||||||
"url": "https://v1.124.1.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "v1.124.0",
|
|
||||||
"url": "https://v1.124.0.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.123.0",
|
"label": "v1.123.0",
|
||||||
"url": "https://v1.123.0.archive.immich.app"
|
"url": "https://v1.123.0.archive.immich.app"
|
||||||
@@ -111,18 +55,6 @@
|
|||||||
"label": "v1.122.3",
|
"label": "v1.122.3",
|
||||||
"url": "https://v1.122.3.archive.immich.app"
|
"url": "https://v1.122.3.archive.immich.app"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "v1.122.2",
|
|
||||||
"url": "https://v1.122.2.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "v1.122.1",
|
|
||||||
"url": "https://v1.122.1.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "v1.122.0",
|
|
||||||
"url": "https://v1.122.0.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.121.0",
|
"label": "v1.121.0",
|
||||||
"url": "https://v1.121.0.archive.immich.app"
|
"url": "https://v1.121.0.archive.immich.app"
|
||||||
@@ -131,34 +63,14 @@
|
|||||||
"label": "v1.120.2",
|
"label": "v1.120.2",
|
||||||
"url": "https://v1.120.2.archive.immich.app"
|
"url": "https://v1.120.2.archive.immich.app"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "v1.120.1",
|
|
||||||
"url": "https://v1.120.1.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "v1.120.0",
|
|
||||||
"url": "https://v1.120.0.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.119.1",
|
"label": "v1.119.1",
|
||||||
"url": "https://v1.119.1.archive.immich.app"
|
"url": "https://v1.119.1.archive.immich.app"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "v1.119.0",
|
|
||||||
"url": "https://v1.119.0.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.118.2",
|
"label": "v1.118.2",
|
||||||
"url": "https://v1.118.2.archive.immich.app"
|
"url": "https://v1.118.2.archive.immich.app"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "v1.118.1",
|
|
||||||
"url": "https://v1.118.1.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "v1.118.0",
|
|
||||||
"url": "https://v1.118.0.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.117.0",
|
"label": "v1.117.0",
|
||||||
"url": "https://v1.117.0.archive.immich.app"
|
"url": "https://v1.117.0.archive.immich.app"
|
||||||
@@ -167,14 +79,6 @@
|
|||||||
"label": "v1.116.2",
|
"label": "v1.116.2",
|
||||||
"url": "https://v1.116.2.archive.immich.app"
|
"url": "https://v1.116.2.archive.immich.app"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "v1.116.1",
|
|
||||||
"url": "https://v1.116.1.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "v1.116.0",
|
|
||||||
"url": "https://v1.116.0.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.115.0",
|
"label": "v1.115.0",
|
||||||
"url": "https://v1.115.0.archive.immich.app"
|
"url": "https://v1.115.0.archive.immich.app"
|
||||||
@@ -187,18 +91,10 @@
|
|||||||
"label": "v1.113.1",
|
"label": "v1.113.1",
|
||||||
"url": "https://v1.113.1.archive.immich.app"
|
"url": "https://v1.113.1.archive.immich.app"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "v1.113.0",
|
|
||||||
"url": "https://v1.113.0.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.112.1",
|
"label": "v1.112.1",
|
||||||
"url": "https://v1.112.1.archive.immich.app"
|
"url": "https://v1.112.1.archive.immich.app"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "v1.112.0",
|
|
||||||
"url": "https://v1.112.0.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.111.0",
|
"label": "v1.111.0",
|
||||||
"url": "https://v1.111.0.archive.immich.app"
|
"url": "https://v1.111.0.archive.immich.app"
|
||||||
@@ -211,14 +107,6 @@
|
|||||||
"label": "v1.109.2",
|
"label": "v1.109.2",
|
||||||
"url": "https://v1.109.2.archive.immich.app"
|
"url": "https://v1.109.2.archive.immich.app"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "v1.109.1",
|
|
||||||
"url": "https://v1.109.1.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "v1.109.0",
|
|
||||||
"url": "https://v1.109.0.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.108.0",
|
"label": "v1.108.0",
|
||||||
"url": "https://v1.108.0.archive.immich.app"
|
"url": "https://v1.108.0.archive.immich.app"
|
||||||
@@ -227,38 +115,14 @@
|
|||||||
"label": "v1.107.2",
|
"label": "v1.107.2",
|
||||||
"url": "https://v1.107.2.archive.immich.app"
|
"url": "https://v1.107.2.archive.immich.app"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "v1.107.1",
|
|
||||||
"url": "https://v1.107.1.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "v1.107.0",
|
|
||||||
"url": "https://v1.107.0.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.106.4",
|
"label": "v1.106.4",
|
||||||
"url": "https://v1.106.4.archive.immich.app"
|
"url": "https://v1.106.4.archive.immich.app"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "v1.106.3",
|
|
||||||
"url": "https://v1.106.3.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "v1.106.2",
|
|
||||||
"url": "https://v1.106.2.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "v1.106.1",
|
|
||||||
"url": "https://v1.106.1.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.105.1",
|
"label": "v1.105.1",
|
||||||
"url": "https://v1.105.1.archive.immich.app"
|
"url": "https://v1.105.1.archive.immich.app"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "v1.105.0",
|
|
||||||
"url": "https://v1.105.0.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.104.0",
|
"label": "v1.104.0",
|
||||||
"url": "https://v1.104.0.archive.immich.app"
|
"url": "https://v1.104.0.archive.immich.app"
|
||||||
@@ -267,26 +131,10 @@
|
|||||||
"label": "v1.103.1",
|
"label": "v1.103.1",
|
||||||
"url": "https://v1.103.1.archive.immich.app"
|
"url": "https://v1.103.1.archive.immich.app"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "v1.103.0",
|
|
||||||
"url": "https://v1.103.0.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.102.3",
|
"label": "v1.102.3",
|
||||||
"url": "https://v1.102.3.archive.immich.app"
|
"url": "https://v1.102.3.archive.immich.app"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "v1.102.2",
|
|
||||||
"url": "https://v1.102.2.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "v1.102.1",
|
|
||||||
"url": "https://v1.102.1.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "v1.102.0",
|
|
||||||
"url": "https://v1.102.0.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.101.0",
|
"label": "v1.101.0",
|
||||||
"url": "https://v1.101.0.archive.immich.app"
|
"url": "https://v1.101.0.archive.immich.app"
|
||||||
|
|||||||
BIN
docs/static/fonts/overpass/Overpass-Italic.ttf
vendored
Normal file
BIN
docs/static/fonts/overpass/Overpass-Italic.ttf
vendored
Normal file
Binary file not shown.
BIN
docs/static/fonts/overpass/Overpass.ttf
vendored
Normal file
BIN
docs/static/fonts/overpass/Overpass.ttf
vendored
Normal file
Binary file not shown.
BIN
docs/static/fonts/overpass/OverpassMono.ttf
vendored
Normal file
BIN
docs/static/fonts/overpass/OverpassMono.ttf
vendored
Normal file
Binary file not shown.
22
docs/static/img/logomark-dark-with-futo.svg
vendored
Normal file
22
docs/static/img/logomark-dark-with-futo.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 75 KiB |
21
docs/static/img/logomark-light-with-futo.svg
vendored
Normal file
21
docs/static/img/logomark-light-with-futo.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 75 KiB |
@@ -1 +1 @@
|
|||||||
22.14.0
|
22.16.0
|
||||||
|
|||||||
@@ -28,8 +28,10 @@ services:
|
|||||||
extra_hosts:
|
extra_hosts:
|
||||||
- 'auth-server:host-gateway'
|
- 'auth-server:host-gateway'
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
redis:
|
||||||
- database
|
condition: service_started
|
||||||
|
database:
|
||||||
|
condition: service_healthy
|
||||||
ports:
|
ports:
|
||||||
- 2285:2285
|
- 2285:2285
|
||||||
|
|
||||||
@@ -37,11 +39,17 @@ services:
|
|||||||
image: redis:6.2-alpine@sha256:3211c33a618c457e5d241922c975dbc4f446d0bdb2dc75694f5573ef8e2d01fa
|
image: redis:6.2-alpine@sha256:3211c33a618c457e5d241922c975dbc4f446d0bdb2dc75694f5573ef8e2d01fa
|
||||||
|
|
||||||
database:
|
database:
|
||||||
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:9c704fb49ce27549df00f1b096cc93f8b0c959ef087507704d74954808f78a82
|
||||||
command: -c fsync=off -c shared_preload_libraries=vectors.so
|
command: -c fsync=off -c shared_preload_libraries=vchord.so -c config_file=/var/lib/postgresql/data/postgresql.conf
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
POSTGRES_DB: immich
|
POSTGRES_DB: immich
|
||||||
ports:
|
ports:
|
||||||
- 5435:5432
|
- 5435:5432
|
||||||
|
healthcheck:
|
||||||
|
test: ['CMD-SHELL', 'pg_isready -U postgres -d immich']
|
||||||
|
interval: 1s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 30
|
||||||
|
start_period: 10s
|
||||||
|
|||||||
1560
e2e/package-lock.json
generated
1560
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.132.3",
|
"version": "1.134.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -25,21 +25,21 @@
|
|||||||
"@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.14.1",
|
"@types/node": "^22.15.29",
|
||||||
"@types/oidc-provider": "^8.5.1",
|
"@types/oidc-provider": "^9.0.0",
|
||||||
"@types/pg": "^8.11.0",
|
"@types/pg": "^8.15.1",
|
||||||
"@types/pngjs": "^6.0.4",
|
"@types/pngjs": "^6.0.4",
|
||||||
"@types/supertest": "^6.0.2",
|
"@types/supertest": "^6.0.2",
|
||||||
"@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": "^57.0.0",
|
"eslint-plugin-unicorn": "^59.0.0",
|
||||||
"exiftool-vendored": "^28.3.1",
|
"exiftool-vendored": "^28.3.1",
|
||||||
"globals": "^16.0.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": "^9.0.0",
|
||||||
"pg": "^8.11.3",
|
"pg": "^8.11.3",
|
||||||
"pngjs": "^7.0.0",
|
"pngjs": "^7.0.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
@@ -52,6 +52,6 @@
|
|||||||
"vitest": "^3.0.0"
|
"vitest": "^3.0.0"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "22.14.0"
|
"node": "22.16.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,38 +46,6 @@ describe('/activities', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /activities', () => {
|
describe('GET /activities', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get('/activities');
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require an albumId', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.get('/activities')
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
|
||||||
expect(status).toEqual(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['albumId must be a UUID'])));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reject an invalid albumId', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.get('/activities')
|
|
||||||
.query({ albumId: uuidDto.invalid })
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
|
||||||
expect(status).toEqual(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['albumId must be a UUID'])));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reject an invalid assetId', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.get('/activities')
|
|
||||||
.query({ albumId: uuidDto.notFound, assetId: uuidDto.invalid })
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
|
||||||
expect(status).toEqual(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['assetId must be a UUID'])));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should start off empty', async () => {
|
it('should start off empty', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/activities')
|
.get('/activities')
|
||||||
@@ -192,30 +160,6 @@ describe('/activities', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /activities', () => {
|
describe('POST /activities', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).post('/activities');
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require an albumId', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.post('/activities')
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send({ albumId: uuidDto.invalid });
|
|
||||||
expect(status).toEqual(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['albumId must be a UUID'])));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require a comment when type is comment', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.post('/activities')
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send({ albumId: uuidDto.notFound, type: 'comment', comment: null });
|
|
||||||
expect(status).toEqual(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(['comment must be a string', 'comment should not be empty']));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add a comment to an album', async () => {
|
it('should add a comment to an album', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.post('/activities')
|
.post('/activities')
|
||||||
@@ -330,20 +274,6 @@ describe('/activities', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('DELETE /activities/:id', () => {
|
describe('DELETE /activities/:id', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).delete(`/activities/${uuidDto.notFound}`);
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require a valid uuid', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.delete(`/activities/${uuidDto.invalid}`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should remove a comment from an album', async () => {
|
it('should remove a comment from an album', async () => {
|
||||||
const reaction = await createActivity({
|
const reaction = await createActivity({
|
||||||
albumId: album.id,
|
albumId: album.id,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
LoginResponseDto,
|
LoginResponseDto,
|
||||||
SharedLinkType,
|
SharedLinkType,
|
||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
import { createUserDto, uuidDto } from 'src/fixtures';
|
import { createUserDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { app, asBearerAuth, utils } from 'src/utils';
|
import { app, asBearerAuth, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
@@ -128,28 +128,6 @@ describe('/albums', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /albums', () => {
|
describe('GET /albums', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get('/albums');
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reject an invalid shared param', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.get('/albums?shared=invalid')
|
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
|
||||||
expect(status).toEqual(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(['shared must be a boolean value']));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reject an invalid assetId param', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.get('/albums?assetId=invalid')
|
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
|
||||||
expect(status).toEqual(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(['assetId must be a UUID']));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not show other users' favorites", async () => {
|
it("should not show other users' favorites", async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get(`/albums/${user1Albums[0].id}?withoutAssets=false`)
|
.get(`/albums/${user1Albums[0].id}?withoutAssets=false`)
|
||||||
@@ -323,12 +301,6 @@ describe('/albums', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /albums/:id', () => {
|
describe('GET /albums/:id', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get(`/albums/${user1Albums[0].id}`);
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return album info for own album', async () => {
|
it('should return album info for own album', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get(`/albums/${user1Albums[0].id}?withoutAssets=false`)
|
.get(`/albums/${user1Albums[0].id}?withoutAssets=false`)
|
||||||
@@ -421,12 +393,6 @@ describe('/albums', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /albums/statistics', () => {
|
describe('GET /albums/statistics', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get('/albums/statistics');
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return total count of albums the user has access to', async () => {
|
it('should return total count of albums the user has access to', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/albums/statistics')
|
.get('/albums/statistics')
|
||||||
@@ -438,12 +404,6 @@ describe('/albums', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /albums', () => {
|
describe('POST /albums', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).post('/albums').send({ albumName: 'New album' });
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create an album', async () => {
|
it('should create an album', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.post('/albums')
|
.post('/albums')
|
||||||
@@ -468,15 +428,18 @@ describe('/albums', () => {
|
|||||||
order: AssetOrder.Desc,
|
order: AssetOrder.Desc,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not be able to share album with owner', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/albums')
|
||||||
|
.send({ albumName: 'New album', albumUsers: [{ role: AlbumUserRole.Editor, userId: user1.userId }] })
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest('Cannot share album with owner'));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('PUT /albums/:id/assets', () => {
|
describe('PUT /albums/:id/assets', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).put(`/albums/${user1Albums[0].id}/assets`);
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to add own asset to own album', async () => {
|
it('should be able to add own asset to own album', async () => {
|
||||||
const asset = await utils.createAsset(user1.accessToken);
|
const asset = await utils.createAsset(user1.accessToken);
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
@@ -526,14 +489,6 @@ describe('/albums', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('PATCH /albums/:id', () => {
|
describe('PATCH /albums/:id', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.patch(`/albums/${uuidDto.notFound}`)
|
|
||||||
.send({ albumName: 'New album name' });
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update an album', async () => {
|
it('should update an album', async () => {
|
||||||
const album = await utils.createAlbum(user1.accessToken, {
|
const album = await utils.createAlbum(user1.accessToken, {
|
||||||
albumName: 'New album',
|
albumName: 'New album',
|
||||||
@@ -576,15 +531,6 @@ describe('/albums', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('DELETE /albums/:id/assets', () => {
|
describe('DELETE /albums/:id/assets', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.delete(`/albums/${user1Albums[0].id}/assets`)
|
|
||||||
.send({ ids: [user1Asset1.id] });
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require authorization', async () => {
|
it('should require authorization', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.delete(`/albums/${user1Albums[1].id}/assets`)
|
.delete(`/albums/${user1Albums[1].id}/assets`)
|
||||||
@@ -679,13 +625,6 @@ describe('/albums', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).put(`/albums/${user1Albums[0].id}/users`).send({ sharedUserIds: [] });
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to add user to own album', async () => {
|
it('should be able to add user to own album', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/albums/${album.id}/users`)
|
.put(`/albums/${album.id}/users`)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { LoginResponseDto, Permission, createApiKey } from '@immich/sdk';
|
import { LoginResponseDto, Permission, createApiKey } from '@immich/sdk';
|
||||||
import { createUserDto, uuidDto } from 'src/fixtures';
|
import { createUserDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { app, asBearerAuth, utils } from 'src/utils';
|
import { app, asBearerAuth, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
@@ -24,12 +24,6 @@ describe('/api-keys', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /api-keys', () => {
|
describe('POST /api-keys', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).post('/api-keys').send({ name: 'API Key' });
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not work without permission', async () => {
|
it('should not work without permission', async () => {
|
||||||
const { secret } = await create(user.accessToken, [Permission.ApiKeyRead]);
|
const { secret } = await create(user.accessToken, [Permission.ApiKeyRead]);
|
||||||
const { status, body } = await request(app).post('/api-keys').set('x-api-key', secret).send({ name: 'API Key' });
|
const { status, body } = await request(app).post('/api-keys').set('x-api-key', secret).send({ name: 'API Key' });
|
||||||
@@ -99,12 +93,6 @@ describe('/api-keys', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /api-keys', () => {
|
describe('GET /api-keys', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get('/api-keys');
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should start off empty', async () => {
|
it('should start off empty', async () => {
|
||||||
const { status, body } = await request(app).get('/api-keys').set('Authorization', `Bearer ${admin.accessToken}`);
|
const { status, body } = await request(app).get('/api-keys').set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
expect(body).toEqual([]);
|
expect(body).toEqual([]);
|
||||||
@@ -125,12 +113,6 @@ describe('/api-keys', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /api-keys/:id', () => {
|
describe('GET /api-keys/:id', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get(`/api-keys/${uuidDto.notFound}`);
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require authorization', async () => {
|
it('should require authorization', async () => {
|
||||||
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
@@ -140,14 +122,6 @@ describe('/api-keys', () => {
|
|||||||
expect(body).toEqual(errorDto.badRequest('API Key not found'));
|
expect(body).toEqual(errorDto.badRequest('API Key not found'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require a valid uuid', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.get(`/api-keys/${uuidDto.invalid}`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get api key details', async () => {
|
it('should get api key details', async () => {
|
||||||
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
@@ -165,42 +139,30 @@ describe('/api-keys', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('PUT /api-keys/:id', () => {
|
describe('PUT /api-keys/:id', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).put(`/api-keys/${uuidDto.notFound}`).send({ name: 'new name' });
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require authorization', async () => {
|
it('should require authorization', async () => {
|
||||||
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/api-keys/${apiKey.id}`)
|
.put(`/api-keys/${apiKey.id}`)
|
||||||
.send({ name: 'new name' })
|
.send({ name: 'new name', permissions: [Permission.All] })
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
expect(status).toBe(400);
|
expect(status).toBe(400);
|
||||||
expect(body).toEqual(errorDto.badRequest('API Key not found'));
|
expect(body).toEqual(errorDto.badRequest('API Key not found'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require a valid uuid', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.put(`/api-keys/${uuidDto.invalid}`)
|
|
||||||
.send({ name: 'new name' })
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update api key details', async () => {
|
it('should update api key details', async () => {
|
||||||
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/api-keys/${apiKey.id}`)
|
.put(`/api-keys/${apiKey.id}`)
|
||||||
.send({ name: 'new name' })
|
.send({
|
||||||
|
name: 'new name',
|
||||||
|
permissions: [Permission.ActivityCreate, Permission.ActivityRead, Permission.ActivityUpdate],
|
||||||
|
})
|
||||||
.set('Authorization', `Bearer ${user.accessToken}`);
|
.set('Authorization', `Bearer ${user.accessToken}`);
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({
|
expect(body).toEqual({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
name: 'new name',
|
name: 'new name',
|
||||||
permissions: [Permission.All],
|
permissions: [Permission.ActivityCreate, Permission.ActivityRead, Permission.ActivityUpdate],
|
||||||
createdAt: expect.any(String),
|
createdAt: expect.any(String),
|
||||||
updatedAt: expect.any(String),
|
updatedAt: expect.any(String),
|
||||||
});
|
});
|
||||||
@@ -208,12 +170,6 @@ describe('/api-keys', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('DELETE /api-keys/:id', () => {
|
describe('DELETE /api-keys/:id', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).delete(`/api-keys/${uuidDto.notFound}`);
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require authorization', async () => {
|
it('should require authorization', async () => {
|
||||||
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
@@ -223,14 +179,6 @@ describe('/api-keys', () => {
|
|||||||
expect(body).toEqual(errorDto.badRequest('API Key not found'));
|
expect(body).toEqual(errorDto.badRequest('API Key not found'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require a valid uuid', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.delete(`/api-keys/${uuidDto.invalid}`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should delete an api key', async () => {
|
it('should delete an api key', async () => {
|
||||||
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
||||||
const { status } = await request(app)
|
const { status } = await request(app)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
AssetMediaStatus,
|
AssetMediaStatus,
|
||||||
AssetResponseDto,
|
AssetResponseDto,
|
||||||
AssetTypeEnum,
|
AssetTypeEnum,
|
||||||
|
AssetVisibility,
|
||||||
getAssetInfo,
|
getAssetInfo,
|
||||||
getMyUser,
|
getMyUser,
|
||||||
LoginResponseDto,
|
LoginResponseDto,
|
||||||
@@ -22,27 +23,9 @@ import { app, asBearerAuth, tempDir, TEN_TIMES, testAssetDir, utils } from 'src/
|
|||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
const makeUploadDto = (options?: { omit: string }): Record<string, any> => {
|
|
||||||
const dto: Record<string, any> = {
|
|
||||||
deviceAssetId: 'example-image',
|
|
||||||
deviceId: 'TEST',
|
|
||||||
fileCreatedAt: new Date().toISOString(),
|
|
||||||
fileModifiedAt: new Date().toISOString(),
|
|
||||||
isFavorite: 'testing',
|
|
||||||
duration: '0:00:00.000000',
|
|
||||||
};
|
|
||||||
|
|
||||||
const omit = options?.omit;
|
|
||||||
if (omit) {
|
|
||||||
delete dto[omit];
|
|
||||||
}
|
|
||||||
|
|
||||||
return dto;
|
|
||||||
};
|
|
||||||
|
|
||||||
const locationAssetFilepath = `${testAssetDir}/metadata/gps-position/thompson-springs.jpg`;
|
const locationAssetFilepath = `${testAssetDir}/metadata/gps-position/thompson-springs.jpg`;
|
||||||
const ratingAssetFilepath = `${testAssetDir}/metadata/rating/mongolels.jpg`;
|
const ratingAssetFilepath = `${testAssetDir}/metadata/rating/mongolels.jpg`;
|
||||||
const facesAssetFilepath = `${testAssetDir}/metadata/faces/portrait.jpg`;
|
const facesAssetDir = `${testAssetDir}/metadata/faces`;
|
||||||
|
|
||||||
const readTags = async (bytes: Buffer, filename: string) => {
|
const readTags = async (bytes: Buffer, filename: string) => {
|
||||||
const filepath = join(tempDir, filename);
|
const filepath = join(tempDir, filename);
|
||||||
@@ -137,9 +120,9 @@ describe('/asset', () => {
|
|||||||
// stats
|
// stats
|
||||||
utils.createAsset(statsUser.accessToken),
|
utils.createAsset(statsUser.accessToken),
|
||||||
utils.createAsset(statsUser.accessToken, { isFavorite: true }),
|
utils.createAsset(statsUser.accessToken, { isFavorite: true }),
|
||||||
utils.createAsset(statsUser.accessToken, { isArchived: true }),
|
utils.createAsset(statsUser.accessToken, { visibility: AssetVisibility.Archive }),
|
||||||
utils.createAsset(statsUser.accessToken, {
|
utils.createAsset(statsUser.accessToken, {
|
||||||
isArchived: true,
|
visibility: AssetVisibility.Archive,
|
||||||
isFavorite: true,
|
isFavorite: true,
|
||||||
assetData: { filename: 'example.mp4' },
|
assetData: { filename: 'example.mp4' },
|
||||||
}),
|
}),
|
||||||
@@ -160,13 +143,6 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /assets/:id/original', () => {
|
describe('GET /assets/:id/original', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get(`/assets/${uuidDto.notFound}/original`);
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should download the file', async () => {
|
it('should download the file', async () => {
|
||||||
const response = await request(app)
|
const response = await request(app)
|
||||||
.get(`/assets/${user1Assets[0].id}/original`)
|
.get(`/assets/${user1Assets[0].id}/original`)
|
||||||
@@ -178,20 +154,6 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /assets/:id', () => {
|
describe('GET /assets/:id', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get(`/assets/${uuidDto.notFound}`);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
expect(status).toBe(401);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require a valid id', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.get(`/assets/${uuidDto.invalid}`)
|
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require access', async () => {
|
it('should require access', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get(`/assets/${user2Assets[0].id}`)
|
.get(`/assets/${user2Assets[0].id}`)
|
||||||
@@ -224,31 +186,22 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get the asset faces', async () => {
|
describe('faces', () => {
|
||||||
const config = await utils.getSystemConfig(admin.accessToken);
|
const metadataFaceTests = [
|
||||||
config.metadata.faces.import = true;
|
{
|
||||||
await updateConfig({ systemConfigDto: config }, { headers: asBearerAuth(admin.accessToken) });
|
description: 'without orientation',
|
||||||
|
|
||||||
// asset faces
|
|
||||||
const facesAsset = await utils.createAsset(admin.accessToken, {
|
|
||||||
assetData: {
|
|
||||||
filename: 'portrait.jpg',
|
filename: 'portrait.jpg',
|
||||||
bytes: await readFile(facesAssetFilepath),
|
|
||||||
},
|
},
|
||||||
});
|
{
|
||||||
|
description: 'adjusting face regions to orientation',
|
||||||
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: facesAsset.id });
|
filename: 'portrait-orientation-6.jpg',
|
||||||
|
},
|
||||||
const { status, body } = await request(app)
|
];
|
||||||
.get(`/assets/${facesAsset.id}`)
|
// should produce same resulting face region coordinates for any orientation
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
const expectedFaces = [
|
||||||
expect(status).toBe(200);
|
|
||||||
expect(body.id).toEqual(facesAsset.id);
|
|
||||||
expect(body.people).toMatchObject([
|
|
||||||
{
|
{
|
||||||
name: 'Marie Curie',
|
name: 'Marie Curie',
|
||||||
birthDate: null,
|
birthDate: null,
|
||||||
thumbnailPath: '',
|
|
||||||
isHidden: false,
|
isHidden: false,
|
||||||
faces: [
|
faces: [
|
||||||
{
|
{
|
||||||
@@ -265,7 +218,6 @@ describe('/asset', () => {
|
|||||||
{
|
{
|
||||||
name: 'Pierre Curie',
|
name: 'Pierre Curie',
|
||||||
birthDate: null,
|
birthDate: null,
|
||||||
thumbnailPath: '',
|
|
||||||
isHidden: false,
|
isHidden: false,
|
||||||
faces: [
|
faces: [
|
||||||
{
|
{
|
||||||
@@ -279,7 +231,30 @@ describe('/asset', () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
];
|
||||||
|
|
||||||
|
it.each(metadataFaceTests)('should get the asset faces from $filename $description', async ({ filename }) => {
|
||||||
|
const config = await utils.getSystemConfig(admin.accessToken);
|
||||||
|
config.metadata.faces.import = true;
|
||||||
|
await updateConfig({ systemConfigDto: config }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
|
||||||
|
const facesAsset = await utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: {
|
||||||
|
filename,
|
||||||
|
bytes: await readFile(`${facesAssetDir}/${filename}`),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: facesAsset.id });
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get(`/assets/${facesAsset.id}`)
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.id).toEqual(facesAsset.id);
|
||||||
|
expect(body.people).toMatchObject(expectedFaces);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work with a shared link', async () => {
|
it('should work with a shared link', async () => {
|
||||||
@@ -333,7 +308,7 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('disallows viewing archived assets', async () => {
|
it('disallows viewing archived assets', async () => {
|
||||||
const asset = await utils.createAsset(user1.accessToken, { isArchived: true });
|
const asset = await utils.createAsset(user1.accessToken, { visibility: AssetVisibility.Archive });
|
||||||
|
|
||||||
const { status } = await request(app)
|
const { status } = await request(app)
|
||||||
.get(`/assets/${asset.id}`)
|
.get(`/assets/${asset.id}`)
|
||||||
@@ -354,13 +329,6 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /assets/statistics', () => {
|
describe('GET /assets/statistics', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get('/assets/statistics');
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return stats of all assets', async () => {
|
it('should return stats of all assets', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/assets/statistics')
|
.get('/assets/statistics')
|
||||||
@@ -384,7 +352,7 @@ describe('/asset', () => {
|
|||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/assets/statistics')
|
.get('/assets/statistics')
|
||||||
.set('Authorization', `Bearer ${statsUser.accessToken}`)
|
.set('Authorization', `Bearer ${statsUser.accessToken}`)
|
||||||
.query({ isArchived: true });
|
.query({ visibility: AssetVisibility.Archive });
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({ images: 1, videos: 1, total: 2 });
|
expect(body).toEqual({ images: 1, videos: 1, total: 2 });
|
||||||
@@ -394,7 +362,7 @@ describe('/asset', () => {
|
|||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/assets/statistics')
|
.get('/assets/statistics')
|
||||||
.set('Authorization', `Bearer ${statsUser.accessToken}`)
|
.set('Authorization', `Bearer ${statsUser.accessToken}`)
|
||||||
.query({ isFavorite: true, isArchived: true });
|
.query({ isFavorite: true, visibility: AssetVisibility.Archive });
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({ images: 0, videos: 1, total: 1 });
|
expect(body).toEqual({ images: 0, videos: 1, total: 1 });
|
||||||
@@ -404,7 +372,7 @@ describe('/asset', () => {
|
|||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/assets/statistics')
|
.get('/assets/statistics')
|
||||||
.set('Authorization', `Bearer ${statsUser.accessToken}`)
|
.set('Authorization', `Bearer ${statsUser.accessToken}`)
|
||||||
.query({ isFavorite: false, isArchived: false });
|
.query({ isFavorite: false, visibility: AssetVisibility.Timeline });
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({ images: 1, videos: 0, total: 1 });
|
expect(body).toEqual({ images: 1, videos: 0, total: 1 });
|
||||||
@@ -425,13 +393,6 @@ describe('/asset', () => {
|
|||||||
await utils.waitForQueueFinish(admin.accessToken, 'thumbnailGeneration');
|
await utils.waitForQueueFinish(admin.accessToken, 'thumbnailGeneration');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get('/assets/random');
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it.each(TEN_TIMES)('should return 1 random assets', async () => {
|
it.each(TEN_TIMES)('should return 1 random assets', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/assets/random')
|
.get('/assets/random')
|
||||||
@@ -467,31 +428,9 @@ describe('/asset', () => {
|
|||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual([expect.objectContaining({ id: user2Assets[0].id })]);
|
expect(body).toEqual([expect.objectContaining({ id: user2Assets[0].id })]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return error', async () => {
|
|
||||||
const { status } = await request(app)
|
|
||||||
.get('/assets/random?count=ABC')
|
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
|
||||||
|
|
||||||
expect(status).toBe(400);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('PUT /assets/:id', () => {
|
describe('PUT /assets/:id', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).put(`/assets/:${uuidDto.notFound}`);
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require a valid id', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.put(`/assets/${uuidDto.invalid}`)
|
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require access', async () => {
|
it('should require access', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/assets/${user2Assets[0].id}`)
|
.put(`/assets/${user2Assets[0].id}`)
|
||||||
@@ -519,7 +458,7 @@ describe('/asset', () => {
|
|||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/assets/${user1Assets[0].id}`)
|
.put(`/assets/${user1Assets[0].id}`)
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
.send({ isArchived: true });
|
.send({ visibility: AssetVisibility.Archive });
|
||||||
expect(body).toMatchObject({ id: user1Assets[0].id, isArchived: true });
|
expect(body).toMatchObject({ id: user1Assets[0].id, isArchived: true });
|
||||||
expect(status).toEqual(200);
|
expect(status).toEqual(200);
|
||||||
});
|
});
|
||||||
@@ -619,28 +558,6 @@ describe('/asset', () => {
|
|||||||
expect(status).toEqual(200);
|
expect(status).toEqual(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reject invalid gps coordinates', async () => {
|
|
||||||
for (const test of [
|
|
||||||
{ latitude: 12 },
|
|
||||||
{ longitude: 12 },
|
|
||||||
{ latitude: 12, longitude: 'abc' },
|
|
||||||
{ latitude: 'abc', longitude: 12 },
|
|
||||||
{ latitude: null, longitude: 12 },
|
|
||||||
{ latitude: 12, longitude: null },
|
|
||||||
{ latitude: 91, longitude: 12 },
|
|
||||||
{ latitude: -91, longitude: 12 },
|
|
||||||
{ latitude: 12, longitude: -181 },
|
|
||||||
{ latitude: 12, longitude: 181 },
|
|
||||||
]) {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.put(`/assets/${user1Assets[0].id}`)
|
|
||||||
.send(test)
|
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update gps data', async () => {
|
it('should update gps data', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/assets/${user1Assets[0].id}`)
|
.put(`/assets/${user1Assets[0].id}`)
|
||||||
@@ -712,17 +629,6 @@ describe('/asset', () => {
|
|||||||
expect(status).toEqual(200);
|
expect(status).toEqual(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reject invalid rating', async () => {
|
|
||||||
for (const test of [{ rating: 7 }, { rating: 3.5 }, { rating: null }]) {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.put(`/assets/${user1Assets[0].id}`)
|
|
||||||
.send(test)
|
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return tagged people', async () => {
|
it('should return tagged people', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/assets/${user1Assets[0].id}`)
|
.put(`/assets/${user1Assets[0].id}`)
|
||||||
@@ -746,25 +652,6 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('DELETE /assets', () => {
|
describe('DELETE /assets', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.delete(`/assets`)
|
|
||||||
.send({ ids: [uuidDto.notFound] });
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require a valid uuid', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.delete(`/assets`)
|
|
||||||
.send({ ids: [uuidDto.invalid] })
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
|
||||||
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(['each value in ids must be a UUID']));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error when the id is not found', async () => {
|
it('should throw an error when the id is not found', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.delete(`/assets`)
|
.delete(`/assets`)
|
||||||
@@ -877,13 +764,6 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /assets/:id/thumbnail', () => {
|
describe('GET /assets/:id/thumbnail', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get(`/assets/${locationAsset.id}/thumbnail`);
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not include gps data for webp thumbnails', async () => {
|
it('should not include gps data for webp thumbnails', async () => {
|
||||||
await utils.waitForWebsocketEvent({
|
await utils.waitForWebsocketEvent({
|
||||||
event: 'assetUpload',
|
event: 'assetUpload',
|
||||||
@@ -919,13 +799,6 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /assets/:id/original', () => {
|
describe('GET /assets/:id/original', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get(`/assets/${locationAsset.id}/original`);
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should download the original', async () => {
|
it('should download the original', async () => {
|
||||||
const { status, body, type } = await request(app)
|
const { status, body, type } = await request(app)
|
||||||
.get(`/assets/${locationAsset.id}/original`)
|
.get(`/assets/${locationAsset.id}/original`)
|
||||||
@@ -946,43 +819,9 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('PUT /assets', () => {
|
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).put('/assets');
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('POST /assets', () => {
|
describe('POST /assets', () => {
|
||||||
beforeAll(setupTests, 30_000);
|
beforeAll(setupTests, 30_000);
|
||||||
|
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).post(`/assets`);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
expect(status).toBe(401);
|
|
||||||
});
|
|
||||||
|
|
||||||
it.each([
|
|
||||||
{ should: 'require `deviceAssetId`', dto: { ...makeUploadDto({ omit: 'deviceAssetId' }) } },
|
|
||||||
{ should: 'require `deviceId`', dto: { ...makeUploadDto({ omit: 'deviceId' }) } },
|
|
||||||
{ should: 'require `fileCreatedAt`', dto: { ...makeUploadDto({ omit: 'fileCreatedAt' }) } },
|
|
||||||
{ should: 'require `fileModifiedAt`', dto: { ...makeUploadDto({ omit: 'fileModifiedAt' }) } },
|
|
||||||
{ should: 'require `duration`', dto: { ...makeUploadDto({ omit: 'duration' }) } },
|
|
||||||
{ should: 'throw if `isFavorite` is not a boolean', dto: { ...makeUploadDto(), isFavorite: 'not-a-boolean' } },
|
|
||||||
{ should: 'throw if `isVisible` is not a boolean', dto: { ...makeUploadDto(), isVisible: 'not-a-boolean' } },
|
|
||||||
{ should: 'throw if `isArchived` is not a boolean', dto: { ...makeUploadDto(), isArchived: 'not-a-boolean' } },
|
|
||||||
])('should $should', async ({ dto }) => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.post('/assets')
|
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
|
||||||
.attach('assetData', makeRandomImage(), 'example.png')
|
|
||||||
.field(dto);
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest());
|
|
||||||
});
|
|
||||||
|
|
||||||
const tests = [
|
const tests = [
|
||||||
{
|
{
|
||||||
input: 'formats/avif/8bit-sRGB.avif',
|
input: 'formats/avif/8bit-sRGB.avif',
|
||||||
@@ -1244,31 +1083,21 @@ describe('/asset', () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
it(`should upload and generate a thumbnail for different file types`, async () => {
|
it.each(tests)(`should upload and generate a thumbnail for different file types`, async ({ input, expected }) => {
|
||||||
// upload in parallel
|
const filepath = join(testAssetDir, input);
|
||||||
const assets = await Promise.all(
|
const response = await utils.createAsset(admin.accessToken, {
|
||||||
tests.map(async ({ input }) => {
|
assetData: { bytes: await readFile(filepath), filename: basename(filepath) },
|
||||||
const filepath = join(testAssetDir, input);
|
});
|
||||||
return utils.createAsset(admin.accessToken, {
|
|
||||||
assetData: { bytes: await readFile(filepath), filename: basename(filepath) },
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const { id, status } of assets) {
|
expect(response.status).toBe(AssetMediaStatus.Created);
|
||||||
expect(status).toBe(AssetMediaStatus.Created);
|
const id = response.id;
|
||||||
// longer timeout as the thumbnail generation from full-size raw files can take a while
|
// 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 });
|
||||||
}
|
|
||||||
|
|
||||||
for (const [i, { id }] of assets.entries()) {
|
const asset = await utils.getAssetInfo(admin.accessToken, id);
|
||||||
const { expected } = tests[i];
|
expect(asset.exifInfo).toBeDefined();
|
||||||
const asset = await utils.getAssetInfo(admin.accessToken, id);
|
expect(asset.exifInfo).toMatchObject(expected.exifInfo);
|
||||||
|
expect(asset).toMatchObject(expected);
|
||||||
expect(asset.exifInfo).toBeDefined();
|
|
||||||
expect(asset.exifInfo).toMatchObject(expected.exifInfo);
|
|
||||||
expect(asset).toMatchObject(expected);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle a duplicate', async () => {
|
it('should handle a duplicate', async () => {
|
||||||
|
|||||||
@@ -19,17 +19,6 @@ describe(`/auth/admin-sign-up`, () => {
|
|||||||
expect(body).toEqual(signupResponseDto.admin);
|
expect(body).toEqual(signupResponseDto.admin);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sign up the admin with a local domain', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.post('/auth/admin-sign-up')
|
|
||||||
.send({ ...signupDto.admin, email: 'admin@local' });
|
|
||||||
expect(status).toEqual(201);
|
|
||||||
expect(body).toEqual({
|
|
||||||
...signupResponseDto.admin,
|
|
||||||
email: 'admin@local',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not allow a second admin to sign up', async () => {
|
it('should not allow a second admin to sign up', async () => {
|
||||||
await signUpAdmin({ signUpDto: signupDto.admin });
|
await signUpAdmin({ signUpDto: signupDto.admin });
|
||||||
|
|
||||||
@@ -57,22 +46,6 @@ describe('/auth/*', () => {
|
|||||||
expect(body).toEqual(errorDto.incorrectLogin);
|
expect(body).toEqual(errorDto.incorrectLogin);
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const key of Object.keys(loginDto.admin)) {
|
|
||||||
it(`should not allow null ${key}`, async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.post('/auth/login')
|
|
||||||
.send({ ...loginDto.admin, [key]: null });
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reject an invalid email', async () => {
|
|
||||||
const { status, body } = await request(app).post('/auth/login').send({ email: [], password });
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.invalidEmail);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should accept a correct password', async () => {
|
it('should accept a correct password', async () => {
|
||||||
const { status, body, headers } = await request(app).post('/auth/login').send({ email, password });
|
const { status, body, headers } = await request(app).post('/auth/login').send({ email, password });
|
||||||
expect(status).toBe(201);
|
expect(status).toBe(201);
|
||||||
@@ -127,14 +100,6 @@ describe('/auth/*', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /auth/change-password', () => {
|
describe('POST /auth/change-password', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.post(`/auth/change-password`)
|
|
||||||
.send({ password, newPassword: 'Password1234' });
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require the current password', async () => {
|
it('should require the current password', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.post(`/auth/change-password`)
|
.post(`/auth/change-password`)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { AssetMediaResponseDto, LoginResponseDto } from '@immich/sdk';
|
import { AssetMediaResponseDto, LoginResponseDto } from '@immich/sdk';
|
||||||
import { readFile, writeFile } from 'node:fs/promises';
|
import { readFile, writeFile } from 'node:fs/promises';
|
||||||
import { errorDto } from 'src/responses';
|
|
||||||
import { app, tempDir, utils } from 'src/utils';
|
import { app, tempDir, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
@@ -17,15 +16,6 @@ describe('/download', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /download/info', () => {
|
describe('POST /download/info', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.post(`/download/info`)
|
|
||||||
.send({ assetIds: [asset1.id] });
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should download info', async () => {
|
it('should download info', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.post('/download/info')
|
.post('/download/info')
|
||||||
@@ -42,15 +32,6 @@ describe('/download', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /download/archive', () => {
|
describe('POST /download/archive', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.post(`/download/archive`)
|
|
||||||
.send({ assetIds: [asset1.id, asset2.id] });
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should download an archive', async () => {
|
it('should download an archive', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.post('/download/archive')
|
.post('/download/archive')
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { LoginResponseDto } from '@immich/sdk';
|
import { AssetVisibility, LoginResponseDto } from '@immich/sdk';
|
||||||
import { readFile } from 'node:fs/promises';
|
import { readFile } from 'node:fs/promises';
|
||||||
import { basename, join } from 'node:path';
|
import { basename, join } from 'node:path';
|
||||||
import { Socket } from 'socket.io-client';
|
import { Socket } from 'socket.io-client';
|
||||||
@@ -44,7 +44,7 @@ describe('/map', () => {
|
|||||||
it('should get map markers for all non-archived assets', async () => {
|
it('should get map markers for all non-archived assets', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/map/markers')
|
.get('/map/markers')
|
||||||
.query({ isArchived: false })
|
.query({ visibility: AssetVisibility.Timeline })
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
|
|||||||
@@ -5,22 +5,6 @@ import { app, asBearerAuth, utils } from 'src/utils';
|
|||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
const invalidBirthday = [
|
|
||||||
{
|
|
||||||
birthDate: 'false',
|
|
||||||
response: ['birthDate must be a string in the format yyyy-MM-dd', 'Birth date cannot be in the future'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
birthDate: '123567',
|
|
||||||
response: ['birthDate must be a string in the format yyyy-MM-dd', 'Birth date cannot be in the future'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
birthDate: 123_567,
|
|
||||||
response: ['birthDate must be a string in the format yyyy-MM-dd', 'Birth date cannot be in the future'],
|
|
||||||
},
|
|
||||||
{ birthDate: '9999-01-01', response: ['Birth date cannot be in the future'] },
|
|
||||||
];
|
|
||||||
|
|
||||||
describe('/people', () => {
|
describe('/people', () => {
|
||||||
let admin: LoginResponseDto;
|
let admin: LoginResponseDto;
|
||||||
let visiblePerson: PersonResponseDto;
|
let visiblePerson: PersonResponseDto;
|
||||||
@@ -58,14 +42,6 @@ describe('/people', () => {
|
|||||||
|
|
||||||
describe('GET /people', () => {
|
describe('GET /people', () => {
|
||||||
beforeEach(async () => {});
|
beforeEach(async () => {});
|
||||||
|
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get('/people');
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return all people (including hidden)', async () => {
|
it('should return all people (including hidden)', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/people')
|
.get('/people')
|
||||||
@@ -117,13 +93,6 @@ describe('/people', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /people/:id', () => {
|
describe('GET /people/:id', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get(`/people/${uuidDto.notFound}`);
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw error if person with id does not exist', async () => {
|
it('should throw error if person with id does not exist', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get(`/people/${uuidDto.notFound}`)
|
.get(`/people/${uuidDto.notFound}`)
|
||||||
@@ -144,13 +113,6 @@ describe('/people', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /people/:id/statistics', () => {
|
describe('GET /people/:id/statistics', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get(`/people/${multipleAssetsPerson.id}/statistics`);
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw error if person with id does not exist', async () => {
|
it('should throw error if person with id does not exist', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get(`/people/${uuidDto.notFound}/statistics`)
|
.get(`/people/${uuidDto.notFound}/statistics`)
|
||||||
@@ -171,23 +133,6 @@ describe('/people', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /people', () => {
|
describe('POST /people', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).post(`/people`);
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const { birthDate, response } of invalidBirthday) {
|
|
||||||
it(`should not accept an invalid birth date [${birthDate}]`, async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.post(`/people`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send({ birthDate });
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(response));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should create a person', async () => {
|
it('should create a person', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.post(`/people`)
|
.post(`/people`)
|
||||||
@@ -223,39 +168,6 @@ describe('/people', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('PUT /people/:id', () => {
|
describe('PUT /people/:id', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).put(`/people/${uuidDto.notFound}`);
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const { key, type } of [
|
|
||||||
{ key: 'name', type: 'string' },
|
|
||||||
{ key: 'featureFaceAssetId', type: 'string' },
|
|
||||||
{ key: 'isHidden', type: 'boolean value' },
|
|
||||||
{ key: 'isFavorite', type: 'boolean value' },
|
|
||||||
]) {
|
|
||||||
it(`should not allow null ${key}`, async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.put(`/people/${visiblePerson.id}`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send({ [key]: null });
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest([`${key} must be a ${type}`]));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const { birthDate, response } of invalidBirthday) {
|
|
||||||
it(`should not accept an invalid birth date [${birthDate}]`, async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.put(`/people/${visiblePerson.id}`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send({ birthDate });
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(response));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should update a date of birth', async () => {
|
it('should update a date of birth', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/people/${visiblePerson.id}`)
|
.put(`/people/${visiblePerson.id}`)
|
||||||
@@ -312,12 +224,6 @@ describe('/people', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /people/:id/merge', () => {
|
describe('POST /people/:id/merge', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).post(`/people/${uuidDto.notFound}/merge`);
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not supporting merging a person into themselves', async () => {
|
it('should not supporting merging a person into themselves', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.post(`/people/${visiblePerson.id}/merge`)
|
.post(`/people/${visiblePerson.id}/merge`)
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
import { AssetMediaResponseDto, AssetResponseDto, deleteAssets, LoginResponseDto, updateAsset } from '@immich/sdk';
|
import {
|
||||||
|
AssetMediaResponseDto,
|
||||||
|
AssetResponseDto,
|
||||||
|
AssetVisibility,
|
||||||
|
deleteAssets,
|
||||||
|
LoginResponseDto,
|
||||||
|
updateAsset,
|
||||||
|
} from '@immich/sdk';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { readFile } from 'node:fs/promises';
|
import { readFile } from 'node:fs/promises';
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
import { Socket } from 'socket.io-client';
|
import { Socket } from 'socket.io-client';
|
||||||
import { errorDto } from 'src/responses';
|
|
||||||
import { app, asBearerAuth, TEN_TIMES, testAssetDir, utils } from 'src/utils';
|
import { app, asBearerAuth, TEN_TIMES, testAssetDir, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
||||||
@@ -50,7 +56,7 @@ describe('/search', () => {
|
|||||||
{ filename: '/formats/motionphoto/samsung-one-ui-6.heic' },
|
{ filename: '/formats/motionphoto/samsung-one-ui-6.heic' },
|
||||||
{ filename: '/formats/motionphoto/samsung-one-ui-5.jpg' },
|
{ filename: '/formats/motionphoto/samsung-one-ui-5.jpg' },
|
||||||
|
|
||||||
{ filename: '/metadata/gps-position/thompson-springs.jpg', dto: { isArchived: true } },
|
{ filename: '/metadata/gps-position/thompson-springs.jpg', dto: { visibility: AssetVisibility.Archive } },
|
||||||
|
|
||||||
// used for search suggestions
|
// used for search suggestions
|
||||||
{ filename: '/formats/png/density_plot.png' },
|
{ filename: '/formats/png/density_plot.png' },
|
||||||
@@ -141,65 +147,6 @@ describe('/search', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /search/metadata', () => {
|
describe('POST /search/metadata', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).post('/search/metadata');
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
const badTests = [
|
|
||||||
{
|
|
||||||
should: 'should reject page as a string',
|
|
||||||
dto: { page: 'abc' },
|
|
||||||
expected: ['page must not be less than 1', 'page must be an integer number'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
should: 'should reject page as a decimal',
|
|
||||||
dto: { page: 1.5 },
|
|
||||||
expected: ['page must be an integer number'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
should: 'should reject page as a negative number',
|
|
||||||
dto: { page: -10 },
|
|
||||||
expected: ['page must not be less than 1'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
should: 'should reject page as 0',
|
|
||||||
dto: { page: 0 },
|
|
||||||
expected: ['page must not be less than 1'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
should: 'should reject size as a string',
|
|
||||||
dto: { size: 'abc' },
|
|
||||||
expected: [
|
|
||||||
'size must not be greater than 1000',
|
|
||||||
'size must not be less than 1',
|
|
||||||
'size must be an integer number',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
should: 'should reject an invalid size',
|
|
||||||
dto: { size: -1.5 },
|
|
||||||
expected: ['size must not be less than 1', 'size must be an integer number'],
|
|
||||||
},
|
|
||||||
...['isArchived', 'isFavorite', 'isEncoded', 'isOffline', 'isMotion', 'isVisible'].map((value) => ({
|
|
||||||
should: `should reject ${value} not a boolean`,
|
|
||||||
dto: { [value]: 'immich' },
|
|
||||||
expected: [`${value} must be a boolean value`],
|
|
||||||
})),
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const { should, dto, expected } of badTests) {
|
|
||||||
it(should, async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.post('/search/metadata')
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send(dto);
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(expected));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const searchTests = [
|
const searchTests = [
|
||||||
{
|
{
|
||||||
should: 'should get my assets',
|
should: 'should get my assets',
|
||||||
@@ -231,12 +178,12 @@ describe('/search', () => {
|
|||||||
deferred: () => ({ dto: { size: 1, isFavorite: false }, assets: [assetLast] }),
|
deferred: () => ({ dto: { size: 1, isFavorite: false }, assets: [assetLast] }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
should: 'should search by isArchived (true)',
|
should: 'should search by visibility (AssetVisibility.Archive)',
|
||||||
deferred: () => ({ dto: { isArchived: true }, assets: [assetSprings] }),
|
deferred: () => ({ dto: { visibility: AssetVisibility.Archive }, assets: [assetSprings] }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
should: 'should search by isArchived (false)',
|
should: 'should search by visibility (AssetVisibility.Timeline)',
|
||||||
deferred: () => ({ dto: { size: 1, isArchived: false }, assets: [assetLast] }),
|
deferred: () => ({ dto: { size: 1, visibility: AssetVisibility.Timeline }, assets: [assetLast] }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
should: 'should search by type (image)',
|
should: 'should search by type (image)',
|
||||||
@@ -245,7 +192,7 @@ describe('/search', () => {
|
|||||||
{
|
{
|
||||||
should: 'should search by type (video)',
|
should: 'should search by type (video)',
|
||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: { type: 'VIDEO' },
|
dto: { type: 'VIDEO', visibility: AssetVisibility.Hidden },
|
||||||
assets: [
|
assets: [
|
||||||
// the three live motion photos
|
// the three live motion photos
|
||||||
{ id: expect.any(String) },
|
{ id: expect.any(String) },
|
||||||
@@ -289,13 +236,6 @@ describe('/search', () => {
|
|||||||
should: 'should search by takenAfter (no results)',
|
should: 'should search by takenAfter (no results)',
|
||||||
deferred: () => ({ dto: { takenAfter: today.plus({ hour: 1 }).toJSDate() }, assets: [] }),
|
deferred: () => ({ dto: { takenAfter: today.plus({ hour: 1 }).toJSDate() }, assets: [] }),
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// should: 'should search by originalPath',
|
|
||||||
// deferred: () => ({
|
|
||||||
// dto: { originalPath: asset1.originalPath },
|
|
||||||
// assets: [asset1],
|
|
||||||
// }),
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
should: 'should search by originalFilename',
|
should: 'should search by originalFilename',
|
||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
@@ -325,7 +265,7 @@ describe('/search', () => {
|
|||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: {
|
dto: {
|
||||||
city: '',
|
city: '',
|
||||||
isVisible: true,
|
visibility: AssetVisibility.Timeline,
|
||||||
includeNull: true,
|
includeNull: true,
|
||||||
},
|
},
|
||||||
assets: [assetLast],
|
assets: [assetLast],
|
||||||
@@ -336,7 +276,7 @@ describe('/search', () => {
|
|||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: {
|
dto: {
|
||||||
city: null,
|
city: null,
|
||||||
isVisible: true,
|
visibility: AssetVisibility.Timeline,
|
||||||
includeNull: true,
|
includeNull: true,
|
||||||
},
|
},
|
||||||
assets: [assetLast],
|
assets: [assetLast],
|
||||||
@@ -357,7 +297,7 @@ describe('/search', () => {
|
|||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: {
|
dto: {
|
||||||
state: '',
|
state: '',
|
||||||
isVisible: true,
|
visibility: AssetVisibility.Timeline,
|
||||||
withExif: true,
|
withExif: true,
|
||||||
includeNull: true,
|
includeNull: true,
|
||||||
},
|
},
|
||||||
@@ -369,7 +309,7 @@ describe('/search', () => {
|
|||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: {
|
dto: {
|
||||||
state: null,
|
state: null,
|
||||||
isVisible: true,
|
visibility: AssetVisibility.Timeline,
|
||||||
includeNull: true,
|
includeNull: true,
|
||||||
},
|
},
|
||||||
assets: [assetLast, assetNotocactus],
|
assets: [assetLast, assetNotocactus],
|
||||||
@@ -390,7 +330,7 @@ describe('/search', () => {
|
|||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: {
|
dto: {
|
||||||
country: '',
|
country: '',
|
||||||
isVisible: true,
|
visibility: AssetVisibility.Timeline,
|
||||||
includeNull: true,
|
includeNull: true,
|
||||||
},
|
},
|
||||||
assets: [assetLast],
|
assets: [assetLast],
|
||||||
@@ -401,7 +341,7 @@ describe('/search', () => {
|
|||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: {
|
dto: {
|
||||||
country: null,
|
country: null,
|
||||||
isVisible: true,
|
visibility: AssetVisibility.Timeline,
|
||||||
includeNull: true,
|
includeNull: true,
|
||||||
},
|
},
|
||||||
assets: [assetLast],
|
assets: [assetLast],
|
||||||
@@ -454,14 +394,6 @@ describe('/search', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /search/smart', () => {
|
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).post('/search/smart');
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('POST /search/random', () => {
|
describe('POST /search/random', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@@ -476,13 +408,6 @@ describe('/search', () => {
|
|||||||
await utils.waitForQueueFinish(admin.accessToken, 'thumbnailGeneration');
|
await utils.waitForQueueFinish(admin.accessToken, 'thumbnailGeneration');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).post('/search/random').send({ size: 1 });
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it.each(TEN_TIMES)('should return 1 random assets', async () => {
|
it.each(TEN_TIMES)('should return 1 random assets', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.post('/search/random')
|
.post('/search/random')
|
||||||
@@ -512,12 +437,6 @@ describe('/search', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /search/explore', () => {
|
describe('GET /search/explore', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get('/search/explore');
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get explore data', async () => {
|
it('should get explore data', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/search/explore')
|
.get('/search/explore')
|
||||||
@@ -528,12 +447,6 @@ describe('/search', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /search/places', () => {
|
describe('GET /search/places', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get('/search/places');
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get relevant places', async () => {
|
it('should get relevant places', async () => {
|
||||||
const name = 'Paris';
|
const name = 'Paris';
|
||||||
|
|
||||||
@@ -552,12 +465,6 @@ describe('/search', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /search/cities', () => {
|
describe('GET /search/cities', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get('/search/cities');
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get all cities', async () => {
|
it('should get all cities', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/search/cities')
|
.get('/search/cities')
|
||||||
@@ -576,12 +483,6 @@ describe('/search', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /search/suggestions', () => {
|
describe('GET /search/suggestions', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get('/search/suggestions');
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get suggestions for country (including null)', async () => {
|
it('should get suggestions for country (including null)', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/search/suggestions?type=country&includeNull=true')
|
.get('/search/suggestions?type=country&includeNull=true')
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
import { AssetMediaResponseDto, LoginResponseDto, SharedLinkType, TimeBucketSize } from '@immich/sdk';
|
import {
|
||||||
|
AssetMediaResponseDto,
|
||||||
|
AssetVisibility,
|
||||||
|
LoginResponseDto,
|
||||||
|
SharedLinkType,
|
||||||
|
TimeBucketAssetResponseDto,
|
||||||
|
} from '@immich/sdk';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { createUserDto } from 'src/fixtures';
|
import { createUserDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
@@ -19,7 +25,8 @@ describe('/timeline', () => {
|
|||||||
let user: LoginResponseDto;
|
let user: LoginResponseDto;
|
||||||
let timeBucketUser: LoginResponseDto;
|
let timeBucketUser: LoginResponseDto;
|
||||||
|
|
||||||
let userAssets: AssetMediaResponseDto[];
|
let user1Assets: AssetMediaResponseDto[];
|
||||||
|
let user2Assets: AssetMediaResponseDto[];
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await utils.resetDatabase();
|
await utils.resetDatabase();
|
||||||
@@ -29,7 +36,7 @@ describe('/timeline', () => {
|
|||||||
utils.userSetup(admin.accessToken, createUserDto.create('time-bucket')),
|
utils.userSetup(admin.accessToken, createUserDto.create('time-bucket')),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
userAssets = await Promise.all([
|
user1Assets = await Promise.all([
|
||||||
utils.createAsset(user.accessToken),
|
utils.createAsset(user.accessToken),
|
||||||
utils.createAsset(user.accessToken),
|
utils.createAsset(user.accessToken),
|
||||||
utils.createAsset(user.accessToken, {
|
utils.createAsset(user.accessToken, {
|
||||||
@@ -42,17 +49,20 @@ describe('/timeline', () => {
|
|||||||
utils.createAsset(user.accessToken),
|
utils.createAsset(user.accessToken),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await Promise.all([
|
user2Assets = await Promise.all([
|
||||||
utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-01-01').toISOString() }),
|
utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-01-01').toISOString() }),
|
||||||
utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-02-10').toISOString() }),
|
utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-02-10').toISOString() }),
|
||||||
utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-02-11').toISOString() }),
|
utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-02-11').toISOString() }),
|
||||||
utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-02-11').toISOString() }),
|
utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-02-11').toISOString() }),
|
||||||
|
utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-02-12').toISOString() }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
await utils.deleteAssets(timeBucketUser.accessToken, [user2Assets[4].id]);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /timeline/buckets', () => {
|
describe('GET /timeline/buckets', () => {
|
||||||
it('should require authentication', async () => {
|
it('should require authentication', async () => {
|
||||||
const { status, body } = await request(app).get('/timeline/buckets').query({ size: TimeBucketSize.Month });
|
const { status, body } = await request(app).get('/timeline/buckets');
|
||||||
expect(status).toBe(401);
|
expect(status).toBe(401);
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
});
|
});
|
||||||
@@ -60,14 +70,13 @@ describe('/timeline', () => {
|
|||||||
it('should get time buckets by month', async () => {
|
it('should get time buckets by month', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/timeline/buckets')
|
.get('/timeline/buckets')
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`);
|
||||||
.query({ size: TimeBucketSize.Month });
|
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual(
|
expect(body).toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
{ count: 3, timeBucket: '1970-02-01T00:00:00.000Z' },
|
{ count: 3, timeBucket: '1970-02-01' },
|
||||||
{ count: 1, timeBucket: '1970-01-01T00:00:00.000Z' },
|
{ count: 1, timeBucket: '1970-01-01' },
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -75,36 +84,20 @@ describe('/timeline', () => {
|
|||||||
it('should not allow access for unrelated shared links', async () => {
|
it('should not allow access for unrelated shared links', async () => {
|
||||||
const sharedLink = await utils.createSharedLink(user.accessToken, {
|
const sharedLink = await utils.createSharedLink(user.accessToken, {
|
||||||
type: SharedLinkType.Individual,
|
type: SharedLinkType.Individual,
|
||||||
assetIds: userAssets.map(({ id }) => id),
|
assetIds: user1Assets.map(({ id }) => id),
|
||||||
});
|
});
|
||||||
|
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app).get('/timeline/buckets').query({ key: sharedLink.key });
|
||||||
.get('/timeline/buckets')
|
|
||||||
.query({ key: sharedLink.key, size: TimeBucketSize.Month });
|
|
||||||
|
|
||||||
expect(status).toBe(400);
|
expect(status).toBe(400);
|
||||||
expect(body).toEqual(errorDto.noPermission);
|
expect(body).toEqual(errorDto.noPermission);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get time buckets by day', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.get('/timeline/buckets')
|
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
|
||||||
.query({ size: TimeBucketSize.Day });
|
|
||||||
|
|
||||||
expect(status).toBe(200);
|
|
||||||
expect(body).toEqual([
|
|
||||||
{ count: 2, timeBucket: '1970-02-11T00:00:00.000Z' },
|
|
||||||
{ count: 1, timeBucket: '1970-02-10T00:00:00.000Z' },
|
|
||||||
{ count: 1, timeBucket: '1970-01-01T00:00:00.000Z' },
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return error if time bucket is requested with partners asset and archived', async () => {
|
it('should return error if time bucket is requested with partners asset and archived', async () => {
|
||||||
const req1 = await request(app)
|
const req1 = await request(app)
|
||||||
.get('/timeline/buckets')
|
.get('/timeline/buckets')
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
||||||
.query({ size: TimeBucketSize.Month, withPartners: true, isArchived: true });
|
.query({ withPartners: true, visibility: AssetVisibility.Archive });
|
||||||
|
|
||||||
expect(req1.status).toBe(400);
|
expect(req1.status).toBe(400);
|
||||||
expect(req1.body).toEqual(errorDto.badRequest());
|
expect(req1.body).toEqual(errorDto.badRequest());
|
||||||
@@ -112,7 +105,7 @@ describe('/timeline', () => {
|
|||||||
const req2 = await request(app)
|
const req2 = await request(app)
|
||||||
.get('/timeline/buckets')
|
.get('/timeline/buckets')
|
||||||
.set('Authorization', `Bearer ${user.accessToken}`)
|
.set('Authorization', `Bearer ${user.accessToken}`)
|
||||||
.query({ size: TimeBucketSize.Month, withPartners: true, isArchived: undefined });
|
.query({ withPartners: true, visibility: undefined });
|
||||||
|
|
||||||
expect(req2.status).toBe(400);
|
expect(req2.status).toBe(400);
|
||||||
expect(req2.body).toEqual(errorDto.badRequest());
|
expect(req2.body).toEqual(errorDto.badRequest());
|
||||||
@@ -122,7 +115,7 @@ describe('/timeline', () => {
|
|||||||
const req1 = await request(app)
|
const req1 = await request(app)
|
||||||
.get('/timeline/buckets')
|
.get('/timeline/buckets')
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
||||||
.query({ size: TimeBucketSize.Month, withPartners: true, isFavorite: true });
|
.query({ withPartners: true, isFavorite: true });
|
||||||
|
|
||||||
expect(req1.status).toBe(400);
|
expect(req1.status).toBe(400);
|
||||||
expect(req1.body).toEqual(errorDto.badRequest());
|
expect(req1.body).toEqual(errorDto.badRequest());
|
||||||
@@ -130,7 +123,7 @@ describe('/timeline', () => {
|
|||||||
const req2 = await request(app)
|
const req2 = await request(app)
|
||||||
.get('/timeline/buckets')
|
.get('/timeline/buckets')
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
||||||
.query({ size: TimeBucketSize.Month, withPartners: true, isFavorite: false });
|
.query({ withPartners: true, isFavorite: false });
|
||||||
|
|
||||||
expect(req2.status).toBe(400);
|
expect(req2.status).toBe(400);
|
||||||
expect(req2.body).toEqual(errorDto.badRequest());
|
expect(req2.body).toEqual(errorDto.badRequest());
|
||||||
@@ -140,7 +133,7 @@ describe('/timeline', () => {
|
|||||||
const req = await request(app)
|
const req = await request(app)
|
||||||
.get('/timeline/buckets')
|
.get('/timeline/buckets')
|
||||||
.set('Authorization', `Bearer ${user.accessToken}`)
|
.set('Authorization', `Bearer ${user.accessToken}`)
|
||||||
.query({ size: TimeBucketSize.Month, withPartners: true, isTrashed: true });
|
.query({ withPartners: true, isTrashed: true });
|
||||||
|
|
||||||
expect(req.status).toBe(400);
|
expect(req.status).toBe(400);
|
||||||
expect(req.body).toEqual(errorDto.badRequest());
|
expect(req.body).toEqual(errorDto.badRequest());
|
||||||
@@ -150,7 +143,6 @@ describe('/timeline', () => {
|
|||||||
describe('GET /timeline/bucket', () => {
|
describe('GET /timeline/bucket', () => {
|
||||||
it('should require authentication', async () => {
|
it('should require authentication', async () => {
|
||||||
const { status, body } = await request(app).get('/timeline/bucket').query({
|
const { status, body } = await request(app).get('/timeline/bucket').query({
|
||||||
size: TimeBucketSize.Month,
|
|
||||||
timeBucket: '1900-01-01',
|
timeBucket: '1900-01-01',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -161,11 +153,28 @@ describe('/timeline', () => {
|
|||||||
it('should handle 5 digit years', async () => {
|
it('should handle 5 digit years', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/timeline/bucket')
|
.get('/timeline/bucket')
|
||||||
.query({ size: TimeBucketSize.Month, timeBucket: '012345-01-01' })
|
.query({ timeBucket: '012345-01-01' })
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`);
|
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`);
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual([]);
|
expect(body).toEqual({
|
||||||
|
city: [],
|
||||||
|
country: [],
|
||||||
|
duration: [],
|
||||||
|
id: [],
|
||||||
|
visibility: [],
|
||||||
|
isFavorite: [],
|
||||||
|
isImage: [],
|
||||||
|
isTrashed: [],
|
||||||
|
livePhotoVideoId: [],
|
||||||
|
fileCreatedAt: [],
|
||||||
|
localOffsetHours: [],
|
||||||
|
ownerId: [],
|
||||||
|
projectionType: [],
|
||||||
|
ratio: [],
|
||||||
|
status: [],
|
||||||
|
thumbhash: [],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO enable date string validation while still accepting 5 digit years
|
// TODO enable date string validation while still accepting 5 digit years
|
||||||
@@ -173,7 +182,7 @@ describe('/timeline', () => {
|
|||||||
// const { status, body } = await request(app)
|
// const { status, body } = await request(app)
|
||||||
// .get('/timeline/bucket')
|
// .get('/timeline/bucket')
|
||||||
// .set('Authorization', `Bearer ${user.accessToken}`)
|
// .set('Authorization', `Bearer ${user.accessToken}`)
|
||||||
// .query({ size: TimeBucketSize.Month, timeBucket: 'foo' });
|
// .query({ timeBucket: 'foo' });
|
||||||
|
|
||||||
// expect(status).toBe(400);
|
// expect(status).toBe(400);
|
||||||
// expect(body).toEqual(errorDto.badRequest);
|
// expect(body).toEqual(errorDto.badRequest);
|
||||||
@@ -183,10 +192,39 @@ describe('/timeline', () => {
|
|||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/timeline/bucket')
|
.get('/timeline/bucket')
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
||||||
.query({ size: TimeBucketSize.Month, timeBucket: '1970-02-10' });
|
.query({ timeBucket: '1970-02-10' });
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual([]);
|
expect(body).toEqual({
|
||||||
|
city: [],
|
||||||
|
country: [],
|
||||||
|
duration: [],
|
||||||
|
id: [],
|
||||||
|
visibility: [],
|
||||||
|
isFavorite: [],
|
||||||
|
isImage: [],
|
||||||
|
isTrashed: [],
|
||||||
|
livePhotoVideoId: [],
|
||||||
|
fileCreatedAt: [],
|
||||||
|
localOffsetHours: [],
|
||||||
|
ownerId: [],
|
||||||
|
projectionType: [],
|
||||||
|
ratio: [],
|
||||||
|
status: [],
|
||||||
|
thumbhash: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return time bucket in trash', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/timeline/bucket')
|
||||||
|
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
||||||
|
.query({ timeBucket: '1970-02-01T00:00:00.000Z', isTrashed: true });
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
|
||||||
|
const timeBucket: TimeBucketAssetResponseDto = body;
|
||||||
|
expect(timeBucket.isTrashed).toEqual([true]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ describe('/admin/users', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should ignore `isAdmin`', async () => {
|
it('should accept `isAdmin`', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.post(`/admin/users`)
|
.post(`/admin/users`)
|
||||||
.send({
|
.send({
|
||||||
@@ -130,7 +130,7 @@ describe('/admin/users', () => {
|
|||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
email: 'user5@immich.cloud',
|
email: 'user5@immich.cloud',
|
||||||
isAdmin: false,
|
isAdmin: true,
|
||||||
shouldChangePassword: true,
|
shouldChangePassword: true,
|
||||||
});
|
});
|
||||||
expect(status).toBe(201);
|
expect(status).toBe(201);
|
||||||
@@ -163,14 +163,15 @@ describe('/admin/users', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should not allow a non-admin to become an admin', async () => {
|
it('should allow a non-admin to become an admin', async () => {
|
||||||
|
const user = await utils.userSetup(admin.accessToken, createUserDto.create('admin2'));
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/admin/users/${nonAdmin.userId}`)
|
.put(`/admin/users/${user.userId}`)
|
||||||
.send({ isAdmin: true })
|
.send({ isAdmin: true })
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toMatchObject({ isAdmin: false });
|
expect(body).toMatchObject({ isAdmin: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('ignores updates to profileImagePath', async () => {
|
it('ignores updates to profileImagePath', async () => {
|
||||||
|
|||||||
@@ -7,6 +7,44 @@ describe(`immich-admin`, () => {
|
|||||||
await utils.adminSetup();
|
await utils.adminSetup();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('revoke-admin', () => {
|
||||||
|
it('should revoke admin privileges from a user', async () => {
|
||||||
|
const { child, promise } = immichAdmin(['revoke-admin']);
|
||||||
|
|
||||||
|
let data = '';
|
||||||
|
child.stdout.on('data', (chunk) => {
|
||||||
|
data += chunk;
|
||||||
|
if (data.includes('Please enter the user email:')) {
|
||||||
|
child.stdin.end('admin@immich.cloud\n');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const { stdout, exitCode } = await promise;
|
||||||
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
|
expect(stdout).toContain('Admin access has been revoked from');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('grant-admin', () => {
|
||||||
|
it('should grant admin privileges to a user', async () => {
|
||||||
|
const { child, promise } = immichAdmin(['grant-admin']);
|
||||||
|
|
||||||
|
let data = '';
|
||||||
|
child.stdout.on('data', (chunk) => {
|
||||||
|
data += chunk;
|
||||||
|
if (data.includes('Please enter the user email:')) {
|
||||||
|
child.stdin.end('admin@immich.cloud\n');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const { stdout, exitCode } = await promise;
|
||||||
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
|
expect(stdout).toContain('Admin access has been granted to');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('list-users', () => {
|
describe('list-users', () => {
|
||||||
it('should list the admin user', async () => {
|
it('should list the admin user', async () => {
|
||||||
const { stdout, exitCode } = await immichAdmin(['list-users']).promise;
|
const { stdout, exitCode } = await immichAdmin(['list-users']).promise;
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ export const loginResponseDto = {
|
|||||||
accessToken: expect.any(String),
|
accessToken: expect.any(String),
|
||||||
name: 'Immich Admin',
|
name: 'Immich Admin',
|
||||||
isAdmin: true,
|
isAdmin: true,
|
||||||
|
isOnboarded: false,
|
||||||
profileImagePath: '',
|
profileImagePath: '',
|
||||||
shouldChangePassword: true,
|
shouldChangePassword: true,
|
||||||
userEmail: 'admin@immich.cloud',
|
userEmail: 'admin@immich.cloud',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
AssetMediaCreateDto,
|
AssetMediaCreateDto,
|
||||||
AssetMediaResponseDto,
|
AssetMediaResponseDto,
|
||||||
AssetResponseDto,
|
AssetResponseDto,
|
||||||
|
AssetVisibility,
|
||||||
CheckExistingAssetsDto,
|
CheckExistingAssetsDto,
|
||||||
CreateAlbumDto,
|
CreateAlbumDto,
|
||||||
CreateLibraryDto,
|
CreateLibraryDto,
|
||||||
@@ -429,7 +430,10 @@ export const utils = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
archiveAssets: (accessToken: string, ids: string[]) =>
|
archiveAssets: (accessToken: string, ids: string[]) =>
|
||||||
updateAssets({ assetBulkUpdateDto: { ids, isArchived: true } }, { headers: asBearerAuth(accessToken) }),
|
updateAssets(
|
||||||
|
{ assetBulkUpdateDto: { ids, visibility: AssetVisibility.Archive } },
|
||||||
|
{ headers: asBearerAuth(accessToken) },
|
||||||
|
),
|
||||||
|
|
||||||
deleteAssets: (accessToken: string, ids: string[]) =>
|
deleteAssets: (accessToken: string, ids: string[]) =>
|
||||||
deleteAssets({ assetBulkDeleteDto: { ids } }, { headers: asBearerAuth(accessToken) }),
|
deleteAssets({ assetBulkDeleteDto: { ids } }, { headers: asBearerAuth(accessToken) }),
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ test.describe('Registration', () => {
|
|||||||
// onboarding
|
// onboarding
|
||||||
await expect(page).toHaveURL('/auth/onboarding');
|
await expect(page).toHaveURL('/auth/onboarding');
|
||||||
await page.getByRole('button', { name: 'Theme' }).click();
|
await page.getByRole('button', { name: 'Theme' }).click();
|
||||||
await page.getByRole('button', { name: 'Privacy' }).click();
|
await page.getByRole('button', { name: 'Language' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Server Privacy' }).click();
|
||||||
|
await page.getByRole('button', { name: 'User Privacy' }).click();
|
||||||
await page.getByRole('button', { name: 'Storage Template' }).click();
|
await page.getByRole('button', { name: 'Storage Template' }).click();
|
||||||
await page.getByRole('button', { name: 'Done' }).click();
|
await page.getByRole('button', { name: 'Done' }).click();
|
||||||
|
|
||||||
@@ -77,6 +79,13 @@ test.describe('Registration', () => {
|
|||||||
await page.getByLabel('Password').fill('new-password');
|
await page.getByLabel('Password').fill('new-password');
|
||||||
await page.getByRole('button', { name: 'Login' }).click();
|
await page.getByRole('button', { name: 'Login' }).click();
|
||||||
|
|
||||||
|
// onboarding
|
||||||
|
await expect(page).toHaveURL('/auth/onboarding');
|
||||||
|
await page.getByRole('button', { name: 'Theme' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Language' }).click();
|
||||||
|
await page.getByRole('button', { name: 'User Privacy' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Done' }).click();
|
||||||
|
|
||||||
// success
|
// success
|
||||||
await expect(page).toHaveURL(/\/photos/);
|
await expect(page).toHaveURL(/\/photos/);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -47,15 +47,13 @@ test.describe('Shared Links', () => {
|
|||||||
await page.locator(`[data-asset-id="${asset.id}"]`).hover();
|
await page.locator(`[data-asset-id="${asset.id}"]`).hover();
|
||||||
await page.waitForSelector('[data-group] 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 Promise.all([page.waitForEvent('download'), page.getByRole('button', { name: 'Download' }).click()]);
|
||||||
await page.waitForEvent('download');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('download all from shared link', async ({ page }) => {
|
test('download all from shared link', async ({ page }) => {
|
||||||
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.getByRole('button', { name: 'Download' }).click();
|
await Promise.all([page.waitForEvent('download'), page.getByRole('button', { name: 'Download' }).click()]);
|
||||||
await page.waitForEvent('download');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('enter password for a shared link', async ({ page }) => {
|
test('enter password for a shared link', async ({ page }) => {
|
||||||
|
|||||||
89
e2e/src/web/specs/user-admin.e2e-spec.ts
Normal file
89
e2e/src/web/specs/user-admin.e2e-spec.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { getUserAdmin } from '@immich/sdk';
|
||||||
|
import { expect, test } from '@playwright/test';
|
||||||
|
import { asBearerAuth, utils } from 'src/utils';
|
||||||
|
|
||||||
|
test.describe('User Administration', () => {
|
||||||
|
test.beforeAll(() => {
|
||||||
|
utils.initSdk();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.beforeEach(async () => {
|
||||||
|
await utils.resetDatabase();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('validate admin/users link', async ({ context, page }) => {
|
||||||
|
const admin = await utils.adminSetup();
|
||||||
|
await utils.setAuthCookies(context, admin.accessToken);
|
||||||
|
|
||||||
|
// Navigate to user management page and verify title and header
|
||||||
|
await page.goto(`/admin/users`);
|
||||||
|
await expect(page).toHaveTitle(/User Management/);
|
||||||
|
await expect(page.getByText('User Management')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('create user', async ({ context, page }) => {
|
||||||
|
const admin = await utils.adminSetup();
|
||||||
|
await utils.setAuthCookies(context, admin.accessToken);
|
||||||
|
|
||||||
|
// Create a new user
|
||||||
|
await page.goto('/admin/users');
|
||||||
|
await page.getByRole('button', { name: 'Create user' }).click();
|
||||||
|
await page.getByLabel('Email').fill('user@immich.cloud');
|
||||||
|
await page.getByLabel('Password', { exact: true }).fill('password');
|
||||||
|
await page.getByLabel('Confirm Password').fill('password');
|
||||||
|
await page.getByLabel('Name').fill('Immich User');
|
||||||
|
await page.getByRole('button', { name: 'Create', exact: true }).click();
|
||||||
|
|
||||||
|
// Verify the user exists in the user list
|
||||||
|
await page.getByRole('row', { name: 'user@immich.cloud' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('promote to admin', async ({ context, page }) => {
|
||||||
|
const admin = await utils.adminSetup();
|
||||||
|
await utils.setAuthCookies(context, admin.accessToken);
|
||||||
|
|
||||||
|
const user = await utils.userSetup(admin.accessToken, {
|
||||||
|
name: 'Admin 2',
|
||||||
|
email: 'admin2@immich.cloud',
|
||||||
|
password: 'password',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.isAdmin).toBe(false);
|
||||||
|
|
||||||
|
await page.goto(`/admin/users/${user.userId}`);
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Edit user' }).click();
|
||||||
|
await expect(page.getByLabel('Admin User')).not.toBeChecked();
|
||||||
|
await page.getByText('Admin User').click();
|
||||||
|
await expect(page.getByLabel('Admin User')).toBeChecked();
|
||||||
|
await page.getByRole('button', { name: 'Confirm' }).click();
|
||||||
|
|
||||||
|
const updated = await getUserAdmin({ id: user.userId }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
expect(updated.isAdmin).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('revoke admin access', async ({ context, page }) => {
|
||||||
|
const admin = await utils.adminSetup();
|
||||||
|
await utils.setAuthCookies(context, admin.accessToken);
|
||||||
|
|
||||||
|
const user = await utils.userSetup(admin.accessToken, {
|
||||||
|
name: 'Admin 2',
|
||||||
|
email: 'admin2@immich.cloud',
|
||||||
|
password: 'password',
|
||||||
|
isAdmin: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.isAdmin).toBe(true);
|
||||||
|
|
||||||
|
await page.goto(`/admin/users/${user.userId}`);
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Edit user' }).click();
|
||||||
|
await expect(page.getByLabel('Admin User')).toBeChecked();
|
||||||
|
await page.getByText('Admin User').click();
|
||||||
|
await expect(page.getByLabel('Admin User')).not.toBeChecked();
|
||||||
|
await page.getByRole('button', { name: 'Confirm' }).click();
|
||||||
|
|
||||||
|
const updated = await getUserAdmin({ id: user.userId }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
expect(updated.isAdmin).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
Submodule e2e/test-assets updated: 9e3b964b08...8885d6d01c
@@ -40,7 +40,6 @@
|
|||||||
"backup_keep_last_amount": "Aantal vorige rugsteune om te hou",
|
"backup_keep_last_amount": "Aantal vorige rugsteune om te hou",
|
||||||
"backup_settings": "Rugsteun instellings",
|
"backup_settings": "Rugsteun instellings",
|
||||||
"backup_settings_description": "Bestuur databasis rugsteun instellings",
|
"backup_settings_description": "Bestuur databasis rugsteun instellings",
|
||||||
"check_all": "Kies Alles",
|
|
||||||
"cleared_jobs": "Poste gevee vir: {job}",
|
"cleared_jobs": "Poste gevee vir: {job}",
|
||||||
"config_set_by_file": "Config word tans deur 'n konfigurasielêer gestel",
|
"config_set_by_file": "Config word tans deur 'n konfigurasielêer gestel",
|
||||||
"confirm_delete_library": "Is jy seker jy wil {library}-biblioteek uitvee?",
|
"confirm_delete_library": "Is jy seker jy wil {library}-biblioteek uitvee?",
|
||||||
@@ -55,12 +54,10 @@
|
|||||||
"disable_login": "Deaktiveer aanmelding",
|
"disable_login": "Deaktiveer aanmelding",
|
||||||
"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_management": "Eksterne Biblioteekbestuur",
|
"external_library_management": "Eksterne Biblioteekbestuur",
|
||||||
"face_detection": "Gesig deteksie",
|
"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",
|
|
||||||
"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",
|
||||||
|
|||||||
236
i18n/ar.json
236
i18n/ar.json
@@ -14,7 +14,6 @@
|
|||||||
"add_a_location": "إضافة موقع",
|
"add_a_location": "إضافة موقع",
|
||||||
"add_a_name": "إضافة إسم",
|
"add_a_name": "إضافة إسم",
|
||||||
"add_a_title": "إضافة عنوان",
|
"add_a_title": "إضافة عنوان",
|
||||||
"add_endpoint": "Add endpoint",
|
|
||||||
"add_exclusion_pattern": "إضافة نمط إستثناء",
|
"add_exclusion_pattern": "إضافة نمط إستثناء",
|
||||||
"add_import_path": "إضافة مسار الإستيراد",
|
"add_import_path": "إضافة مسار الإستيراد",
|
||||||
"add_location": "إضافة موقع",
|
"add_location": "إضافة موقع",
|
||||||
@@ -44,8 +43,6 @@
|
|||||||
"backup_keep_last_amount": "مقدار النسخ الاحتياطية السابقة للاحتفاظ بها",
|
"backup_keep_last_amount": "مقدار النسخ الاحتياطية السابقة للاحتفاظ بها",
|
||||||
"backup_settings": "إعدادات النسخ الاحتياطي",
|
"backup_settings": "إعدادات النسخ الاحتياطي",
|
||||||
"backup_settings_description": "إدارة إعدادات النسخ الاحتياطي لقاعدة البيانات",
|
"backup_settings_description": "إدارة إعدادات النسخ الاحتياطي لقاعدة البيانات",
|
||||||
"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}؟",
|
||||||
@@ -60,14 +57,12 @@
|
|||||||
"disable_login": "تعطيل تسجيل الدخول",
|
"disable_login": "تعطيل تسجيل الدخول",
|
||||||
"duplicate_detection_job_description": "بدء التعلم الآلي على المحتوى للعثور على الصور المتشابهة. يعتمد على البحث الذكي",
|
"duplicate_detection_job_description": "بدء التعلم الآلي على المحتوى للعثور على الصور المتشابهة. يعتمد على البحث الذكي",
|
||||||
"exclusion_pattern_description": "تتيح لك أنماط الاستبعاد تجاهل الملفات والمجلدات عند فحص مكتبتك. يعد هذا مفيدًا إذا كان لديك مجلدات تحتوي على ملفات لا تريد استيرادها، مثل ملفات RAW.",
|
"exclusion_pattern_description": "تتيح لك أنماط الاستبعاد تجاهل الملفات والمجلدات عند فحص مكتبتك. يعد هذا مفيدًا إذا كان لديك مجلدات تحتوي على ملفات لا تريد استيرادها، مثل ملفات RAW.",
|
||||||
"external_library_created_at": "مكتبة خارجية (أُنشئت في {date})",
|
|
||||||
"external_library_management": "إدارة المكتبة الخارجية",
|
"external_library_management": "إدارة المكتبة الخارجية",
|
||||||
"face_detection": "إكتشاف الوجوه",
|
"face_detection": "إكتشاف الوجوه",
|
||||||
"face_detection_description": "اكتشف الوجوه في الأصول باستخدام التعلم الآلي. بالنسبة لمقاطع الفيديو، يتم اعتبار الصورة المصغرة فقط. \"تحديث\" (إعادة) معالجة جميع الأصول. \"إعادة تعيين\" تمسح أيضًا جميع بيانات الوجوه الحالية. \"مفقود\" يضع الأصول التي لم تتم معالجتها بعد في قائمة الانتظار. سيتم وضع الوجوه المكتشفة في قائمة الانتظار للتعرف على الوجه بعد اكتمال اكتشاف الوجه، وتجميعها في أشخاص موجودين أو جدد.",
|
"face_detection_description": "اكتشف الوجوه في الأصول باستخدام التعلم الآلي. بالنسبة لمقاطع الفيديو، يتم اعتبار الصورة المصغرة فقط. \"تحديث\" (إعادة) معالجة جميع الأصول. \"إعادة تعيين\" تمسح أيضًا جميع بيانات الوجوه الحالية. \"مفقود\" يضع الأصول التي لم تتم معالجتها بعد في قائمة الانتظار. سيتم وضع الوجوه المكتشفة في قائمة الانتظار للتعرف على الوجه بعد اكتمال اكتشاف الوجه، وتجميعها في أشخاص موجودين أو جدد.",
|
||||||
"facial_recognition_job_description": "تجميع الوجوه المكتشفة كأشخاص. يتم تنفيذ هذه الخطوة بعد اكتمال اكتشاف الوجه. خيار \"إعادة التعيين\" يعيد تجميع جميع الوجوه. خيار \"المفقود\" يضع في قائمة الانتظار الوجوه التي لم يتم تعيين شخص لها.",
|
"facial_recognition_job_description": "تجميع الوجوه المكتشفة كأشخاص. يتم تنفيذ هذه الخطوة بعد اكتمال اكتشاف الوجه. خيار \"إعادة التعيين\" يعيد تجميع جميع الوجوه. خيار \"المفقود\" يضع في قائمة الانتظار الوجوه التي لم يتم تعيين شخص لها.",
|
||||||
"failed_job_command": "فشل الأمر {command} للمهمة: {job}",
|
"failed_job_command": "فشل الأمر {command} للمهمة: {job}",
|
||||||
"force_delete_user_warning": "تحذير: سيؤدي ذلك إلى إزالة المستخدم وجميع محتوياته على الفور. لا يمكن التراجع عن هذا الإجراء ولا يمكن استرداد الملفات.",
|
"force_delete_user_warning": "تحذير: سيؤدي ذلك إلى إزالة المستخدم وجميع محتوياته على الفور. لا يمكن التراجع عن هذا الإجراء ولا يمكن استرداد الملفات.",
|
||||||
"forcing_refresh_library_files": "إجبار التحديث لجميع ملفات المكتبة",
|
|
||||||
"image_format": "التنسيق",
|
"image_format": "التنسيق",
|
||||||
"image_format_description": "يُنتج WebP ملفات أصغر حجمًا من ملفات JPEG، ولكنه أبطأ في عملية الترميز.",
|
"image_format_description": "يُنتج WebP ملفات أصغر حجمًا من ملفات JPEG، ولكنه أبطأ في عملية الترميز.",
|
||||||
"image_prefer_embedded_preview": "تفضيل المعاينة المدمجة",
|
"image_prefer_embedded_preview": "تفضيل المعاينة المدمجة",
|
||||||
@@ -187,28 +182,19 @@
|
|||||||
"oauth_auto_register": "التسجيل التلقائي",
|
"oauth_auto_register": "التسجيل التلقائي",
|
||||||
"oauth_auto_register_description": "التسجيل التلقائي للمستخدمين الجدد بعد تسجيل الدخول باستخدام OAuth",
|
"oauth_auto_register_description": "التسجيل التلقائي للمستخدمين الجدد بعد تسجيل الدخول باستخدام OAuth",
|
||||||
"oauth_button_text": "نص الزر",
|
"oauth_button_text": "نص الزر",
|
||||||
"oauth_client_id": "معرف العميل",
|
|
||||||
"oauth_client_secret": "الرمز السري للعميل",
|
|
||||||
"oauth_enable_description": "تسجيل الدخول باستخدام OAuth",
|
"oauth_enable_description": "تسجيل الدخول باستخدام OAuth",
|
||||||
"oauth_issuer_url": "عنوان URL الخاص بجهة الإصدار",
|
|
||||||
"oauth_mobile_redirect_uri": "عنوان URI لإعادة التوجيه على الهاتف",
|
"oauth_mobile_redirect_uri": "عنوان URI لإعادة التوجيه على الهاتف",
|
||||||
"oauth_mobile_redirect_uri_override": "تجاوز عنوان URI لإعادة التوجيه على الهاتف",
|
"oauth_mobile_redirect_uri_override": "تجاوز عنوان URI لإعادة التوجيه على الهاتف",
|
||||||
"oauth_mobile_redirect_uri_override_description": "قم بتفعيله عندما لا يسمح موفر OAuth بمعرف URI للجوال، مثل '{callback}'",
|
"oauth_mobile_redirect_uri_override_description": "قم بتفعيله عندما لا يسمح موفر OAuth بمعرف URI للجوال، مثل ''{callback}''",
|
||||||
"oauth_profile_signing_algorithm": "خوارزمية توقيع الملف الشخصي",
|
|
||||||
"oauth_profile_signing_algorithm_description": "الخوارزمية المستخدمة للتوقيع على ملف تعريف المستخدم.",
|
|
||||||
"oauth_scope": "النطاق",
|
|
||||||
"oauth_settings": "OAuth",
|
"oauth_settings": "OAuth",
|
||||||
"oauth_settings_description": "إدارة إعدادات تسجيل الدخول OAuth",
|
"oauth_settings_description": "إدارة إعدادات تسجيل الدخول OAuth",
|
||||||
"oauth_settings_more_details": "لمزيد من التفاصيل حول هذه الميزة، يرجى الرجوع إلى <link>الوثائق</link>.",
|
"oauth_settings_more_details": "لمزيد من التفاصيل حول هذه الميزة، يرجى الرجوع إلى <link>الوثائق</link>.",
|
||||||
"oauth_signing_algorithm": "خوارزمية التوقيع",
|
|
||||||
"oauth_storage_label_claim": "المطالبة بتصنيف التخزين",
|
"oauth_storage_label_claim": "المطالبة بتصنيف التخزين",
|
||||||
"oauth_storage_label_claim_description": "قم تلقائيًا بتعيين تصنيف التخزين الخاص بالمستخدم على قيمة هذه المطالبة.",
|
"oauth_storage_label_claim_description": "قم تلقائيًا بتعيين تصنيف التخزين الخاص بالمستخدم على قيمة هذه المطالبة.",
|
||||||
"oauth_storage_quota_claim": "المطالبة بحصة التخزين",
|
"oauth_storage_quota_claim": "المطالبة بحصة التخزين",
|
||||||
"oauth_storage_quota_claim_description": "قم تلقائيًا بتعيين حصة التخزين للمستخدم على قيمة هذه المطالبة.",
|
"oauth_storage_quota_claim_description": "قم تلقائيًا بتعيين حصة التخزين للمستخدم على قيمة هذه المطالبة.",
|
||||||
"oauth_storage_quota_default": "حصة التخزين الافتراضية (جيجابايت)",
|
"oauth_storage_quota_default": "حصة التخزين الافتراضية (جيجابايت)",
|
||||||
"oauth_storage_quota_default_description": "الحصة بالجيجابايت التي سيتم استخدامها عندما لا يتم توفير مطالبة (أدخل 0 لحصة غير محدودة).",
|
"oauth_storage_quota_default_description": "الحصة بالجيجابايت التي سيتم استخدامها عندما لا يتم توفير مطالبة (أدخل 0 لحصة غير محدودة).",
|
||||||
"offline_paths": "مسارات غير متصلة",
|
|
||||||
"offline_paths_description": "قد تكون هذه النتائج ناتجة عن حذف يدوي لملفات لا تتبع لمكتبة خارجية.",
|
|
||||||
"password_enable_description": "تسجيل الدخول باستخدام البريد الكتروني وكلمة المرور",
|
"password_enable_description": "تسجيل الدخول باستخدام البريد الكتروني وكلمة المرور",
|
||||||
"password_settings": "تسجيل الدخول بكلمة المرور",
|
"password_settings": "تسجيل الدخول بكلمة المرور",
|
||||||
"password_settings_description": "إدارة تسجيل الدخول بكلمة المرور",
|
"password_settings_description": "إدارة تسجيل الدخول بكلمة المرور",
|
||||||
@@ -218,9 +204,6 @@
|
|||||||
"refreshing_all_libraries": "تحديث كافة المكتبات",
|
"refreshing_all_libraries": "تحديث كافة المكتبات",
|
||||||
"registration": "تسجيل المدير",
|
"registration": "تسجيل المدير",
|
||||||
"registration_description": "بما أنك أول مستخدم في النظام، سيتم تعيينك كمسؤول وستكون مسؤولًا عن المهام الإدارية، وسيتم إنشاء مستخدمين إضافيين بواسطتك.",
|
"registration_description": "بما أنك أول مستخدم في النظام، سيتم تعيينك كمسؤول وستكون مسؤولًا عن المهام الإدارية، وسيتم إنشاء مستخدمين إضافيين بواسطتك.",
|
||||||
"repair_all": "إصلاح الكل",
|
|
||||||
"repair_matched_items": "تمت مطابقة {count, plural, one {# عنصر} other {# عناصر}}",
|
|
||||||
"repaired_items": "تم إصلاح {count, plural, one {# عنصر} other {# عناصر}}",
|
|
||||||
"require_password_change_on_login": "الطلب من المستخدم تغيير كلمة المرور عند تسجيل الدخول الأول",
|
"require_password_change_on_login": "الطلب من المستخدم تغيير كلمة المرور عند تسجيل الدخول الأول",
|
||||||
"reset_settings_to_default": "إعادة ضبط الإعدادات إلى الوضع الافتراضي",
|
"reset_settings_to_default": "إعادة ضبط الإعدادات إلى الوضع الافتراضي",
|
||||||
"reset_settings_to_recent_saved": "إعادة ضبط الإعدادات إلى الإعدادات المحفوظة مؤخرًا",
|
"reset_settings_to_recent_saved": "إعادة ضبط الإعدادات إلى الإعدادات المحفوظة مؤخرًا",
|
||||||
@@ -261,7 +244,6 @@
|
|||||||
"template_email_invite_album": "قالب دعوة الألبوم",
|
"template_email_invite_album": "قالب دعوة الألبوم",
|
||||||
"template_email_preview": "عرض مسبق",
|
"template_email_preview": "عرض مسبق",
|
||||||
"template_email_settings": "نماذج البريد الالكتروني",
|
"template_email_settings": "نماذج البريد الالكتروني",
|
||||||
"template_email_settings_description": "إدارة قوالب إشعارات البريد الإلكتروني المخصصة",
|
|
||||||
"template_email_update_album": "تحديث قالب الألبوم",
|
"template_email_update_album": "تحديث قالب الألبوم",
|
||||||
"template_email_welcome": "قالب البريد الإلكتروني الترحيبي",
|
"template_email_welcome": "قالب البريد الإلكتروني الترحيبي",
|
||||||
"template_settings": "قوالب الإشعارات",
|
"template_settings": "قوالب الإشعارات",
|
||||||
@@ -270,7 +252,6 @@
|
|||||||
"theme_custom_css_settings_description": "أوراق الأنماط المتتالية تسمح بتخصيص تصميم Immich.",
|
"theme_custom_css_settings_description": "أوراق الأنماط المتتالية تسمح بتخصيص تصميم Immich.",
|
||||||
"theme_settings": "إعدادات السمة",
|
"theme_settings": "إعدادات السمة",
|
||||||
"theme_settings_description": "إدارة تخصيص واجهة ويب Immich",
|
"theme_settings_description": "إدارة تخصيص واجهة ويب Immich",
|
||||||
"these_files_matched_by_checksum": "تتم مطابقة هذه الملفات من خلال المجاميع الاختبارية الخاصة بهم",
|
|
||||||
"thumbnail_generation_job": "إنشاء الصور المصغرة",
|
"thumbnail_generation_job": "إنشاء الصور المصغرة",
|
||||||
"thumbnail_generation_job_description": "إنشاء صور مصغرة كبيرة وصغيرة وغير واضحة لكل أصل، بالإضافة إلى صور مصغرة لكل شخص",
|
"thumbnail_generation_job_description": "إنشاء صور مصغرة كبيرة وصغيرة وغير واضحة لكل أصل، بالإضافة إلى صور مصغرة لكل شخص",
|
||||||
"transcoding_acceleration_api": "واجهة برمجة التطبيقات للتسريع",
|
"transcoding_acceleration_api": "واجهة برمجة التطبيقات للتسريع",
|
||||||
@@ -301,7 +282,6 @@
|
|||||||
"transcoding_hardware_acceleration_description": "تجريبي؛ أسرع بكثير، ولكن ستكون جودتها أقل عند نفس معدل البت",
|
"transcoding_hardware_acceleration_description": "تجريبي؛ أسرع بكثير، ولكن ستكون جودتها أقل عند نفس معدل البت",
|
||||||
"transcoding_hardware_decoding": "فك تشفير الأجهزة",
|
"transcoding_hardware_decoding": "فك تشفير الأجهزة",
|
||||||
"transcoding_hardware_decoding_setting_description": "ينطبق ذلك فقط على NVENC، QSV، و RKMPP. يمكن التسريع من طرف لطرف بدلاً من تسريع الترميز فقط. قد لا يعمل على جميع مقاطع الفيديو.",
|
"transcoding_hardware_decoding_setting_description": "ينطبق ذلك فقط على NVENC، QSV، و RKMPP. يمكن التسريع من طرف لطرف بدلاً من تسريع الترميز فقط. قد لا يعمل على جميع مقاطع الفيديو.",
|
||||||
"transcoding_hevc_codec": "كود HEVC",
|
|
||||||
"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": "الحد الأقصى لمعدل البت",
|
||||||
@@ -339,8 +319,6 @@
|
|||||||
"trash_number_of_days_description": "عدد أيام الاحتفاظ بالمحتويات في سلة المهملات قبل حذفها نهائيًا",
|
"trash_number_of_days_description": "عدد أيام الاحتفاظ بالمحتويات في سلة المهملات قبل حذفها نهائيًا",
|
||||||
"trash_settings": "إعدادات سلة المهملات",
|
"trash_settings": "إعدادات سلة المهملات",
|
||||||
"trash_settings_description": "إدارة إعدادات سلة المهملات",
|
"trash_settings_description": "إدارة إعدادات سلة المهملات",
|
||||||
"untracked_files": "الملفات التي لم يتم تعقبها",
|
|
||||||
"untracked_files_description": "لا يتم تعقب هذه الملفات بواسطة التطبيق. يمكن أن تكون نتيجة لعمليات نقل فاشلة، أو عمليات تحميل متقطعة، أو يتم تركها في الخلف بسبب خطأ ما",
|
|
||||||
"user_cleanup_job": "تنظيف المستخدم",
|
"user_cleanup_job": "تنظيف المستخدم",
|
||||||
"user_delete_delay": "سيتم جدولة حساب <b>{user}</b> ومحتوياته للحذف النهائي في غضون {delay, plural, one {# يوم} other {# أيام}}.",
|
"user_delete_delay": "سيتم جدولة حساب <b>{user}</b> ومحتوياته للحذف النهائي في غضون {delay, plural, one {# يوم} other {# أيام}}.",
|
||||||
"user_delete_delay_settings": "فترة التأخير قبل الحذف",
|
"user_delete_delay_settings": "فترة التأخير قبل الحذف",
|
||||||
@@ -366,12 +344,8 @@
|
|||||||
"admin_password": "كلمة سر المشرف",
|
"admin_password": "كلمة سر المشرف",
|
||||||
"administration": "الإدارة",
|
"administration": "الإدارة",
|
||||||
"advanced": "متقدم",
|
"advanced": "متقدم",
|
||||||
"advanced_settings_log_level_title": "Log level: {}",
|
|
||||||
"advanced_settings_prefer_remote_subtitle": "تكون بعض الأجهزة بطيئة للغاية في تحميل الصور المصغرة من الأصول الموجودة على الجهاز. قم بتنشيط هذا الإعداد لتحميل الصور البعيدة بدلاً من ذلك.",
|
"advanced_settings_prefer_remote_subtitle": "تكون بعض الأجهزة بطيئة للغاية في تحميل الصور المصغرة من الأصول الموجودة على الجهاز. قم بتنشيط هذا الإعداد لتحميل الصور البعيدة بدلاً من ذلك.",
|
||||||
"advanced_settings_prefer_remote_title": "تفضل الصور البعيدة",
|
"advanced_settings_prefer_remote_title": "تفضل الصور البعيدة",
|
||||||
"advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request",
|
|
||||||
"advanced_settings_proxy_headers_title": "Proxy Headers",
|
|
||||||
"advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.",
|
|
||||||
"advanced_settings_self_signed_ssl_title": "السماح بشهادات SSL الموقعة ذاتيًا",
|
"advanced_settings_self_signed_ssl_title": "السماح بشهادات SSL الموقعة ذاتيًا",
|
||||||
"advanced_settings_tile_subtitle": "إعدادات المستخدم المتقدمة",
|
"advanced_settings_tile_subtitle": "إعدادات المستخدم المتقدمة",
|
||||||
"advanced_settings_troubleshooting_subtitle": "تمكين الميزات الإضافية لاستكشاف الأخطاء وإصلاحها",
|
"advanced_settings_troubleshooting_subtitle": "تمكين الميزات الإضافية لاستكشاف الأخطاء وإصلاحها",
|
||||||
@@ -394,10 +368,6 @@
|
|||||||
"album_remove_user": "هل ترغب في إزالة المستخدم؟",
|
"album_remove_user": "هل ترغب في إزالة المستخدم؟",
|
||||||
"album_remove_user_confirmation": "هل أنت متأكد أنك تريد إزالة {user}؟",
|
"album_remove_user_confirmation": "هل أنت متأكد أنك تريد إزالة {user}؟",
|
||||||
"album_share_no_users": "يبدو أنك قمت بمشاركة هذا الألبوم مع جميع المستخدمين أو ليس لديك أي مستخدم للمشاركة معه.",
|
"album_share_no_users": "يبدو أنك قمت بمشاركة هذا الألبوم مع جميع المستخدمين أو ليس لديك أي مستخدم للمشاركة معه.",
|
||||||
"album_thumbnail_card_item": "عنصر واحد",
|
|
||||||
"album_thumbnail_card_items": "{} items",
|
|
||||||
"album_thumbnail_card_shared": " · . مشترك",
|
|
||||||
"album_thumbnail_shared_by": "Shared by {}",
|
|
||||||
"album_updated": "تم تحديث الألبوم",
|
"album_updated": "تم تحديث الألبوم",
|
||||||
"album_updated_setting_description": "تلقي إشعارًا عبر البريد الإلكتروني عندما يحتوي الألبوم المشترك على محتويات جديدة",
|
"album_updated_setting_description": "تلقي إشعارًا عبر البريد الإلكتروني عندما يحتوي الألبوم المشترك على محتويات جديدة",
|
||||||
"album_user_left": "تم ترك {album}",
|
"album_user_left": "تم ترك {album}",
|
||||||
@@ -435,10 +405,8 @@
|
|||||||
"archive": "الأرشيف",
|
"archive": "الأرشيف",
|
||||||
"archive_or_unarchive_photo": "أرشفة الصورة أو إلغاء أرشفتها",
|
"archive_or_unarchive_photo": "أرشفة الصورة أو إلغاء أرشفتها",
|
||||||
"archive_page_no_archived_assets": "لم يتم العثور على الأصول المؤرشفة",
|
"archive_page_no_archived_assets": "لم يتم العثور على الأصول المؤرشفة",
|
||||||
"archive_page_title": "Archive ({})",
|
|
||||||
"archive_size": "حجم الأرشيف",
|
"archive_size": "حجم الأرشيف",
|
||||||
"archive_size_description": "تكوين حجم الأرشيف للتنزيلات (بالجيجابايت)",
|
"archive_size_description": "تكوين حجم الأرشيف للتنزيلات (بالجيجابايت)",
|
||||||
"archived": "Archived",
|
|
||||||
"archived_count": "{count, plural, other {الأرشيف #}}",
|
"archived_count": "{count, plural, other {الأرشيف #}}",
|
||||||
"are_these_the_same_person": "هل هؤلاء هم نفس الشخص؟",
|
"are_these_the_same_person": "هل هؤلاء هم نفس الشخص؟",
|
||||||
"are_you_sure_to_do_this": "هل انت متأكد من أنك تريد أن تفعل هذا؟",
|
"are_you_sure_to_do_this": "هل انت متأكد من أنك تريد أن تفعل هذا؟",
|
||||||
@@ -460,39 +428,26 @@
|
|||||||
"asset_list_settings_title": "شبكة الصور",
|
"asset_list_settings_title": "شبكة الصور",
|
||||||
"asset_offline": "المحتوى غير اتصال",
|
"asset_offline": "المحتوى غير اتصال",
|
||||||
"asset_offline_description": "لم يعد هذا الأصل الخارجي موجودًا على القرص. يرجى الاتصال بمسؤول Immich للحصول على المساعدة.",
|
"asset_offline_description": "لم يعد هذا الأصل الخارجي موجودًا على القرص. يرجى الاتصال بمسؤول Immich للحصول على المساعدة.",
|
||||||
"asset_restored_successfully": "Asset restored successfully",
|
|
||||||
"asset_skipped": "تم تخطيه",
|
"asset_skipped": "تم تخطيه",
|
||||||
"asset_skipped_in_trash": "في سلة المهملات",
|
"asset_skipped_in_trash": "في سلة المهملات",
|
||||||
"asset_uploaded": "تم الرفع",
|
"asset_uploaded": "تم الرفع",
|
||||||
"asset_uploading": "جارٍ الرفع…",
|
"asset_uploading": "جارٍ الرفع…",
|
||||||
"asset_viewer_settings_subtitle": "Manage your gallery viewer settings",
|
|
||||||
"asset_viewer_settings_title": "عارض الأصول",
|
"asset_viewer_settings_title": "عارض الأصول",
|
||||||
"assets": "المحتويات",
|
"assets": "المحتويات",
|
||||||
"assets_added_count": "تمت إضافة {count, plural, one {# محتوى} other {# محتويات}}",
|
"assets_added_count": "تمت إضافة {count, plural, one {# محتوى} other {# محتويات}}",
|
||||||
"assets_added_to_album_count": "تمت إضافة {count, plural, one {# الأصل} other {# الأصول}} إلى الألبوم",
|
"assets_added_to_album_count": "تمت إضافة {count, plural, one {# الأصل} other {# الأصول}} إلى الألبوم",
|
||||||
"assets_added_to_name_count": "تم إضافة {count, plural, one {# محتوى} other {# محتويات }} إلى {hasName, select, true {<b>{name}</b>} other {ألبوم جديد}}",
|
"assets_added_to_name_count": "تم إضافة {count, plural, one {# محتوى} other {# محتويات }} إلى {hasName, select, true {<b>{name}</b>} other {ألبوم جديد}}",
|
||||||
"assets_count": "{count, plural, one {# محتوى} other {# محتويات}}",
|
"assets_count": "{count, plural, one {# محتوى} other {# محتويات}}",
|
||||||
"assets_deleted_permanently": "{} asset(s) deleted permanently",
|
|
||||||
"assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server",
|
|
||||||
"assets_moved_to_trash_count": "تم نقل {count, plural, one {# محتوى} other {# محتويات}} إلى سلة المهملات",
|
"assets_moved_to_trash_count": "تم نقل {count, plural, one {# محتوى} other {# محتويات}} إلى سلة المهملات",
|
||||||
"assets_permanently_deleted_count": "تم حذف {count, plural, one {# هذا المحتوى} other {# هذه المحتويات}} بشكل دائم",
|
"assets_permanently_deleted_count": "تم حذف {count, plural, one {# هذا المحتوى} other {# هذه المحتويات}} بشكل دائم",
|
||||||
"assets_removed_count": "تمت إزالة {count, plural, one {# محتوى} other {# محتويات}}",
|
"assets_removed_count": "تمت إزالة {count, plural, one {# محتوى} other {# محتويات}}",
|
||||||
"assets_removed_permanently_from_device": "{} asset(s) removed permanently from your device",
|
|
||||||
"assets_restore_confirmation": "هل أنت متأكد من أنك تريد استعادة جميع الأصول المحذوفة؟ لا يمكنك التراجع عن هذا الإجراء! لاحظ أنه لا يمكن استعادة أي أصول غير متصلة بهذه الطريقة.",
|
"assets_restore_confirmation": "هل أنت متأكد من أنك تريد استعادة جميع الأصول المحذوفة؟ لا يمكنك التراجع عن هذا الإجراء! لاحظ أنه لا يمكن استعادة أي أصول غير متصلة بهذه الطريقة.",
|
||||||
"assets_restored_count": "تمت استعادة {count, plural, one {# محتوى} other {# محتويات}}",
|
"assets_restored_count": "تمت استعادة {count, plural, one {# محتوى} other {# محتويات}}",
|
||||||
"assets_restored_successfully": "{} asset(s) restored successfully",
|
|
||||||
"assets_trashed": "{} asset(s) trashed",
|
|
||||||
"assets_trashed_count": "تم إرسال {count, plural, one {# محتوى} other {# محتويات}} إلى سلة المهملات",
|
"assets_trashed_count": "تم إرسال {count, plural, one {# محتوى} other {# محتويات}} إلى سلة المهملات",
|
||||||
"assets_trashed_from_server": "{} asset(s) trashed from the Immich server",
|
|
||||||
"assets_were_part_of_album_count": "{count, plural, one {هذا المحتوى} other {هذه المحتويات}} في الألبوم بالفعل",
|
"assets_were_part_of_album_count": "{count, plural, one {هذا المحتوى} other {هذه المحتويات}} في الألبوم بالفعل",
|
||||||
"authorized_devices": "الأجهزه المخولة",
|
"authorized_devices": "الأجهزه المخولة",
|
||||||
"automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere",
|
|
||||||
"automatic_endpoint_switching_title": "Automatic URL switching",
|
|
||||||
"back": "خلف",
|
"back": "خلف",
|
||||||
"back_close_deselect": "الرجوع أو الإغلاق أو إلغاء التحديد",
|
"back_close_deselect": "الرجوع أو الإغلاق أو إلغاء التحديد",
|
||||||
"background_location_permission": "Background location permission",
|
|
||||||
"background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name",
|
|
||||||
"backup_album_selection_page_albums_device": "Albums on device ({})",
|
|
||||||
"backup_album_selection_page_albums_tap": "انقر للتضمين، وانقر نقرًا مزدوجًا للاستثناء",
|
"backup_album_selection_page_albums_tap": "انقر للتضمين، وانقر نقرًا مزدوجًا للاستثناء",
|
||||||
"backup_album_selection_page_assets_scatter": "يمكن أن تنتشر الأصول عبر ألبومات متعددة. وبالتالي، يمكن تضمين الألبومات أو استبعادها أثناء عملية النسخ الاحتياطي.",
|
"backup_album_selection_page_assets_scatter": "يمكن أن تنتشر الأصول عبر ألبومات متعددة. وبالتالي، يمكن تضمين الألبومات أو استبعادها أثناء عملية النسخ الاحتياطي.",
|
||||||
"backup_album_selection_page_select_albums": "حدد الألبومات",
|
"backup_album_selection_page_select_albums": "حدد الألبومات",
|
||||||
@@ -501,11 +456,9 @@
|
|||||||
"backup_all": "الجميع",
|
"backup_all": "الجميع",
|
||||||
"backup_background_service_backup_failed_message": "فشل في النسخ الاحتياطي للأصول. جارٍ إعادة المحاولة...",
|
"backup_background_service_backup_failed_message": "فشل في النسخ الاحتياطي للأصول. جارٍ إعادة المحاولة...",
|
||||||
"backup_background_service_connection_failed_message": "فشل في الاتصال بالخادم. جارٍ إعادة المحاولة...",
|
"backup_background_service_connection_failed_message": "فشل في الاتصال بالخادم. جارٍ إعادة المحاولة...",
|
||||||
"backup_background_service_current_upload_notification": "Uploading {}",
|
|
||||||
"backup_background_service_default_notification": "التحقق من الأصول الجديدة ...",
|
"backup_background_service_default_notification": "التحقق من الأصول الجديدة ...",
|
||||||
"backup_background_service_error_title": "خطأ في النسخ الاحتياطي",
|
"backup_background_service_error_title": "خطأ في النسخ الاحتياطي",
|
||||||
"backup_background_service_in_progress_notification": "النسخ الاحتياطي للأصول الخاصة بك...",
|
"backup_background_service_in_progress_notification": "النسخ الاحتياطي للأصول الخاصة بك...",
|
||||||
"backup_background_service_upload_failure_notification": "Failed to upload {}",
|
|
||||||
"backup_controller_page_albums": "ألبومات احتياطية",
|
"backup_controller_page_albums": "ألبومات احتياطية",
|
||||||
"backup_controller_page_background_app_refresh_disabled_content": "قم بتمكين تحديث تطبيق الخلفية في الإعدادات > عام > تحديث تطبيق الخلفية لاستخدام النسخ الاحتياطي في الخلفية.",
|
"backup_controller_page_background_app_refresh_disabled_content": "قم بتمكين تحديث تطبيق الخلفية في الإعدادات > عام > تحديث تطبيق الخلفية لاستخدام النسخ الاحتياطي في الخلفية.",
|
||||||
"backup_controller_page_background_app_refresh_disabled_title": "تم تعطيل تحديث التطبيق في الخلفية",
|
"backup_controller_page_background_app_refresh_disabled_title": "تم تعطيل تحديث التطبيق في الخلفية",
|
||||||
@@ -516,7 +469,6 @@
|
|||||||
"backup_controller_page_background_battery_info_title": "تحسين البطارية",
|
"backup_controller_page_background_battery_info_title": "تحسين البطارية",
|
||||||
"backup_controller_page_background_charging": "فقط أثناء الشحن",
|
"backup_controller_page_background_charging": "فقط أثناء الشحن",
|
||||||
"backup_controller_page_background_configure_error": "فشل في تكوين خدمة الخلفية",
|
"backup_controller_page_background_configure_error": "فشل في تكوين خدمة الخلفية",
|
||||||
"backup_controller_page_background_delay": "Delay new assets backup: {}",
|
|
||||||
"backup_controller_page_background_description": "قم بتشغيل خدمة الخلفية لإجراء نسخ احتياطي لأي أصول جديدة تلقائيًا دون الحاجة إلى فتح التطبيق",
|
"backup_controller_page_background_description": "قم بتشغيل خدمة الخلفية لإجراء نسخ احتياطي لأي أصول جديدة تلقائيًا دون الحاجة إلى فتح التطبيق",
|
||||||
"backup_controller_page_background_is_off": "تم إيقاف النسخ الاحتياطي التلقائي للخلفية",
|
"backup_controller_page_background_is_off": "تم إيقاف النسخ الاحتياطي التلقائي للخلفية",
|
||||||
"backup_controller_page_background_is_on": "النسخ الاحتياطي التلقائي للخلفية قيد التشغيل",
|
"backup_controller_page_background_is_on": "النسخ الاحتياطي التلقائي للخلفية قيد التشغيل",
|
||||||
@@ -526,12 +478,8 @@
|
|||||||
"backup_controller_page_backup": "دعم",
|
"backup_controller_page_backup": "دعم",
|
||||||
"backup_controller_page_backup_selected": "المحدد: ",
|
"backup_controller_page_backup_selected": "المحدد: ",
|
||||||
"backup_controller_page_backup_sub": "النسخ الاحتياطي للصور ومقاطع الفيديو",
|
"backup_controller_page_backup_sub": "النسخ الاحتياطي للصور ومقاطع الفيديو",
|
||||||
"backup_controller_page_created": "Created on: {}",
|
|
||||||
"backup_controller_page_desc_backup": "قم بتشغيل النسخ الاحتياطي الأمامي لتحميل الأصول الجديدة تلقائيًا إلى الخادم عند فتح التطبيق.",
|
"backup_controller_page_desc_backup": "قم بتشغيل النسخ الاحتياطي الأمامي لتحميل الأصول الجديدة تلقائيًا إلى الخادم عند فتح التطبيق.",
|
||||||
"backup_controller_page_excluded": "مستبعد: ",
|
"backup_controller_page_excluded": "مستبعد: ",
|
||||||
"backup_controller_page_failed": "Failed ({})",
|
|
||||||
"backup_controller_page_filename": "File name: {} [{}]",
|
|
||||||
"backup_controller_page_id": "ID: {}",
|
|
||||||
"backup_controller_page_info": "معلومات النسخ الاحتياطي",
|
"backup_controller_page_info": "معلومات النسخ الاحتياطي",
|
||||||
"backup_controller_page_none_selected": "لم يتم التحديد",
|
"backup_controller_page_none_selected": "لم يتم التحديد",
|
||||||
"backup_controller_page_remainder": "بقية",
|
"backup_controller_page_remainder": "بقية",
|
||||||
@@ -540,7 +488,6 @@
|
|||||||
"backup_controller_page_start_backup": "بدء النسخ الاحتياطي",
|
"backup_controller_page_start_backup": "بدء النسخ الاحتياطي",
|
||||||
"backup_controller_page_status_off": "النسخة الاحتياطية التلقائية غير فعالة",
|
"backup_controller_page_status_off": "النسخة الاحتياطية التلقائية غير فعالة",
|
||||||
"backup_controller_page_status_on": "النسخة الاحتياطية التلقائية فعالة",
|
"backup_controller_page_status_on": "النسخة الاحتياطية التلقائية فعالة",
|
||||||
"backup_controller_page_storage_format": "{} of {} used",
|
|
||||||
"backup_controller_page_to_backup": "الألبومات الاحتياطية",
|
"backup_controller_page_to_backup": "الألبومات الاحتياطية",
|
||||||
"backup_controller_page_total_sub": "جميع الصور ومقاطع الفيديو الفريدة من ألبومات مختارة",
|
"backup_controller_page_total_sub": "جميع الصور ومقاطع الفيديو الفريدة من ألبومات مختارة",
|
||||||
"backup_controller_page_turn_off": "قم بإيقاف تشغيل النسخ الاحتياطي المقدمة",
|
"backup_controller_page_turn_off": "قم بإيقاف تشغيل النسخ الاحتياطي المقدمة",
|
||||||
@@ -553,7 +500,6 @@
|
|||||||
"backup_manual_success": "نجاح",
|
"backup_manual_success": "نجاح",
|
||||||
"backup_manual_title": "حالة التحميل",
|
"backup_manual_title": "حالة التحميل",
|
||||||
"backup_options_page_title": "خيارات النسخ الاحتياطي",
|
"backup_options_page_title": "خيارات النسخ الاحتياطي",
|
||||||
"backup_setting_subtitle": "Manage background and foreground upload settings",
|
|
||||||
"backward": "الى الوراء",
|
"backward": "الى الوراء",
|
||||||
"birthdate_saved": "تم حفظ تاريخ الميلاد بنجاح",
|
"birthdate_saved": "تم حفظ تاريخ الميلاد بنجاح",
|
||||||
"birthdate_set_description": "يتم استخدام تاريخ الميلاد لحساب عمر هذا الشخص وقت التقاط الصورة.",
|
"birthdate_set_description": "يتم استخدام تاريخ الميلاد لحساب عمر هذا الشخص وقت التقاط الصورة.",
|
||||||
@@ -565,21 +511,16 @@
|
|||||||
"bulk_keep_duplicates_confirmation": "هل أنت متأكد من أنك تريد الاحتفاظ بـ {count, plural, one {# محتوى مكرر} other {# محتويات مكررة}}؟ سيؤدي هذا إلى حل جميع مجموعات النسخ المكررة دون حذف أي شيء.",
|
"bulk_keep_duplicates_confirmation": "هل أنت متأكد من أنك تريد الاحتفاظ بـ {count, plural, one {# محتوى مكرر} other {# محتويات مكررة}}؟ سيؤدي هذا إلى حل جميع مجموعات النسخ المكررة دون حذف أي شيء.",
|
||||||
"bulk_trash_duplicates_confirmation": "هل أنت متأكد من أنك تريد إرسال {count, plural, one {# محتوى مكرر} other {# محتويات مكررة}} إلى سلة المهملات ؟ سيحتفظ هذا بأكبر محتوى من كل مجموعة ويرسل جميع النسخ المكررة الأخرى إلى سلة المهملات.",
|
"bulk_trash_duplicates_confirmation": "هل أنت متأكد من أنك تريد إرسال {count, plural, one {# محتوى مكرر} other {# محتويات مكررة}} إلى سلة المهملات ؟ سيحتفظ هذا بأكبر محتوى من كل مجموعة ويرسل جميع النسخ المكررة الأخرى إلى سلة المهملات.",
|
||||||
"buy": "شراء immich",
|
"buy": "شراء immich",
|
||||||
"cache_settings_album_thumbnails": "Library page thumbnails ({} assets)",
|
|
||||||
"cache_settings_clear_cache_button": "مسح ذاكرة التخزين المؤقت",
|
"cache_settings_clear_cache_button": "مسح ذاكرة التخزين المؤقت",
|
||||||
"cache_settings_clear_cache_button_title": "يقوم بمسح ذاكرة التخزين المؤقت للتطبيق.سيؤثر هذا بشكل كبير على أداء التطبيق حتى إعادة بناء ذاكرة التخزين المؤقت.",
|
"cache_settings_clear_cache_button_title": "يقوم بمسح ذاكرة التخزين المؤقت للتطبيق.سيؤثر هذا بشكل كبير على أداء التطبيق حتى إعادة بناء ذاكرة التخزين المؤقت.",
|
||||||
"cache_settings_duplicated_assets_clear_button": "واضح",
|
"cache_settings_duplicated_assets_clear_button": "واضح",
|
||||||
"cache_settings_duplicated_assets_subtitle": "الصور ومقاطع الفيديو اللتي تم تجاهلها المدرجة في التطبيق",
|
"cache_settings_duplicated_assets_subtitle": "الصور ومقاطع الفيديو اللتي تم تجاهلها المدرجة في التطبيق",
|
||||||
"cache_settings_duplicated_assets_title": "Duplicated Assets ({})",
|
|
||||||
"cache_settings_image_cache_size": "Image cache size ({} assets)",
|
|
||||||
"cache_settings_statistics_album": "مكتبه الصور المصغره",
|
"cache_settings_statistics_album": "مكتبه الصور المصغره",
|
||||||
"cache_settings_statistics_assets": "{} assets ({})",
|
|
||||||
"cache_settings_statistics_full": "صور كاملة",
|
"cache_settings_statistics_full": "صور كاملة",
|
||||||
"cache_settings_statistics_shared": "صورة ألبوم مشتركة",
|
"cache_settings_statistics_shared": "صورة ألبوم مشتركة",
|
||||||
"cache_settings_statistics_thumbnail": "الصورة المصغرة",
|
"cache_settings_statistics_thumbnail": "الصورة المصغرة",
|
||||||
"cache_settings_statistics_title": "استخدام ذاكرة التخزين المؤقت",
|
"cache_settings_statistics_title": "استخدام ذاكرة التخزين المؤقت",
|
||||||
"cache_settings_subtitle": "تحكم في سلوك التخزين المؤقت لتطبيق الجوال.",
|
"cache_settings_subtitle": "تحكم في سلوك التخزين المؤقت لتطبيق الجوال.",
|
||||||
"cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)",
|
|
||||||
"cache_settings_tile_subtitle": "التحكم في سلوك التخزين المحلي",
|
"cache_settings_tile_subtitle": "التحكم في سلوك التخزين المحلي",
|
||||||
"cache_settings_tile_title": "التخزين المحلي",
|
"cache_settings_tile_title": "التخزين المحلي",
|
||||||
"cache_settings_title": "إعدادات التخزين المؤقت",
|
"cache_settings_title": "إعدادات التخزين المؤقت",
|
||||||
@@ -588,12 +529,10 @@
|
|||||||
"camera_model": "طراز الكاميرا",
|
"camera_model": "طراز الكاميرا",
|
||||||
"cancel": "إلغاء",
|
"cancel": "إلغاء",
|
||||||
"cancel_search": "الغي البحث",
|
"cancel_search": "الغي البحث",
|
||||||
"canceled": "Canceled",
|
|
||||||
"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": "غيّر التاريخ",
|
||||||
"change_display_order": "Change display order",
|
|
||||||
"change_expiration_time": "تغيير وقت انتهاء الصلاحية",
|
"change_expiration_time": "تغيير وقت انتهاء الصلاحية",
|
||||||
"change_location": "غيّر الموقع",
|
"change_location": "غيّر الموقع",
|
||||||
"change_name": "تغيير الإسم",
|
"change_name": "تغيير الإسم",
|
||||||
@@ -605,12 +544,9 @@
|
|||||||
"change_password_form_new_password": "كلمة المرور الجديدة",
|
"change_password_form_new_password": "كلمة المرور الجديدة",
|
||||||
"change_password_form_password_mismatch": "كلمة المرور غير مطابقة",
|
"change_password_form_password_mismatch": "كلمة المرور غير مطابقة",
|
||||||
"change_password_form_reenter_new_password": "أعد إدخال كلمة مرور جديدة",
|
"change_password_form_reenter_new_password": "أعد إدخال كلمة مرور جديدة",
|
||||||
|
"change_pin_code": "تغيير الرقم السري",
|
||||||
"change_your_password": "غير كلمة المرور الخاصة بك",
|
"change_your_password": "غير كلمة المرور الخاصة بك",
|
||||||
"changed_visibility_successfully": "تم تغيير الرؤية بنجاح",
|
"changed_visibility_successfully": "تم تغيير الرؤية بنجاح",
|
||||||
"check_all": "تحقق من الكل",
|
|
||||||
"check_corrupt_asset_backup": "Check for corrupt asset backups",
|
|
||||||
"check_corrupt_asset_backup_button": "Perform check",
|
|
||||||
"check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.",
|
|
||||||
"check_logs": "تحقق من السجلات",
|
"check_logs": "تحقق من السجلات",
|
||||||
"choose_matching_people_to_merge": "اختر الأشخاص المتطابقين لدمجهم",
|
"choose_matching_people_to_merge": "اختر الأشخاص المتطابقين لدمجهم",
|
||||||
"city": "المدينة",
|
"city": "المدينة",
|
||||||
@@ -619,14 +555,6 @@
|
|||||||
"clear_all_recent_searches": "مسح جميع عمليات البحث الأخيرة",
|
"clear_all_recent_searches": "مسح جميع عمليات البحث الأخيرة",
|
||||||
"clear_message": "إخلاء الرسالة",
|
"clear_message": "إخلاء الرسالة",
|
||||||
"clear_value": "إخلاء القيمة",
|
"clear_value": "إخلاء القيمة",
|
||||||
"client_cert_dialog_msg_confirm": "OK",
|
|
||||||
"client_cert_enter_password": "Enter Password",
|
|
||||||
"client_cert_import": "Import",
|
|
||||||
"client_cert_import_success_msg": "Client certificate is imported",
|
|
||||||
"client_cert_invalid_msg": "Invalid certificate file or wrong password",
|
|
||||||
"client_cert_remove_msg": "Client certificate is removed",
|
|
||||||
"client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login",
|
|
||||||
"client_cert_title": "SSL Client Certificate",
|
|
||||||
"clockwise": "باتجاه عقارب الساعة",
|
"clockwise": "باتجاه عقارب الساعة",
|
||||||
"close": "إغلاق",
|
"close": "إغلاق",
|
||||||
"collapse": "طي",
|
"collapse": "طي",
|
||||||
@@ -639,23 +567,21 @@
|
|||||||
"comments_are_disabled": "التعليقات معطلة",
|
"comments_are_disabled": "التعليقات معطلة",
|
||||||
"common_create_new_album": "إنشاء ألبوم جديد",
|
"common_create_new_album": "إنشاء ألبوم جديد",
|
||||||
"common_server_error": "يرجى التحقق من اتصال الشبكة الخاص بك ، والتأكد من أن الجهاز قابل للوصول وإصدارات التطبيق/الجهاز متوافقة.",
|
"common_server_error": "يرجى التحقق من اتصال الشبكة الخاص بك ، والتأكد من أن الجهاز قابل للوصول وإصدارات التطبيق/الجهاز متوافقة.",
|
||||||
"completed": "Completed",
|
|
||||||
"confirm": "تأكيد",
|
"confirm": "تأكيد",
|
||||||
"confirm_admin_password": "تأكيد كلمة مرور المسؤول",
|
"confirm_admin_password": "تأكيد كلمة مرور المسؤول",
|
||||||
"confirm_delete_face": "هل أنت متأكد من حذف وجه {name} من الأصول؟",
|
"confirm_delete_face": "هل أنت متأكد من حذف وجه {name} من الأصول؟",
|
||||||
"confirm_delete_shared_link": "هل أنت متأكد أنك تريد حذف هذا الرابط المشترك؟",
|
"confirm_delete_shared_link": "هل أنت متأكد أنك تريد حذف هذا الرابط المشترك؟",
|
||||||
"confirm_keep_this_delete_others": "سيتم حذف جميع الأصول الأخرى في المجموعة باستثناء هذا الأصل. هل أنت متأكد من أنك تريد المتابعة؟",
|
"confirm_keep_this_delete_others": "سيتم حذف جميع الأصول الأخرى في المجموعة باستثناء هذا الأصل. هل أنت متأكد من أنك تريد المتابعة؟",
|
||||||
|
"confirm_new_pin_code": "ثبت الرقم السري الجديد",
|
||||||
"confirm_password": "تأكيد كلمة المرور",
|
"confirm_password": "تأكيد كلمة المرور",
|
||||||
"contain": "محتواة",
|
"contain": "محتواة",
|
||||||
"context": "السياق",
|
"context": "السياق",
|
||||||
"continue": "متابعة",
|
"continue": "متابعة",
|
||||||
"control_bottom_app_bar_album_info_shared": "{} items · Shared",
|
|
||||||
"control_bottom_app_bar_create_new_album": "إنشاء ألبوم جديد",
|
"control_bottom_app_bar_create_new_album": "إنشاء ألبوم جديد",
|
||||||
"control_bottom_app_bar_delete_from_immich": " حذف منال تطبيق",
|
"control_bottom_app_bar_delete_from_immich": " حذف منال تطبيق",
|
||||||
"control_bottom_app_bar_delete_from_local": "حذف من الجهاز",
|
"control_bottom_app_bar_delete_from_local": "حذف من الجهاز",
|
||||||
"control_bottom_app_bar_edit_location": "تحديد الوجهة",
|
"control_bottom_app_bar_edit_location": "تحديد الوجهة",
|
||||||
"control_bottom_app_bar_edit_time": "تحرير التاريخ والوقت",
|
"control_bottom_app_bar_edit_time": "تحرير التاريخ والوقت",
|
||||||
"control_bottom_app_bar_share_link": "Share Link",
|
|
||||||
"control_bottom_app_bar_share_to": "مشاركة إلى",
|
"control_bottom_app_bar_share_to": "مشاركة إلى",
|
||||||
"control_bottom_app_bar_trash_from_immich": "حذفه ونقله في سله المهملات",
|
"control_bottom_app_bar_trash_from_immich": "حذفه ونقله في سله المهملات",
|
||||||
"copied_image_to_clipboard": "تم نسخ الصورة إلى الحافظة.",
|
"copied_image_to_clipboard": "تم نسخ الصورة إلى الحافظة.",
|
||||||
@@ -677,7 +603,6 @@
|
|||||||
"create_link": "إنشاء رابط",
|
"create_link": "إنشاء رابط",
|
||||||
"create_link_to_share": "إنشاء رابط للمشاركة",
|
"create_link_to_share": "إنشاء رابط للمشاركة",
|
||||||
"create_link_to_share_description": "السماح لأي شخص لديه الرابط بمشاهدة الصورة (الصور) المحددة",
|
"create_link_to_share_description": "السماح لأي شخص لديه الرابط بمشاهدة الصورة (الصور) المحددة",
|
||||||
"create_new": "CREATE NEW",
|
|
||||||
"create_new_person": "إنشاء شخص جديد",
|
"create_new_person": "إنشاء شخص جديد",
|
||||||
"create_new_person_hint": "تعيين المحتويات المحددة لشخص جديد",
|
"create_new_person_hint": "تعيين المحتويات المحددة لشخص جديد",
|
||||||
"create_new_user": "إنشاء مستخدم جديد",
|
"create_new_user": "إنشاء مستخدم جديد",
|
||||||
@@ -687,10 +612,9 @@
|
|||||||
"create_tag_description": "أنشئ علامة جديدة. بالنسبة للعلامات المتداخلة، يرجى إدخال المسار الكامل للعلامة بما في ذلك الخطوط المائلة للأمام.",
|
"create_tag_description": "أنشئ علامة جديدة. بالنسبة للعلامات المتداخلة، يرجى إدخال المسار الكامل للعلامة بما في ذلك الخطوط المائلة للأمام.",
|
||||||
"create_user": "إنشاء مستخدم",
|
"create_user": "إنشاء مستخدم",
|
||||||
"created": "تم الإنشاء",
|
"created": "تم الإنشاء",
|
||||||
"crop": "Crop",
|
|
||||||
"curated_object_page_title": "أشياء",
|
"curated_object_page_title": "أشياء",
|
||||||
"current_device": "الجهاز الحالي",
|
"current_device": "الجهاز الحالي",
|
||||||
"current_server_address": "Current server address",
|
"current_pin_code": "الرقم السري الحالي",
|
||||||
"custom_locale": "لغة مخصصة",
|
"custom_locale": "لغة مخصصة",
|
||||||
"custom_locale_description": "تنسيق التواريخ والأرقام بناءً على اللغة والمنطقة",
|
"custom_locale_description": "تنسيق التواريخ والأرقام بناءً على اللغة والمنطقة",
|
||||||
"daily_title_text_date": "E ، MMM DD",
|
"daily_title_text_date": "E ، MMM DD",
|
||||||
@@ -741,7 +665,6 @@
|
|||||||
"direction": "الإتجاه",
|
"direction": "الإتجاه",
|
||||||
"disabled": "معطل",
|
"disabled": "معطل",
|
||||||
"disallow_edits": "منع التعديلات",
|
"disallow_edits": "منع التعديلات",
|
||||||
"discord": "Discord",
|
|
||||||
"discover": "اكتشف",
|
"discover": "اكتشف",
|
||||||
"dismiss_all_errors": "تجاهل كافة الأخطاء",
|
"dismiss_all_errors": "تجاهل كافة الأخطاء",
|
||||||
"dismiss_error": "تجاهل الخطأ",
|
"dismiss_error": "تجاهل الخطأ",
|
||||||
@@ -753,26 +676,12 @@
|
|||||||
"documentation": "الوثائق",
|
"documentation": "الوثائق",
|
||||||
"done": "تم",
|
"done": "تم",
|
||||||
"download": "تنزيل",
|
"download": "تنزيل",
|
||||||
"download_canceled": "Download canceled",
|
|
||||||
"download_complete": "Download complete",
|
|
||||||
"download_enqueue": "Download enqueued",
|
|
||||||
"download_error": "Download Error",
|
|
||||||
"download_failed": "Download failed",
|
|
||||||
"download_filename": "file: {}",
|
|
||||||
"download_finished": "Download finished",
|
|
||||||
"download_include_embedded_motion_videos": "مقاطع الفيديو المدمجة",
|
"download_include_embedded_motion_videos": "مقاطع الفيديو المدمجة",
|
||||||
"download_include_embedded_motion_videos_description": "تضمين مقاطع الفيديو المضمنة في الصور المتحركة كملف منفصل",
|
"download_include_embedded_motion_videos_description": "تضمين مقاطع الفيديو المضمنة في الصور المتحركة كملف منفصل",
|
||||||
"download_notfound": "Download not found",
|
|
||||||
"download_paused": "Download paused",
|
|
||||||
"download_settings": "التنزيلات",
|
"download_settings": "التنزيلات",
|
||||||
"download_settings_description": "إدارة الإعدادات المتعلقة بتنزيل المحتويات",
|
"download_settings_description": "إدارة الإعدادات المتعلقة بتنزيل المحتويات",
|
||||||
"download_started": "Download started",
|
|
||||||
"download_sucess": "Download success",
|
|
||||||
"download_sucess_android": "The media has been downloaded to DCIM/Immich",
|
|
||||||
"download_waiting_to_retry": "Waiting to retry",
|
|
||||||
"downloading": "جارٍ التنزيل",
|
"downloading": "جارٍ التنزيل",
|
||||||
"downloading_asset_filename": "{filename} قيد التنزيل",
|
"downloading_asset_filename": "{filename} قيد التنزيل",
|
||||||
"downloading_media": "Downloading media",
|
|
||||||
"drop_files_to_upload": "قم بإسقاط الملفات في أي مكان لرفعها",
|
"drop_files_to_upload": "قم بإسقاط الملفات في أي مكان لرفعها",
|
||||||
"duplicates": "التكرارات",
|
"duplicates": "التكرارات",
|
||||||
"duplicates_description": "قم بحل كل مجموعة من خلال الإشارة إلى التكرارات، إن وجدت",
|
"duplicates_description": "قم بحل كل مجموعة من خلال الإشارة إلى التكرارات، إن وجدت",
|
||||||
@@ -802,19 +711,15 @@
|
|||||||
"editor_crop_tool_h2_aspect_ratios": "نسب العرض إلى الارتفاع",
|
"editor_crop_tool_h2_aspect_ratios": "نسب العرض إلى الارتفاع",
|
||||||
"editor_crop_tool_h2_rotation": "التدوير",
|
"editor_crop_tool_h2_rotation": "التدوير",
|
||||||
"email": "البريد الإلكتروني",
|
"email": "البريد الإلكتروني",
|
||||||
"empty_folder": "This folder is empty",
|
|
||||||
"empty_trash": "أفرغ سلة المهملات",
|
"empty_trash": "أفرغ سلة المهملات",
|
||||||
"empty_trash_confirmation": "هل أنت متأكد أنك تريد إفراغ سلة المهملات؟ سيؤدي هذا إلى إزالة جميع المحتويات الموجودة في سلة المهملات بشكل نهائي من Immich.\nلا يمكنك التراجع عن هذا الإجراء!",
|
"empty_trash_confirmation": "هل أنت متأكد أنك تريد إفراغ سلة المهملات؟ سيؤدي هذا إلى إزالة جميع المحتويات الموجودة في سلة المهملات بشكل نهائي من Immich.\nلا يمكنك التراجع عن هذا الإجراء!",
|
||||||
"enable": "تفعيل",
|
"enable": "تفعيل",
|
||||||
"enabled": "مفعل",
|
"enabled": "مفعل",
|
||||||
"end_date": "تاريخ الإنتهاء",
|
"end_date": "تاريخ الإنتهاء",
|
||||||
"enqueued": "Enqueued",
|
|
||||||
"enter_wifi_name": "Enter WiFi name",
|
"enter_wifi_name": "Enter WiFi name",
|
||||||
"error": "خطأ",
|
"error": "خطأ",
|
||||||
"error_change_sort_album": "Failed to change album sort order",
|
|
||||||
"error_delete_face": "حدث خطأ في حذف الوجه من الأصول",
|
"error_delete_face": "حدث خطأ في حذف الوجه من الأصول",
|
||||||
"error_loading_image": "حدث خطأ أثناء تحميل الصورة",
|
"error_loading_image": "حدث خطأ أثناء تحميل الصورة",
|
||||||
"error_saving_image": "Error: {}",
|
|
||||||
"error_title": "خطأ - حدث خللٌ ما",
|
"error_title": "خطأ - حدث خللٌ ما",
|
||||||
"errors": {
|
"errors": {
|
||||||
"cannot_navigate_next_asset": "لا يمكن الانتقال إلى المحتوى التالي",
|
"cannot_navigate_next_asset": "لا يمكن الانتقال إلى المحتوى التالي",
|
||||||
@@ -827,7 +732,6 @@
|
|||||||
"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}",
|
|
||||||
"error_adding_assets_to_album": "حدث خطأٌ أثناء إضافة المحتويات إلى الألبوم",
|
"error_adding_assets_to_album": "حدث خطأٌ أثناء إضافة المحتويات إلى الألبوم",
|
||||||
"error_adding_users_to_album": "حدث خطأٌ أثناء إضافة المستخدمين إلى الألبوم",
|
"error_adding_users_to_album": "حدث خطأٌ أثناء إضافة المستخدمين إلى الألبوم",
|
||||||
"error_deleting_shared_user": "حدث خطأٌ أثناء حذف المستخدم المشترك",
|
"error_deleting_shared_user": "حدث خطأٌ أثناء حذف المستخدم المشترك",
|
||||||
@@ -836,7 +740,6 @@
|
|||||||
"error_removing_assets_from_album": "خطأٌّ في إزالة المحتويات من الألبوم، تحقق من وحدة التحكم للحصول على مزيدٍ من التفاصيل",
|
"error_removing_assets_from_album": "خطأٌّ في إزالة المحتويات من الألبوم، تحقق من وحدة التحكم للحصول على مزيدٍ من التفاصيل",
|
||||||
"error_selecting_all_assets": "خطأٌ في تحديد جميع المحتويات",
|
"error_selecting_all_assets": "خطأٌ في تحديد جميع المحتويات",
|
||||||
"exclusion_pattern_already_exists": "نمط الاستبعاد هذا موجود مسبقًا.",
|
"exclusion_pattern_already_exists": "نمط الاستبعاد هذا موجود مسبقًا.",
|
||||||
"failed_job_command": "فشل الأمر {command} لوظيفة: {job}",
|
|
||||||
"failed_to_create_album": "فشل إنشاء الألبوم",
|
"failed_to_create_album": "فشل إنشاء الألبوم",
|
||||||
"failed_to_create_shared_link": "فشل إنشاء رابط مشترك",
|
"failed_to_create_shared_link": "فشل إنشاء رابط مشترك",
|
||||||
"failed_to_edit_shared_link": "فشل تعديل الرابط المشترك",
|
"failed_to_edit_shared_link": "فشل تعديل الرابط المشترك",
|
||||||
@@ -853,7 +756,6 @@
|
|||||||
"paths_validation_failed": "فشل في التحقق من {paths, plural, one {# مسار} other {# مسارات}}",
|
"paths_validation_failed": "فشل في التحقق من {paths, plural, one {# مسار} other {# مسارات}}",
|
||||||
"profile_picture_transparent_pixels": "لا يمكن أن تحتوي صور الملف الشخصي على أجزاء/بكسلات شفافة. يرجى التكبير و/أو تحريك الصورة.",
|
"profile_picture_transparent_pixels": "لا يمكن أن تحتوي صور الملف الشخصي على أجزاء/بكسلات شفافة. يرجى التكبير و/أو تحريك الصورة.",
|
||||||
"quota_higher_than_disk_size": "لقد قمت بتعيين حصة نسبية أعلى من حجم القرص",
|
"quota_higher_than_disk_size": "لقد قمت بتعيين حصة نسبية أعلى من حجم القرص",
|
||||||
"repair_unable_to_check_items": "تعذر التحقق من {count, select, one {عنصر} other {عناصر}}",
|
|
||||||
"unable_to_add_album_users": "تعذر إضافة مستخدمين إلى الألبوم",
|
"unable_to_add_album_users": "تعذر إضافة مستخدمين إلى الألبوم",
|
||||||
"unable_to_add_assets_to_shared_link": "تعذر إضافة المحتويات إلى الرابط المشترك",
|
"unable_to_add_assets_to_shared_link": "تعذر إضافة المحتويات إلى الرابط المشترك",
|
||||||
"unable_to_add_comment": "تعذر إضافة التعليق",
|
"unable_to_add_comment": "تعذر إضافة التعليق",
|
||||||
@@ -871,7 +773,6 @@
|
|||||||
"unable_to_change_visibility": "غير قادر على تغيير الظهور لـ {count, plural, one {# شخص} other {# أشخاص}}",
|
"unable_to_change_visibility": "غير قادر على تغيير الظهور لـ {count, plural, one {# شخص} other {# أشخاص}}",
|
||||||
"unable_to_complete_oauth_login": "غير قادر على إكمال تسجيل الدخول عبر OAuth",
|
"unable_to_complete_oauth_login": "غير قادر على إكمال تسجيل الدخول عبر OAuth",
|
||||||
"unable_to_connect": "غير قادر على الإتصال",
|
"unable_to_connect": "غير قادر على الإتصال",
|
||||||
"unable_to_connect_to_server": "غير قادر على الإتصال بالسيرفر",
|
|
||||||
"unable_to_copy_to_clipboard": "لا يمكن النسخ إلى الحافظة، تأكد من استخدامك للصفحة عبر https",
|
"unable_to_copy_to_clipboard": "لا يمكن النسخ إلى الحافظة، تأكد من استخدامك للصفحة عبر https",
|
||||||
"unable_to_create_admin_account": "غير قادر على إنشاء حساب المسؤول",
|
"unable_to_create_admin_account": "غير قادر على إنشاء حساب المسؤول",
|
||||||
"unable_to_create_api_key": "غير قادر على إنشاء مفتاح API جديد",
|
"unable_to_create_api_key": "غير قادر على إنشاء مفتاح API جديد",
|
||||||
@@ -895,10 +796,6 @@
|
|||||||
"unable_to_hide_person": "غير قادر على إخفاء الشخص",
|
"unable_to_hide_person": "غير قادر على إخفاء الشخص",
|
||||||
"unable_to_link_motion_video": "غير قادر على ربط فيديو الحركة",
|
"unable_to_link_motion_video": "غير قادر على ربط فيديو الحركة",
|
||||||
"unable_to_link_oauth_account": "غير قادر على ربط حساب OAuth",
|
"unable_to_link_oauth_account": "غير قادر على ربط حساب OAuth",
|
||||||
"unable_to_load_album": "غير قادر على تحميل الألبوم",
|
|
||||||
"unable_to_load_asset_activity": "غير قادر على تحميل نشاط المحتويات",
|
|
||||||
"unable_to_load_items": "غير قادر على تحميل العناصر",
|
|
||||||
"unable_to_load_liked_status": "غير قادر على تحميل حالة الإعجاب",
|
|
||||||
"unable_to_log_out_all_devices": "غير قادر على تسجيل الخروج من جميع الأجهزة",
|
"unable_to_log_out_all_devices": "غير قادر على تسجيل الخروج من جميع الأجهزة",
|
||||||
"unable_to_log_out_device": "غير قادر على تسجيل الخروج من الجهاز",
|
"unable_to_log_out_device": "غير قادر على تسجيل الخروج من الجهاز",
|
||||||
"unable_to_login_with_oauth": "غير قادر على تسجيل الدخول باستخدام OAuth",
|
"unable_to_login_with_oauth": "غير قادر على تسجيل الدخول باستخدام OAuth",
|
||||||
@@ -909,11 +806,9 @@
|
|||||||
"unable_to_remove_album_users": "تعذر إزالة المستخدمين من الألبوم",
|
"unable_to_remove_album_users": "تعذر إزالة المستخدمين من الألبوم",
|
||||||
"unable_to_remove_api_key": "تعذر إزالة مفتاح API",
|
"unable_to_remove_api_key": "تعذر إزالة مفتاح API",
|
||||||
"unable_to_remove_assets_from_shared_link": "غير قادر على إزالة المحتويات من الرابط المشترك",
|
"unable_to_remove_assets_from_shared_link": "غير قادر على إزالة المحتويات من الرابط المشترك",
|
||||||
"unable_to_remove_deleted_assets": "غير قادر على إزالة الملفات غير المتصلة",
|
|
||||||
"unable_to_remove_library": "غير قادر على إزالة المكتبة",
|
"unable_to_remove_library": "غير قادر على إزالة المكتبة",
|
||||||
"unable_to_remove_partner": "غير قادر على إزالة الشريك",
|
"unable_to_remove_partner": "غير قادر على إزالة الشريك",
|
||||||
"unable_to_remove_reaction": "غير قادر على إزالة رد الفعل",
|
"unable_to_remove_reaction": "غير قادر على إزالة رد الفعل",
|
||||||
"unable_to_repair_items": "غير قادر على إصلاح العناصر",
|
|
||||||
"unable_to_reset_password": "غير قادر على إعادة تعيين كلمة المرور",
|
"unable_to_reset_password": "غير قادر على إعادة تعيين كلمة المرور",
|
||||||
"unable_to_resolve_duplicate": "غير قادر على حل التكرارات",
|
"unable_to_resolve_duplicate": "غير قادر على حل التكرارات",
|
||||||
"unable_to_restore_assets": "غير قادر على استعادة المحتويات",
|
"unable_to_restore_assets": "غير قادر على استعادة المحتويات",
|
||||||
@@ -948,10 +843,6 @@
|
|||||||
"exif_bottom_sheet_location": "موقع",
|
"exif_bottom_sheet_location": "موقع",
|
||||||
"exif_bottom_sheet_people": "الناس",
|
"exif_bottom_sheet_people": "الناس",
|
||||||
"exif_bottom_sheet_person_add_person": "اضف اسما",
|
"exif_bottom_sheet_person_add_person": "اضف اسما",
|
||||||
"exif_bottom_sheet_person_age": "Age {}",
|
|
||||||
"exif_bottom_sheet_person_age_months": "Age {} months",
|
|
||||||
"exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months",
|
|
||||||
"exif_bottom_sheet_person_age_years": "Age {}",
|
|
||||||
"exit_slideshow": "خروج من العرض التقديمي",
|
"exit_slideshow": "خروج من العرض التقديمي",
|
||||||
"expand_all": "توسيع الكل",
|
"expand_all": "توسيع الكل",
|
||||||
"experimental_settings_new_asset_list_subtitle": "أعمال جارية",
|
"experimental_settings_new_asset_list_subtitle": "أعمال جارية",
|
||||||
@@ -968,12 +859,9 @@
|
|||||||
"extension": "الإمتداد",
|
"extension": "الإمتداد",
|
||||||
"external": "خارجي",
|
"external": "خارجي",
|
||||||
"external_libraries": "المكتبات الخارجية",
|
"external_libraries": "المكتبات الخارجية",
|
||||||
"external_network": "External network",
|
|
||||||
"external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom",
|
"external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom",
|
||||||
"face_unassigned": "غير معين",
|
"face_unassigned": "غير معين",
|
||||||
"failed": "Failed",
|
|
||||||
"failed_to_load_assets": "فشل تحميل الأصول",
|
"failed_to_load_assets": "فشل تحميل الأصول",
|
||||||
"failed_to_load_folder": "Failed to load folder",
|
|
||||||
"favorite": "مفضل",
|
"favorite": "مفضل",
|
||||||
"favorite_or_unfavorite_photo": "تفضيل أو إلغاء تفضيل الصورة",
|
"favorite_or_unfavorite_photo": "تفضيل أو إلغاء تفضيل الصورة",
|
||||||
"favorites": "المفضلة",
|
"favorites": "المفضلة",
|
||||||
@@ -985,23 +873,18 @@
|
|||||||
"file_name_or_extension": "اسم الملف أو امتداده",
|
"file_name_or_extension": "اسم الملف أو امتداده",
|
||||||
"filename": "اسم الملف",
|
"filename": "اسم الملف",
|
||||||
"filetype": "نوع الملف",
|
"filetype": "نوع الملف",
|
||||||
"filter": "Filter",
|
|
||||||
"filter_people": "تصفية الاشخاص",
|
"filter_people": "تصفية الاشخاص",
|
||||||
"find_them_fast": "يمكنك العثور عليها بسرعة بالاسم من خلال البحث",
|
"find_them_fast": "يمكنك العثور عليها بسرعة بالاسم من خلال البحث",
|
||||||
"fix_incorrect_match": "إصلاح المطابقة غير الصحيحة",
|
"fix_incorrect_match": "إصلاح المطابقة غير الصحيحة",
|
||||||
"folder": "Folder",
|
|
||||||
"folder_not_found": "Folder not found",
|
|
||||||
"folders": "المجلدات",
|
"folders": "المجلدات",
|
||||||
"folders_feature_description": "تصفح عرض المجلد للصور ومقاطع الفيديو الموجودة على نظام الملفات",
|
"folders_feature_description": "تصفح عرض المجلد للصور ومقاطع الفيديو الموجودة على نظام الملفات",
|
||||||
"forward": "إلى الأمام",
|
"forward": "إلى الأمام",
|
||||||
"general": "عام",
|
"general": "عام",
|
||||||
"get_help": "الحصول على المساعدة",
|
"get_help": "الحصول على المساعدة",
|
||||||
"get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network",
|
|
||||||
"getting_started": "البدء",
|
"getting_started": "البدء",
|
||||||
"go_back": "الرجوع للخلف",
|
"go_back": "الرجوع للخلف",
|
||||||
"go_to_folder": "اذهب إلى المجلد",
|
"go_to_folder": "اذهب إلى المجلد",
|
||||||
"go_to_search": "اذهب إلى البحث",
|
"go_to_search": "اذهب إلى البحث",
|
||||||
"grant_permission": "Grant permission",
|
|
||||||
"group_albums_by": "تجميع الألبومات حسب...",
|
"group_albums_by": "تجميع الألبومات حسب...",
|
||||||
"group_country": "مجموعة البلد",
|
"group_country": "مجموعة البلد",
|
||||||
"group_no": "بدون تجميع",
|
"group_no": "بدون تجميع",
|
||||||
@@ -1011,12 +894,6 @@
|
|||||||
"haptic_feedback_switch": "تمكين ردود الفعل اللمسية",
|
"haptic_feedback_switch": "تمكين ردود الفعل اللمسية",
|
||||||
"haptic_feedback_title": "ردود فعل لمسية",
|
"haptic_feedback_title": "ردود فعل لمسية",
|
||||||
"has_quota": "محدد بحصة",
|
"has_quota": "محدد بحصة",
|
||||||
"header_settings_add_header_tip": "Add Header",
|
|
||||||
"header_settings_field_validator_msg": "Value cannot be empty",
|
|
||||||
"header_settings_header_name_input": "Header name",
|
|
||||||
"header_settings_header_value_input": "Header value",
|
|
||||||
"headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request",
|
|
||||||
"headers_settings_tile_title": "Custom proxy headers",
|
|
||||||
"hi_user": "مرحبا {name} ({email})",
|
"hi_user": "مرحبا {name} ({email})",
|
||||||
"hide_all_people": "إخفاء جميع الأشخاص",
|
"hide_all_people": "إخفاء جميع الأشخاص",
|
||||||
"hide_gallery": "اخفاء المعرض",
|
"hide_gallery": "اخفاء المعرض",
|
||||||
@@ -1040,8 +917,6 @@
|
|||||||
"home_page_upload_err_limit": "لا يمكن إلا تحميل 30 أحد الأصول في وقت واحد ، سوف يتخطى",
|
"home_page_upload_err_limit": "لا يمكن إلا تحميل 30 أحد الأصول في وقت واحد ، سوف يتخطى",
|
||||||
"host": "المضيف",
|
"host": "المضيف",
|
||||||
"hour": "ساعة",
|
"hour": "ساعة",
|
||||||
"ignore_icloud_photos": "Ignore iCloud photos",
|
|
||||||
"ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server",
|
|
||||||
"image": "صورة",
|
"image": "صورة",
|
||||||
"image_alt_text_date": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {date}",
|
"image_alt_text_date": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {date}",
|
||||||
"image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} تم التقاطها مع {person1} في {date}",
|
"image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} تم التقاطها مع {person1} في {date}",
|
||||||
@@ -1053,7 +928,6 @@
|
|||||||
"image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}، {country} مع {person1} و{person2} في {date}",
|
"image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}، {country} مع {person1} و{person2} في {date}",
|
||||||
"image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}، {country} مع {person1}، {person2}، و{person3} في {date}",
|
"image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}، {country} مع {person1}، {person2}، و{person3} في {date}",
|
||||||
"image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}, {country} with {person1}, {person2}, مع {additionalCount, number} آخرين في {date}",
|
"image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}, {country} with {person1}, {person2}, مع {additionalCount, number} آخرين في {date}",
|
||||||
"image_saved_successfully": "Image saved",
|
|
||||||
"image_viewer_page_state_provider_download_started": "بدأ التنزيل",
|
"image_viewer_page_state_provider_download_started": "بدأ التنزيل",
|
||||||
"image_viewer_page_state_provider_download_success": "تم التنزيل بنجاح",
|
"image_viewer_page_state_provider_download_success": "تم التنزيل بنجاح",
|
||||||
"image_viewer_page_state_provider_share_error": "خطأ في المشاركة",
|
"image_viewer_page_state_provider_share_error": "خطأ في المشاركة",
|
||||||
@@ -1075,8 +949,6 @@
|
|||||||
"night_at_midnight": "كل ليلة عند منتصف الليل",
|
"night_at_midnight": "كل ليلة عند منتصف الليل",
|
||||||
"night_at_twoam": "كل ليلة الساعة 2 صباحا"
|
"night_at_twoam": "كل ليلة الساعة 2 صباحا"
|
||||||
},
|
},
|
||||||
"invalid_date": "Invalid date",
|
|
||||||
"invalid_date_format": "Invalid date format",
|
|
||||||
"invite_people": "دعوة الأشخاص",
|
"invite_people": "دعوة الأشخاص",
|
||||||
"invite_to_album": "دعوة إلى الألبوم",
|
"invite_to_album": "دعوة إلى الألبوم",
|
||||||
"items_count": "{count, plural, one {# عنصر} other {# عناصر}}",
|
"items_count": "{count, plural, one {# عنصر} other {# عناصر}}",
|
||||||
@@ -1112,9 +984,6 @@
|
|||||||
"list": "قائمة",
|
"list": "قائمة",
|
||||||
"loading": "تحميل",
|
"loading": "تحميل",
|
||||||
"loading_search_results_failed": "فشل تحميل نتائج البحث",
|
"loading_search_results_failed": "فشل تحميل نتائج البحث",
|
||||||
"local_network": "Local network",
|
|
||||||
"local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network",
|
|
||||||
"location_permission": "Location permission",
|
|
||||||
"location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name",
|
"location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name",
|
||||||
"location_picker_choose_on_map": "اختر على الخريطة",
|
"location_picker_choose_on_map": "اختر على الخريطة",
|
||||||
"location_picker_latitude_error": "أدخل خط عرض صالح",
|
"location_picker_latitude_error": "أدخل خط عرض صالح",
|
||||||
@@ -1130,7 +999,6 @@
|
|||||||
"login_form_api_exception": " استثناء برمجة التطبيقات. يرجى التحقق من عنوان الخادم والمحاولة مرة أخرى ",
|
"login_form_api_exception": " استثناء برمجة التطبيقات. يرجى التحقق من عنوان الخادم والمحاولة مرة أخرى ",
|
||||||
"login_form_back_button_text": "الرجوع للخلف",
|
"login_form_back_button_text": "الرجوع للخلف",
|
||||||
"login_form_email_hint": "yoursemail@email.com",
|
"login_form_email_hint": "yoursemail@email.com",
|
||||||
"login_form_endpoint_hint": "http://your-server-ip:port",
|
|
||||||
"login_form_endpoint_url": "url نقطة نهاية الخادم",
|
"login_form_endpoint_url": "url نقطة نهاية الخادم",
|
||||||
"login_form_err_http": "يرجى تحديد http:// أو https://",
|
"login_form_err_http": "يرجى تحديد http:// أو https://",
|
||||||
"login_form_err_invalid_email": "بريد إلكتروني خاطئ",
|
"login_form_err_invalid_email": "بريد إلكتروني خاطئ",
|
||||||
@@ -1164,8 +1032,6 @@
|
|||||||
"manage_your_devices": "إدارة الأجهزة التي تم تسجيل الدخول إليها",
|
"manage_your_devices": "إدارة الأجهزة التي تم تسجيل الدخول إليها",
|
||||||
"manage_your_oauth_connection": "إدارة اتصال OAuth الخاص بك",
|
"manage_your_oauth_connection": "إدارة اتصال OAuth الخاص بك",
|
||||||
"map": "الخريطة",
|
"map": "الخريطة",
|
||||||
"map_assets_in_bound": "{} photo",
|
|
||||||
"map_assets_in_bounds": "{} photos",
|
|
||||||
"map_cannot_get_user_location": "لا يمكن الحصول على موقع المستخدم",
|
"map_cannot_get_user_location": "لا يمكن الحصول على موقع المستخدم",
|
||||||
"map_location_dialog_yes": "نعم",
|
"map_location_dialog_yes": "نعم",
|
||||||
"map_location_picker_page_use_location": "استخدم هذا الموقع",
|
"map_location_picker_page_use_location": "استخدم هذا الموقع",
|
||||||
@@ -1179,9 +1045,7 @@
|
|||||||
"map_settings": "إعدادات الخريطة",
|
"map_settings": "إعدادات الخريطة",
|
||||||
"map_settings_dark_mode": "الوضع المظلم",
|
"map_settings_dark_mode": "الوضع المظلم",
|
||||||
"map_settings_date_range_option_day": "24 ساعة الماضية",
|
"map_settings_date_range_option_day": "24 ساعة الماضية",
|
||||||
"map_settings_date_range_option_days": "Past {} days",
|
|
||||||
"map_settings_date_range_option_year": "السنة الفائتة",
|
"map_settings_date_range_option_year": "السنة الفائتة",
|
||||||
"map_settings_date_range_option_years": "Past {} years",
|
|
||||||
"map_settings_dialog_title": "إعدادات الخريطة",
|
"map_settings_dialog_title": "إعدادات الخريطة",
|
||||||
"map_settings_include_show_archived": "تشمل الأرشفة",
|
"map_settings_include_show_archived": "تشمل الأرشفة",
|
||||||
"map_settings_include_show_partners": "تضمين الشركاء",
|
"map_settings_include_show_partners": "تضمين الشركاء",
|
||||||
@@ -1196,8 +1060,6 @@
|
|||||||
"memories_setting_description": "إدارة ما تراه في ذكرياتك",
|
"memories_setting_description": "إدارة ما تراه في ذكرياتك",
|
||||||
"memories_start_over": "ابدأ من جديد",
|
"memories_start_over": "ابدأ من جديد",
|
||||||
"memories_swipe_to_close": "اسحب لأعلى للإغلاق",
|
"memories_swipe_to_close": "اسحب لأعلى للإغلاق",
|
||||||
"memories_year_ago": "A year ago",
|
|
||||||
"memories_years_ago": "{} years ago",
|
|
||||||
"memory": "ذكرى",
|
"memory": "ذكرى",
|
||||||
"memory_lane_title": "ذكرياتٌ من {title}",
|
"memory_lane_title": "ذكرياتٌ من {title}",
|
||||||
"menu": "القائمة",
|
"menu": "القائمة",
|
||||||
@@ -1221,13 +1083,12 @@
|
|||||||
"my_albums": "ألبوماتي",
|
"my_albums": "ألبوماتي",
|
||||||
"name": "الاسم",
|
"name": "الاسم",
|
||||||
"name_or_nickname": "الاسم أو اللقب",
|
"name_or_nickname": "الاسم أو اللقب",
|
||||||
"networking_settings": "Networking",
|
|
||||||
"networking_subtitle": "Manage the server endpoint settings",
|
|
||||||
"never": "أبداً",
|
"never": "أبداً",
|
||||||
"new_album": "البوم جديد",
|
"new_album": "البوم جديد",
|
||||||
"new_api_key": "مفتاح API جديد",
|
"new_api_key": "مفتاح API جديد",
|
||||||
"new_password": "كلمة المرور الجديدة",
|
"new_password": "كلمة المرور الجديدة",
|
||||||
"new_person": "شخص جديد",
|
"new_person": "شخص جديد",
|
||||||
|
"new_pin_code": "الرقم السري الجديد",
|
||||||
"new_user_created": "تم إنشاء مستخدم جديد",
|
"new_user_created": "تم إنشاء مستخدم جديد",
|
||||||
"new_version_available": "إصدار جديد متاح",
|
"new_version_available": "إصدار جديد متاح",
|
||||||
"newest_first": "الأحدث أولاً",
|
"newest_first": "الأحدث أولاً",
|
||||||
@@ -1251,7 +1112,6 @@
|
|||||||
"no_results_description": "جرب كلمة رئيسية مرادفة أو أكثر عمومية",
|
"no_results_description": "جرب كلمة رئيسية مرادفة أو أكثر عمومية",
|
||||||
"no_shared_albums_message": "قم بإنشاء ألبوم لمشاركة الصور ومقاطع الفيديو مع الأشخاص في شبكتك",
|
"no_shared_albums_message": "قم بإنشاء ألبوم لمشاركة الصور ومقاطع الفيديو مع الأشخاص في شبكتك",
|
||||||
"not_in_any_album": "ليست في أي ألبوم",
|
"not_in_any_album": "ليست في أي ألبوم",
|
||||||
"not_selected": "Not selected",
|
|
||||||
"note_apply_storage_label_to_previously_uploaded assets": "ملاحظة: لتطبيق تسمية التخزين على المحتويات التي تم رفعها مسبقًا، قم بتشغيل",
|
"note_apply_storage_label_to_previously_uploaded assets": "ملاحظة: لتطبيق تسمية التخزين على المحتويات التي تم رفعها مسبقًا، قم بتشغيل",
|
||||||
"notes": "ملاحظات",
|
"notes": "ملاحظات",
|
||||||
"notification_permission_dialog_content": "لتمكين الإخطارات ، انتقل إلى الإعدادات و اختار السماح.",
|
"notification_permission_dialog_content": "لتمكين الإخطارات ، انتقل إلى الإعدادات و اختار السماح.",
|
||||||
@@ -1261,18 +1121,13 @@
|
|||||||
"notification_toggle_setting_description": "تفعيل إشعارات البريد الإلكتروني",
|
"notification_toggle_setting_description": "تفعيل إشعارات البريد الإلكتروني",
|
||||||
"notifications": "إشعارات",
|
"notifications": "إشعارات",
|
||||||
"notifications_setting_description": "إدارة الإشعارات",
|
"notifications_setting_description": "إدارة الإشعارات",
|
||||||
"oauth": "OAuth",
|
|
||||||
"official_immich_resources": "الموارد الرسمية لشركة Immich",
|
"official_immich_resources": "الموارد الرسمية لشركة Immich",
|
||||||
"offline": "غير متصل",
|
"offline": "غير متصل",
|
||||||
"offline_paths": "مسارات غير متصلة",
|
|
||||||
"offline_paths_description": "قد تكون هذه النتائج بسبب الحذف اليدوي للملفات التي لا تشكل جزءًا من مكتبة خارجية.",
|
|
||||||
"ok": "نعم",
|
"ok": "نعم",
|
||||||
"oldest_first": "الأقدم أولا",
|
"oldest_first": "الأقدم أولا",
|
||||||
"on_this_device": "On this device",
|
|
||||||
"onboarding": "الإعداد الأولي",
|
"onboarding": "الإعداد الأولي",
|
||||||
"onboarding_privacy_description": "تعتمد الميزات التالية (اختياري) على خدمات خارجية، ويمكن تعطيلها في أي وقت في إعدادات الإدارة.",
|
"onboarding_privacy_description": "تعتمد الميزات التالية (اختياري) على خدمات خارجية، ويمكن تعطيلها في أي وقت في إعدادات الإدارة.",
|
||||||
"onboarding_theme_description": "اختر نسق الألوان للنسخة الخاصة بك. يمكنك تغيير ذلك لاحقًا في إعداداتك.",
|
"onboarding_theme_description": "اختر نسق الألوان للنسخة الخاصة بك. يمكنك تغيير ذلك لاحقًا في إعداداتك.",
|
||||||
"onboarding_welcome_description": "لنقم بإعداد نسختك باستخدام بعض الإعدادات الشائعة.",
|
|
||||||
"onboarding_welcome_user": "مرحبا، {user}",
|
"onboarding_welcome_user": "مرحبا، {user}",
|
||||||
"online": "متصل",
|
"online": "متصل",
|
||||||
"only_favorites": "المفضلة فقط",
|
"only_favorites": "المفضلة فقط",
|
||||||
@@ -1292,14 +1147,12 @@
|
|||||||
"partner_can_access": "يستطيع {partner} الوصول",
|
"partner_can_access": "يستطيع {partner} الوصول",
|
||||||
"partner_can_access_assets": "جميع الصور ومقاطع الفيديو الخاصة بك باستثناء تلك الموجودة في المؤرشفة والمحذوفة",
|
"partner_can_access_assets": "جميع الصور ومقاطع الفيديو الخاصة بك باستثناء تلك الموجودة في المؤرشفة والمحذوفة",
|
||||||
"partner_can_access_location": "الموقع الذي تم التقاط صورك فيه",
|
"partner_can_access_location": "الموقع الذي تم التقاط صورك فيه",
|
||||||
"partner_list_user_photos": "{user}'s photos",
|
|
||||||
"partner_list_view_all": "عرض الكل",
|
"partner_list_view_all": "عرض الكل",
|
||||||
"partner_page_empty_message": "لم يتم مشاركة صورك بعد مع أي شريك.",
|
"partner_page_empty_message": "لم يتم مشاركة صورك بعد مع أي شريك.",
|
||||||
"partner_page_no_more_users": "لا مزيد من المستخدمين لإضافة",
|
"partner_page_no_more_users": "لا مزيد من المستخدمين لإضافة",
|
||||||
"partner_page_partner_add_failed": "فشل في إضافة شريك",
|
"partner_page_partner_add_failed": "فشل في إضافة شريك",
|
||||||
"partner_page_select_partner": "حدد شريكًا",
|
"partner_page_select_partner": "حدد شريكًا",
|
||||||
"partner_page_shared_to_title": "مشترك ل",
|
"partner_page_shared_to_title": "مشترك ل",
|
||||||
"partner_page_stop_sharing_content": "{} will no longer be able to access your photos.",
|
|
||||||
"partner_sharing": "مشاركة الشركاء",
|
"partner_sharing": "مشاركة الشركاء",
|
||||||
"partners": "الشركاء",
|
"partners": "الشركاء",
|
||||||
"password": "كلمة المرور",
|
"password": "كلمة المرور",
|
||||||
@@ -1345,6 +1198,9 @@
|
|||||||
"photos_count": "{count, plural, one {{count, number} صورة} other {{count, number} صور}}",
|
"photos_count": "{count, plural, one {{count, number} صورة} other {{count, number} صور}}",
|
||||||
"photos_from_previous_years": "صور من السنوات السابقة",
|
"photos_from_previous_years": "صور من السنوات السابقة",
|
||||||
"pick_a_location": "اختر موقعًا",
|
"pick_a_location": "اختر موقعًا",
|
||||||
|
"pin_code_changed_successfully": "تم تغير الرقم السري",
|
||||||
|
"pin_code_reset_successfully": "تم اعادة تعيين الرقم السري",
|
||||||
|
"pin_code_setup_successfully": "تم انشاء رقم سري",
|
||||||
"place": "مكان",
|
"place": "مكان",
|
||||||
"places": "الأماكن",
|
"places": "الأماكن",
|
||||||
"places_count": "{count, plural, one {{count, number} مكان} other {{count, number} أماكن}}",
|
"places_count": "{count, plural, one {{count, number} مكان} other {{count, number} أماكن}}",
|
||||||
@@ -1353,7 +1209,6 @@
|
|||||||
"play_motion_photo": "تشغيل الصور المتحركة",
|
"play_motion_photo": "تشغيل الصور المتحركة",
|
||||||
"play_or_pause_video": "تشغيل الفيديو أو إيقافه مؤقتًا",
|
"play_or_pause_video": "تشغيل الفيديو أو إيقافه مؤقتًا",
|
||||||
"port": "المنفذ",
|
"port": "المنفذ",
|
||||||
"preferences_settings_subtitle": "Manage the app's preferences",
|
|
||||||
"preferences_settings_title": "التفضيلات",
|
"preferences_settings_title": "التفضيلات",
|
||||||
"preset": "الإعداد المسبق",
|
"preset": "الإعداد المسبق",
|
||||||
"preview": "معاينة",
|
"preview": "معاينة",
|
||||||
@@ -1375,7 +1230,7 @@
|
|||||||
"public_share": "مشاركة عامة",
|
"public_share": "مشاركة عامة",
|
||||||
"purchase_account_info": "داعم",
|
"purchase_account_info": "داعم",
|
||||||
"purchase_activated_subtitle": "شكرًا لك على دعمك لـ Immich والبرمجيات مفتوحة المصدر",
|
"purchase_activated_subtitle": "شكرًا لك على دعمك لـ Immich والبرمجيات مفتوحة المصدر",
|
||||||
"purchase_activated_time": "تم التفعيل في {date, date}",
|
"purchase_activated_time": "تم التفعيل في {date}",
|
||||||
"purchase_activated_title": "لقد تم تفعيل مفتاحك بنجاح",
|
"purchase_activated_title": "لقد تم تفعيل مفتاحك بنجاح",
|
||||||
"purchase_button_activate": "تنشيط",
|
"purchase_button_activate": "تنشيط",
|
||||||
"purchase_button_buy": "شراء",
|
"purchase_button_buy": "شراء",
|
||||||
@@ -1418,7 +1273,6 @@
|
|||||||
"recent": "حديث",
|
"recent": "حديث",
|
||||||
"recent-albums": "ألبومات الحديثة",
|
"recent-albums": "ألبومات الحديثة",
|
||||||
"recent_searches": "عمليات البحث الأخيرة",
|
"recent_searches": "عمليات البحث الأخيرة",
|
||||||
"recently_added": "Recently added",
|
|
||||||
"recently_added_page_title": "أضيف مؤخرا",
|
"recently_added_page_title": "أضيف مؤخرا",
|
||||||
"refresh": "تحديث",
|
"refresh": "تحديث",
|
||||||
"refresh_encoded_videos": "تحديث مقاطع الفيديو المشفرة",
|
"refresh_encoded_videos": "تحديث مقاطع الفيديو المشفرة",
|
||||||
@@ -1476,7 +1330,6 @@
|
|||||||
"role_editor": "المحرر",
|
"role_editor": "المحرر",
|
||||||
"role_viewer": "العارض",
|
"role_viewer": "العارض",
|
||||||
"save": "حفظ",
|
"save": "حفظ",
|
||||||
"save_to_gallery": "Save to gallery",
|
|
||||||
"saved_api_key": "تم حفظ مفتاح الـ API",
|
"saved_api_key": "تم حفظ مفتاح الـ API",
|
||||||
"saved_profile": "تم حفظ الملف",
|
"saved_profile": "تم حفظ الملف",
|
||||||
"saved_settings": "تم حفظ الإعدادات",
|
"saved_settings": "تم حفظ الإعدادات",
|
||||||
@@ -1498,31 +1351,17 @@
|
|||||||
"search_city": "البحث حسب المدينة...",
|
"search_city": "البحث حسب المدينة...",
|
||||||
"search_country": "البحث حسب الدولة...",
|
"search_country": "البحث حسب الدولة...",
|
||||||
"search_filter_apply": "اختار الفلتر ",
|
"search_filter_apply": "اختار الفلتر ",
|
||||||
"search_filter_camera_title": "Select camera type",
|
|
||||||
"search_filter_date": "Date",
|
|
||||||
"search_filter_date_interval": "{start} to {end}",
|
|
||||||
"search_filter_date_title": "Select a date range",
|
|
||||||
"search_filter_display_option_not_in_album": "ليس في الألبوم",
|
"search_filter_display_option_not_in_album": "ليس في الألبوم",
|
||||||
"search_filter_display_options": "Display Options",
|
|
||||||
"search_filter_filename": "Search by file name",
|
|
||||||
"search_filter_location": "Location",
|
|
||||||
"search_filter_location_title": "Select location",
|
|
||||||
"search_filter_media_type": "Media Type",
|
|
||||||
"search_filter_media_type_title": "Select media type",
|
|
||||||
"search_filter_people_title": "Select people",
|
|
||||||
"search_for": "البحث عن",
|
"search_for": "البحث عن",
|
||||||
"search_for_existing_person": "البحث عن شخص موجود",
|
"search_for_existing_person": "البحث عن شخص موجود",
|
||||||
"search_no_more_result": "No more results",
|
|
||||||
"search_no_people": "لا يوجد أشخاص",
|
"search_no_people": "لا يوجد أشخاص",
|
||||||
"search_no_people_named": "لا يوجد أشخاص بالاسم \"{name}\"",
|
"search_no_people_named": "لا يوجد أشخاص بالاسم \"{name}\"",
|
||||||
"search_no_result": "No results found, try a different search term or combination",
|
|
||||||
"search_options": "خيارات البحث",
|
"search_options": "خيارات البحث",
|
||||||
"search_page_categories": "فئات",
|
"search_page_categories": "فئات",
|
||||||
"search_page_motion_photos": "الصور المتحركه",
|
"search_page_motion_photos": "الصور المتحركه",
|
||||||
"search_page_no_objects": "لا توجد معلومات عن أشياء متاحة",
|
"search_page_no_objects": "لا توجد معلومات عن أشياء متاحة",
|
||||||
"search_page_no_places": "لا توجد معلومات متوفرة للأماكن",
|
"search_page_no_places": "لا توجد معلومات متوفرة للأماكن",
|
||||||
"search_page_screenshots": "لقطات الشاشة",
|
"search_page_screenshots": "لقطات الشاشة",
|
||||||
"search_page_search_photos_videos": "Search for your photos and videos",
|
|
||||||
"search_page_selfies": " صور ذاتيه",
|
"search_page_selfies": " صور ذاتيه",
|
||||||
"search_page_things": "أشياء",
|
"search_page_things": "أشياء",
|
||||||
"search_page_view_all_button": "عرض الكل",
|
"search_page_view_all_button": "عرض الكل",
|
||||||
@@ -1561,7 +1400,6 @@
|
|||||||
"selected_count": "{count, plural, other {# محددة }}",
|
"selected_count": "{count, plural, other {# محددة }}",
|
||||||
"send_message": "إرسال رسالة",
|
"send_message": "إرسال رسالة",
|
||||||
"send_welcome_email": "إرسال بريدًا إلكترونيًا ترحيبيًا",
|
"send_welcome_email": "إرسال بريدًا إلكترونيًا ترحيبيًا",
|
||||||
"server_endpoint": "Server Endpoint",
|
|
||||||
"server_info_box_app_version": "نسخة التطبيق",
|
"server_info_box_app_version": "نسخة التطبيق",
|
||||||
"server_info_box_server_url": "عنوان URL الخادم",
|
"server_info_box_server_url": "عنوان URL الخادم",
|
||||||
"server_offline": "الخادم غير متصل",
|
"server_offline": "الخادم غير متصل",
|
||||||
@@ -1582,28 +1420,20 @@
|
|||||||
"setting_image_viewer_preview_title": "تحميل صورة معاينة",
|
"setting_image_viewer_preview_title": "تحميل صورة معاينة",
|
||||||
"setting_image_viewer_title": "الصور",
|
"setting_image_viewer_title": "الصور",
|
||||||
"setting_languages_apply": "تغيير الإعدادات",
|
"setting_languages_apply": "تغيير الإعدادات",
|
||||||
"setting_languages_subtitle": "Change the app's language",
|
|
||||||
"setting_languages_title": "اللغات",
|
|
||||||
"setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}",
|
|
||||||
"setting_notifications_notify_hours": "{} hours",
|
|
||||||
"setting_notifications_notify_immediately": "في الحال",
|
"setting_notifications_notify_immediately": "في الحال",
|
||||||
"setting_notifications_notify_minutes": "{} minutes",
|
|
||||||
"setting_notifications_notify_never": "أبداً",
|
"setting_notifications_notify_never": "أبداً",
|
||||||
"setting_notifications_notify_seconds": "{} seconds",
|
|
||||||
"setting_notifications_single_progress_subtitle": "معلومات التقدم التفصيلية تحميل لكل أصل",
|
"setting_notifications_single_progress_subtitle": "معلومات التقدم التفصيلية تحميل لكل أصل",
|
||||||
"setting_notifications_single_progress_title": "إظهار تقدم التفاصيل الاحتياطية الخلفية",
|
"setting_notifications_single_progress_title": "إظهار تقدم التفاصيل الاحتياطية الخلفية",
|
||||||
"setting_notifications_subtitle": "اضبط تفضيلات الإخطار",
|
"setting_notifications_subtitle": "اضبط تفضيلات الإخطار",
|
||||||
"setting_notifications_total_progress_subtitle": "التقدم التحميل العام (تم القيام به/إجمالي الأصول)",
|
"setting_notifications_total_progress_subtitle": "التقدم التحميل العام (تم القيام به/إجمالي الأصول)",
|
||||||
"setting_notifications_total_progress_title": "إظهار النسخ الاحتياطي الخلفية التقدم المحرز",
|
"setting_notifications_total_progress_title": "إظهار النسخ الاحتياطي الخلفية التقدم المحرز",
|
||||||
"setting_video_viewer_looping_title": "تكرار مقطع فيديو تلقائيًا",
|
"setting_video_viewer_looping_title": "تكرار مقطع فيديو تلقائيًا",
|
||||||
"setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.",
|
|
||||||
"setting_video_viewer_original_video_title": "Force original video",
|
|
||||||
"settings": "الإعدادات",
|
"settings": "الإعدادات",
|
||||||
"settings_require_restart": "يرجى إعادة تشغيل لتطبيق هذا الإعداد",
|
"settings_require_restart": "يرجى إعادة تشغيل لتطبيق هذا الإعداد",
|
||||||
"settings_saved": "تم حفظ الإعدادات",
|
"settings_saved": "تم حفظ الإعدادات",
|
||||||
|
"setup_pin_code": "تحديد رقم سري",
|
||||||
"share": "مشاركة",
|
"share": "مشاركة",
|
||||||
"share_add_photos": "إضافة الصور",
|
"share_add_photos": "إضافة الصور",
|
||||||
"share_assets_selected": "{} selected",
|
|
||||||
"share_dialog_preparing": "تحضير...",
|
"share_dialog_preparing": "تحضير...",
|
||||||
"shared": "مُشتَرك",
|
"shared": "مُشتَرك",
|
||||||
"shared_album_activities_input_disable": "التعليق معطل",
|
"shared_album_activities_input_disable": "التعليق معطل",
|
||||||
@@ -1617,40 +1447,22 @@
|
|||||||
"shared_by_user": "تمت المشاركة بواسطة {user}",
|
"shared_by_user": "تمت المشاركة بواسطة {user}",
|
||||||
"shared_by_you": "تمت مشاركته من قِبلك",
|
"shared_by_you": "تمت مشاركته من قِبلك",
|
||||||
"shared_from_partner": "صور من {partner}",
|
"shared_from_partner": "صور من {partner}",
|
||||||
"shared_intent_upload_button_progress_text": "{} / {} Uploaded",
|
|
||||||
"shared_link_app_bar_title": "روابط مشتركة",
|
"shared_link_app_bar_title": "روابط مشتركة",
|
||||||
"shared_link_clipboard_copied_massage": "نسخ إلى الحافظة",
|
"shared_link_clipboard_copied_massage": "نسخ إلى الحافظة",
|
||||||
"shared_link_clipboard_text": "Link: {}\nPassword: {}",
|
|
||||||
"shared_link_create_error": "خطأ أثناء إنشاء رابط مشترك",
|
"shared_link_create_error": "خطأ أثناء إنشاء رابط مشترك",
|
||||||
"shared_link_edit_description_hint": "أدخل وصف المشاركة",
|
"shared_link_edit_description_hint": "أدخل وصف المشاركة",
|
||||||
"shared_link_edit_expire_after_option_day": "يوم 1",
|
"shared_link_edit_expire_after_option_day": "يوم 1",
|
||||||
"shared_link_edit_expire_after_option_days": "{} days",
|
|
||||||
"shared_link_edit_expire_after_option_hour": "1 ساعة",
|
"shared_link_edit_expire_after_option_hour": "1 ساعة",
|
||||||
"shared_link_edit_expire_after_option_hours": "{} hours",
|
|
||||||
"shared_link_edit_expire_after_option_minute": "1 دقيقة",
|
"shared_link_edit_expire_after_option_minute": "1 دقيقة",
|
||||||
"shared_link_edit_expire_after_option_minutes": "{} minutes",
|
|
||||||
"shared_link_edit_expire_after_option_months": "{} months",
|
|
||||||
"shared_link_edit_expire_after_option_year": "{} year",
|
|
||||||
"shared_link_edit_password_hint": "أدخل كلمة مرور المشاركة",
|
"shared_link_edit_password_hint": "أدخل كلمة مرور المشاركة",
|
||||||
"shared_link_edit_submit_button": "تحديث الرابط",
|
"shared_link_edit_submit_button": "تحديث الرابط",
|
||||||
"shared_link_error_server_url_fetch": "لا يمكن جلب عنوان الخادم",
|
"shared_link_error_server_url_fetch": "لا يمكن جلب عنوان الخادم",
|
||||||
"shared_link_expires_day": "Expires in {} day",
|
|
||||||
"shared_link_expires_days": "Expires in {} days",
|
|
||||||
"shared_link_expires_hour": "Expires in {} hour",
|
|
||||||
"shared_link_expires_hours": "Expires in {} hours",
|
|
||||||
"shared_link_expires_minute": "Expires in {} minute",
|
|
||||||
"shared_link_expires_minutes": "Expires in {} minutes",
|
|
||||||
"shared_link_expires_never": "تنتهي ∞",
|
"shared_link_expires_never": "تنتهي ∞",
|
||||||
"shared_link_expires_second": "Expires in {} second",
|
|
||||||
"shared_link_expires_seconds": "Expires in {} seconds",
|
|
||||||
"shared_link_individual_shared": "Individual shared",
|
|
||||||
"shared_link_info_chip_metadata": "EXIF",
|
|
||||||
"shared_link_manage_links": "إدارة الروابط المشتركة",
|
"shared_link_manage_links": "إدارة الروابط المشتركة",
|
||||||
"shared_link_options": "خيارات الرابط المشترك",
|
"shared_link_options": "خيارات الرابط المشترك",
|
||||||
"shared_links": "روابط مشتركة",
|
"shared_links": "روابط مشتركة",
|
||||||
"shared_links_description": "وصف الروابط المشتركة",
|
"shared_links_description": "وصف الروابط المشتركة",
|
||||||
"shared_photos_and_videos_count": "{assetCount, plural, other {# الصور ومقاطع الفيديو المُشارَكة.}}",
|
"shared_photos_and_videos_count": "{assetCount, plural, other {# الصور ومقاطع الفيديو المُشارَكة.}}",
|
||||||
"shared_with_me": "Shared with me",
|
|
||||||
"shared_with_partner": "تمت المشاركة مع {partner}",
|
"shared_with_partner": "تمت المشاركة مع {partner}",
|
||||||
"sharing": "مشاركة",
|
"sharing": "مشاركة",
|
||||||
"sharing_enter_password": "الرجاء إدخال كلمة المرور لعرض هذه الصفحة.",
|
"sharing_enter_password": "الرجاء إدخال كلمة المرور لعرض هذه الصفحة.",
|
||||||
@@ -1726,9 +1538,6 @@
|
|||||||
"support_third_party_description": "تم حزم تثبيت immich الخاص بك بواسطة جهة خارجية. قد تكون المشكلات التي تواجهها ناجمة عن هذه الحزمة، لذا يرجى طرح المشكلات معهم في المقام الأول باستخدام الروابط أدناه.",
|
"support_third_party_description": "تم حزم تثبيت immich الخاص بك بواسطة جهة خارجية. قد تكون المشكلات التي تواجهها ناجمة عن هذه الحزمة، لذا يرجى طرح المشكلات معهم في المقام الأول باستخدام الروابط أدناه.",
|
||||||
"swap_merge_direction": "تبديل اتجاه الدمج",
|
"swap_merge_direction": "تبديل اتجاه الدمج",
|
||||||
"sync": "مزامنة",
|
"sync": "مزامنة",
|
||||||
"sync_albums": "Sync albums",
|
|
||||||
"sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums",
|
|
||||||
"sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich",
|
|
||||||
"tag": "العلامة",
|
"tag": "العلامة",
|
||||||
"tag_assets": "أصول العلامة",
|
"tag_assets": "أصول العلامة",
|
||||||
"tag_created": "تم إنشاء العلامة: {tag}",
|
"tag_created": "تم إنشاء العلامة: {tag}",
|
||||||
@@ -1743,14 +1552,8 @@
|
|||||||
"theme_selection": "اختيار السمة",
|
"theme_selection": "اختيار السمة",
|
||||||
"theme_selection_description": "قم بتعيين السمة تلقائيًا على اللون الفاتح أو الداكن بناءً على تفضيلات نظام المتصفح الخاص بك",
|
"theme_selection_description": "قم بتعيين السمة تلقائيًا على اللون الفاتح أو الداكن بناءً على تفضيلات نظام المتصفح الخاص بك",
|
||||||
"theme_setting_asset_list_storage_indicator_title": "عرض مؤشر التخزين على بلاط الأصول",
|
"theme_setting_asset_list_storage_indicator_title": "عرض مؤشر التخزين على بلاط الأصول",
|
||||||
"theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({})",
|
|
||||||
"theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.",
|
|
||||||
"theme_setting_colorful_interface_title": "Colorful interface",
|
|
||||||
"theme_setting_image_viewer_quality_subtitle": "اضبط جودة عارض الصورة التفصيلية",
|
"theme_setting_image_viewer_quality_subtitle": "اضبط جودة عارض الصورة التفصيلية",
|
||||||
"theme_setting_image_viewer_quality_title": "جودة عارض الصورة",
|
"theme_setting_image_viewer_quality_title": "جودة عارض الصورة",
|
||||||
"theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.",
|
|
||||||
"theme_setting_primary_color_title": "Primary color",
|
|
||||||
"theme_setting_system_primary_color_title": "Use system color",
|
|
||||||
"theme_setting_system_theme_switch": "تلقائي (اتبع إعداد النظام)",
|
"theme_setting_system_theme_switch": "تلقائي (اتبع إعداد النظام)",
|
||||||
"theme_setting_theme_subtitle": "اختر إعدادات مظهر التطبيق",
|
"theme_setting_theme_subtitle": "اختر إعدادات مظهر التطبيق",
|
||||||
"theme_setting_three_stage_loading_subtitle": "قد يزيد التحميل من ثلاث مراحل من أداء التحميل ولكنه يسبب تحميل شبكة أعلى بكثير",
|
"theme_setting_three_stage_loading_subtitle": "قد يزيد التحميل من ثلاث مراحل من أداء التحميل ولكنه يسبب تحميل شبكة أعلى بكثير",
|
||||||
@@ -1767,24 +1570,22 @@
|
|||||||
"to_parent": "انتقل إلى الوالد",
|
"to_parent": "انتقل إلى الوالد",
|
||||||
"to_trash": "حذف",
|
"to_trash": "حذف",
|
||||||
"toggle_settings": "الإعدادات",
|
"toggle_settings": "الإعدادات",
|
||||||
"toggle_theme": "تبديل المظهر الداكن",
|
|
||||||
"total": "الإجمالي",
|
"total": "الإجمالي",
|
||||||
"total_usage": "الاستخدام الإجمالي",
|
"total_usage": "الاستخدام الإجمالي",
|
||||||
"trash": "المهملات",
|
"trash": "المهملات",
|
||||||
"trash_all": "نقل الكل إلى سلة المهملات",
|
"trash_all": "نقل الكل إلى سلة المهملات",
|
||||||
"trash_count": "سلة المحملات {count, number}",
|
"trash_count": "سلة المحملات {count, number}",
|
||||||
"trash_delete_asset": "حذف/نقل المحتوى إلى سلة المهملات",
|
"trash_delete_asset": "حذف/نقل المحتوى إلى سلة المهملات",
|
||||||
"trash_emptied": "Emptied trash",
|
|
||||||
"trash_no_results_message": "ستظهر هنا الصور ومقاطع الفيديو المحذوفة.",
|
"trash_no_results_message": "ستظهر هنا الصور ومقاطع الفيديو المحذوفة.",
|
||||||
"trash_page_delete_all": "حذف الكل",
|
"trash_page_delete_all": "حذف الكل",
|
||||||
"trash_page_empty_trash_dialog_content": "هل تريد تفريغ أصولك المهملة؟ ستتم إزالة هذه العناصر نهائيًا من التطبيق",
|
"trash_page_empty_trash_dialog_content": "هل تريد تفريغ أصولك المهملة؟ ستتم إزالة هذه العناصر نهائيًا من التطبيق",
|
||||||
"trash_page_info": "Trashed items will be permanently deleted after {} days",
|
|
||||||
"trash_page_no_assets": "لا توجد اصول في سله المهملات",
|
"trash_page_no_assets": "لا توجد اصول في سله المهملات",
|
||||||
"trash_page_restore_all": "استعادة الكل",
|
"trash_page_restore_all": "استعادة الكل",
|
||||||
"trash_page_select_assets_btn": "اختر الأصول ",
|
"trash_page_select_assets_btn": "اختر الأصول ",
|
||||||
"trash_page_title": "Trash ({})",
|
|
||||||
"trashed_items_will_be_permanently_deleted_after": "سيتم حذفُ العناصر المحذوفة نِهائيًا بعد {days, plural, one {# يوم} other {# أيام }}.",
|
"trashed_items_will_be_permanently_deleted_after": "سيتم حذفُ العناصر المحذوفة نِهائيًا بعد {days, plural, one {# يوم} other {# أيام }}.",
|
||||||
"type": "النوع",
|
"type": "النوع",
|
||||||
|
"unable_to_change_pin_code": "تفيير الرقم السري غير ممكن",
|
||||||
|
"unable_to_setup_pin_code": "انشاء الرقم السري غير ممكن",
|
||||||
"unarchive": "أخرج من الأرشيف",
|
"unarchive": "أخرج من الأرشيف",
|
||||||
"unarchived_count": "{count, plural, other {غير مؤرشفة #}}",
|
"unarchived_count": "{count, plural, other {غير مؤرشفة #}}",
|
||||||
"unfavorite": "أزل التفضيل",
|
"unfavorite": "أزل التفضيل",
|
||||||
@@ -1805,8 +1606,6 @@
|
|||||||
"unselect_all_duplicates": "إلغاء تحديد كافة النسخ المكررة",
|
"unselect_all_duplicates": "إلغاء تحديد كافة النسخ المكررة",
|
||||||
"unstack": "فك الكومه",
|
"unstack": "فك الكومه",
|
||||||
"unstacked_assets_count": "تم إخراج {count, plural, one {# الأصل} other {# الأصول}} من التكديس",
|
"unstacked_assets_count": "تم إخراج {count, plural, one {# الأصل} other {# الأصول}} من التكديس",
|
||||||
"untracked_files": "الملفات التي لم يتم تعقبها",
|
|
||||||
"untracked_files_decription": "لا يتم تعقب هذه الملفات بواسطة التطبيق. يمكن أن تكون نتيجةً لعمليات نقل فاشلة، أو عمليات رفع متقطعة، أو يتم تركها في الخلف بسبب خللاً ما",
|
|
||||||
"up_next": "التالي",
|
"up_next": "التالي",
|
||||||
"updated_password": "تم تحديث كلمة المرور",
|
"updated_password": "تم تحديث كلمة المرور",
|
||||||
"upload": "رفع",
|
"upload": "رفع",
|
||||||
@@ -1820,15 +1619,14 @@
|
|||||||
"upload_status_errors": "الأخطاء",
|
"upload_status_errors": "الأخطاء",
|
||||||
"upload_status_uploaded": "تم الرفع",
|
"upload_status_uploaded": "تم الرفع",
|
||||||
"upload_success": "تم الرفع بنجاح، قم بتحديث الصفحة لرؤية المحتويات المرفوعة الجديدة.",
|
"upload_success": "تم الرفع بنجاح، قم بتحديث الصفحة لرؤية المحتويات المرفوعة الجديدة.",
|
||||||
"upload_to_immich": "Upload to Immich ({})",
|
|
||||||
"uploading": "Uploading",
|
|
||||||
"url": "عنوان URL",
|
"url": "عنوان URL",
|
||||||
"usage": "الاستخدام",
|
"usage": "الاستخدام",
|
||||||
"use_current_connection": "use current connection",
|
|
||||||
"use_custom_date_range": "استخدم النطاق الزمني المخصص بدلاً من ذلك",
|
"use_custom_date_range": "استخدم النطاق الزمني المخصص بدلاً من ذلك",
|
||||||
"user": "مستخدم",
|
"user": "مستخدم",
|
||||||
"user_id": "معرف المستخدم",
|
"user_id": "معرف المستخدم",
|
||||||
"user_liked": "قام {user} بالإعجاب {type, select, photo {بهذه الصورة} video {بهذا الفيديو} asset {بهذا المحتوى} other {بها}}",
|
"user_liked": "قام {user} بالإعجاب {type, select, photo {بهذه الصورة} video {بهذا الفيديو} asset {بهذا المحتوى} other {بها}}",
|
||||||
|
"user_pin_code_settings": "الرقم السري",
|
||||||
|
"user_pin_code_settings_description": "تغير الرقم السري",
|
||||||
"user_purchase_settings": "الشراء",
|
"user_purchase_settings": "الشراء",
|
||||||
"user_purchase_settings_description": "إدارة عملية الشراء الخاصة بك",
|
"user_purchase_settings_description": "إدارة عملية الشراء الخاصة بك",
|
||||||
"user_role_set": "قم بتعيين {user} كـ {role}",
|
"user_role_set": "قم بتعيين {user} كـ {role}",
|
||||||
@@ -1839,16 +1637,10 @@
|
|||||||
"users": "المستخدمين",
|
"users": "المستخدمين",
|
||||||
"utilities": "أدوات",
|
"utilities": "أدوات",
|
||||||
"validate": "تحقْق",
|
"validate": "تحقْق",
|
||||||
"validate_endpoint_error": "Please enter a valid URL",
|
|
||||||
"variables": "المتغيرات",
|
"variables": "المتغيرات",
|
||||||
"version": "الإصدار",
|
"version": "الإصدار",
|
||||||
"version_announcement_closing": "صديقك، أليكس",
|
"version_announcement_closing": "صديقك، أليكس",
|
||||||
"version_announcement_message": "مرحبًا! يتوفر إصدار جديد من Immich. يُرجى تخصيص بعض الوقت لقراءة <link>ملاحظات الإصدار</link> للتأكد من تحديث إعداداتك لمنع أي أخطاء في التكوين، خاصة إذا كنت تستخدم WatchTower أو أي آلية تتولى تحديث مثيل Immich الخاص بك تلقائيًا.",
|
"version_announcement_message": "مرحبًا! يتوفر إصدار جديد من Immich. يُرجى تخصيص بعض الوقت لقراءة <link>ملاحظات الإصدار</link> للتأكد من تحديث إعداداتك لمنع أي أخطاء في التكوين، خاصة إذا كنت تستخدم WatchTower أو أي آلية تتولى تحديث مثيل Immich الخاص بك تلقائيًا.",
|
||||||
"version_announcement_overlay_release_notes": "ملاحظات الإصدار",
|
|
||||||
"version_announcement_overlay_text_1": "مرحبًا يا صديقي ، هناك إصدار جديد",
|
|
||||||
"version_announcement_overlay_text_2": "من فضلك خذ وقتك لزيارة",
|
|
||||||
"version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.",
|
|
||||||
"version_announcement_overlay_title": "نسخه جديده متاحه للخادم ",
|
|
||||||
"version_history": "تاريخ الإصدار",
|
"version_history": "تاريخ الإصدار",
|
||||||
"version_history_item": "تم تثبيت {version} في {date}",
|
"version_history_item": "تم تثبيت {version} في {date}",
|
||||||
"video": "فيديو",
|
"video": "فيديو",
|
||||||
|
|||||||
@@ -34,18 +34,15 @@
|
|||||||
"backup_database_enable_description": "Verilənlər bazasının ehtiyat nüsxələrini aktiv et",
|
"backup_database_enable_description": "Verilənlər bazasının ehtiyat nüsxələrini aktiv et",
|
||||||
"backup_settings": "Ehtiyat Nüsxə Parametrləri",
|
"backup_settings": "Ehtiyat Nüsxə Parametrləri",
|
||||||
"backup_settings_description": "Verilənlər bazasının ehtiyat nüsxə parametrlərini idarə et",
|
"backup_settings_description": "Verilənlər bazasının ehtiyat nüsxə parametrlərini idarə et",
|
||||||
"check_all": "Hamısını yoxla",
|
|
||||||
"config_set_by_file": "Konfiqurasiya hal-hazırda konfiqurasiya faylı ilə təyin olunub",
|
"config_set_by_file": "Konfiqurasiya hal-hazırda konfiqurasiya faylı ilə təyin olunub",
|
||||||
"confirm_delete_library": "{library} kitabxanasını silmək istədiyinizdən əminmisiniz?",
|
"confirm_delete_library": "{library} kitabxanasını silmək istədiyinizdən əminmisiniz?",
|
||||||
"confirm_email_below": "Təsdiqləmək üçün aşağıya {email} yazın",
|
"confirm_email_below": "Təsdiqləmək üçün aşağıya {email} yazın",
|
||||||
"confirm_user_password_reset": "{user} adlı istifadəçinin şifrəsini sıfırlamaq istədiyinizdən əminmisiniz?",
|
"confirm_user_password_reset": "{user} adlı istifadəçinin şifrəsini sıfırlamaq istədiyinizdən əminmisiniz?",
|
||||||
"disable_login": "Giriş etməni söndür",
|
"disable_login": "Giriş etməni söndür",
|
||||||
"duplicate_detection_job_description": "Bənzər şəkilləri tapmaq üçün maşın öyrənməsini işə salın. Bu prosses Smart Search funksiyasına əsaslanır",
|
"duplicate_detection_job_description": "Bənzər şəkilləri tapmaq üçün maşın öyrənməsini işə salın. Bu prosses Smart Search funksiyasına əsaslanır",
|
||||||
"external_library_created_at": "Xarici kitabxana ({date} (tarixində yaradıldı)",
|
|
||||||
"external_library_management": "Xarici kitabxana idarəetməsi",
|
"external_library_management": "Xarici kitabxana idarəetməsi",
|
||||||
"face_detection": "Üz tanıma",
|
"face_detection": "Üz tanıma",
|
||||||
"force_delete_user_warning": "XƏBƏRDARLIQ: Bu əməliyyat istifadəçi və bütün məlumatları siləcəkdir. Bu prossesi və silinən faylları geri qaytarmaq olmaz.",
|
"force_delete_user_warning": "XƏBƏRDARLIQ: Bu əməliyyat istifadəçi və bütün məlumatları siləcəkdir. Bu prossesi və silinən faylları geri qaytarmaq olmaz.",
|
||||||
"forcing_refresh_library_files": "Bütün kitabxana fayllarını məcburi yeniləmə",
|
|
||||||
"image_format_description": "WebP, JPEG faylına görə daha kiçik həcmə sahibdir, lakin onu kodlaşdırmaq daha çox vaxt alır.",
|
"image_format_description": "WebP, JPEG faylına görə daha kiçik həcmə sahibdir, lakin onu kodlaşdırmaq daha çox vaxt alır.",
|
||||||
"image_preview_title": "Önizləmə parametrləri",
|
"image_preview_title": "Önizləmə parametrləri",
|
||||||
"image_quality": "Keyfiyyət",
|
"image_quality": "Keyfiyyət",
|
||||||
@@ -76,7 +73,6 @@
|
|||||||
"library_watching_settings_description": "Dəyişdirilən faylları avtomatik olaraq yoxla",
|
"library_watching_settings_description": "Dəyişdirilən faylları avtomatik olaraq yoxla",
|
||||||
"logging_enable_description": "Jurnalı aktivləşdir",
|
"logging_enable_description": "Jurnalı aktivləşdir",
|
||||||
"logging_level_description": "Aktiv edildikdə hansı jurnal səviyyəsi istifadə olunur.",
|
"logging_level_description": "Aktiv edildikdə hansı jurnal səviyyəsi istifadə olunur.",
|
||||||
"logging_settings": "",
|
|
||||||
"machine_learning_clip_model": "CLIP modeli",
|
"machine_learning_clip_model": "CLIP modeli",
|
||||||
"machine_learning_clip_model_description": "<link>Burada</link>qeyd olunan CLIP modelinin adı. Modeli dəyişdirdikdən sonra bütün şəkillər üçün 'Ağıllı Axtarış' funksiyasını yenidən işə salmalısınız.",
|
"machine_learning_clip_model_description": "<link>Burada</link>qeyd olunan CLIP modelinin adı. Modeli dəyişdirdikdən sonra bütün şəkillər üçün 'Ağıllı Axtarış' funksiyasını yenidən işə salmalısınız.",
|
||||||
"machine_learning_duplicate_detection": "Dublikat Aşkarlama",
|
"machine_learning_duplicate_detection": "Dublikat Aşkarlama",
|
||||||
|
|||||||
15
i18n/be.json
15
i18n/be.json
@@ -14,6 +14,7 @@
|
|||||||
"add_a_location": "Дадаць месца",
|
"add_a_location": "Дадаць месца",
|
||||||
"add_a_name": "Дадаць імя",
|
"add_a_name": "Дадаць імя",
|
||||||
"add_a_title": "Дадаць загаловак",
|
"add_a_title": "Дадаць загаловак",
|
||||||
|
"add_endpoint": "Дадаць кропку доступу",
|
||||||
"add_exclusion_pattern": "Дадаць шаблон выключэння",
|
"add_exclusion_pattern": "Дадаць шаблон выключэння",
|
||||||
"add_import_path": "Дадаць шлях імпарту",
|
"add_import_path": "Дадаць шлях імпарту",
|
||||||
"add_location": "Дадайце месца",
|
"add_location": "Дадайце месца",
|
||||||
@@ -42,9 +43,7 @@
|
|||||||
"backup_database_enable_description": "Уключыць рэзерваванне базы даных",
|
"backup_database_enable_description": "Уключыць рэзерваванне базы даных",
|
||||||
"backup_keep_last_amount": "Колькасць папярэдніх рэзервовых копій для захавання",
|
"backup_keep_last_amount": "Колькасць папярэдніх рэзервовых копій для захавання",
|
||||||
"backup_settings": "Налады рэзервовага капіявання",
|
"backup_settings": "Налады рэзервовага капіявання",
|
||||||
"backup_settings_description": "Кіраванне наладкамі рэзервовага капіявання базы даных",
|
"backup_settings_description": "Кіраванне наладамі дампа базы дадзеных. Заўвага: гэтыя задачы не кантралююцца, і ў выпадку няўдачы паведамленне адпраўлена не будзе.",
|
||||||
"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} бібліятэку?",
|
||||||
@@ -59,11 +58,19 @@
|
|||||||
"disable_login": "Адключыць уваход",
|
"disable_login": "Адключыць уваход",
|
||||||
"duplicate_detection_job_description": "Запусціць машыннае навучанне на актывах для выяўлення падобных выяў. Залежыць ад Smart Search",
|
"duplicate_detection_job_description": "Запусціць машыннае навучанне на актывах для выяўлення падобных выяў. Залежыць ад Smart Search",
|
||||||
"exclusion_pattern_description": "Шаблоны выключэння дазваляюць ігнараваць файлы і папкі пры сканаванні вашай бібліятэкі. Гэта карысна, калі ў вас ёсць папкі, якія змяшчаюць файлы, якія вы не хочаце імпартаваць, напрыклад, файлы RAW.",
|
"exclusion_pattern_description": "Шаблоны выключэння дазваляюць ігнараваць файлы і папкі пры сканаванні вашай бібліятэкі. Гэта карысна, калі ў вас ёсць папкі, якія змяшчаюць файлы, якія вы не хочаце імпартаваць, напрыклад, файлы RAW.",
|
||||||
"external_library_created_at": "Знешняя бібліятэка (створана {date})",
|
|
||||||
"external_library_management": "Кіраванне знешняй бібліятэкай",
|
"external_library_management": "Кіраванне знешняй бібліятэкай",
|
||||||
"face_detection": "Выяўленне твараў",
|
"face_detection": "Выяўленне твараў",
|
||||||
|
"face_detection_description": "Выяўляць твары на фотаздымках і відэа з дапамогай машыннага навучання. Для відэа ўлічваецца толькі мініяцюра. \"Абнавіць\" (пера)апрацоўвае ўсе медыя. \"Скінуць\" дадаткова ачышчае ўсе бягучыя дадзеныя пра твары. \"Адсутнічае\" ставіць у чаргу медыя, якія яшчэ не былі апрацаваныя. Выяўленыя твары будуць пастаўлены ў чаргу для распазнавання асоб пасля завяршэння выяўлення твараў, з групаваннем іх па існуючых або новых людзях.",
|
||||||
|
"facial_recognition_job_description": "Групаваць выяўленыя твары па асобах. Гэты этап выконваецца пасля завяршэння выяўлення твараў. \"Скінуць\" (паўторна) перагрупоўвае ўсе твары. \"Адсутнічае\" ставіць у чаргу твары, якія яшчэ не прыпісаныя да якой-небудзь асобы.",
|
||||||
|
"failed_job_command": "Каманда {command} не выканалася для задання: {job}",
|
||||||
"force_delete_user_warning": "ПАПЯРЭДЖАННЕ: Гэта дзеянне неадкладна выдаліць карыстальніка і ўсе аб'екты. Гэта дзеянне не можа быць адроблена і файлы немагчыма будзе аднавіць.",
|
"force_delete_user_warning": "ПАПЯРЭДЖАННЕ: Гэта дзеянне неадкладна выдаліць карыстальніка і ўсе аб'екты. Гэта дзеянне не можа быць адроблена і файлы немагчыма будзе аднавіць.",
|
||||||
"image_format": "Фармат",
|
"image_format": "Фармат",
|
||||||
|
"image_format_description": "WebP стварае меншыя файлы, чым JPEG, але павольней кадуе.",
|
||||||
|
"image_fullsize_description": "Выява ў поўным памеры без метаданых, выкарыстоўваецца пры павелічэнні",
|
||||||
|
"image_fullsize_enabled": "Уключыць стварэнне выявы ў поўным памеры",
|
||||||
|
"image_fullsize_enabled_description": "Ствараць выяву ў поўным памеры для фарматаў, што не прыдатныя для вэб. Калі ўключана опцыя \"Аддаваць перавагу ўбудаванай праяве\", прагляды выкарыстоўваюцца непасрэдна без канвертацыі. Не ўплывае на вэб-прыдатныя фарматы, такія як JPEG.",
|
||||||
|
"image_fullsize_quality_description": "Якасць выявы ў поўным памеры ад 1 да 100. Больш высокае значэнне лепшае, але прыводзіць да павелічэння памеру файла.",
|
||||||
|
"image_fullsize_title": "Налады выявы ў поўным памеры",
|
||||||
"image_preview_title": "Налады папярэдняга прагляду",
|
"image_preview_title": "Налады папярэдняга прагляду",
|
||||||
"image_quality": "Якасць",
|
"image_quality": "Якасць",
|
||||||
"image_resolution": "Раздзяляльнасць",
|
"image_resolution": "Раздзяляльнасць",
|
||||||
|
|||||||
656
i18n/bg.json
656
i18n/bg.json
File diff suppressed because it is too large
Load Diff
849
i18n/bi.json
849
i18n/bi.json
@@ -3,8 +3,6 @@
|
|||||||
"account": "Akaont",
|
"account": "Akaont",
|
||||||
"account_settings": "Seting blo Akaont",
|
"account_settings": "Seting blo Akaont",
|
||||||
"acknowledge": "Akcept",
|
"acknowledge": "Akcept",
|
||||||
"action": "",
|
|
||||||
"actions": "",
|
|
||||||
"active": "Stap Mekem",
|
"active": "Stap Mekem",
|
||||||
"activity": "Wanem hemi Mekem",
|
"activity": "Wanem hemi Mekem",
|
||||||
"activity_changed": "WAnem hemi Mekem hemi",
|
"activity_changed": "WAnem hemi Mekem hemi",
|
||||||
@@ -16,850 +14,5 @@
|
|||||||
"add_exclusion_pattern": "Putem wan paten wae hemi karem aot",
|
"add_exclusion_pattern": "Putem wan paten wae hemi karem aot",
|
||||||
"add_import_path": "Putem wan pat blo import",
|
"add_import_path": "Putem wan pat blo import",
|
||||||
"add_location": "Putem wan place blo hem",
|
"add_location": "Putem wan place blo hem",
|
||||||
"add_more_users": "Putem mor man",
|
"add_more_users": "Putem mor man"
|
||||||
"add_partner": "",
|
|
||||||
"add_path": "",
|
|
||||||
"add_photos": "",
|
|
||||||
"add_to": "",
|
|
||||||
"add_to_album": "",
|
|
||||||
"add_to_shared_album": "",
|
|
||||||
"admin": {
|
|
||||||
"add_exclusion_pattern_description": "",
|
|
||||||
"authentication_settings": "",
|
|
||||||
"authentication_settings_description": "",
|
|
||||||
"background_task_job": "",
|
|
||||||
"check_all": "",
|
|
||||||
"config_set_by_file": "",
|
|
||||||
"confirm_delete_library": "",
|
|
||||||
"confirm_delete_library_assets": "",
|
|
||||||
"confirm_email_below": "",
|
|
||||||
"confirm_reprocess_all_faces": "",
|
|
||||||
"confirm_user_password_reset": "",
|
|
||||||
"disable_login": "",
|
|
||||||
"duplicate_detection_job_description": "",
|
|
||||||
"exclusion_pattern_description": "",
|
|
||||||
"external_library_created_at": "",
|
|
||||||
"external_library_management": "",
|
|
||||||
"face_detection": "",
|
|
||||||
"face_detection_description": "",
|
|
||||||
"facial_recognition_job_description": "",
|
|
||||||
"force_delete_user_warning": "",
|
|
||||||
"forcing_refresh_library_files": "",
|
|
||||||
"image_format_description": "",
|
|
||||||
"image_prefer_embedded_preview": "",
|
|
||||||
"image_prefer_embedded_preview_setting_description": "",
|
|
||||||
"image_prefer_wide_gamut": "",
|
|
||||||
"image_prefer_wide_gamut_setting_description": "",
|
|
||||||
"image_quality": "",
|
|
||||||
"image_settings": "",
|
|
||||||
"image_settings_description": "",
|
|
||||||
"job_concurrency": "",
|
|
||||||
"job_not_concurrency_safe": "",
|
|
||||||
"job_settings": "",
|
|
||||||
"job_settings_description": "",
|
|
||||||
"job_status": "",
|
|
||||||
"jobs_delayed": "",
|
|
||||||
"jobs_failed": "",
|
|
||||||
"library_created": "",
|
|
||||||
"library_deleted": "",
|
|
||||||
"library_import_path_description": "",
|
|
||||||
"library_scanning": "",
|
|
||||||
"library_scanning_description": "",
|
|
||||||
"library_scanning_enable_description": "",
|
|
||||||
"library_settings": "",
|
|
||||||
"library_settings_description": "",
|
|
||||||
"library_tasks_description": "",
|
|
||||||
"library_watching_enable_description": "",
|
|
||||||
"library_watching_settings": "",
|
|
||||||
"library_watching_settings_description": "",
|
|
||||||
"logging_enable_description": "",
|
|
||||||
"logging_level_description": "",
|
|
||||||
"logging_settings": "",
|
|
||||||
"machine_learning_clip_model": "",
|
|
||||||
"machine_learning_duplicate_detection": "",
|
|
||||||
"machine_learning_duplicate_detection_enabled_description": "",
|
|
||||||
"machine_learning_duplicate_detection_setting_description": "",
|
|
||||||
"machine_learning_enabled_description": "",
|
|
||||||
"machine_learning_facial_recognition": "",
|
|
||||||
"machine_learning_facial_recognition_description": "",
|
|
||||||
"machine_learning_facial_recognition_model": "",
|
|
||||||
"machine_learning_facial_recognition_model_description": "",
|
|
||||||
"machine_learning_facial_recognition_setting_description": "",
|
|
||||||
"machine_learning_max_detection_distance": "",
|
|
||||||
"machine_learning_max_detection_distance_description": "",
|
|
||||||
"machine_learning_max_recognition_distance": "",
|
|
||||||
"machine_learning_max_recognition_distance_description": "",
|
|
||||||
"machine_learning_min_detection_score": "",
|
|
||||||
"machine_learning_min_detection_score_description": "",
|
|
||||||
"machine_learning_min_recognized_faces": "",
|
|
||||||
"machine_learning_min_recognized_faces_description": "",
|
|
||||||
"machine_learning_settings": "",
|
|
||||||
"machine_learning_settings_description": "",
|
|
||||||
"machine_learning_smart_search": "",
|
|
||||||
"machine_learning_smart_search_description": "",
|
|
||||||
"machine_learning_smart_search_enabled_description": "",
|
|
||||||
"machine_learning_url_description": "",
|
|
||||||
"manage_concurrency": "",
|
|
||||||
"manage_log_settings": "",
|
|
||||||
"map_dark_style": "",
|
|
||||||
"map_enable_description": "",
|
|
||||||
"map_light_style": "",
|
|
||||||
"map_reverse_geocoding": "",
|
|
||||||
"map_reverse_geocoding_enable_description": "",
|
|
||||||
"map_reverse_geocoding_settings": "",
|
|
||||||
"map_settings": "",
|
|
||||||
"map_settings_description": "",
|
|
||||||
"map_style_description": "",
|
|
||||||
"metadata_extraction_job": "",
|
|
||||||
"metadata_extraction_job_description": "",
|
|
||||||
"migration_job": "",
|
|
||||||
"migration_job_description": "",
|
|
||||||
"no_paths_added": "",
|
|
||||||
"no_pattern_added": "",
|
|
||||||
"note_apply_storage_label_previous_assets": "",
|
|
||||||
"note_cannot_be_changed_later": "",
|
|
||||||
"notification_email_from_address": "",
|
|
||||||
"notification_email_from_address_description": "",
|
|
||||||
"notification_email_host_description": "",
|
|
||||||
"notification_email_ignore_certificate_errors": "",
|
|
||||||
"notification_email_ignore_certificate_errors_description": "",
|
|
||||||
"notification_email_password_description": "",
|
|
||||||
"notification_email_port_description": "",
|
|
||||||
"notification_email_sent_test_email_button": "",
|
|
||||||
"notification_email_setting_description": "",
|
|
||||||
"notification_email_test_email_failed": "",
|
|
||||||
"notification_email_test_email_sent": "",
|
|
||||||
"notification_email_username_description": "",
|
|
||||||
"notification_enable_email_notifications": "",
|
|
||||||
"notification_settings": "",
|
|
||||||
"notification_settings_description": "",
|
|
||||||
"oauth_auto_launch": "",
|
|
||||||
"oauth_auto_launch_description": "",
|
|
||||||
"oauth_auto_register": "",
|
|
||||||
"oauth_auto_register_description": "",
|
|
||||||
"oauth_button_text": "",
|
|
||||||
"oauth_client_id": "",
|
|
||||||
"oauth_client_secret": "",
|
|
||||||
"oauth_enable_description": "",
|
|
||||||
"oauth_issuer_url": "",
|
|
||||||
"oauth_mobile_redirect_uri": "",
|
|
||||||
"oauth_mobile_redirect_uri_override": "",
|
|
||||||
"oauth_mobile_redirect_uri_override_description": "",
|
|
||||||
"oauth_scope": "",
|
|
||||||
"oauth_settings": "",
|
|
||||||
"oauth_settings_description": "",
|
|
||||||
"oauth_signing_algorithm": "",
|
|
||||||
"oauth_storage_label_claim": "",
|
|
||||||
"oauth_storage_label_claim_description": "",
|
|
||||||
"oauth_storage_quota_claim": "",
|
|
||||||
"oauth_storage_quota_claim_description": "",
|
|
||||||
"oauth_storage_quota_default": "",
|
|
||||||
"oauth_storage_quota_default_description": "",
|
|
||||||
"offline_paths": "",
|
|
||||||
"offline_paths_description": "",
|
|
||||||
"password_enable_description": "",
|
|
||||||
"password_settings": "",
|
|
||||||
"password_settings_description": "",
|
|
||||||
"paths_validated_successfully": "",
|
|
||||||
"quota_size_gib": "",
|
|
||||||
"refreshing_all_libraries": "",
|
|
||||||
"repair_all": "",
|
|
||||||
"repair_matched_items": "",
|
|
||||||
"repaired_items": "",
|
|
||||||
"require_password_change_on_login": "",
|
|
||||||
"reset_settings_to_default": "",
|
|
||||||
"reset_settings_to_recent_saved": "",
|
|
||||||
"send_welcome_email": "",
|
|
||||||
"server_external_domain_settings": "",
|
|
||||||
"server_external_domain_settings_description": "",
|
|
||||||
"server_settings": "",
|
|
||||||
"server_settings_description": "",
|
|
||||||
"server_welcome_message": "",
|
|
||||||
"server_welcome_message_description": "",
|
|
||||||
"sidecar_job": "",
|
|
||||||
"sidecar_job_description": "",
|
|
||||||
"slideshow_duration_description": "",
|
|
||||||
"smart_search_job_description": "",
|
|
||||||
"storage_template_enable_description": "",
|
|
||||||
"storage_template_hash_verification_enabled": "",
|
|
||||||
"storage_template_hash_verification_enabled_description": "",
|
|
||||||
"storage_template_migration": "",
|
|
||||||
"storage_template_migration_job": "",
|
|
||||||
"storage_template_settings": "",
|
|
||||||
"storage_template_settings_description": "",
|
|
||||||
"system_settings": "",
|
|
||||||
"theme_custom_css_settings": "",
|
|
||||||
"theme_custom_css_settings_description": "",
|
|
||||||
"theme_settings": "",
|
|
||||||
"theme_settings_description": "",
|
|
||||||
"these_files_matched_by_checksum": "",
|
|
||||||
"thumbnail_generation_job": "",
|
|
||||||
"thumbnail_generation_job_description": "",
|
|
||||||
"transcoding_acceleration_api": "",
|
|
||||||
"transcoding_acceleration_api_description": "",
|
|
||||||
"transcoding_acceleration_nvenc": "",
|
|
||||||
"transcoding_acceleration_qsv": "",
|
|
||||||
"transcoding_acceleration_rkmpp": "",
|
|
||||||
"transcoding_acceleration_vaapi": "",
|
|
||||||
"transcoding_accepted_audio_codecs": "",
|
|
||||||
"transcoding_accepted_audio_codecs_description": "",
|
|
||||||
"transcoding_accepted_video_codecs": "",
|
|
||||||
"transcoding_accepted_video_codecs_description": "",
|
|
||||||
"transcoding_advanced_options_description": "",
|
|
||||||
"transcoding_audio_codec": "",
|
|
||||||
"transcoding_audio_codec_description": "",
|
|
||||||
"transcoding_bitrate_description": "",
|
|
||||||
"transcoding_constant_quality_mode": "",
|
|
||||||
"transcoding_constant_quality_mode_description": "",
|
|
||||||
"transcoding_constant_rate_factor": "",
|
|
||||||
"transcoding_constant_rate_factor_description": "",
|
|
||||||
"transcoding_disabled_description": "",
|
|
||||||
"transcoding_hardware_acceleration": "",
|
|
||||||
"transcoding_hardware_acceleration_description": "",
|
|
||||||
"transcoding_hardware_decoding": "",
|
|
||||||
"transcoding_hardware_decoding_setting_description": "",
|
|
||||||
"transcoding_hevc_codec": "",
|
|
||||||
"transcoding_max_b_frames": "",
|
|
||||||
"transcoding_max_b_frames_description": "",
|
|
||||||
"transcoding_max_bitrate": "",
|
|
||||||
"transcoding_max_bitrate_description": "",
|
|
||||||
"transcoding_max_keyframe_interval": "",
|
|
||||||
"transcoding_max_keyframe_interval_description": "",
|
|
||||||
"transcoding_optimal_description": "",
|
|
||||||
"transcoding_preferred_hardware_device": "",
|
|
||||||
"transcoding_preferred_hardware_device_description": "",
|
|
||||||
"transcoding_preset_preset": "",
|
|
||||||
"transcoding_preset_preset_description": "",
|
|
||||||
"transcoding_reference_frames": "",
|
|
||||||
"transcoding_reference_frames_description": "",
|
|
||||||
"transcoding_required_description": "",
|
|
||||||
"transcoding_settings": "",
|
|
||||||
"transcoding_settings_description": "",
|
|
||||||
"transcoding_target_resolution": "",
|
|
||||||
"transcoding_target_resolution_description": "",
|
|
||||||
"transcoding_temporal_aq": "",
|
|
||||||
"transcoding_temporal_aq_description": "",
|
|
||||||
"transcoding_threads": "",
|
|
||||||
"transcoding_threads_description": "",
|
|
||||||
"transcoding_tone_mapping": "",
|
|
||||||
"transcoding_tone_mapping_description": "",
|
|
||||||
"transcoding_transcode_policy": "",
|
|
||||||
"transcoding_transcode_policy_description": "",
|
|
||||||
"transcoding_two_pass_encoding": "",
|
|
||||||
"transcoding_two_pass_encoding_setting_description": "",
|
|
||||||
"transcoding_video_codec": "",
|
|
||||||
"transcoding_video_codec_description": "",
|
|
||||||
"trash_enabled_description": "",
|
|
||||||
"trash_number_of_days": "",
|
|
||||||
"trash_number_of_days_description": "",
|
|
||||||
"trash_settings": "",
|
|
||||||
"trash_settings_description": "",
|
|
||||||
"untracked_files": "",
|
|
||||||
"untracked_files_description": "",
|
|
||||||
"user_delete_delay_settings": "",
|
|
||||||
"user_delete_delay_settings_description": "",
|
|
||||||
"user_management": "",
|
|
||||||
"user_password_has_been_reset": "",
|
|
||||||
"user_password_reset_description": "",
|
|
||||||
"user_settings": "",
|
|
||||||
"user_settings_description": "",
|
|
||||||
"user_successfully_removed": "",
|
|
||||||
"version_check_enabled_description": "",
|
|
||||||
"version_check_settings": "",
|
|
||||||
"version_check_settings_description": "",
|
|
||||||
"video_conversion_job": "",
|
|
||||||
"video_conversion_job_description": ""
|
|
||||||
},
|
|
||||||
"admin_email": "",
|
|
||||||
"admin_password": "",
|
|
||||||
"administration": "",
|
|
||||||
"advanced": "",
|
|
||||||
"album_added": "",
|
|
||||||
"album_added_notification_setting_description": "",
|
|
||||||
"album_cover_updated": "",
|
|
||||||
"album_info_updated": "",
|
|
||||||
"album_name": "",
|
|
||||||
"album_options": "",
|
|
||||||
"album_updated": "",
|
|
||||||
"album_updated_setting_description": "",
|
|
||||||
"albums": "",
|
|
||||||
"albums_count": "",
|
|
||||||
"all": "",
|
|
||||||
"all_people": "",
|
|
||||||
"allow_dark_mode": "",
|
|
||||||
"allow_edits": "",
|
|
||||||
"api_key": "",
|
|
||||||
"api_keys": "",
|
|
||||||
"app_settings": "",
|
|
||||||
"appears_in": "",
|
|
||||||
"archive": "",
|
|
||||||
"archive_or_unarchive_photo": "",
|
|
||||||
"asset_offline": "",
|
|
||||||
"assets": "",
|
|
||||||
"authorized_devices": "",
|
|
||||||
"back": "",
|
|
||||||
"backward": "",
|
|
||||||
"blurred_background": "",
|
|
||||||
"camera": "",
|
|
||||||
"camera_brand": "",
|
|
||||||
"camera_model": "",
|
|
||||||
"cancel": "",
|
|
||||||
"cancel_search": "",
|
|
||||||
"cannot_merge_people": "",
|
|
||||||
"cannot_update_the_description": "",
|
|
||||||
"change_date": "",
|
|
||||||
"change_expiration_time": "",
|
|
||||||
"change_location": "",
|
|
||||||
"change_name": "",
|
|
||||||
"change_name_successfully": "",
|
|
||||||
"change_password": "",
|
|
||||||
"change_your_password": "",
|
|
||||||
"changed_visibility_successfully": "",
|
|
||||||
"check_all": "",
|
|
||||||
"check_logs": "",
|
|
||||||
"choose_matching_people_to_merge": "",
|
|
||||||
"city": "",
|
|
||||||
"clear": "",
|
|
||||||
"clear_all": "",
|
|
||||||
"clear_message": "",
|
|
||||||
"clear_value": "",
|
|
||||||
"close": "",
|
|
||||||
"collapse_all": "",
|
|
||||||
"color_theme": "",
|
|
||||||
"comment_options": "",
|
|
||||||
"comments_are_disabled": "",
|
|
||||||
"confirm": "",
|
|
||||||
"confirm_admin_password": "",
|
|
||||||
"confirm_delete_shared_link": "",
|
|
||||||
"confirm_password": "",
|
|
||||||
"contain": "",
|
|
||||||
"context": "",
|
|
||||||
"continue": "",
|
|
||||||
"copied_image_to_clipboard": "",
|
|
||||||
"copied_to_clipboard": "",
|
|
||||||
"copy_error": "",
|
|
||||||
"copy_file_path": "",
|
|
||||||
"copy_image": "",
|
|
||||||
"copy_link": "",
|
|
||||||
"copy_link_to_clipboard": "",
|
|
||||||
"copy_password": "",
|
|
||||||
"copy_to_clipboard": "",
|
|
||||||
"country": "",
|
|
||||||
"cover": "",
|
|
||||||
"covers": "",
|
|
||||||
"create": "",
|
|
||||||
"create_album": "",
|
|
||||||
"create_library": "",
|
|
||||||
"create_link": "",
|
|
||||||
"create_link_to_share": "",
|
|
||||||
"create_new_person": "",
|
|
||||||
"create_new_user": "",
|
|
||||||
"create_user": "",
|
|
||||||
"created": "",
|
|
||||||
"current_device": "",
|
|
||||||
"custom_locale": "",
|
|
||||||
"custom_locale_description": "",
|
|
||||||
"dark": "",
|
|
||||||
"date_after": "",
|
|
||||||
"date_and_time": "",
|
|
||||||
"date_before": "",
|
|
||||||
"date_range": "",
|
|
||||||
"day": "",
|
|
||||||
"default_locale": "",
|
|
||||||
"default_locale_description": "",
|
|
||||||
"delete": "",
|
|
||||||
"delete_album": "",
|
|
||||||
"delete_api_key_prompt": "",
|
|
||||||
"delete_key": "",
|
|
||||||
"delete_library": "",
|
|
||||||
"delete_link": "",
|
|
||||||
"delete_shared_link": "",
|
|
||||||
"delete_user": "",
|
|
||||||
"deleted_shared_link": "",
|
|
||||||
"description": "",
|
|
||||||
"details": "",
|
|
||||||
"direction": "",
|
|
||||||
"disabled": "",
|
|
||||||
"disallow_edits": "",
|
|
||||||
"discover": "",
|
|
||||||
"dismiss_all_errors": "",
|
|
||||||
"dismiss_error": "",
|
|
||||||
"display_options": "",
|
|
||||||
"display_order": "",
|
|
||||||
"display_original_photos": "",
|
|
||||||
"display_original_photos_setting_description": "",
|
|
||||||
"done": "",
|
|
||||||
"download": "",
|
|
||||||
"downloading": "",
|
|
||||||
"duration": "",
|
|
||||||
"edit_album": "",
|
|
||||||
"edit_avatar": "",
|
|
||||||
"edit_date": "",
|
|
||||||
"edit_date_and_time": "",
|
|
||||||
"edit_exclusion_pattern": "",
|
|
||||||
"edit_faces": "",
|
|
||||||
"edit_import_path": "",
|
|
||||||
"edit_import_paths": "",
|
|
||||||
"edit_key": "",
|
|
||||||
"edit_link": "",
|
|
||||||
"edit_location": "",
|
|
||||||
"edit_name": "",
|
|
||||||
"edit_people": "",
|
|
||||||
"edit_title": "",
|
|
||||||
"edit_user": "",
|
|
||||||
"edited": "",
|
|
||||||
"editor": "",
|
|
||||||
"email": "",
|
|
||||||
"empty_trash": "",
|
|
||||||
"enable": "",
|
|
||||||
"enabled": "",
|
|
||||||
"end_date": "",
|
|
||||||
"error": "",
|
|
||||||
"error_loading_image": "",
|
|
||||||
"errors": {
|
|
||||||
"cleared_jobs": "",
|
|
||||||
"exclusion_pattern_already_exists": "",
|
|
||||||
"failed_job_command": "",
|
|
||||||
"import_path_already_exists": "",
|
|
||||||
"paths_validation_failed": "",
|
|
||||||
"quota_higher_than_disk_size": "",
|
|
||||||
"repair_unable_to_check_items": "",
|
|
||||||
"unable_to_add_album_users": "",
|
|
||||||
"unable_to_add_comment": "",
|
|
||||||
"unable_to_add_exclusion_pattern": "",
|
|
||||||
"unable_to_add_import_path": "",
|
|
||||||
"unable_to_add_partners": "",
|
|
||||||
"unable_to_change_album_user_role": "",
|
|
||||||
"unable_to_change_date": "",
|
|
||||||
"unable_to_change_location": "",
|
|
||||||
"unable_to_change_password": "",
|
|
||||||
"unable_to_copy_to_clipboard": "",
|
|
||||||
"unable_to_create_api_key": "",
|
|
||||||
"unable_to_create_library": "",
|
|
||||||
"unable_to_create_user": "",
|
|
||||||
"unable_to_delete_album": "",
|
|
||||||
"unable_to_delete_asset": "",
|
|
||||||
"unable_to_delete_exclusion_pattern": "",
|
|
||||||
"unable_to_delete_import_path": "",
|
|
||||||
"unable_to_delete_shared_link": "",
|
|
||||||
"unable_to_delete_user": "",
|
|
||||||
"unable_to_edit_exclusion_pattern": "",
|
|
||||||
"unable_to_edit_import_path": "",
|
|
||||||
"unable_to_empty_trash": "",
|
|
||||||
"unable_to_enter_fullscreen": "",
|
|
||||||
"unable_to_exit_fullscreen": "",
|
|
||||||
"unable_to_hide_person": "",
|
|
||||||
"unable_to_link_oauth_account": "",
|
|
||||||
"unable_to_load_album": "",
|
|
||||||
"unable_to_load_asset_activity": "",
|
|
||||||
"unable_to_load_items": "",
|
|
||||||
"unable_to_load_liked_status": "",
|
|
||||||
"unable_to_play_video": "",
|
|
||||||
"unable_to_refresh_user": "",
|
|
||||||
"unable_to_remove_album_users": "",
|
|
||||||
"unable_to_remove_api_key": "",
|
|
||||||
"unable_to_remove_deleted_assets": "",
|
|
||||||
"unable_to_remove_library": "",
|
|
||||||
"unable_to_remove_partner": "",
|
|
||||||
"unable_to_remove_reaction": "",
|
|
||||||
"unable_to_repair_items": "",
|
|
||||||
"unable_to_reset_password": "",
|
|
||||||
"unable_to_resolve_duplicate": "",
|
|
||||||
"unable_to_restore_assets": "",
|
|
||||||
"unable_to_restore_trash": "",
|
|
||||||
"unable_to_restore_user": "",
|
|
||||||
"unable_to_save_album": "",
|
|
||||||
"unable_to_save_api_key": "",
|
|
||||||
"unable_to_save_name": "",
|
|
||||||
"unable_to_save_profile": "",
|
|
||||||
"unable_to_save_settings": "",
|
|
||||||
"unable_to_scan_libraries": "",
|
|
||||||
"unable_to_scan_library": "",
|
|
||||||
"unable_to_set_profile_picture": "",
|
|
||||||
"unable_to_submit_job": "",
|
|
||||||
"unable_to_trash_asset": "",
|
|
||||||
"unable_to_unlink_account": "",
|
|
||||||
"unable_to_update_library": "",
|
|
||||||
"unable_to_update_location": "",
|
|
||||||
"unable_to_update_settings": "",
|
|
||||||
"unable_to_update_timeline_display_status": "",
|
|
||||||
"unable_to_update_user": ""
|
|
||||||
},
|
|
||||||
"exit_slideshow": "",
|
|
||||||
"expand_all": "",
|
|
||||||
"expire_after": "",
|
|
||||||
"expired": "",
|
|
||||||
"explore": "",
|
|
||||||
"export": "",
|
|
||||||
"export_as_json": "",
|
|
||||||
"extension": "",
|
|
||||||
"external": "",
|
|
||||||
"external_libraries": "",
|
|
||||||
"favorite": "",
|
|
||||||
"favorite_or_unfavorite_photo": "",
|
|
||||||
"favorites": "",
|
|
||||||
"feature_photo_updated": "",
|
|
||||||
"file_name": "",
|
|
||||||
"file_name_or_extension": "",
|
|
||||||
"filename": "",
|
|
||||||
"filetype": "",
|
|
||||||
"filter_people": "",
|
|
||||||
"find_them_fast": "",
|
|
||||||
"fix_incorrect_match": "",
|
|
||||||
"forward": "",
|
|
||||||
"general": "",
|
|
||||||
"get_help": "",
|
|
||||||
"getting_started": "",
|
|
||||||
"go_back": "",
|
|
||||||
"go_to_search": "",
|
|
||||||
"group_albums_by": "",
|
|
||||||
"has_quota": "",
|
|
||||||
"hide_gallery": "",
|
|
||||||
"hide_password": "",
|
|
||||||
"hide_person": "",
|
|
||||||
"host": "",
|
|
||||||
"hour": "",
|
|
||||||
"image": "",
|
|
||||||
"immich_logo": "",
|
|
||||||
"import_from_json": "",
|
|
||||||
"import_path": "",
|
|
||||||
"in_archive": "",
|
|
||||||
"include_archived": "",
|
|
||||||
"include_shared_albums": "",
|
|
||||||
"include_shared_partner_assets": "",
|
|
||||||
"individual_share": "",
|
|
||||||
"info": "",
|
|
||||||
"interval": {
|
|
||||||
"day_at_onepm": "",
|
|
||||||
"hours": "",
|
|
||||||
"night_at_midnight": "",
|
|
||||||
"night_at_twoam": ""
|
|
||||||
},
|
|
||||||
"invite_people": "",
|
|
||||||
"invite_to_album": "",
|
|
||||||
"jobs": "",
|
|
||||||
"keep": "",
|
|
||||||
"keyboard_shortcuts": "",
|
|
||||||
"language": "",
|
|
||||||
"language_setting_description": "",
|
|
||||||
"last_seen": "",
|
|
||||||
"leave": "",
|
|
||||||
"let_others_respond": "",
|
|
||||||
"level": "",
|
|
||||||
"library": "",
|
|
||||||
"library_options": "",
|
|
||||||
"light": "",
|
|
||||||
"link_options": "",
|
|
||||||
"link_to_oauth": "",
|
|
||||||
"linked_oauth_account": "",
|
|
||||||
"list": "",
|
|
||||||
"loading": "",
|
|
||||||
"loading_search_results_failed": "",
|
|
||||||
"log_out": "",
|
|
||||||
"log_out_all_devices": "",
|
|
||||||
"login_has_been_disabled": "",
|
|
||||||
"look": "",
|
|
||||||
"loop_videos": "",
|
|
||||||
"loop_videos_description": "",
|
|
||||||
"make": "",
|
|
||||||
"manage_shared_links": "",
|
|
||||||
"manage_sharing_with_partners": "",
|
|
||||||
"manage_the_app_settings": "",
|
|
||||||
"manage_your_account": "",
|
|
||||||
"manage_your_api_keys": "",
|
|
||||||
"manage_your_devices": "",
|
|
||||||
"manage_your_oauth_connection": "",
|
|
||||||
"map": "",
|
|
||||||
"map_marker_with_image": "",
|
|
||||||
"map_settings": "",
|
|
||||||
"matches": "",
|
|
||||||
"media_type": "",
|
|
||||||
"memories": "",
|
|
||||||
"memories_setting_description": "",
|
|
||||||
"menu": "",
|
|
||||||
"merge": "",
|
|
||||||
"merge_people": "",
|
|
||||||
"merge_people_successfully": "",
|
|
||||||
"minimize": "",
|
|
||||||
"minute": "",
|
|
||||||
"missing": "",
|
|
||||||
"model": "",
|
|
||||||
"month": "",
|
|
||||||
"more": "",
|
|
||||||
"moved_to_trash": "",
|
|
||||||
"my_albums": "",
|
|
||||||
"name": "",
|
|
||||||
"name_or_nickname": "",
|
|
||||||
"never": "",
|
|
||||||
"new_api_key": "",
|
|
||||||
"new_password": "",
|
|
||||||
"new_person": "",
|
|
||||||
"new_user_created": "",
|
|
||||||
"newest_first": "",
|
|
||||||
"next": "",
|
|
||||||
"next_memory": "",
|
|
||||||
"no": "",
|
|
||||||
"no_albums_message": "",
|
|
||||||
"no_archived_assets_message": "",
|
|
||||||
"no_assets_message": "",
|
|
||||||
"no_duplicates_found": "",
|
|
||||||
"no_exif_info_available": "",
|
|
||||||
"no_explore_results_message": "",
|
|
||||||
"no_favorites_message": "",
|
|
||||||
"no_libraries_message": "",
|
|
||||||
"no_name": "",
|
|
||||||
"no_places": "",
|
|
||||||
"no_results": "",
|
|
||||||
"no_shared_albums_message": "",
|
|
||||||
"not_in_any_album": "",
|
|
||||||
"note_apply_storage_label_to_previously_uploaded assets": "",
|
|
||||||
"notes": "",
|
|
||||||
"notification_toggle_setting_description": "",
|
|
||||||
"notifications": "",
|
|
||||||
"notifications_setting_description": "",
|
|
||||||
"oauth": "",
|
|
||||||
"offline": "",
|
|
||||||
"offline_paths": "",
|
|
||||||
"offline_paths_description": "",
|
|
||||||
"ok": "",
|
|
||||||
"oldest_first": "",
|
|
||||||
"online": "",
|
|
||||||
"only_favorites": "",
|
|
||||||
"open_the_search_filters": "",
|
|
||||||
"options": "",
|
|
||||||
"organize_your_library": "",
|
|
||||||
"other": "",
|
|
||||||
"other_devices": "",
|
|
||||||
"other_variables": "",
|
|
||||||
"owned": "",
|
|
||||||
"owner": "",
|
|
||||||
"partner_can_access": "",
|
|
||||||
"partner_can_access_assets": "",
|
|
||||||
"partner_can_access_location": "",
|
|
||||||
"partner_sharing": "",
|
|
||||||
"partners": "",
|
|
||||||
"password": "",
|
|
||||||
"password_does_not_match": "",
|
|
||||||
"password_required": "",
|
|
||||||
"password_reset_success": "",
|
|
||||||
"past_durations": {
|
|
||||||
"days": "",
|
|
||||||
"hours": "",
|
|
||||||
"years": ""
|
|
||||||
},
|
|
||||||
"path": "",
|
|
||||||
"pattern": "",
|
|
||||||
"pause": "",
|
|
||||||
"pause_memories": "",
|
|
||||||
"paused": "",
|
|
||||||
"pending": "",
|
|
||||||
"people": "",
|
|
||||||
"people_sidebar_description": "",
|
|
||||||
"permanent_deletion_warning": "",
|
|
||||||
"permanent_deletion_warning_setting_description": "",
|
|
||||||
"permanently_delete": "",
|
|
||||||
"permanently_deleted_asset": "",
|
|
||||||
"photos": "",
|
|
||||||
"photos_count": "",
|
|
||||||
"photos_from_previous_years": "",
|
|
||||||
"pick_a_location": "",
|
|
||||||
"place": "",
|
|
||||||
"places": "",
|
|
||||||
"play": "",
|
|
||||||
"play_memories": "",
|
|
||||||
"play_motion_photo": "",
|
|
||||||
"play_or_pause_video": "",
|
|
||||||
"port": "",
|
|
||||||
"preset": "",
|
|
||||||
"preview": "",
|
|
||||||
"previous": "",
|
|
||||||
"previous_memory": "",
|
|
||||||
"previous_or_next_photo": "",
|
|
||||||
"primary": "",
|
|
||||||
"profile_picture_set": "",
|
|
||||||
"public_share": "",
|
|
||||||
"reaction_options": "",
|
|
||||||
"read_changelog": "",
|
|
||||||
"recent": "",
|
|
||||||
"recent_searches": "",
|
|
||||||
"refresh": "",
|
|
||||||
"refreshed": "",
|
|
||||||
"refreshes_every_file": "",
|
|
||||||
"remove": "",
|
|
||||||
"remove_deleted_assets": "",
|
|
||||||
"remove_from_album": "",
|
|
||||||
"remove_from_favorites": "",
|
|
||||||
"remove_from_shared_link": "",
|
|
||||||
"removed_api_key": "",
|
|
||||||
"rename": "",
|
|
||||||
"repair": "",
|
|
||||||
"repair_no_results_message": "",
|
|
||||||
"replace_with_upload": "",
|
|
||||||
"require_password": "",
|
|
||||||
"require_user_to_change_password_on_first_login": "",
|
|
||||||
"reset": "",
|
|
||||||
"reset_password": "",
|
|
||||||
"reset_people_visibility": "",
|
|
||||||
"restore": "",
|
|
||||||
"restore_all": "",
|
|
||||||
"restore_user": "",
|
|
||||||
"resume": "",
|
|
||||||
"retry_upload": "",
|
|
||||||
"review_duplicates": "",
|
|
||||||
"role": "",
|
|
||||||
"save": "",
|
|
||||||
"saved_api_key": "",
|
|
||||||
"saved_profile": "",
|
|
||||||
"saved_settings": "",
|
|
||||||
"say_something": "",
|
|
||||||
"scan_all_libraries": "",
|
|
||||||
"scan_settings": "",
|
|
||||||
"search": "",
|
|
||||||
"search_albums": "",
|
|
||||||
"search_by_context": "",
|
|
||||||
"search_camera_make": "",
|
|
||||||
"search_camera_model": "",
|
|
||||||
"search_city": "",
|
|
||||||
"search_country": "",
|
|
||||||
"search_for_existing_person": "",
|
|
||||||
"search_people": "",
|
|
||||||
"search_places": "",
|
|
||||||
"search_state": "",
|
|
||||||
"search_timezone": "",
|
|
||||||
"search_type": "",
|
|
||||||
"search_your_photos": "",
|
|
||||||
"searching_locales": "",
|
|
||||||
"second": "",
|
|
||||||
"select_album_cover": "",
|
|
||||||
"select_all": "",
|
|
||||||
"select_avatar_color": "",
|
|
||||||
"select_face": "",
|
|
||||||
"select_featured_photo": "",
|
|
||||||
"select_keep_all": "",
|
|
||||||
"select_library_owner": "",
|
|
||||||
"select_new_face": "",
|
|
||||||
"select_photos": "",
|
|
||||||
"select_trash_all": "",
|
|
||||||
"selected": "",
|
|
||||||
"send_message": "",
|
|
||||||
"send_welcome_email": "",
|
|
||||||
"server_stats": "",
|
|
||||||
"set": "",
|
|
||||||
"set_as_album_cover": "",
|
|
||||||
"set_as_profile_picture": "",
|
|
||||||
"set_date_of_birth": "",
|
|
||||||
"set_profile_picture": "",
|
|
||||||
"set_slideshow_to_fullscreen": "",
|
|
||||||
"settings": "",
|
|
||||||
"settings_saved": "",
|
|
||||||
"share": "",
|
|
||||||
"shared": "",
|
|
||||||
"shared_by": "",
|
|
||||||
"shared_by_you": "",
|
|
||||||
"shared_from_partner": "",
|
|
||||||
"shared_links": "",
|
|
||||||
"shared_with_partner": "",
|
|
||||||
"sharing": "",
|
|
||||||
"sharing_sidebar_description": "",
|
|
||||||
"show_album_options": "",
|
|
||||||
"show_and_hide_people": "",
|
|
||||||
"show_file_location": "",
|
|
||||||
"show_gallery": "",
|
|
||||||
"show_hidden_people": "",
|
|
||||||
"show_in_timeline": "",
|
|
||||||
"show_in_timeline_setting_description": "",
|
|
||||||
"show_keyboard_shortcuts": "",
|
|
||||||
"show_metadata": "",
|
|
||||||
"show_or_hide_info": "",
|
|
||||||
"show_password": "",
|
|
||||||
"show_person_options": "",
|
|
||||||
"show_progress_bar": "",
|
|
||||||
"show_search_options": "",
|
|
||||||
"shuffle": "",
|
|
||||||
"sign_out": "",
|
|
||||||
"sign_up": "",
|
|
||||||
"size": "",
|
|
||||||
"skip_to_content": "",
|
|
||||||
"slideshow": "",
|
|
||||||
"slideshow_settings": "",
|
|
||||||
"sort_albums_by": "",
|
|
||||||
"stack": "",
|
|
||||||
"stack_selected_photos": "",
|
|
||||||
"stacktrace": "",
|
|
||||||
"start": "",
|
|
||||||
"start_date": "",
|
|
||||||
"state": "",
|
|
||||||
"status": "",
|
|
||||||
"stop_motion_photo": "",
|
|
||||||
"stop_photo_sharing": "",
|
|
||||||
"stop_photo_sharing_description": "",
|
|
||||||
"stop_sharing_photos_with_user": "",
|
|
||||||
"storage": "",
|
|
||||||
"storage_label": "",
|
|
||||||
"storage_usage": "",
|
|
||||||
"submit": "",
|
|
||||||
"suggestions": "",
|
|
||||||
"sunrise_on_the_beach": "",
|
|
||||||
"swap_merge_direction": "",
|
|
||||||
"sync": "",
|
|
||||||
"template": "",
|
|
||||||
"theme": "",
|
|
||||||
"theme_selection": "",
|
|
||||||
"theme_selection_description": "",
|
|
||||||
"time_based_memories": "",
|
|
||||||
"timezone": "",
|
|
||||||
"to_archive": "",
|
|
||||||
"to_favorite": "",
|
|
||||||
"toggle_settings": "",
|
|
||||||
"toggle_theme": "",
|
|
||||||
"total_usage": "",
|
|
||||||
"trash": "",
|
|
||||||
"trash_all": "",
|
|
||||||
"trash_no_results_message": "",
|
|
||||||
"trashed_items_will_be_permanently_deleted_after": "",
|
|
||||||
"type": "",
|
|
||||||
"unarchive": "",
|
|
||||||
"unfavorite": "",
|
|
||||||
"unhide_person": "",
|
|
||||||
"unknown": "",
|
|
||||||
"unknown_year": "",
|
|
||||||
"unlimited": "",
|
|
||||||
"unlink_oauth": "",
|
|
||||||
"unlinked_oauth_account": "",
|
|
||||||
"unselect_all": "",
|
|
||||||
"unstack": "",
|
|
||||||
"untracked_files": "",
|
|
||||||
"untracked_files_decription": "",
|
|
||||||
"up_next": "",
|
|
||||||
"updated_password": "",
|
|
||||||
"upload": "",
|
|
||||||
"upload_concurrency": "",
|
|
||||||
"url": "",
|
|
||||||
"usage": "",
|
|
||||||
"user": "",
|
|
||||||
"user_id": "",
|
|
||||||
"user_usage_detail": "",
|
|
||||||
"username": "",
|
|
||||||
"users": "",
|
|
||||||
"utilities": "",
|
|
||||||
"validate": "",
|
|
||||||
"variables": "",
|
|
||||||
"version": "",
|
|
||||||
"video": "",
|
|
||||||
"video_hover_setting": "",
|
|
||||||
"video_hover_setting_description": "",
|
|
||||||
"videos": "",
|
|
||||||
"videos_count": "",
|
|
||||||
"view_all": "",
|
|
||||||
"view_all_users": "",
|
|
||||||
"view_links": "",
|
|
||||||
"view_next_asset": "",
|
|
||||||
"view_previous_asset": "",
|
|
||||||
"waiting": "",
|
|
||||||
"week": "",
|
|
||||||
"welcome_to_immich": "",
|
|
||||||
"year": "",
|
|
||||||
"yes": "",
|
|
||||||
"you_dont_have_any_shared_links": "",
|
|
||||||
"zoom_image": ""
|
|
||||||
}
|
}
|
||||||
|
|||||||
18
i18n/bn.json
18
i18n/bn.json
@@ -1 +1,17 @@
|
|||||||
{}
|
{
|
||||||
|
"about": "সম্পর্কে",
|
||||||
|
"account": "অ্যাকাউন্ট",
|
||||||
|
"account_settings": "অ্যাকাউন্ট সেটিংস",
|
||||||
|
"acknowledge": "স্বীকৃতি",
|
||||||
|
"action": "কার্য",
|
||||||
|
"action_common_update": "আপডেট",
|
||||||
|
"actions": "কর্ম",
|
||||||
|
"active": "সচল",
|
||||||
|
"activity": "কার্যকলাপ",
|
||||||
|
"add": "যোগ করুন",
|
||||||
|
"add_a_description": "একটি বিবরণ যোগ করুন",
|
||||||
|
"add_a_location": "একটি অবস্থান যোগ করুন",
|
||||||
|
"add_a_name": "একটি নাম যোগ করুন",
|
||||||
|
"add_a_title": "একটি শিরোনাম যোগ করুন",
|
||||||
|
"add_endpoint": "এন্ডপয়েন্ট যোগ করুন"
|
||||||
|
}
|
||||||
|
|||||||
341
i18n/ca.json
341
i18n/ca.json
File diff suppressed because it is too large
Load Diff
302
i18n/cs.json
302
i18n/cs.json
@@ -22,6 +22,7 @@
|
|||||||
"add_partner": "Přidat partnera",
|
"add_partner": "Přidat partnera",
|
||||||
"add_path": "Přidat cestu",
|
"add_path": "Přidat cestu",
|
||||||
"add_photos": "Přidat fotky",
|
"add_photos": "Přidat fotky",
|
||||||
|
"add_tag": "Přidat značku",
|
||||||
"add_to": "Přidat do…",
|
"add_to": "Přidat do…",
|
||||||
"add_to_album": "Přidat do alba",
|
"add_to_album": "Přidat do alba",
|
||||||
"add_to_album_bottom_sheet_added": "Přidáno do {album}",
|
"add_to_album_bottom_sheet_added": "Přidáno do {album}",
|
||||||
@@ -43,9 +44,7 @@
|
|||||||
"backup_database_enable_description": "Povolit výpisy z databáze",
|
"backup_database_enable_description": "Povolit výpisy z databáze",
|
||||||
"backup_keep_last_amount": "Počet předchozích výpisů, které se mají ponechat",
|
"backup_keep_last_amount": "Počet předchozích výpisů, které se mají ponechat",
|
||||||
"backup_settings": "Nastavení výpisu databáze",
|
"backup_settings": "Nastavení výpisu databáze",
|
||||||
"backup_settings_description": "Správa nastavení výpisu databáze. Poznámka: Tyto úlohy nejsou monitorovány a nebudete upozorněni na jejich selhání.",
|
"backup_settings_description": "Správa nastavení výpisu databáze.",
|
||||||
"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}?",
|
||||||
@@ -53,6 +52,7 @@
|
|||||||
"confirm_email_below": "Pro potvrzení zadejte níže \"{email}\"",
|
"confirm_email_below": "Pro potvrzení zadejte níže \"{email}\"",
|
||||||
"confirm_reprocess_all_faces": "Opravdu chcete znovu zpracovat všechny obličeje? Tím se vymažou i pojmenované osoby.",
|
"confirm_reprocess_all_faces": "Opravdu chcete znovu zpracovat všechny obličeje? Tím se vymažou i pojmenované osoby.",
|
||||||
"confirm_user_password_reset": "Opravdu chcete obnovit heslo uživatele {user}?",
|
"confirm_user_password_reset": "Opravdu chcete obnovit heslo uživatele {user}?",
|
||||||
|
"confirm_user_pin_code_reset": "Opravdu chcete resetovat PIN kód uživatele {user}?",
|
||||||
"create_job": "Vytvořit úlohu",
|
"create_job": "Vytvořit úlohu",
|
||||||
"cron_expression": "Výraz cron",
|
"cron_expression": "Výraz cron",
|
||||||
"cron_expression_description": "Nastavte interval prohledávání pomocí cron formátu. Další informace naleznete např. v <link>Crontab Guru</link>",
|
"cron_expression_description": "Nastavte interval prohledávání pomocí cron formátu. Další informace naleznete např. v <link>Crontab Guru</link>",
|
||||||
@@ -60,14 +60,12 @@
|
|||||||
"disable_login": "Zakázat přihlášení",
|
"disable_login": "Zakázat přihlášení",
|
||||||
"duplicate_detection_job_description": "Spuštění strojového učení na položkách za účelem detekce podobných obrázků. Spoléhá na Chytré vyhledávání",
|
"duplicate_detection_job_description": "Spuštění strojového učení na položkách za účelem detekce podobných obrázků. Spoléhá na Chytré vyhledávání",
|
||||||
"exclusion_pattern_description": "Vzory vyloučení umožňují při prohledávání knihovny ignorovat soubory a složky. To je užitečné, pokud máte složky obsahující soubory, které nechcete importovat, například RAW soubory.",
|
"exclusion_pattern_description": "Vzory vyloučení umožňují při prohledávání knihovny ignorovat soubory a složky. To je užitečné, pokud máte složky obsahující soubory, které nechcete importovat, například RAW soubory.",
|
||||||
"external_library_created_at": "Externí knihovna (vytvořena {date})",
|
|
||||||
"external_library_management": "Správa externích knihoven",
|
"external_library_management": "Správa externích knihoven",
|
||||||
"face_detection": "Detekce obličejů",
|
"face_detection": "Detekce obličejů",
|
||||||
"face_detection_description": "Detekce obličejů v obrázcích pomocí strojového učení. U videí se bere v úvahu pouze miniatura. „Obnovit“ znovu zpracuje všechny položky. „Resetovat“ navíc vymaže všechna aktuální data obličejů. „Chybějící“ zařadí do fronty položky, které ještě nebyly zpracovány. Zjištěné obličeje budou po dokončení funkce Rozpoznávání obličejů zařazeny do fronty a seskupeny do stávajících nebo nových osob.",
|
"face_detection_description": "Detekce obličejů v obrázcích pomocí strojového učení. U videí se bere v úvahu pouze miniatura. „Obnovit“ znovu zpracuje všechny položky. „Resetovat“ navíc vymaže všechna aktuální data obličejů. „Chybějící“ zařadí do fronty položky, které ještě nebyly zpracovány. Zjištěné obličeje budou po dokončení funkce Rozpoznávání obličejů zařazeny do fronty a seskupeny do stávajících nebo nových osob.",
|
||||||
"facial_recognition_job_description": "Seskupí nalezené obličeje do osob. Tento krok se spustí po dokončení detekce obličejů. „Resetovat“ znovu seskupí všechny obličeje. „Chybějící“ zpracuje obličeje, které nemají přiřazenou osobu.",
|
"facial_recognition_job_description": "Seskupí nalezené obličeje do osob. Tento krok se spustí po dokončení detekce obličejů. „Resetovat“ znovu seskupí všechny obličeje. „Chybějící“ zpracuje obličeje, které nemají přiřazenou osobu.",
|
||||||
"failed_job_command": "Příkaz {command} se nezdařil pro úlohu: {job}",
|
"failed_job_command": "Příkaz {command} se nezdařil pro úlohu: {job}",
|
||||||
"force_delete_user_warning": "UPOZORNĚNÍ: Tímto okamžitě odstraníte uživatele a všechny jeho položky. Tento krok nelze vrátit zpět a soubory nelze obnovit.",
|
"force_delete_user_warning": "UPOZORNĚNÍ: Tímto okamžitě odstraníte uživatele a všechny jeho položky. Tento krok nelze vrátit zpět a soubory nelze obnovit.",
|
||||||
"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_description": "Obrázek v plné velikosti s odstraněnými metadaty, použito při přiblížení",
|
||||||
@@ -172,7 +170,7 @@
|
|||||||
"note_apply_storage_label_previous_assets": "Upozornění: Pro uplatnění Štítku úložiště na dříve nahrané položky spusťte",
|
"note_apply_storage_label_previous_assets": "Upozornění: Pro uplatnění Štítku úložiště na dříve nahrané položky spusťte",
|
||||||
"note_cannot_be_changed_later": "UPOZORNĚNÍ: Toto nelze později změnit!",
|
"note_cannot_be_changed_later": "UPOZORNĚNÍ: Toto nelze později změnit!",
|
||||||
"notification_email_from_address": "Adresa Od",
|
"notification_email_from_address": "Adresa Od",
|
||||||
"notification_email_from_address_description": "E-mailová adresa odesílatele, např.: „Immich Photo Server <noreply@example.com>“",
|
"notification_email_from_address_description": "E-mailová adresa odesílatele, např.: „Immich Photo Server <noreply@example.com>“. Ujistěte se, že používáte adresu, ze které smíte odesílat e-maily.",
|
||||||
"notification_email_host_description": "Adresa e-mailového serveru (např. smtp.immich.app)",
|
"notification_email_host_description": "Adresa e-mailového serveru (např. smtp.immich.app)",
|
||||||
"notification_email_ignore_certificate_errors": "Ignorovat chyby certifikátů",
|
"notification_email_ignore_certificate_errors": "Ignorovat chyby certifikátů",
|
||||||
"notification_email_ignore_certificate_errors_description": "Ignorovat chyby ověření certifikátu TLS (nedoporučuje se)",
|
"notification_email_ignore_certificate_errors_description": "Ignorovat chyby ověření certifikátu TLS (nedoporučuje se)",
|
||||||
@@ -192,28 +190,22 @@
|
|||||||
"oauth_auto_register": "Automatická registrace",
|
"oauth_auto_register": "Automatická registrace",
|
||||||
"oauth_auto_register_description": "Automaticky registrovat nové uživatele po přihlášení pomocí OAuth",
|
"oauth_auto_register_description": "Automaticky registrovat nové uživatele po přihlášení pomocí OAuth",
|
||||||
"oauth_button_text": "Text tlačítka",
|
"oauth_button_text": "Text tlačítka",
|
||||||
"oauth_client_id": "Client ID",
|
"oauth_client_secret_description": "Vyžaduje se, pokud poskytovatel OAuth nepodporuje PKCE (Proof Key for Code Exchange)",
|
||||||
"oauth_client_secret": "Client Secret",
|
|
||||||
"oauth_enable_description": "Přihlásit pomocí OAuth",
|
"oauth_enable_description": "Přihlásit pomocí OAuth",
|
||||||
"oauth_issuer_url": "URL vydavatele",
|
|
||||||
"oauth_mobile_redirect_uri": "Mobilní přesměrování URI",
|
"oauth_mobile_redirect_uri": "Mobilní přesměrování URI",
|
||||||
"oauth_mobile_redirect_uri_override": "Přepsat mobilní přesměrování URI",
|
"oauth_mobile_redirect_uri_override": "Přepsat mobilní přesměrování URI",
|
||||||
"oauth_mobile_redirect_uri_override_description": "Povolit, pokud poskytovatel OAuth nepovoluje mobilní URI, například '{callback}'",
|
"oauth_mobile_redirect_uri_override_description": "Povolit, pokud poskytovatel OAuth nepovoluje mobilní URI, například ''{callback}''",
|
||||||
"oauth_profile_signing_algorithm": "Algoritmus podepisování profilu",
|
|
||||||
"oauth_profile_signing_algorithm_description": "Algoritmus použitý k podepsání profilu uživatele.",
|
|
||||||
"oauth_scope": "Rozsah",
|
|
||||||
"oauth_settings": "OAuth",
|
"oauth_settings": "OAuth",
|
||||||
"oauth_settings_description": "Správa nastavení OAuth přihlášení",
|
"oauth_settings_description": "Správa nastavení OAuth přihlášení",
|
||||||
"oauth_settings_more_details": "Další podrobnosti o této funkci naleznete v <link>dokumentaci</link>.",
|
"oauth_settings_more_details": "Další podrobnosti o této funkci naleznete v <link>dokumentaci</link>.",
|
||||||
"oauth_signing_algorithm": "Algoritmus podepisování",
|
|
||||||
"oauth_storage_label_claim": "Deklarace štítku úložiště",
|
"oauth_storage_label_claim": "Deklarace štítku úložiště",
|
||||||
"oauth_storage_label_claim_description": "Automaticky nastavit štítek úložiště uživatele na hodnotu této deklarace.",
|
"oauth_storage_label_claim_description": "Automaticky nastavit štítek úložiště uživatele na hodnotu této deklarace.",
|
||||||
"oauth_storage_quota_claim": "Deklarace kvóty úložiště",
|
"oauth_storage_quota_claim": "Deklarace kvóty úložiště",
|
||||||
"oauth_storage_quota_claim_description": "Automaticky nastavit kvótu úložiště uživatele na hodnotu této deklarace.",
|
"oauth_storage_quota_claim_description": "Automaticky nastavit kvótu úložiště uživatele na hodnotu této deklarace.",
|
||||||
"oauth_storage_quota_default": "Výchozí kvóta úložiště (GiB)",
|
"oauth_storage_quota_default": "Výchozí kvóta úložiště (GiB)",
|
||||||
"oauth_storage_quota_default_description": "Kvóta v GiB, která se použije, pokud není poskytnuta žádná deklarace (pro neomezenou kvótu zadejte 0).",
|
"oauth_storage_quota_default_description": "Kvóta v GiB, která se použije, pokud není poskytnuta žádná deklarace (pro neomezenou kvótu zadejte 0).",
|
||||||
"offline_paths": "Cesty offline",
|
"oauth_timeout": "Časový limit požadavku",
|
||||||
"offline_paths_description": "Tyto výsledky mohou být způsobeny ručním odstraněním souborů, které nejsou součástí externí knihovny.",
|
"oauth_timeout_description": "Časový limit pro požadavky v milisekundách",
|
||||||
"password_enable_description": "Přihlášení pomocí e-mailu a hesla",
|
"password_enable_description": "Přihlášení pomocí e-mailu a hesla",
|
||||||
"password_settings": "Přihlášení heslem",
|
"password_settings": "Přihlášení heslem",
|
||||||
"password_settings_description": "Správa nastavení přihlašování pomocí hesla",
|
"password_settings_description": "Správa nastavení přihlašování pomocí hesla",
|
||||||
@@ -223,9 +215,6 @@
|
|||||||
"refreshing_all_libraries": "Obnovení všech knihoven",
|
"refreshing_all_libraries": "Obnovení všech knihoven",
|
||||||
"registration": "Registrace správce",
|
"registration": "Registrace správce",
|
||||||
"registration_description": "Vzhledem k tomu, že jste prvním uživatelem v systému, budete přiřazen jako správce a budete zodpovědný za úkoly správy a další uživatelé budou vytvořeni vámi.",
|
"registration_description": "Vzhledem k tomu, že jste prvním uživatelem v systému, budete přiřazen jako správce a budete zodpovědný za úkoly správy a další uživatelé budou vytvořeni vámi.",
|
||||||
"repair_all": "Opravit vše",
|
|
||||||
"repair_matched_items": "Shoda {count, plural, one {# položky} other {# položek}}",
|
|
||||||
"repaired_items": "{count, plural, one {Opravena # položka} few {Opraveny # položky} other {Opraveno # položek}}",
|
|
||||||
"require_password_change_on_login": "Požadovat, aby si uživatel při prvním přihlášení změnil heslo",
|
"require_password_change_on_login": "Požadovat, aby si uživatel při prvním přihlášení změnil heslo",
|
||||||
"reset_settings_to_default": "Obnovení výchozího nastavení",
|
"reset_settings_to_default": "Obnovení výchozího nastavení",
|
||||||
"reset_settings_to_recent_saved": "Obnovit poslední uložené nastavení",
|
"reset_settings_to_recent_saved": "Obnovit poslední uložené nastavení",
|
||||||
@@ -266,16 +255,14 @@
|
|||||||
"template_email_invite_album": "Šablona pozvánky do alba",
|
"template_email_invite_album": "Šablona pozvánky do alba",
|
||||||
"template_email_preview": "Náhled",
|
"template_email_preview": "Náhled",
|
||||||
"template_email_settings": "Šablony e-mailů",
|
"template_email_settings": "Šablony e-mailů",
|
||||||
"template_email_settings_description": "Správa vlastních šablon e-mailových oznámení",
|
|
||||||
"template_email_update_album": "Šablona aktualizace alba",
|
"template_email_update_album": "Šablona aktualizace alba",
|
||||||
"template_email_welcome": "Šablona uvítacího e-mailu",
|
"template_email_welcome": "Šablona uvítacího e-mailu",
|
||||||
"template_settings": "Šablony oznámení",
|
"template_settings": "Šablony oznámení",
|
||||||
"template_settings_description": "Správa vlastních šablon oznámení.",
|
"template_settings_description": "Správa vlastních šablon oznámení",
|
||||||
"theme_custom_css_settings": "Vlastní CSS",
|
"theme_custom_css_settings": "Vlastní CSS",
|
||||||
"theme_custom_css_settings_description": "Kaskádové styly umožňují přizpůsobit design aplikace Immich.",
|
"theme_custom_css_settings_description": "Kaskádové styly umožňují přizpůsobit design aplikace Immich.",
|
||||||
"theme_settings": "Motivy",
|
"theme_settings": "Motivy",
|
||||||
"theme_settings_description": "Správa přizpůsobení webového rozhraní Immich",
|
"theme_settings_description": "Správa přizpůsobení webového rozhraní Immich",
|
||||||
"these_files_matched_by_checksum": "Tyto soubory jsou porovnávány podle jejich kontrolních součtů",
|
|
||||||
"thumbnail_generation_job": "Generování miniatur",
|
"thumbnail_generation_job": "Generování miniatur",
|
||||||
"thumbnail_generation_job_description": "Generování velkých, malých a rozmazaných miniatur pro každý obrázek a miniatur pro každou osobu",
|
"thumbnail_generation_job_description": "Generování velkých, malých a rozmazaných miniatur pro každý obrázek a miniatur pro každou osobu",
|
||||||
"transcoding_acceleration_api": "API pro akceleraci",
|
"transcoding_acceleration_api": "API pro akceleraci",
|
||||||
@@ -303,10 +290,9 @@
|
|||||||
"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",
|
||||||
"transcoding_hardware_acceleration_description": "Experimentální; mnohem rychlejší, ale při stejném datovém toku bude mít nižší kvalitu",
|
"transcoding_hardware_acceleration_description": "Experimentální: rychlejší kódování, ale při stejném datovém toku může mít nižší kvalitu",
|
||||||
"transcoding_hardware_decoding": "Hardwarové dekódování",
|
"transcoding_hardware_decoding": "Hardwarové dekódování",
|
||||||
"transcoding_hardware_decoding_setting_description": "Povoluje kompletní akceleraci namísto akcelerace pouze kódování. Nemusí fungovat u všech videí.",
|
"transcoding_hardware_decoding_setting_description": "Povoluje kompletní akceleraci namísto akcelerace pouze kódování. Nemusí fungovat u všech videí.",
|
||||||
"transcoding_hevc_codec": "Kodek HEVC",
|
|
||||||
"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",
|
||||||
@@ -344,14 +330,13 @@
|
|||||||
"trash_number_of_days_description": "Počet dní, po které je třeba položku ponechat v koši, než bude trvale odstraněna",
|
"trash_number_of_days_description": "Počet dní, po které je třeba položku ponechat v koši, než bude trvale odstraněna",
|
||||||
"trash_settings": "Koš",
|
"trash_settings": "Koš",
|
||||||
"trash_settings_description": "Správa nastavení koše",
|
"trash_settings_description": "Správa nastavení koše",
|
||||||
"untracked_files": "Neznámé soubory",
|
|
||||||
"untracked_files_description": "Tyto soubory nejsou aplikaci známy. Mohou být výsledkem neúspěšných přesunů, přerušeného nahrávání nebo mohou zůstat pozadu kvůli chybě",
|
|
||||||
"user_cleanup_job": "Promazání uživatelů",
|
"user_cleanup_job": "Promazání uživatelů",
|
||||||
"user_delete_delay": "Účet a položky uživatele <b>{user}</b> budou trvale smazány za {delay, plural, one {# den} few {# dny} other {# dní}}.",
|
"user_delete_delay": "Účet a položky uživatele <b>{user}</b> budou trvale smazány za {delay, plural, one {# den} few {# dny} other {# dní}}.",
|
||||||
"user_delete_delay_settings": "Odložení odstranění",
|
"user_delete_delay_settings": "Odložení odstranění",
|
||||||
"user_delete_delay_settings_description": "Počet dní po odstranění, po kterých bude odstraněn účet a položky uživatele. Úloha odstraňování uživatelů se spouští o půlnoci a kontroluje uživatele, kteří jsou připraveni k odstranění. Změny tohoto nastavení se vyhodnotí při dalším spuštění.",
|
"user_delete_delay_settings_description": "Počet dní po odstranění, po kterých bude odstraněn účet a položky uživatele. Úloha odstraňování uživatelů se spouští o půlnoci a kontroluje uživatele, kteří jsou připraveni k odstranění. Změny tohoto nastavení se vyhodnotí při dalším spuštění.",
|
||||||
"user_delete_immediately": "Účet a položky uživatele <b>{user}</b> budou zařazeny do fronty k trvalému smazání <b>okamžitě</b>.",
|
"user_delete_immediately": "Účet a položky uživatele <b>{user}</b> budou zařazeny do fronty k trvalému smazání <b>okamžitě</b>.",
|
||||||
"user_delete_immediately_checkbox": "Uživatele a položky zařadit do fronty k okamžitému smazání",
|
"user_delete_immediately_checkbox": "Uživatele a položky zařadit do fronty k okamžitému smazání",
|
||||||
|
"user_details": "Podrobnosti o uživateli",
|
||||||
"user_management": "Správa uživatelů",
|
"user_management": "Správa uživatelů",
|
||||||
"user_password_has_been_reset": "Heslo uživatele bylo obnoveno:",
|
"user_password_has_been_reset": "Heslo uživatele bylo obnoveno:",
|
||||||
"user_password_reset_description": "Poskytněte uživateli dočasné heslo a informujte ho, že si ho bude muset při příštím přihlášení změnit.",
|
"user_password_reset_description": "Poskytněte uživateli dočasné heslo a informujte ho, že si ho bude muset při příštím přihlášení změnit.",
|
||||||
@@ -373,7 +358,7 @@
|
|||||||
"advanced": "Pokročilé",
|
"advanced": "Pokročilé",
|
||||||
"advanced_settings_enable_alternate_media_filter_subtitle": "Tuto možnost použijte k filtrování médií během synchronizace na základě alternativních kritérií. Tuto možnost vyzkoušejte pouze v případě, že máte problémy s detekcí všech alb v aplikaci.",
|
"advanced_settings_enable_alternate_media_filter_subtitle": "Tuto možnost použijte k filtrování médií během synchronizace na základě alternativních kritérií. Tuto možnost vyzkoušejte pouze v případě, že máte problémy s detekcí všech alb v aplikaci.",
|
||||||
"advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTÁLNÍ] Použít alternativní filtr pro synchronizaci alb zařízení",
|
"advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTÁLNÍ] Použít alternativní filtr pro synchronizaci alb zařízení",
|
||||||
"advanced_settings_log_level_title": "Úroveň protokolování: {}",
|
"advanced_settings_log_level_title": "Úroveň protokolování: {level}",
|
||||||
"advanced_settings_prefer_remote_subtitle": "U některých zařízení je načítání miniatur z prostředků v zařízení velmi pomalé. Aktivujte toto nastavení, aby se místo toho načítaly vzdálené obrázky.",
|
"advanced_settings_prefer_remote_subtitle": "U některých zařízení je načítání miniatur z prostředků v zařízení velmi pomalé. Aktivujte toto nastavení, aby se místo toho načítaly vzdálené obrázky.",
|
||||||
"advanced_settings_prefer_remote_title": "Preferovat vzdálené obrázky",
|
"advanced_settings_prefer_remote_title": "Preferovat vzdálené obrázky",
|
||||||
"advanced_settings_proxy_headers_subtitle": "Definice hlaviček proxy serveru, které by měl Immich odesílat s každým síťovým požadavkem",
|
"advanced_settings_proxy_headers_subtitle": "Definice hlaviček proxy serveru, které by měl Immich odesílat s každým síťovým požadavkem",
|
||||||
@@ -403,10 +388,6 @@
|
|||||||
"album_remove_user": "Odebrat uživatele?",
|
"album_remove_user": "Odebrat uživatele?",
|
||||||
"album_remove_user_confirmation": "Opravdu chcete odebrat uživatele {user}?",
|
"album_remove_user_confirmation": "Opravdu chcete odebrat uživatele {user}?",
|
||||||
"album_share_no_users": "Zřejmě jste toto album sdíleli se všemi uživateli, nebo nemáte žádného uživatele, se kterým byste ho mohli sdílet.",
|
"album_share_no_users": "Zřejmě jste toto album sdíleli se všemi uživateli, nebo nemáte žádného uživatele, se kterým byste ho mohli sdílet.",
|
||||||
"album_thumbnail_card_item": "1 položka",
|
|
||||||
"album_thumbnail_card_items": "{} položek",
|
|
||||||
"album_thumbnail_card_shared": " · Sdíleno",
|
|
||||||
"album_thumbnail_shared_by": "Sdílel(a) {}",
|
|
||||||
"album_updated": "Album aktualizováno",
|
"album_updated": "Album aktualizováno",
|
||||||
"album_updated_setting_description": "Dostávat e-mailová oznámení o nových položkách sdíleného alba",
|
"album_updated_setting_description": "Dostávat e-mailová oznámení o nových položkách sdíleného alba",
|
||||||
"album_user_left": "Opustil {album}",
|
"album_user_left": "Opustil {album}",
|
||||||
@@ -422,6 +403,9 @@
|
|||||||
"album_with_link_access": "Nechte kohokoli s odkazem zobrazit fotografie a lidi v tomto albu.",
|
"album_with_link_access": "Nechte kohokoli s odkazem zobrazit fotografie a lidi v tomto albu.",
|
||||||
"albums": "Alba",
|
"albums": "Alba",
|
||||||
"albums_count": "{count, plural, one {{count, number} album} few {{count, number} alba} other {{count, number} alb}}",
|
"albums_count": "{count, plural, one {{count, number} album} few {{count, number} alba} other {{count, number} alb}}",
|
||||||
|
"albums_default_sort_order": "Výchozí řazení alb",
|
||||||
|
"albums_default_sort_order_description": "Výchozí řazení položek při vytváření nových alb.",
|
||||||
|
"albums_feature_description": "Sbírky položek, které lze sdílet s ostatními uživateli.",
|
||||||
"all": "Vše",
|
"all": "Vše",
|
||||||
"all_albums": "Všechna alba",
|
"all_albums": "Všechna alba",
|
||||||
"all_people": "Všichni lidé",
|
"all_people": "Všichni lidé",
|
||||||
@@ -444,7 +428,7 @@
|
|||||||
"archive": "Archiv",
|
"archive": "Archiv",
|
||||||
"archive_or_unarchive_photo": "Archivovat nebo odarchivovat fotku",
|
"archive_or_unarchive_photo": "Archivovat nebo odarchivovat fotku",
|
||||||
"archive_page_no_archived_assets": "Nebyla nalezena žádná archivovaná média",
|
"archive_page_no_archived_assets": "Nebyla nalezena žádná archivovaná média",
|
||||||
"archive_page_title": "Archiv ({})",
|
"archive_page_title": "Archiv ({count})",
|
||||||
"archive_size": "Velikost archivu",
|
"archive_size": "Velikost archivu",
|
||||||
"archive_size_description": "Nastavte velikost archivu pro stahování (v GiB)",
|
"archive_size_description": "Nastavte velikost archivu pro stahování (v GiB)",
|
||||||
"archived": "Archiv",
|
"archived": "Archiv",
|
||||||
@@ -480,28 +464,32 @@
|
|||||||
"assets_added_count": "{count, plural, one {Přidána # položka} few {Přidány # položky} other {Přidáno # položek}}",
|
"assets_added_count": "{count, plural, one {Přidána # položka} few {Přidány # položky} other {Přidáno # položek}}",
|
||||||
"assets_added_to_album_count": "Do alba {count, plural, one {byla přidána # položka} few {byly přidány # položky} other {bylo přidáno # položek}}",
|
"assets_added_to_album_count": "Do alba {count, plural, one {byla přidána # položka} few {byly přidány # položky} other {bylo přidáno # položek}}",
|
||||||
"assets_added_to_name_count": "{count, plural, one {Přidána # položka} few {Přidány # položky} other {Přidáno # položek}} do {hasName, select, true {alba <b>{name}</b>} other {nového alba}}",
|
"assets_added_to_name_count": "{count, plural, one {Přidána # položka} few {Přidány # položky} other {Přidáno # položek}} do {hasName, select, true {alba <b>{name}</b>} other {nového alba}}",
|
||||||
|
"assets_cannot_be_added_to_album_count": "{count, plural, one {Položku} other {Položky}} nelze přidat do alba",
|
||||||
"assets_count": "{count, plural, one {# položka} few {# položky} other {# položek}}",
|
"assets_count": "{count, plural, one {# položka} few {# položky} other {# položek}}",
|
||||||
"assets_deleted_permanently": "{} položek trvale odstraněno",
|
"assets_deleted_permanently": "{count} položek trvale odstraněno",
|
||||||
"assets_deleted_permanently_from_server": "{} položek trvale odstraněno z Immich serveru",
|
"assets_deleted_permanently_from_server": "{count} položek trvale odstraněno z Immich serveru",
|
||||||
|
"assets_downloaded_failed": "{count, plural, one {Stažen # soubor - {error} souborů selhalo} few {Staženy # soubory - {error} souborů selhalo} other {Staženo # souborů - {error} souborů selhalo}}",
|
||||||
|
"assets_downloaded_successfully": "{count, plural, one {Úspěšně stažen # soubor} few {Úspěšně staženy # soubory} other {Úspěšně staženo # souborů}}",
|
||||||
"assets_moved_to_trash_count": "Do koše {count, plural, one {přesunuta # položka} few {přesunuty # položky} other {přesunuto # položek}}",
|
"assets_moved_to_trash_count": "Do koše {count, plural, one {přesunuta # položka} few {přesunuty # položky} other {přesunuto # položek}}",
|
||||||
"assets_permanently_deleted_count": "Trvale {count, plural, one {smazána # položka} few {smazány # položky} other {smazáno # položek}}",
|
"assets_permanently_deleted_count": "Trvale {count, plural, one {smazána # položka} few {smazány # položky} other {smazáno # položek}}",
|
||||||
"assets_removed_count": "{count, plural, one {Odstraněna # položka} few {Odstraněny # položky} other {Odstraněno # položek}}",
|
"assets_removed_count": "{count, plural, one {Odstraněna # položka} few {Odstraněny # položky} other {Odstraněno # položek}}",
|
||||||
"assets_removed_permanently_from_device": "{} položek trvale odstraněno z vašeho zařízení",
|
"assets_removed_permanently_from_device": "{count} položek trvale odstraněno z vašeho zařízení",
|
||||||
"assets_restore_confirmation": "Opravdu chcete obnovit všechny vyhozené položky? Tuto akci nelze vrátit zpět! Upozorňujeme, že tímto způsobem nelze obnovit žádné offline položky.",
|
"assets_restore_confirmation": "Opravdu chcete obnovit všechny vyhozené položky? Tuto akci nelze vrátit zpět! Upozorňujeme, že tímto způsobem nelze obnovit žádné offline položky.",
|
||||||
"assets_restored_count": "{count, plural, one {Obnovena # položka} few {Obnoveny # položky} other {Obnoveno # položek}}",
|
"assets_restored_count": "{count, plural, one {Obnovena # položka} few {Obnoveny # položky} other {Obnoveno # položek}}",
|
||||||
"assets_restored_successfully": "{} položek úspěšně obnoveno",
|
"assets_restored_successfully": "{count} položek úspěšně obnoveno",
|
||||||
"assets_trashed": "{} položek vyhozeno do koše",
|
"assets_trashed": "{count} položek vyhozeno do koše",
|
||||||
"assets_trashed_count": "{count, plural, one {Vyhozena # položka} few {Vyhozeny # položky} other {Vyhozeno # položek}}",
|
"assets_trashed_count": "{count, plural, one {Vyhozena # položka} few {Vyhozeny # položky} other {Vyhozeno # položek}}",
|
||||||
"assets_trashed_from_server": "{} položek vyhozeno do koše na Immich serveru",
|
"assets_trashed_from_server": "{count} položek vyhozeno do koše na Immich serveru",
|
||||||
"assets_were_part_of_album_count": "{count, plural, one {Položka byla} other {Položky byly}} součástí alba",
|
"assets_were_part_of_album_count": "{count, plural, one {Položka byla} other {Položky byly}} součástí alba",
|
||||||
"authorized_devices": "Autorizovaná zařízení",
|
"authorized_devices": "Autorizovaná zařízení",
|
||||||
"automatic_endpoint_switching_subtitle": "Připojit se místně přes určenou Wi-Fi, pokud je k dispozici, a používat alternativní připojení jinde",
|
"automatic_endpoint_switching_subtitle": "Připojit se místně přes určenou Wi-Fi, pokud je k dispozici, a používat alternativní připojení jinde",
|
||||||
"automatic_endpoint_switching_title": "Automatické přepínání URL",
|
"automatic_endpoint_switching_title": "Automatické přepínání URL",
|
||||||
|
"autoplay_slideshow": "Automatické přehrávání prezentace",
|
||||||
"back": "Zpět",
|
"back": "Zpět",
|
||||||
"back_close_deselect": "Zpět, zavřít nebo zrušit výběr",
|
"back_close_deselect": "Zpět, zavřít nebo zrušit výběr",
|
||||||
"background_location_permission": "Povolení polohy na pozadí",
|
"background_location_permission": "Povolení polohy na pozadí",
|
||||||
"background_location_permission_content": "Aby bylo možné přepínat sítě při běhu na pozadí, musí mít Immich *vždy* přístup k přesné poloze, aby mohl zjistit název Wi-Fi sítě",
|
"background_location_permission_content": "Aby bylo možné přepínat sítě při běhu na pozadí, musí mít Immich *vždy* přístup k přesné poloze, aby mohl zjistit název Wi-Fi sítě",
|
||||||
"backup_album_selection_page_albums_device": "Alba v zařízení ({})",
|
"backup_album_selection_page_albums_device": "Alba v zařízení ({count})",
|
||||||
"backup_album_selection_page_albums_tap": "Klepnutím na položku ji zahrnete, opětovným klepnutím ji vyloučíte",
|
"backup_album_selection_page_albums_tap": "Klepnutím na položku ji zahrnete, opětovným klepnutím ji vyloučíte",
|
||||||
"backup_album_selection_page_assets_scatter": "Položky mohou být roztroušeny ve více albech. To umožňuje zahrnout nebo vyloučit alba během procesu zálohování.",
|
"backup_album_selection_page_assets_scatter": "Položky mohou být roztroušeny ve více albech. To umožňuje zahrnout nebo vyloučit alba během procesu zálohování.",
|
||||||
"backup_album_selection_page_select_albums": "Vybraná alba",
|
"backup_album_selection_page_select_albums": "Vybraná alba",
|
||||||
@@ -510,11 +498,11 @@
|
|||||||
"backup_all": "Vše",
|
"backup_all": "Vše",
|
||||||
"backup_background_service_backup_failed_message": "Zálohování médií selhalo. Zkouším to znovu…",
|
"backup_background_service_backup_failed_message": "Zálohování médií selhalo. Zkouším to znovu…",
|
||||||
"backup_background_service_connection_failed_message": "Nepodařilo se připojit k serveru. Zkouším to znovu…",
|
"backup_background_service_connection_failed_message": "Nepodařilo se připojit k serveru. Zkouším to znovu…",
|
||||||
"backup_background_service_current_upload_notification": "Nahrávání {}",
|
"backup_background_service_current_upload_notification": "Nahrávání {filename}",
|
||||||
"backup_background_service_default_notification": "Kontrola nových médií…",
|
"backup_background_service_default_notification": "Kontrola nových médií…",
|
||||||
"backup_background_service_error_title": "Chyba zálohování",
|
"backup_background_service_error_title": "Chyba zálohování",
|
||||||
"backup_background_service_in_progress_notification": "Zálohování vašich médií…",
|
"backup_background_service_in_progress_notification": "Zálohování vašich médií…",
|
||||||
"backup_background_service_upload_failure_notification": "Nepodařilo se nahrát {}",
|
"backup_background_service_upload_failure_notification": "Nepodařilo se nahrát {filename}",
|
||||||
"backup_controller_page_albums": "Zálohovaná alba",
|
"backup_controller_page_albums": "Zálohovaná alba",
|
||||||
"backup_controller_page_background_app_refresh_disabled_content": "Povolte obnovení aplikace na pozadí v Nastavení > Obecné > Obnovení aplikace na pozadí, abyste mohli používat zálohování na pozadí.",
|
"backup_controller_page_background_app_refresh_disabled_content": "Povolte obnovení aplikace na pozadí v Nastavení > Obecné > Obnovení aplikace na pozadí, abyste mohli používat zálohování na pozadí.",
|
||||||
"backup_controller_page_background_app_refresh_disabled_title": "Obnovování aplikací na pozadí je vypnuté",
|
"backup_controller_page_background_app_refresh_disabled_title": "Obnovování aplikací na pozadí je vypnuté",
|
||||||
@@ -525,7 +513,7 @@
|
|||||||
"backup_controller_page_background_battery_info_title": "Optimalizace baterie",
|
"backup_controller_page_background_battery_info_title": "Optimalizace baterie",
|
||||||
"backup_controller_page_background_charging": "Pouze během nabíjení",
|
"backup_controller_page_background_charging": "Pouze během nabíjení",
|
||||||
"backup_controller_page_background_configure_error": "Nepodařilo se nakonfigurovat službu na pozadí",
|
"backup_controller_page_background_configure_error": "Nepodařilo se nakonfigurovat službu na pozadí",
|
||||||
"backup_controller_page_background_delay": "Zpoždění zálohování nových médií: {}",
|
"backup_controller_page_background_delay": "Zpoždění zálohování nových médií: {duration}",
|
||||||
"backup_controller_page_background_description": "Povolte službu na pozadí pro automatické zálohování všech nových položek bez nutnosti otevření aplikace",
|
"backup_controller_page_background_description": "Povolte službu na pozadí pro automatické zálohování všech nových položek bez nutnosti otevření aplikace",
|
||||||
"backup_controller_page_background_is_off": "Automatické zálohování na pozadí je vypnuto",
|
"backup_controller_page_background_is_off": "Automatické zálohování na pozadí je vypnuto",
|
||||||
"backup_controller_page_background_is_on": "Automatické zálohování na pozadí je zapnuto",
|
"backup_controller_page_background_is_on": "Automatické zálohování na pozadí je zapnuto",
|
||||||
@@ -535,12 +523,12 @@
|
|||||||
"backup_controller_page_backup": "Zálohování",
|
"backup_controller_page_backup": "Zálohování",
|
||||||
"backup_controller_page_backup_selected": "Vybrané: ",
|
"backup_controller_page_backup_selected": "Vybrané: ",
|
||||||
"backup_controller_page_backup_sub": "Zálohované fotografie a videa",
|
"backup_controller_page_backup_sub": "Zálohované fotografie a videa",
|
||||||
"backup_controller_page_created": "Vytvořeno: {}",
|
"backup_controller_page_created": "Vytvořeno: {date}",
|
||||||
"backup_controller_page_desc_backup": "Zapněte zálohování na popředí, aby se nové položky automaticky nahrávaly na server při otevření aplikace.",
|
"backup_controller_page_desc_backup": "Zapněte zálohování na popředí, aby se nové položky automaticky nahrávaly na server při otevření aplikace.",
|
||||||
"backup_controller_page_excluded": "Vyloučeno: ",
|
"backup_controller_page_excluded": "Vyloučeno: ",
|
||||||
"backup_controller_page_failed": "Nepodařilo se ({})",
|
"backup_controller_page_failed": "Nepodařilo se ({count})",
|
||||||
"backup_controller_page_filename": "Název souboru: {} [{}]",
|
"backup_controller_page_filename": "Název souboru: {filename} [{size}]",
|
||||||
"backup_controller_page_id": "ID: {}",
|
"backup_controller_page_id": "ID: {id}",
|
||||||
"backup_controller_page_info": "Informace o zálohování",
|
"backup_controller_page_info": "Informace o zálohování",
|
||||||
"backup_controller_page_none_selected": "Žádné vybrané",
|
"backup_controller_page_none_selected": "Žádné vybrané",
|
||||||
"backup_controller_page_remainder": "Zbývá",
|
"backup_controller_page_remainder": "Zbývá",
|
||||||
@@ -549,7 +537,7 @@
|
|||||||
"backup_controller_page_start_backup": "Spustit zálohování",
|
"backup_controller_page_start_backup": "Spustit zálohování",
|
||||||
"backup_controller_page_status_off": "Automatické zálohování na popředí je vypnuto",
|
"backup_controller_page_status_off": "Automatické zálohování na popředí je vypnuto",
|
||||||
"backup_controller_page_status_on": "Automatické zálohování na popředí je zapnuto",
|
"backup_controller_page_status_on": "Automatické zálohování na popředí je zapnuto",
|
||||||
"backup_controller_page_storage_format": "{} z {} použitých",
|
"backup_controller_page_storage_format": "{used} z {total} použitých",
|
||||||
"backup_controller_page_to_backup": "Alba, která mají být zálohována",
|
"backup_controller_page_to_backup": "Alba, která mají být zálohována",
|
||||||
"backup_controller_page_total_sub": "Všechny jedinečné fotografie a videa z vybraných alb",
|
"backup_controller_page_total_sub": "Všechny jedinečné fotografie a videa z vybraných alb",
|
||||||
"backup_controller_page_turn_off": "Vypnout zálohování na popředí",
|
"backup_controller_page_turn_off": "Vypnout zálohování na popředí",
|
||||||
@@ -564,6 +552,10 @@
|
|||||||
"backup_options_page_title": "Nastavení záloh",
|
"backup_options_page_title": "Nastavení záloh",
|
||||||
"backup_setting_subtitle": "Správa nastavení zálohování na pozadí a na popředí",
|
"backup_setting_subtitle": "Správa nastavení zálohování na pozadí a na popředí",
|
||||||
"backward": "Pozpátku",
|
"backward": "Pozpátku",
|
||||||
|
"biometric_auth_enabled": "Biometrické ověřování je povoleno",
|
||||||
|
"biometric_locked_out": "Jste vyloučeni z biometrického ověřování",
|
||||||
|
"biometric_no_options": "Biometrické možnosti nejsou k dispozici",
|
||||||
|
"biometric_not_available": "Biometrické ověřování není na tomto zařízení k dispozici",
|
||||||
"birthdate_saved": "Datum narození úspěšně uloženo",
|
"birthdate_saved": "Datum narození úspěšně uloženo",
|
||||||
"birthdate_set_description": "Datum narození se používá k výpočtu věku osoby v době pořízení fotografie.",
|
"birthdate_set_description": "Datum narození se používá k výpočtu věku osoby v době pořízení fotografie.",
|
||||||
"blurred_background": "Rozmazané pozadí",
|
"blurred_background": "Rozmazané pozadí",
|
||||||
@@ -574,21 +566,17 @@
|
|||||||
"bulk_keep_duplicates_confirmation": "Opravdu si chcete ponechat {count, plural, one {# duplicitní položku} few {# duplicitní položky} other {# duplicitních položek}}? Tím se vyřeší všechny duplicitní skupiny, aniž by se cokoli odstranilo.",
|
"bulk_keep_duplicates_confirmation": "Opravdu si chcete ponechat {count, plural, one {# duplicitní položku} few {# duplicitní položky} other {# duplicitních položek}}? Tím se vyřeší všechny duplicitní skupiny, aniž by se cokoli odstranilo.",
|
||||||
"bulk_trash_duplicates_confirmation": "Opravdu chcete hromadně vyhodit {count, plural, one {# duplicitní položku} few {# duplicitní položky} other {# duplicitních položek}}? Tím se zachová největší položka z každé skupiny a všechny ostatní duplikáty se vyhodí.",
|
"bulk_trash_duplicates_confirmation": "Opravdu chcete hromadně vyhodit {count, plural, one {# duplicitní položku} few {# duplicitní položky} other {# duplicitních položek}}? Tím se zachová největší položka z každé skupiny a všechny ostatní duplikáty se vyhodí.",
|
||||||
"buy": "Zakoupit Immich",
|
"buy": "Zakoupit Immich",
|
||||||
"cache_settings_album_thumbnails": "Náhledy stránek knihovny (položek {})",
|
|
||||||
"cache_settings_clear_cache_button": "Vymazat vyrovnávací paměť",
|
"cache_settings_clear_cache_button": "Vymazat vyrovnávací paměť",
|
||||||
"cache_settings_clear_cache_button_title": "Vymaže vyrovnávací paměť aplikace. To výrazně ovlivní výkon aplikace, dokud se vyrovnávací paměť neobnoví.",
|
"cache_settings_clear_cache_button_title": "Vymaže vyrovnávací paměť aplikace. To výrazně ovlivní výkon aplikace, dokud se vyrovnávací paměť neobnoví.",
|
||||||
"cache_settings_duplicated_assets_clear_button": "VYMAZAT",
|
"cache_settings_duplicated_assets_clear_button": "VYMAZAT",
|
||||||
"cache_settings_duplicated_assets_subtitle": "Fotografie a videa, které aplikace zařadila na černou listinu",
|
"cache_settings_duplicated_assets_subtitle": "Fotografie a videa, které aplikace zařadila na černou listinu",
|
||||||
"cache_settings_duplicated_assets_title": "Duplicitní položky ({})",
|
"cache_settings_duplicated_assets_title": "Duplicitní položky ({count})",
|
||||||
"cache_settings_image_cache_size": "Velikost vyrovnávací paměti (položek {})",
|
|
||||||
"cache_settings_statistics_album": "Knihovna náhledů",
|
"cache_settings_statistics_album": "Knihovna náhledů",
|
||||||
"cache_settings_statistics_assets": "{} položky ({})",
|
|
||||||
"cache_settings_statistics_full": "Kompletní fotografie",
|
"cache_settings_statistics_full": "Kompletní fotografie",
|
||||||
"cache_settings_statistics_shared": "Sdílené náhledy alb",
|
"cache_settings_statistics_shared": "Sdílené náhledy alb",
|
||||||
"cache_settings_statistics_thumbnail": "Náhledy",
|
"cache_settings_statistics_thumbnail": "Náhledy",
|
||||||
"cache_settings_statistics_title": "Použití vyrovnávací paměti",
|
"cache_settings_statistics_title": "Použití vyrovnávací paměti",
|
||||||
"cache_settings_subtitle": "Ovládání chování mobilní aplikace Immich v mezipaměti",
|
"cache_settings_subtitle": "Ovládání chování mobilní aplikace Immich v mezipaměti",
|
||||||
"cache_settings_thumbnail_size": "Velikost vyrovnávací paměti náhledů (položek {})",
|
|
||||||
"cache_settings_tile_subtitle": "Ovládání chování místního úložiště",
|
"cache_settings_tile_subtitle": "Ovládání chování místního úložiště",
|
||||||
"cache_settings_tile_title": "Místní úložiště",
|
"cache_settings_tile_title": "Místní úložiště",
|
||||||
"cache_settings_title": "Nastavení vyrovnávací paměti",
|
"cache_settings_title": "Nastavení vyrovnávací paměti",
|
||||||
@@ -601,12 +589,15 @@
|
|||||||
"cannot_merge_people": "Nelze sloučit osoby",
|
"cannot_merge_people": "Nelze sloučit osoby",
|
||||||
"cannot_undo_this_action": "Tuto akci nelze vrátit zpět!",
|
"cannot_undo_this_action": "Tuto akci nelze vrátit zpět!",
|
||||||
"cannot_update_the_description": "Nelze aktualizovat popis",
|
"cannot_update_the_description": "Nelze aktualizovat popis",
|
||||||
|
"cast": "Odeslat do zařízení",
|
||||||
|
"cast_description": "Nastavení dostupných cílů přenosu",
|
||||||
"change_date": "Změnit datum",
|
"change_date": "Změnit datum",
|
||||||
|
"change_description": "Změnit popis",
|
||||||
"change_display_order": "Změnit pořadí zobrazení",
|
"change_display_order": "Změnit pořadí zobrazení",
|
||||||
"change_expiration_time": "Změna konce platnosti",
|
"change_expiration_time": "Změna konce platnosti",
|
||||||
"change_location": "Změna polohy",
|
"change_location": "Změna polohy",
|
||||||
"change_name": "Změnit jméno",
|
"change_name": "Změnit jméno",
|
||||||
"change_name_successfully": "Změna jména proběhla úspěšně",
|
"change_name_successfully": "Jméno bylo úspěšně změněno",
|
||||||
"change_password": "Změna hesla",
|
"change_password": "Změna hesla",
|
||||||
"change_password_description": "Buď se do systému přihlašujete poprvé, nebo jste byli požádáni o změnu hesla. Zadejte prosím nové heslo níže.",
|
"change_password_description": "Buď se do systému přihlašujete poprvé, nebo jste byli požádáni o změnu hesla. Zadejte prosím nové heslo níže.",
|
||||||
"change_password_form_confirm_password": "Potvrďte heslo",
|
"change_password_form_confirm_password": "Potvrďte heslo",
|
||||||
@@ -614,9 +605,9 @@
|
|||||||
"change_password_form_new_password": "Nové heslo",
|
"change_password_form_new_password": "Nové heslo",
|
||||||
"change_password_form_password_mismatch": "Hesla se neshodují",
|
"change_password_form_password_mismatch": "Hesla se neshodují",
|
||||||
"change_password_form_reenter_new_password": "Znovu zadejte nové heslo",
|
"change_password_form_reenter_new_password": "Znovu zadejte nové heslo",
|
||||||
|
"change_pin_code": "Změnit PIN kód",
|
||||||
"change_your_password": "Změna vašeho hesla",
|
"change_your_password": "Změna vašeho hesla",
|
||||||
"changed_visibility_successfully": "Změna viditelnosti proběhla úspěšně",
|
"changed_visibility_successfully": "Změna viditelnosti proběhla úspěšně",
|
||||||
"check_all": "Zkontrolovat vše",
|
|
||||||
"check_corrupt_asset_backup": "Kontrola poškozených záloh položek",
|
"check_corrupt_asset_backup": "Kontrola poškozených záloh položek",
|
||||||
"check_corrupt_asset_backup_button": "Provést kontrolu",
|
"check_corrupt_asset_backup_button": "Provést kontrolu",
|
||||||
"check_corrupt_asset_backup_description": "Tuto kontrolu provádějte pouze přes Wi-Fi a po zálohování všech prostředků. Takto operace může trvat několik minut.",
|
"check_corrupt_asset_backup_description": "Tuto kontrolu provádějte pouze přes Wi-Fi a po zálohování všech prostředků. Takto operace může trvat několik minut.",
|
||||||
@@ -654,11 +645,15 @@
|
|||||||
"confirm_delete_face": "Opravdu chcete z položky odstranit obličej osoby {name}?",
|
"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_new_pin_code": "Potvrzení nového PIN kódu",
|
||||||
"confirm_password": "Potvrzení hesla",
|
"confirm_password": "Potvrzení hesla",
|
||||||
|
"confirm_tag_face": "Opravdu chcete označit tento obličej jako {name}?",
|
||||||
|
"confirm_tag_face_unnamed": "Opravdu chcete označit tento obličej?",
|
||||||
|
"connected_device": "Připojené zařízení",
|
||||||
|
"connected_to": "Připojeno k",
|
||||||
"contain": "Obsah",
|
"contain": "Obsah",
|
||||||
"context": "Kontext",
|
"context": "Kontext",
|
||||||
"continue": "Pokračovat",
|
"continue": "Pokračovat",
|
||||||
"control_bottom_app_bar_album_info_shared": "{} položky – sdílené",
|
|
||||||
"control_bottom_app_bar_create_new_album": "Vytvořit nové album",
|
"control_bottom_app_bar_create_new_album": "Vytvořit nové album",
|
||||||
"control_bottom_app_bar_delete_from_immich": "Smazat ze serveru Immich",
|
"control_bottom_app_bar_delete_from_immich": "Smazat ze serveru Immich",
|
||||||
"control_bottom_app_bar_delete_from_local": "Smazat ze zařízení",
|
"control_bottom_app_bar_delete_from_local": "Smazat ze zařízení",
|
||||||
@@ -696,15 +691,18 @@
|
|||||||
"create_tag_description": "Vytvoření nové značky. U vnořených značek zadejte celou cestu ke značce včetně dopředných lomítek.",
|
"create_tag_description": "Vytvoření nové značky. U vnořených značek zadejte celou cestu ke značce včetně dopředných lomítek.",
|
||||||
"create_user": "Vytvořit uživatele",
|
"create_user": "Vytvořit uživatele",
|
||||||
"created": "Vytvořeno",
|
"created": "Vytvořeno",
|
||||||
|
"created_at": "Vytvořeno",
|
||||||
"crop": "Oříznout",
|
"crop": "Oříznout",
|
||||||
"curated_object_page_title": "Věci",
|
"curated_object_page_title": "Věci",
|
||||||
"current_device": "Současné zařízení",
|
"current_device": "Současné zařízení",
|
||||||
|
"current_pin_code": "Aktuální PIN kód",
|
||||||
"current_server_address": "Aktuální adresa serveru",
|
"current_server_address": "Aktuální adresa serveru",
|
||||||
"custom_locale": "Vlastní lokalizace",
|
"custom_locale": "Vlastní lokalizace",
|
||||||
"custom_locale_description": "Formátovat datumy a čísla podle jazyka a oblasti",
|
"custom_locale_description": "Formátovat datumy a čísla podle jazyka a oblasti",
|
||||||
"daily_title_text_date": "EEEE, d. MMMM",
|
"daily_title_text_date": "EEEE, d. MMMM",
|
||||||
"daily_title_text_date_year": "EEEE, d. MMMM y",
|
"daily_title_text_date_year": "EEEE, d. MMMM y",
|
||||||
"dark": "Tmavý",
|
"dark": "Tmavý",
|
||||||
|
"darkTheme": "Přepnout tmavý motiv",
|
||||||
"date_after": "Datum po",
|
"date_after": "Datum po",
|
||||||
"date_and_time": "Datum a čas",
|
"date_and_time": "Datum a čas",
|
||||||
"date_before": "Datum před",
|
"date_before": "Datum před",
|
||||||
@@ -752,6 +750,7 @@
|
|||||||
"disallow_edits": "Zakázat úpravy",
|
"disallow_edits": "Zakázat úpravy",
|
||||||
"discord": "Discord",
|
"discord": "Discord",
|
||||||
"discover": "Objevit",
|
"discover": "Objevit",
|
||||||
|
"discovered_devices": "Nalezená zařízení",
|
||||||
"dismiss_all_errors": "Zrušit všechny chyby",
|
"dismiss_all_errors": "Zrušit všechny chyby",
|
||||||
"dismiss_error": "Zrušit chybu",
|
"dismiss_error": "Zrušit chybu",
|
||||||
"display_options": "Možnosti zobrazení",
|
"display_options": "Možnosti zobrazení",
|
||||||
@@ -767,7 +766,6 @@
|
|||||||
"download_enqueue": "Stahování ve frontě",
|
"download_enqueue": "Stahování ve frontě",
|
||||||
"download_error": "Chyba při stahování",
|
"download_error": "Chyba při stahování",
|
||||||
"download_failed": "Stahování selhalo",
|
"download_failed": "Stahování selhalo",
|
||||||
"download_filename": "soubor: {}",
|
|
||||||
"download_finished": "Stahování dokončeno",
|
"download_finished": "Stahování dokončeno",
|
||||||
"download_include_embedded_motion_videos": "Vložená videa",
|
"download_include_embedded_motion_videos": "Vložená videa",
|
||||||
"download_include_embedded_motion_videos_description": "Zahrnout videa vložená do pohyblivých fotografií jako samostatný soubor",
|
"download_include_embedded_motion_videos_description": "Zahrnout videa vložená do pohyblivých fotografií jako samostatný soubor",
|
||||||
@@ -791,6 +789,8 @@
|
|||||||
"edit_avatar": "Upravit avatar",
|
"edit_avatar": "Upravit avatar",
|
||||||
"edit_date": "Upravit datum",
|
"edit_date": "Upravit datum",
|
||||||
"edit_date_and_time": "Upravit datum a čas",
|
"edit_date_and_time": "Upravit datum a čas",
|
||||||
|
"edit_description": "Upravit popis",
|
||||||
|
"edit_description_prompt": "Vyberte nový popis:",
|
||||||
"edit_exclusion_pattern": "Upravit vzor vyloučení",
|
"edit_exclusion_pattern": "Upravit vzor vyloučení",
|
||||||
"edit_faces": "Upravit obličeje",
|
"edit_faces": "Upravit obličeje",
|
||||||
"edit_import_path": "Upravit cestu importu",
|
"edit_import_path": "Upravit cestu importu",
|
||||||
@@ -811,19 +811,24 @@
|
|||||||
"editor_crop_tool_h2_aspect_ratios": "Poměr stran",
|
"editor_crop_tool_h2_aspect_ratios": "Poměr stran",
|
||||||
"editor_crop_tool_h2_rotation": "Otočení",
|
"editor_crop_tool_h2_rotation": "Otočení",
|
||||||
"email": "E-mail",
|
"email": "E-mail",
|
||||||
|
"email_notifications": "E-mailová oznámení",
|
||||||
"empty_folder": "Tato složka je prázdná",
|
"empty_folder": "Tato složka je prázdná",
|
||||||
"empty_trash": "Vyprázdnit koš",
|
"empty_trash": "Vyprázdnit koš",
|
||||||
"empty_trash_confirmation": "Opravdu chcete vysypat koš? Tím se z Immiche trvale odstraní všechny položky v koši.\nTuto akci nelze vrátit zpět!",
|
"empty_trash_confirmation": "Opravdu chcete vysypat koš? Tím se z Immiche trvale odstraní všechny položky v koši.\nTuto akci nelze vrátit zpět!",
|
||||||
"enable": "Povolit",
|
"enable": "Povolit",
|
||||||
|
"enable_biometric_auth_description": "Zadejte váš PIN kód pro povolení biometrického ověřování",
|
||||||
"enabled": "Povoleno",
|
"enabled": "Povoleno",
|
||||||
"end_date": "Konečné datum",
|
"end_date": "Konečné datum",
|
||||||
"enqueued": "Ve frontě",
|
"enqueued": "Ve frontě",
|
||||||
"enter_wifi_name": "Zadejte název WiFi",
|
"enter_wifi_name": "Zadejte název Wi-Fi",
|
||||||
|
"enter_your_pin_code": "Zadejte PIN kód",
|
||||||
|
"enter_your_pin_code_subtitle": "Zadejte PIN kód pro přístup k uzamčené složce",
|
||||||
"error": "Chyba",
|
"error": "Chyba",
|
||||||
"error_change_sort_album": "Nepodařilo se změnit pořadí alba",
|
"error_change_sort_album": "Nepodařilo se změnit pořadí alba",
|
||||||
"error_delete_face": "Chyba při odstraňování obličeje z položky",
|
"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_saving_image": "Chyba: {}",
|
"error_saving_image": "Chyba: {error}",
|
||||||
|
"error_tag_face_bounding_box": "Chyba při označování obličeje - nelze získat souřadnice ohraničujícího rámečku",
|
||||||
"error_title": "Chyba - Něco se pokazilo",
|
"error_title": "Chyba - Něco se pokazilo",
|
||||||
"errors": {
|
"errors": {
|
||||||
"cannot_navigate_next_asset": "Nelze přejít na další položku",
|
"cannot_navigate_next_asset": "Nelze přejít na další položku",
|
||||||
@@ -836,7 +841,6 @@
|
|||||||
"cant_get_number_of_comments": "Nelze načíst počet komentářů",
|
"cant_get_number_of_comments": "Nelze načíst počet komentářů",
|
||||||
"cant_search_people": "Nelze vyhledávat lidi",
|
"cant_search_people": "Nelze vyhledávat lidi",
|
||||||
"cant_search_places": "Nelze vyhledávat místa",
|
"cant_search_places": "Nelze vyhledávat místa",
|
||||||
"cleared_jobs": "Vyřízené úlohy pro: {job}",
|
|
||||||
"error_adding_assets_to_album": "Chyba při přidávání položek do alba",
|
"error_adding_assets_to_album": "Chyba při přidávání položek do alba",
|
||||||
"error_adding_users_to_album": "Chyba při přidávání uživatelů do alba",
|
"error_adding_users_to_album": "Chyba při přidávání uživatelů do alba",
|
||||||
"error_deleting_shared_user": "Chyba při odstraňování sdíleného uživatele",
|
"error_deleting_shared_user": "Chyba při odstraňování sdíleného uživatele",
|
||||||
@@ -845,7 +849,6 @@
|
|||||||
"error_removing_assets_from_album": "Chyba při odstraňování položek z alba, další podrobnosti najdete v konzoli",
|
"error_removing_assets_from_album": "Chyba při odstraňování položek z alba, další podrobnosti najdete v konzoli",
|
||||||
"error_selecting_all_assets": "Chyba při výběru všech položek",
|
"error_selecting_all_assets": "Chyba při výběru všech položek",
|
||||||
"exclusion_pattern_already_exists": "Tento vzor vyloučení již existuje.",
|
"exclusion_pattern_already_exists": "Tento vzor vyloučení již existuje.",
|
||||||
"failed_job_command": "Příkaz {command} se nezdařil pro úlohu: {job}",
|
|
||||||
"failed_to_create_album": "Nepodařilo se vytvořit album",
|
"failed_to_create_album": "Nepodařilo se vytvořit album",
|
||||||
"failed_to_create_shared_link": "Nepodařilo se vytvořit sdílený odkaz",
|
"failed_to_create_shared_link": "Nepodařilo se vytvořit sdílený odkaz",
|
||||||
"failed_to_edit_shared_link": "Nepodařilo se upravit sdílený odkaz",
|
"failed_to_edit_shared_link": "Nepodařilo se upravit sdílený odkaz",
|
||||||
@@ -853,16 +856,17 @@
|
|||||||
"failed_to_keep_this_delete_others": "Nepodařilo se zachovat tuto položku a odstranit ostatní položky",
|
"failed_to_keep_this_delete_others": "Nepodařilo se zachovat tuto položku a odstranit ostatní položky",
|
||||||
"failed_to_load_asset": "Nepodařilo se načíst položku",
|
"failed_to_load_asset": "Nepodařilo se načíst položku",
|
||||||
"failed_to_load_assets": "Nepodařilo se načíst položky",
|
"failed_to_load_assets": "Nepodařilo se načíst položky",
|
||||||
|
"failed_to_load_notifications": "Nepodařilo se načíst oznámení",
|
||||||
"failed_to_load_people": "Chyba načítání osob",
|
"failed_to_load_people": "Chyba načítání osob",
|
||||||
"failed_to_remove_product_key": "Nepodařilo se odebrat klíč produktu",
|
"failed_to_remove_product_key": "Nepodařilo se odebrat klíč produktu",
|
||||||
"failed_to_stack_assets": "Nepodařilo se seskupit položky",
|
"failed_to_stack_assets": "Nepodařilo se seskupit položky",
|
||||||
"failed_to_unstack_assets": "Nepodařilo se rozložit položky",
|
"failed_to_unstack_assets": "Nepodařilo se rozložit položky",
|
||||||
|
"failed_to_update_notification_status": "Nepodařilo se aktualizovat stav oznámení",
|
||||||
"import_path_already_exists": "Tato cesta importu již existuje.",
|
"import_path_already_exists": "Tato cesta importu již existuje.",
|
||||||
"incorrect_email_or_password": "Nesprávný e-mail nebo heslo",
|
"incorrect_email_or_password": "Nesprávný e-mail nebo heslo",
|
||||||
"paths_validation_failed": "{paths, plural, one {# cesta neprošla} few {# cesty neprošly} other {# cest neprošlo}} kontrolou",
|
"paths_validation_failed": "{paths, plural, one {# cesta neprošla} few {# cesty neprošly} other {# cest neprošlo}} kontrolou",
|
||||||
"profile_picture_transparent_pixels": "Profilové obrázky nemohou mít průhledné pixely. Obrázek si prosím zvětšete nebo posuňte.",
|
"profile_picture_transparent_pixels": "Profilové obrázky nemohou mít průhledné pixely. Obrázek si prosím zvětšete nebo posuňte.",
|
||||||
"quota_higher_than_disk_size": "Nastavili jste kvótu vyšší, než je velikost disku",
|
"quota_higher_than_disk_size": "Nastavili jste kvótu vyšší, než je velikost disku",
|
||||||
"repair_unable_to_check_items": "Nelze zkontrolovat {count, select, one {položku} other {položky}}",
|
|
||||||
"unable_to_add_album_users": "Nelze přidat uživatele do alba",
|
"unable_to_add_album_users": "Nelze přidat uživatele do alba",
|
||||||
"unable_to_add_assets_to_shared_link": "Nelze přidat položky do sdíleného odkazu",
|
"unable_to_add_assets_to_shared_link": "Nelze přidat položky do sdíleného odkazu",
|
||||||
"unable_to_add_comment": "Nelze přidat komentář",
|
"unable_to_add_comment": "Nelze přidat komentář",
|
||||||
@@ -874,13 +878,13 @@
|
|||||||
"unable_to_archive_unarchive": "Nelze {archived, select, true {archivovat} other {odarchivovat}}",
|
"unable_to_archive_unarchive": "Nelze {archived, select, true {archivovat} other {odarchivovat}}",
|
||||||
"unable_to_change_album_user_role": "Nelze změnit roli uživatele alba",
|
"unable_to_change_album_user_role": "Nelze změnit roli uživatele alba",
|
||||||
"unable_to_change_date": "Nelze změnit datum",
|
"unable_to_change_date": "Nelze změnit datum",
|
||||||
|
"unable_to_change_description": "Nelze změnit popis",
|
||||||
"unable_to_change_favorite": "Nelze změnit oblíbení položky",
|
"unable_to_change_favorite": "Nelze změnit oblíbení položky",
|
||||||
"unable_to_change_location": "Nelze změnit polohu",
|
"unable_to_change_location": "Nelze změnit polohu",
|
||||||
"unable_to_change_password": "Nelze změnit heslo",
|
"unable_to_change_password": "Nelze změnit heslo",
|
||||||
"unable_to_change_visibility": "Nelze změnit viditelnost u {count, plural, one {# osoby} few {# osob} other {# lidí}}",
|
"unable_to_change_visibility": "Nelze změnit viditelnost u {count, plural, one {# osoby} few {# osob} other {# lidí}}",
|
||||||
"unable_to_complete_oauth_login": "Nelze dokončit OAuth přihlášení",
|
"unable_to_complete_oauth_login": "Nelze dokončit OAuth přihlášení",
|
||||||
"unable_to_connect": "Nelze se připojit",
|
"unable_to_connect": "Nelze se připojit",
|
||||||
"unable_to_connect_to_server": "Nepodařilo se připojit k serveru",
|
|
||||||
"unable_to_copy_to_clipboard": "Nelze zkopírovat do schránky, ujistěte se, že na stránku přistupujete přes https",
|
"unable_to_copy_to_clipboard": "Nelze zkopírovat do schránky, ujistěte se, že na stránku přistupujete přes https",
|
||||||
"unable_to_create_admin_account": "Nelze vytvořit účet správce",
|
"unable_to_create_admin_account": "Nelze vytvořit účet správce",
|
||||||
"unable_to_create_api_key": "Nelze vytvořit nový API klíč",
|
"unable_to_create_api_key": "Nelze vytvořit nový API klíč",
|
||||||
@@ -904,10 +908,6 @@
|
|||||||
"unable_to_hide_person": "Nelze skrýt osobu",
|
"unable_to_hide_person": "Nelze skrýt osobu",
|
||||||
"unable_to_link_motion_video": "Nelze připojit pohyblivé video",
|
"unable_to_link_motion_video": "Nelze připojit pohyblivé video",
|
||||||
"unable_to_link_oauth_account": "Nelze propojit OAuth účet",
|
"unable_to_link_oauth_account": "Nelze propojit OAuth účet",
|
||||||
"unable_to_load_album": "Nelze načíst album",
|
|
||||||
"unable_to_load_asset_activity": "Nelze načíst aktivitu položky",
|
|
||||||
"unable_to_load_items": "Nelze načíst položky",
|
|
||||||
"unable_to_load_liked_status": "Nelze načíst stav oblíbených",
|
|
||||||
"unable_to_log_out_all_devices": "Nelze odhlásit všechna zařízení",
|
"unable_to_log_out_all_devices": "Nelze odhlásit všechna zařízení",
|
||||||
"unable_to_log_out_device": "Nelze odhlásit zařízení",
|
"unable_to_log_out_device": "Nelze odhlásit zařízení",
|
||||||
"unable_to_login_with_oauth": "Nelze se přihlásit pomocí OAuth",
|
"unable_to_login_with_oauth": "Nelze se přihlásit pomocí OAuth",
|
||||||
@@ -918,12 +918,11 @@
|
|||||||
"unable_to_remove_album_users": "Nelze odebrat uživatele z alba",
|
"unable_to_remove_album_users": "Nelze odebrat uživatele z alba",
|
||||||
"unable_to_remove_api_key": "Nelze odstranit API klíč",
|
"unable_to_remove_api_key": "Nelze odstranit API klíč",
|
||||||
"unable_to_remove_assets_from_shared_link": "Nelze odstranit položky ze sdíleného odkazu",
|
"unable_to_remove_assets_from_shared_link": "Nelze odstranit položky ze sdíleného odkazu",
|
||||||
"unable_to_remove_deleted_assets": "Nelze odstranit offline soubory",
|
|
||||||
"unable_to_remove_library": "Nelze odstranit knihovnu",
|
"unable_to_remove_library": "Nelze odstranit knihovnu",
|
||||||
"unable_to_remove_partner": "Nelze odebrat partnera",
|
"unable_to_remove_partner": "Nelze odebrat partnera",
|
||||||
"unable_to_remove_reaction": "Nelze odstranit reakci",
|
"unable_to_remove_reaction": "Nelze odstranit reakci",
|
||||||
"unable_to_repair_items": "Nelze opravit položky",
|
|
||||||
"unable_to_reset_password": "Nelze obnovit heslo",
|
"unable_to_reset_password": "Nelze obnovit heslo",
|
||||||
|
"unable_to_reset_pin_code": "Nelze resetovat PIN kód",
|
||||||
"unable_to_resolve_duplicate": "Nelze vyřešit duplicitu",
|
"unable_to_resolve_duplicate": "Nelze vyřešit duplicitu",
|
||||||
"unable_to_restore_assets": "Nelze obnovit položky",
|
"unable_to_restore_assets": "Nelze obnovit položky",
|
||||||
"unable_to_restore_trash": "Nelze obnovit koš",
|
"unable_to_restore_trash": "Nelze obnovit koš",
|
||||||
@@ -957,10 +956,9 @@
|
|||||||
"exif_bottom_sheet_location": "POLOHA",
|
"exif_bottom_sheet_location": "POLOHA",
|
||||||
"exif_bottom_sheet_people": "LIDÉ",
|
"exif_bottom_sheet_people": "LIDÉ",
|
||||||
"exif_bottom_sheet_person_add_person": "Přidat jméno",
|
"exif_bottom_sheet_person_add_person": "Přidat jméno",
|
||||||
"exif_bottom_sheet_person_age": "{} let",
|
"exif_bottom_sheet_person_age_months": "{months} měsíců",
|
||||||
"exif_bottom_sheet_person_age_months": "{} měsíců",
|
"exif_bottom_sheet_person_age_year_months": "1 rok a {months} měsíců",
|
||||||
"exif_bottom_sheet_person_age_year_months": "1 rok a {} měsíců",
|
"exif_bottom_sheet_person_age_years": "{years} let",
|
||||||
"exif_bottom_sheet_person_age_years": "{} let",
|
|
||||||
"exit_slideshow": "Ukončit prezentaci",
|
"exit_slideshow": "Ukončit prezentaci",
|
||||||
"expand_all": "Rozbalit vše",
|
"expand_all": "Rozbalit vše",
|
||||||
"experimental_settings_new_asset_list_subtitle": "Zpracovávám",
|
"experimental_settings_new_asset_list_subtitle": "Zpracovávám",
|
||||||
@@ -978,9 +976,10 @@
|
|||||||
"external": "Externí",
|
"external": "Externí",
|
||||||
"external_libraries": "Externí knihovny",
|
"external_libraries": "Externí knihovny",
|
||||||
"external_network": "Externí síť",
|
"external_network": "Externí síť",
|
||||||
"external_network_sheet_info": "Pokud nejste v preferované síti WiFi, aplikace se připojí k serveru prostřednictvím první z níže uvedených adres URL, které může dosáhnout, počínaje shora dolů",
|
"external_network_sheet_info": "Pokud nejste v preferované síti Wi-Fi, aplikace se připojí k serveru prostřednictvím první z níže uvedených adres URL, které může dosáhnout, počínaje shora dolů",
|
||||||
"face_unassigned": "Nepřiřazena",
|
"face_unassigned": "Nepřiřazena",
|
||||||
"failed": "Selhalo",
|
"failed": "Selhalo",
|
||||||
|
"failed_to_authenticate": "Ověření se nezdařilo",
|
||||||
"failed_to_load_assets": "Nepodařilo se načíst položky",
|
"failed_to_load_assets": "Nepodařilo se načíst položky",
|
||||||
"failed_to_load_folder": "Nepodařilo se načíst složku",
|
"failed_to_load_folder": "Nepodařilo se načíst složku",
|
||||||
"favorite": "Oblíbit",
|
"favorite": "Oblíbit",
|
||||||
@@ -1004,6 +1003,8 @@
|
|||||||
"folders": "Složky",
|
"folders": "Složky",
|
||||||
"folders_feature_description": "Procházení zobrazení složek s fotografiemi a videi v souborovém systému",
|
"folders_feature_description": "Procházení zobrazení složek s fotografiemi a videi v souborovém systému",
|
||||||
"forward": "Dopředu",
|
"forward": "Dopředu",
|
||||||
|
"gcast_enabled": "Google Cast",
|
||||||
|
"gcast_enabled_description": "Tato funkce načítá externí zdroje z Googlu, aby mohla fungovat.",
|
||||||
"general": "Obecné",
|
"general": "Obecné",
|
||||||
"get_help": "Získat pomoc",
|
"get_help": "Získat pomoc",
|
||||||
"get_wifiname_error": "Nepodařilo se získat název Wi-Fi. Zkontrolujte, zda jste udělili potřebná oprávnění a zda jste připojeni k Wi-Fi síti",
|
"get_wifiname_error": "Nepodařilo se získat název Wi-Fi. Zkontrolujte, zda jste udělili potřebná oprávnění a zda jste připojeni k Wi-Fi síti",
|
||||||
@@ -1046,10 +1047,13 @@
|
|||||||
"home_page_favorite_err_local": "Zatím není možné zařadit lokální média mezi oblíbená, přeskakuji",
|
"home_page_favorite_err_local": "Zatím není možné zařadit lokální média mezi oblíbená, přeskakuji",
|
||||||
"home_page_favorite_err_partner": "Položky partnera nelze označit jako oblíbené, přeskakuji",
|
"home_page_favorite_err_partner": "Položky partnera nelze označit jako oblíbené, přeskakuji",
|
||||||
"home_page_first_time_notice": "Pokud aplikaci používáte poprvé, nezapomeňte si vybrat zálohovaná alba, aby se na časové ose mohly nacházet fotografie a videa z vybraných alb",
|
"home_page_first_time_notice": "Pokud aplikaci používáte poprvé, nezapomeňte si vybrat zálohovaná alba, aby se na časové ose mohly nacházet fotografie a videa z vybraných alb",
|
||||||
|
"home_page_locked_error_local": "Místní položky nelze přesunout do uzamčené složky, přeskočí se",
|
||||||
|
"home_page_locked_error_partner": "Položky partnera nelze přesunout do uzamčené složky, přeskočí se",
|
||||||
"home_page_share_err_local": "Nelze sdílet místní položky prostřednictvím odkazu, přeskakuji",
|
"home_page_share_err_local": "Nelze sdílet místní položky prostřednictvím odkazu, přeskakuji",
|
||||||
"home_page_upload_err_limit": "Lze nahrát nejvýše 30 položek najednou, přeskakuji",
|
"home_page_upload_err_limit": "Lze nahrát nejvýše 30 položek najednou, přeskakuji",
|
||||||
"host": "Hostitel",
|
"host": "Hostitel",
|
||||||
"hour": "Hodina",
|
"hour": "Hodina",
|
||||||
|
"id": "ID",
|
||||||
"ignore_icloud_photos": "Ignorovat fotografie na iCloudu",
|
"ignore_icloud_photos": "Ignorovat fotografie na iCloudu",
|
||||||
"ignore_icloud_photos_description": "Fotografie uložené na iCloudu se nebudou nahrávat na Immich server",
|
"ignore_icloud_photos_description": "Fotografie uložené na iCloudu se nebudou nahrávat na Immich server",
|
||||||
"image": "Obrázek",
|
"image": "Obrázek",
|
||||||
@@ -1089,6 +1093,12 @@
|
|||||||
"invalid_date_format": "Chybný formát data",
|
"invalid_date_format": "Chybný formát data",
|
||||||
"invite_people": "Pozvat lidi",
|
"invite_people": "Pozvat lidi",
|
||||||
"invite_to_album": "Pozvat do alba",
|
"invite_to_album": "Pozvat do alba",
|
||||||
|
"ios_debug_info_fetch_ran_at": "Data načtena {dateTime}",
|
||||||
|
"ios_debug_info_last_sync_at": "Poslední synchronizace {dateTime}",
|
||||||
|
"ios_debug_info_no_processes_queued": "Žádné procesy na pozadí ve frontě",
|
||||||
|
"ios_debug_info_no_sync_yet": "Dosud nebyla spuštěna žádná úloha synchronizace na pozadí",
|
||||||
|
"ios_debug_info_processes_queued": "{count, plural, one {{count} proces na pozadí ve frontě} few {{count} procesy na pozadí ve frontě} other {{count} procesů na pozadí ve frontě}}",
|
||||||
|
"ios_debug_info_processing_ran_at": "Zpracování spuštěno {dateTime}",
|
||||||
"items_count": "{count, plural, one {# položka} few {# položky} other {# položek}}",
|
"items_count": "{count, plural, one {# položka} few {# položky} other {# položek}}",
|
||||||
"jobs": "Úlohy",
|
"jobs": "Úlohy",
|
||||||
"keep": "Ponechat",
|
"keep": "Ponechat",
|
||||||
@@ -1097,6 +1107,9 @@
|
|||||||
"kept_this_deleted_others": "Ponechána tato položka a {count, plural, one {odstraněna # položka} few {odstraněny # položky} other {odstraněno # položek}}",
|
"kept_this_deleted_others": "Ponechána tato položka a {count, plural, one {odstraněna # položka} few {odstraněny # položky} other {odstraněno # položek}}",
|
||||||
"keyboard_shortcuts": "Klávesové zkratky",
|
"keyboard_shortcuts": "Klávesové zkratky",
|
||||||
"language": "Jazyk",
|
"language": "Jazyk",
|
||||||
|
"language_no_results_subtitle": "Zkuste upravit hledaný výraz",
|
||||||
|
"language_no_results_title": "Nebyly nalezeny žádné jazyky",
|
||||||
|
"language_search_hint": "Vyhledat jazyk...",
|
||||||
"language_setting_description": "Vyberte upřednostňovaný jazyk",
|
"language_setting_description": "Vyberte upřednostňovaný jazyk",
|
||||||
"last_seen": "Naposledy viděno",
|
"last_seen": "Naposledy viděno",
|
||||||
"latest_version": "Nejnovější verze",
|
"latest_version": "Nejnovější verze",
|
||||||
@@ -1122,15 +1135,18 @@
|
|||||||
"list": "Seznam",
|
"list": "Seznam",
|
||||||
"loading": "Načítání",
|
"loading": "Načítání",
|
||||||
"loading_search_results_failed": "Načítání výsledků vyhledávání se nezdařilo",
|
"loading_search_results_failed": "Načítání výsledků vyhledávání se nezdařilo",
|
||||||
|
"local_asset_cast_failed": "Nelze odeslat položku, která není nahraná na serveru",
|
||||||
"local_network": "Místní síť",
|
"local_network": "Místní síť",
|
||||||
"local_network_sheet_info": "Aplikace se při použití zadané sítě Wi-Fi připojí k serveru prostřednictvím tohoto URL",
|
"local_network_sheet_info": "Aplikace se při použití zadané sítě Wi-Fi připojí k serveru prostřednictvím tohoto URL",
|
||||||
"location_permission": "Oprávnění polohy",
|
"location_permission": "Oprávnění polohy",
|
||||||
"location_permission_content": "Aby bylo možné používat funkci automatického přepínání, potřebuje Immich oprávnění k přesné poloze, aby mohl přečíst název aktuální WiFi sítě",
|
"location_permission_content": "Aby bylo možné používat funkci automatického přepínání, potřebuje Immich oprávnění k přesné poloze, aby mohl přečíst název aktuální sítě Wi-Fi",
|
||||||
"location_picker_choose_on_map": "Vyberte na mapě",
|
"location_picker_choose_on_map": "Vyberte na mapě",
|
||||||
"location_picker_latitude_error": "Zadejte platnou zeměpisnou šířku",
|
"location_picker_latitude_error": "Zadejte platnou zeměpisnou šířku",
|
||||||
"location_picker_latitude_hint": "Zadejte vlastní zeměpisnou šířku",
|
"location_picker_latitude_hint": "Zadejte vlastní zeměpisnou šířku",
|
||||||
"location_picker_longitude_error": "Zadejte platnou zeměpisnou délku",
|
"location_picker_longitude_error": "Zadejte platnou zeměpisnou délku",
|
||||||
"location_picker_longitude_hint": "Zadejte vlastní zeměpisnou délku",
|
"location_picker_longitude_hint": "Zadejte vlastní zeměpisnou délku",
|
||||||
|
"lock": "Zamknout",
|
||||||
|
"locked_folder": "Uzamčená složka",
|
||||||
"log_out": "Odhlásit",
|
"log_out": "Odhlásit",
|
||||||
"log_out_all_devices": "Odhlásit všechna zařízení",
|
"log_out_all_devices": "Odhlásit všechna zařízení",
|
||||||
"logged_out_all_devices": "Všechna zařízení odhlášena",
|
"logged_out_all_devices": "Všechna zařízení odhlášena",
|
||||||
@@ -1175,8 +1191,8 @@
|
|||||||
"manage_your_devices": "Správa přihlášených zařízení",
|
"manage_your_devices": "Správa přihlášených zařízení",
|
||||||
"manage_your_oauth_connection": "Správa OAuth propojení",
|
"manage_your_oauth_connection": "Správa OAuth propojení",
|
||||||
"map": "Mapa",
|
"map": "Mapa",
|
||||||
"map_assets_in_bound": "{} fotka",
|
"map_assets_in_bound": "{count} fotka",
|
||||||
"map_assets_in_bounds": "{} fotek",
|
"map_assets_in_bounds": "{count} fotek",
|
||||||
"map_cannot_get_user_location": "Nelze zjistit polohu uživatele",
|
"map_cannot_get_user_location": "Nelze zjistit polohu uživatele",
|
||||||
"map_location_dialog_yes": "Ano",
|
"map_location_dialog_yes": "Ano",
|
||||||
"map_location_picker_page_use_location": "Použít tuto polohu",
|
"map_location_picker_page_use_location": "Použít tuto polohu",
|
||||||
@@ -1190,15 +1206,18 @@
|
|||||||
"map_settings": "Nastavení mapy",
|
"map_settings": "Nastavení mapy",
|
||||||
"map_settings_dark_mode": "Tmavý režim",
|
"map_settings_dark_mode": "Tmavý režim",
|
||||||
"map_settings_date_range_option_day": "Posledních 24 hodin",
|
"map_settings_date_range_option_day": "Posledních 24 hodin",
|
||||||
"map_settings_date_range_option_days": "Posledních {} dní",
|
"map_settings_date_range_option_days": "Posledních {days} dní",
|
||||||
"map_settings_date_range_option_year": "Poslední rok",
|
"map_settings_date_range_option_year": "Poslední rok",
|
||||||
"map_settings_date_range_option_years": "Poslední {} roky",
|
"map_settings_date_range_option_years": "Poslední {years} roky",
|
||||||
"map_settings_dialog_title": "Nastavení map",
|
"map_settings_dialog_title": "Nastavení map",
|
||||||
"map_settings_include_show_archived": "Zahrnout archivované",
|
"map_settings_include_show_archived": "Zahrnout archivované",
|
||||||
"map_settings_include_show_partners": "Včetně partnerů",
|
"map_settings_include_show_partners": "Včetně partnerů",
|
||||||
"map_settings_only_show_favorites": "Zobrazit pouze oblíbené",
|
"map_settings_only_show_favorites": "Zobrazit pouze oblíbené",
|
||||||
"map_settings_theme_settings": "Motiv mapy",
|
"map_settings_theme_settings": "Motiv mapy",
|
||||||
"map_zoom_to_see_photos": "Oddálit pro zobrazení fotografií",
|
"map_zoom_to_see_photos": "Oddálit pro zobrazení fotografií",
|
||||||
|
"mark_all_as_read": "Označit vše jako přečtené",
|
||||||
|
"mark_as_read": "Označit jako přečtené",
|
||||||
|
"marked_all_as_read": "Vše označeno jako přečtené",
|
||||||
"matches": "Shody",
|
"matches": "Shody",
|
||||||
"media_type": "Typ média",
|
"media_type": "Typ média",
|
||||||
"memories": "Vzpomínky",
|
"memories": "Vzpomínky",
|
||||||
@@ -1207,8 +1226,6 @@
|
|||||||
"memories_setting_description": "Správa toho, co vidíte ve svých vzpomínkách",
|
"memories_setting_description": "Správa toho, co vidíte ve svých vzpomínkách",
|
||||||
"memories_start_over": "Začít znovu",
|
"memories_start_over": "Začít znovu",
|
||||||
"memories_swipe_to_close": "Přejetím nahoru zavřete",
|
"memories_swipe_to_close": "Přejetím nahoru zavřete",
|
||||||
"memories_year_ago": "Před rokem",
|
|
||||||
"memories_years_ago": "Před {} lety",
|
|
||||||
"memory": "Vzpomínka",
|
"memory": "Vzpomínka",
|
||||||
"memory_lane_title": "Řada vzpomínek {title}",
|
"memory_lane_title": "Řada vzpomínek {title}",
|
||||||
"menu": "Nabídka",
|
"menu": "Nabídka",
|
||||||
@@ -1225,6 +1242,12 @@
|
|||||||
"month": "Měsíc",
|
"month": "Měsíc",
|
||||||
"monthly_title_text_date_format": "LLLL y",
|
"monthly_title_text_date_format": "LLLL y",
|
||||||
"more": "Více",
|
"more": "Více",
|
||||||
|
"move": "Přesunout",
|
||||||
|
"move_off_locked_folder": "Přesunout z uzamčené složky",
|
||||||
|
"move_to_locked_folder": "Přesunout do uzamčené složky",
|
||||||
|
"move_to_locked_folder_confirmation": "Tyto fotky a videa budou odstraněny ze všech alb a bude je možné zobrazit pouze v uzamčené složce",
|
||||||
|
"moved_to_archive": "{count, plural, one {Přesunuta # položka} few {Přesunuty # položky} other {Přesunuto # položek}} do archivu",
|
||||||
|
"moved_to_library": "{count, plural, one {Přesunuta # položka} few {Přesunuty # položky} other {Přesunuto # položek}} do knihovny",
|
||||||
"moved_to_trash": "Přesunuto do koše",
|
"moved_to_trash": "Přesunuto do koše",
|
||||||
"multiselect_grid_edit_date_time_err_read_only": "Nelze upravit datum položek pouze pro čtení, přeskakuji",
|
"multiselect_grid_edit_date_time_err_read_only": "Nelze upravit datum položek pouze pro čtení, přeskakuji",
|
||||||
"multiselect_grid_edit_gps_err_read_only": "Nelze upravit polohu položek pouze pro čtení, přeskakuji",
|
"multiselect_grid_edit_gps_err_read_only": "Nelze upravit polohu položek pouze pro čtení, přeskakuji",
|
||||||
@@ -1239,6 +1262,8 @@
|
|||||||
"new_api_key": "Nový API klíč",
|
"new_api_key": "Nový API klíč",
|
||||||
"new_password": "Nové heslo",
|
"new_password": "Nové heslo",
|
||||||
"new_person": "Nová osoba",
|
"new_person": "Nová osoba",
|
||||||
|
"new_pin_code": "Nový PIN kód",
|
||||||
|
"new_pin_code_subtitle": "Poprvé přistupujete k uzamčené složce. Vytvořte si kód PIN pro bezpečný přístup na tuto stránku",
|
||||||
"new_user_created": "Vytvořen nový uživatel",
|
"new_user_created": "Vytvořen nový uživatel",
|
||||||
"new_version_available": "NOVÁ VERZE K DISPOZICI",
|
"new_version_available": "NOVÁ VERZE K DISPOZICI",
|
||||||
"newest_first": "Nejnovější první",
|
"newest_first": "Nejnovější první",
|
||||||
@@ -1251,12 +1276,16 @@
|
|||||||
"no_archived_assets_message": "Archivujte fotografie a videa a skryjte je ze zobrazení v sekci Fotky",
|
"no_archived_assets_message": "Archivujte fotografie a videa a skryjte je ze zobrazení v sekci Fotky",
|
||||||
"no_assets_message": "KLIKNĚTE PRO NAHRÁNÍ PRVNÍ FOTOGRAFIE",
|
"no_assets_message": "KLIKNĚTE PRO NAHRÁNÍ PRVNÍ FOTOGRAFIE",
|
||||||
"no_assets_to_show": "Žádné položky k zobrazení",
|
"no_assets_to_show": "Žádné položky k zobrazení",
|
||||||
|
"no_cast_devices_found": "Nebyla nalezena žádná zařízení",
|
||||||
"no_duplicates_found": "Nebyly nalezeny žádné duplicity.",
|
"no_duplicates_found": "Nebyly nalezeny žádné duplicity.",
|
||||||
"no_exif_info_available": "Exif není k dispozici",
|
"no_exif_info_available": "Exif není k dispozici",
|
||||||
"no_explore_results_message": "Nahrajte další fotografie a prozkoumejte svou sbírku.",
|
"no_explore_results_message": "Nahrajte další fotografie a prozkoumejte svou sbírku.",
|
||||||
"no_favorites_message": "Přidejte si oblíbené položky a rychle najděte své nejlepší obrázky a videa",
|
"no_favorites_message": "Přidejte si oblíbené položky a rychle najděte své nejlepší obrázky a videa",
|
||||||
"no_libraries_message": "Vytvořte si externí knihovnu pro zobrazení fotografií a videí",
|
"no_libraries_message": "Vytvořte si externí knihovnu pro zobrazení fotografií a videí",
|
||||||
|
"no_locked_photos_message": "Fotky a videa v uzamčené složce jsou skryté a při procházení nebo vyhledávání v knihovně se nezobrazují.",
|
||||||
"no_name": "Bez jména",
|
"no_name": "Bez jména",
|
||||||
|
"no_notifications": "Žádná oznámení",
|
||||||
|
"no_people_found": "Nebyli nalezeni žádní odpovídající lidé",
|
||||||
"no_places": "Žádná místa",
|
"no_places": "Žádná místa",
|
||||||
"no_results": "Žádné výsledky",
|
"no_results": "Žádné výsledky",
|
||||||
"no_results_description": "Zkuste použít synonymum nebo obecnější klíčové slovo",
|
"no_results_description": "Zkuste použít synonymum nebo obecnější klíčové slovo",
|
||||||
@@ -1265,6 +1294,7 @@
|
|||||||
"not_selected": "Není vybráno",
|
"not_selected": "Není vybráno",
|
||||||
"note_apply_storage_label_to_previously_uploaded assets": "Upozornění: Chcete-li použít štítek úložiště na dříve nahrané položky, spusťte příkaz",
|
"note_apply_storage_label_to_previously_uploaded assets": "Upozornění: Chcete-li použít štítek úložiště na dříve nahrané položky, spusťte příkaz",
|
||||||
"notes": "Poznámky",
|
"notes": "Poznámky",
|
||||||
|
"nothing_here_yet": "Zatím zde nic není",
|
||||||
"notification_permission_dialog_content": "Chcete-li povolit oznámení, přejděte do nastavení a vyberte možnost povolit.",
|
"notification_permission_dialog_content": "Chcete-li povolit oznámení, přejděte do nastavení a vyberte možnost povolit.",
|
||||||
"notification_permission_list_tile_content": "Udělte oprávnění k aktivaci oznámení.",
|
"notification_permission_list_tile_content": "Udělte oprávnění k aktivaci oznámení.",
|
||||||
"notification_permission_list_tile_enable_button": "Povolit oznámení",
|
"notification_permission_list_tile_enable_button": "Povolit oznámení",
|
||||||
@@ -1275,15 +1305,15 @@
|
|||||||
"oauth": "OAuth",
|
"oauth": "OAuth",
|
||||||
"official_immich_resources": "Oficiální zdroje Immich",
|
"official_immich_resources": "Oficiální zdroje Immich",
|
||||||
"offline": "Offline",
|
"offline": "Offline",
|
||||||
"offline_paths": "Offline cesty",
|
|
||||||
"offline_paths_description": "Tyto výsledky mohou být způsobeny ručním odstraněním souborů, které nejsou součástí externí knihovny.",
|
|
||||||
"ok": "Ok",
|
"ok": "Ok",
|
||||||
"oldest_first": "Nejstarší první",
|
"oldest_first": "Nejstarší první",
|
||||||
"on_this_device": "V tomto zařízení",
|
"on_this_device": "V tomto zařízení",
|
||||||
"onboarding": "Zahájení",
|
"onboarding": "Zahájení",
|
||||||
"onboarding_privacy_description": "Následující (volitelné) funkce jsou závislé na externích službách a lze je kdykoli zakázat v nastavení správy.",
|
"onboarding_locale_description": "Vyberte preferovaný jazyk. Tento výběr můžete později změnit v nastavení.",
|
||||||
|
"onboarding_privacy_description": "Následující (volitelné) funkce jsou závislé na externích službách a lze je kdykoli zakázat v nastavení.",
|
||||||
|
"onboarding_server_welcome_description": "Pojďme nastavit vaši instanci pomocí několika běžných nastavení.",
|
||||||
"onboarding_theme_description": "Zvolte si barevný motiv pro svou instanci. Můžete to později změnit v nastavení.",
|
"onboarding_theme_description": "Zvolte si barevný motiv pro svou instanci. Můžete to později změnit v nastavení.",
|
||||||
"onboarding_welcome_description": "Nastavíme vaši instanci pomocí několika běžných nastavení.",
|
"onboarding_user_welcome_description": "Pojďme na to!",
|
||||||
"onboarding_welcome_user": "Vítej, {user}",
|
"onboarding_welcome_user": "Vítej, {user}",
|
||||||
"online": "Online",
|
"online": "Online",
|
||||||
"only_favorites": "Pouze oblíbené",
|
"only_favorites": "Pouze oblíbené",
|
||||||
@@ -1311,7 +1341,7 @@
|
|||||||
"partner_page_partner_add_failed": "Nepodařilo se přidat partnera",
|
"partner_page_partner_add_failed": "Nepodařilo se přidat partnera",
|
||||||
"partner_page_select_partner": "Vyberte partnera",
|
"partner_page_select_partner": "Vyberte partnera",
|
||||||
"partner_page_shared_to_title": "Sdíleno",
|
"partner_page_shared_to_title": "Sdíleno",
|
||||||
"partner_page_stop_sharing_content": "{} již nebude mít přístup k vašim fotografiím.",
|
"partner_page_stop_sharing_content": "{partner} již nebude mít přístup k vašim fotografiím.",
|
||||||
"partner_sharing": "Sdílení mezi partnery",
|
"partner_sharing": "Sdílení mezi partnery",
|
||||||
"partners": "Partneři",
|
"partners": "Partneři",
|
||||||
"password": "Heslo",
|
"password": "Heslo",
|
||||||
@@ -1340,6 +1370,8 @@
|
|||||||
"permanently_delete_assets_prompt": "Opravdu chcete trvale smazat {count, plural, one {tuto položku} few {tyto <b>#</b> položky} other {těchto <b>#</b> položek}}? Tím {count, plural, one {ji také odstraníte z jejích} other {je také odstraníte z jejich}} alb.",
|
"permanently_delete_assets_prompt": "Opravdu chcete trvale smazat {count, plural, one {tuto položku} few {tyto <b>#</b> položky} other {těchto <b>#</b> položek}}? Tím {count, plural, one {ji také odstraníte z jejích} other {je také odstraníte z jejich}} alb.",
|
||||||
"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}}",
|
||||||
|
"permission": "Oprávnění",
|
||||||
|
"permission_empty": "Vaše oprávnění by nemělo být prázdné",
|
||||||
"permission_onboarding_back": "Zpět",
|
"permission_onboarding_back": "Zpět",
|
||||||
"permission_onboarding_continue_anyway": "Přesto pokračovat",
|
"permission_onboarding_continue_anyway": "Přesto pokračovat",
|
||||||
"permission_onboarding_get_started": "Začít",
|
"permission_onboarding_get_started": "Začít",
|
||||||
@@ -1349,7 +1381,7 @@
|
|||||||
"permission_onboarding_permission_limited": "Přístup omezen. Chcete-li používat Immich k zálohování a správě celé vaší kolekce galerií, povolte v nastavení přístup k fotkám a videím.",
|
"permission_onboarding_permission_limited": "Přístup omezen. Chcete-li používat Immich k zálohování a správě celé vaší kolekce galerií, povolte v nastavení přístup k fotkám a videím.",
|
||||||
"permission_onboarding_request": "Immich potřebuje přístup k zobrazení vašich fotek a videí.",
|
"permission_onboarding_request": "Immich potřebuje přístup k zobrazení vašich fotek a videí.",
|
||||||
"person": "Osoba",
|
"person": "Osoba",
|
||||||
"person_birthdate": "Narozen/a {date}",
|
"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",
|
||||||
@@ -1357,6 +1389,10 @@
|
|||||||
"photos_count": "{count, plural, one {{count, number} fotka} few {{count, number} fotky} other {{count, number} fotek}}",
|
"photos_count": "{count, plural, one {{count, number} fotka} few {{count, number} fotky} other {{count, number} fotek}}",
|
||||||
"photos_from_previous_years": "Fotky z předchozích let",
|
"photos_from_previous_years": "Fotky z předchozích let",
|
||||||
"pick_a_location": "Vyberte polohu",
|
"pick_a_location": "Vyberte polohu",
|
||||||
|
"pin_code_changed_successfully": "PIN kód byl úspěšně změněn",
|
||||||
|
"pin_code_reset_successfully": "PIN kód úspěšně resetován",
|
||||||
|
"pin_code_setup_successfully": "PIN kód úspěšně nastaven",
|
||||||
|
"pin_verification": "Ověření PIN kódu",
|
||||||
"place": "Místo",
|
"place": "Místo",
|
||||||
"places": "Místa",
|
"places": "Místa",
|
||||||
"places_count": "{count, plural, one {{count, number} místo} few {{count, number} místa} other {{count, number} míst}}",
|
"places_count": "{count, plural, one {{count, number} místo} few {{count, number} místa} other {{count, number} míst}}",
|
||||||
@@ -1364,6 +1400,7 @@
|
|||||||
"play_memories": "Přehrát vzpomníky",
|
"play_memories": "Přehrát vzpomníky",
|
||||||
"play_motion_photo": "Přehrát pohybovou fotografii",
|
"play_motion_photo": "Přehrát pohybovou fotografii",
|
||||||
"play_or_pause_video": "Přehrát nebo pozastavit video",
|
"play_or_pause_video": "Přehrát nebo pozastavit video",
|
||||||
|
"please_auth_to_access": "Pro přístup se prosím ověřte",
|
||||||
"port": "Port",
|
"port": "Port",
|
||||||
"preferences_settings_subtitle": "Správa předvoleb aplikace",
|
"preferences_settings_subtitle": "Správa předvoleb aplikace",
|
||||||
"preferences_settings_title": "Předvolby",
|
"preferences_settings_title": "Předvolby",
|
||||||
@@ -1371,9 +1408,13 @@
|
|||||||
"preview": "Náhled",
|
"preview": "Náhled",
|
||||||
"previous": "Předchozí",
|
"previous": "Předchozí",
|
||||||
"previous_memory": "Předchozí vzpomínka",
|
"previous_memory": "Předchozí vzpomínka",
|
||||||
"previous_or_next_photo": "Předchozí nebo další fotka",
|
"previous_or_next_day": "Následující/předchozí den",
|
||||||
|
"previous_or_next_month": "Následující/předchozí měsíc",
|
||||||
|
"previous_or_next_photo": "Následující/předchozí fotografie",
|
||||||
|
"previous_or_next_year": "Následující/předchozí rok",
|
||||||
"primary": "Primární",
|
"primary": "Primární",
|
||||||
"privacy": "Soukromí",
|
"privacy": "Soukromí",
|
||||||
|
"profile": "Profil",
|
||||||
"profile_drawer_app_logs": "Logy",
|
"profile_drawer_app_logs": "Logy",
|
||||||
"profile_drawer_client_out_of_date_major": "Mobilní aplikace je zastaralá. Aktualizujte ji na nejnovější hlavní verzi.",
|
"profile_drawer_client_out_of_date_major": "Mobilní aplikace je zastaralá. Aktualizujte ji na nejnovější hlavní verzi.",
|
||||||
"profile_drawer_client_out_of_date_minor": "Mobilní aplikace je zastaralá. Aktualizujte ji na nejnovější verzi.",
|
"profile_drawer_client_out_of_date_minor": "Mobilní aplikace je zastaralá. Aktualizujte ji na nejnovější verzi.",
|
||||||
@@ -1387,7 +1428,7 @@
|
|||||||
"public_share": "Veřejné sdílení",
|
"public_share": "Veřejné sdílení",
|
||||||
"purchase_account_info": "Podporovatel",
|
"purchase_account_info": "Podporovatel",
|
||||||
"purchase_activated_subtitle": "Děkujeme vám za podporu aplikace Immich a softwaru s otevřeným zdrojovým kódem",
|
"purchase_activated_subtitle": "Děkujeme vám za podporu aplikace Immich a softwaru s otevřeným zdrojovým kódem",
|
||||||
"purchase_activated_time": "Aktivováno dne {date, date}",
|
"purchase_activated_time": "Aktivováno dne {date}",
|
||||||
"purchase_activated_title": "Váš klíč byl úspěšně aktivován",
|
"purchase_activated_title": "Váš klíč byl úspěšně aktivován",
|
||||||
"purchase_button_activate": "Aktivovat",
|
"purchase_button_activate": "Aktivovat",
|
||||||
"purchase_button_buy": "Koupit",
|
"purchase_button_buy": "Koupit",
|
||||||
@@ -1432,6 +1473,8 @@
|
|||||||
"recent_searches": "Nedávná vyhledávání",
|
"recent_searches": "Nedávná vyhledávání",
|
||||||
"recently_added": "Nedávno přidané",
|
"recently_added": "Nedávno přidané",
|
||||||
"recently_added_page_title": "Nedávno přidané",
|
"recently_added_page_title": "Nedávno přidané",
|
||||||
|
"recently_taken": "Nedávno pořízené",
|
||||||
|
"recently_taken_page_title": "Nedávno pořízené",
|
||||||
"refresh": "Obnovit",
|
"refresh": "Obnovit",
|
||||||
"refresh_encoded_videos": "Obnovit kódovaná videa",
|
"refresh_encoded_videos": "Obnovit kódovaná videa",
|
||||||
"refresh_faces": "Obnovit obličeje",
|
"refresh_faces": "Obnovit obličeje",
|
||||||
@@ -1451,9 +1494,12 @@
|
|||||||
"remove_deleted_assets": "Odstranit offline soubory",
|
"remove_deleted_assets": "Odstranit offline soubory",
|
||||||
"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_locked_folder": "Odstranit z uzamčené složky",
|
||||||
|
"remove_from_locked_folder_confirmation": "Opravdu chcete tyto fotky a videa přesunout z uzamčené složky? Budou viditelné ve vaší knihovně.",
|
||||||
"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_memory": "Odstranit vzpomínku",
|
||||||
"remove_photo_from_memory": "Odstranit fotografii z této vzpomínky",
|
"remove_photo_from_memory": "Odstranit fotografii z této vzpomínky",
|
||||||
|
"remove_tag": "Odstranit značku",
|
||||||
"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}",
|
||||||
@@ -1474,6 +1520,7 @@
|
|||||||
"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í",
|
||||||
|
"reset_pin_code": "Resetovat PIN kód",
|
||||||
"reset_to_default": "Obnovit výchozí nastavení",
|
"reset_to_default": "Obnovit výchozí nastavení",
|
||||||
"resolve_duplicates": "Vyřešit duplicity",
|
"resolve_duplicates": "Vyřešit duplicity",
|
||||||
"resolved_all_duplicates": "Vyřešeny všechny duplicity",
|
"resolved_all_duplicates": "Vyřešeny všechny duplicity",
|
||||||
@@ -1566,6 +1613,7 @@
|
|||||||
"select_keep_all": "Vybrat ponechat vše",
|
"select_keep_all": "Vybrat ponechat vše",
|
||||||
"select_library_owner": "Vyberte vlastníka knihovny",
|
"select_library_owner": "Vyberte vlastníka knihovny",
|
||||||
"select_new_face": "Výběr nového obličeje",
|
"select_new_face": "Výběr nového obličeje",
|
||||||
|
"select_person_to_tag": "Vyberte osobu, kterou chcete označit",
|
||||||
"select_photos": "Vybrat fotky",
|
"select_photos": "Vybrat fotky",
|
||||||
"select_trash_all": "Vybrat vyhodit vše",
|
"select_trash_all": "Vybrat vyhodit vše",
|
||||||
"select_user_for_sharing_page_err_album": "Nepodařilo se vytvořit album",
|
"select_user_for_sharing_page_err_album": "Nepodařilo se vytvořit album",
|
||||||
@@ -1578,6 +1626,7 @@
|
|||||||
"server_info_box_server_url": "URL serveru",
|
"server_info_box_server_url": "URL serveru",
|
||||||
"server_offline": "Server offline",
|
"server_offline": "Server offline",
|
||||||
"server_online": "Server online",
|
"server_online": "Server online",
|
||||||
|
"server_privacy": "Ochrana soukromí serveru",
|
||||||
"server_stats": "Statistiky serveru",
|
"server_stats": "Statistiky serveru",
|
||||||
"server_version": "Verze serveru",
|
"server_version": "Verze serveru",
|
||||||
"set": "Nastavit",
|
"set": "Nastavit",
|
||||||
@@ -1587,6 +1636,7 @@
|
|||||||
"set_date_of_birth": "Nastavit datum narození",
|
"set_date_of_birth": "Nastavit datum narození",
|
||||||
"set_profile_picture": "Nastavit profilový obrázek",
|
"set_profile_picture": "Nastavit profilový obrázek",
|
||||||
"set_slideshow_to_fullscreen": "Nastavit prezentaci na celou obrazovku",
|
"set_slideshow_to_fullscreen": "Nastavit prezentaci na celou obrazovku",
|
||||||
|
"set_stack_primary_asset": "Nastavit jako hlavní položku",
|
||||||
"setting_image_viewer_help": "V prohlížeči detailů se nejprve načte malá miniatura, poté se načte náhled střední velikosti (je-li povolen) a nakonec se načte originál (je-li povolen).",
|
"setting_image_viewer_help": "V prohlížeči detailů se nejprve načte malá miniatura, poté se načte náhled střední velikosti (je-li povolen) a nakonec se načte originál (je-li povolen).",
|
||||||
"setting_image_viewer_original_subtitle": "Umožňuje načíst původní obrázek v plném rozlišení (velký!). Zakažte pro snížení využití dat (v síti i v mezipaměti zařízení).",
|
"setting_image_viewer_original_subtitle": "Umožňuje načíst původní obrázek v plném rozlišení (velký!). Zakažte pro snížení využití dat (v síti i v mezipaměti zařízení).",
|
||||||
"setting_image_viewer_original_title": "Načíst původní obrázek",
|
"setting_image_viewer_original_title": "Načíst původní obrázek",
|
||||||
@@ -1595,13 +1645,12 @@
|
|||||||
"setting_image_viewer_title": "Obrázky",
|
"setting_image_viewer_title": "Obrázky",
|
||||||
"setting_languages_apply": "Použít",
|
"setting_languages_apply": "Použít",
|
||||||
"setting_languages_subtitle": "Změna jazyka aplikace",
|
"setting_languages_subtitle": "Změna jazyka aplikace",
|
||||||
"setting_languages_title": "Jazyk",
|
"setting_notifications_notify_failures_grace_period": "Oznámení o selhání zálohování na pozadí: {duration}",
|
||||||
"setting_notifications_notify_failures_grace_period": "Oznámení o selhání zálohování na pozadí: {}",
|
"setting_notifications_notify_hours": "{count} hodin",
|
||||||
"setting_notifications_notify_hours": "{} hodin",
|
|
||||||
"setting_notifications_notify_immediately": "okamžitě",
|
"setting_notifications_notify_immediately": "okamžitě",
|
||||||
"setting_notifications_notify_minutes": "{} minut",
|
"setting_notifications_notify_minutes": "{count} minut",
|
||||||
"setting_notifications_notify_never": "nikdy",
|
"setting_notifications_notify_never": "nikdy",
|
||||||
"setting_notifications_notify_seconds": "{} sekundy",
|
"setting_notifications_notify_seconds": "{count} sekund",
|
||||||
"setting_notifications_single_progress_subtitle": "Podrobné informace o průběhu nahrávání položky",
|
"setting_notifications_single_progress_subtitle": "Podrobné informace o průběhu nahrávání položky",
|
||||||
"setting_notifications_single_progress_title": "Zobrazit průběh detailů zálohování na pozadí",
|
"setting_notifications_single_progress_title": "Zobrazit průběh detailů zálohování na pozadí",
|
||||||
"setting_notifications_subtitle": "Přizpůsobení předvoleb oznámení",
|
"setting_notifications_subtitle": "Přizpůsobení předvoleb oznámení",
|
||||||
@@ -1613,10 +1662,12 @@
|
|||||||
"settings": "Nastavení",
|
"settings": "Nastavení",
|
||||||
"settings_require_restart": "Pro použití tohoto nastavení restartujte Immich",
|
"settings_require_restart": "Pro použití tohoto nastavení restartujte Immich",
|
||||||
"settings_saved": "Nastavení uloženo",
|
"settings_saved": "Nastavení uloženo",
|
||||||
|
"setup_pin_code": "Nastavení PIN kódu",
|
||||||
"share": "Sdílet",
|
"share": "Sdílet",
|
||||||
"share_add_photos": "Přidat fotografie",
|
"share_add_photos": "Přidat fotografie",
|
||||||
"share_assets_selected": "{} vybráno",
|
"share_assets_selected": "{count} vybráno",
|
||||||
"share_dialog_preparing": "Připravuji...",
|
"share_dialog_preparing": "Připravuji...",
|
||||||
|
"share_link": "Sdílet odkaz",
|
||||||
"shared": "Sdílené",
|
"shared": "Sdílené",
|
||||||
"shared_album_activities_input_disable": "Komentář je vypnutý",
|
"shared_album_activities_input_disable": "Komentář je vypnutý",
|
||||||
"shared_album_activity_remove_content": "Chcete odstranit tuto aktivitu?",
|
"shared_album_activity_remove_content": "Chcete odstranit tuto aktivitu?",
|
||||||
@@ -1629,32 +1680,32 @@
|
|||||||
"shared_by_user": "Sdílel(a) {user}",
|
"shared_by_user": "Sdílel(a) {user}",
|
||||||
"shared_by_you": "Sdíleli jste",
|
"shared_by_you": "Sdíleli jste",
|
||||||
"shared_from_partner": "Fotky od {partner}",
|
"shared_from_partner": "Fotky od {partner}",
|
||||||
"shared_intent_upload_button_progress_text": "{} / {} nahráno",
|
"shared_intent_upload_button_progress_text": "{current} / {total} nahráno",
|
||||||
"shared_link_app_bar_title": "Sdílené odkazy",
|
"shared_link_app_bar_title": "Sdílené odkazy",
|
||||||
"shared_link_clipboard_copied_massage": "Zkopírováno do schránky",
|
"shared_link_clipboard_copied_massage": "Zkopírováno do schránky",
|
||||||
"shared_link_clipboard_text": "Odkaz: {}\nHeslo: {}",
|
"shared_link_clipboard_text": "Odkaz: {link}\nHeslo: {password}",
|
||||||
"shared_link_create_error": "Chyba při vytváření sdíleného odkazu",
|
"shared_link_create_error": "Chyba při vytváření sdíleného odkazu",
|
||||||
"shared_link_edit_description_hint": "Zadejte popis sdílení",
|
"shared_link_edit_description_hint": "Zadejte popis sdílení",
|
||||||
"shared_link_edit_expire_after_option_day": "1 den",
|
"shared_link_edit_expire_after_option_day": "1 den",
|
||||||
"shared_link_edit_expire_after_option_days": "{} dní",
|
"shared_link_edit_expire_after_option_days": "{count} dní",
|
||||||
"shared_link_edit_expire_after_option_hour": "1 hodina",
|
"shared_link_edit_expire_after_option_hour": "1 hodina",
|
||||||
"shared_link_edit_expire_after_option_hours": "{} hodin",
|
"shared_link_edit_expire_after_option_hours": "{count} hodin",
|
||||||
"shared_link_edit_expire_after_option_minute": "1 minuta",
|
"shared_link_edit_expire_after_option_minute": "1 minuta",
|
||||||
"shared_link_edit_expire_after_option_minutes": "{} minut",
|
"shared_link_edit_expire_after_option_minutes": "{count} minut",
|
||||||
"shared_link_edit_expire_after_option_months": "{} měsíce",
|
"shared_link_edit_expire_after_option_months": "{count} měsíce",
|
||||||
"shared_link_edit_expire_after_option_year": "{} rok",
|
"shared_link_edit_expire_after_option_year": "{count} rok",
|
||||||
"shared_link_edit_password_hint": "Zadejte heslo pro sdílení",
|
"shared_link_edit_password_hint": "Zadejte heslo pro sdílení",
|
||||||
"shared_link_edit_submit_button": "Aktualizovat odkaz",
|
"shared_link_edit_submit_button": "Aktualizovat odkaz",
|
||||||
"shared_link_error_server_url_fetch": "Nelze načíst url serveru",
|
"shared_link_error_server_url_fetch": "Nelze načíst url serveru",
|
||||||
"shared_link_expires_day": "Vyprší za {} den",
|
"shared_link_expires_day": "Vyprší za {count} den",
|
||||||
"shared_link_expires_days": "Vyprší za {} dní",
|
"shared_link_expires_days": "Vyprší za {count} dní",
|
||||||
"shared_link_expires_hour": "Vyprší za {} hodinu",
|
"shared_link_expires_hour": "Vyprší za {count} hodinu",
|
||||||
"shared_link_expires_hours": "Vyprší za {} hodin",
|
"shared_link_expires_hours": "Vyprší za {count} hodin",
|
||||||
"shared_link_expires_minute": "Vyprší za {} minutu",
|
"shared_link_expires_minute": "Vyprší za {count} minutu",
|
||||||
"shared_link_expires_minutes": "Vyprší za {} minut",
|
"shared_link_expires_minutes": "Vyprší za {count} minut",
|
||||||
"shared_link_expires_never": "Platnost ∞",
|
"shared_link_expires_never": "Platnost ∞",
|
||||||
"shared_link_expires_second": "Vyprší za {} sekundu",
|
"shared_link_expires_second": "Vyprší za {count} sekundu",
|
||||||
"shared_link_expires_seconds": "Vyprší za {} sekund",
|
"shared_link_expires_seconds": "Vyprší za {count} sekund",
|
||||||
"shared_link_individual_shared": "Individuální sdílení",
|
"shared_link_individual_shared": "Individuální sdílení",
|
||||||
"shared_link_info_chip_metadata": "EXIF",
|
"shared_link_info_chip_metadata": "EXIF",
|
||||||
"shared_link_manage_links": "Spravovat sdílené odkazy",
|
"shared_link_manage_links": "Spravovat sdílené odkazy",
|
||||||
@@ -1723,12 +1774,14 @@
|
|||||||
"start_date": "Počáteční datum",
|
"start_date": "Počáteční datum",
|
||||||
"state": "Stát",
|
"state": "Stát",
|
||||||
"status": "Stav",
|
"status": "Stav",
|
||||||
|
"stop_casting": "Zastavit odesílání",
|
||||||
"stop_motion_photo": "Zastavit pohyblivou fotografii",
|
"stop_motion_photo": "Zastavit pohyblivou fotografii",
|
||||||
"stop_photo_sharing": "Přestat sdílet své fotografie?",
|
"stop_photo_sharing": "Přestat sdílet své fotografie?",
|
||||||
"stop_photo_sharing_description": "{partner} již nebude mít přístup k vašim fotkám.",
|
"stop_photo_sharing_description": "{partner} již nebude mít přístup k vašim fotkám.",
|
||||||
"stop_sharing_photos_with_user": "Přestat sdílet své fotky s tímto uživatelem",
|
"stop_sharing_photos_with_user": "Přestat sdílet své fotky s tímto uživatelem",
|
||||||
"storage": "Velikost úložiště",
|
"storage": "Velikost úložiště",
|
||||||
"storage_label": "Štítek úložiště",
|
"storage_label": "Štítek úložiště",
|
||||||
|
"storage_quota": "Kvóta úložiště",
|
||||||
"storage_usage": "Využito {used} z {available}",
|
"storage_usage": "Využito {used} z {available}",
|
||||||
"submit": "Odeslat",
|
"submit": "Odeslat",
|
||||||
"suggestions": "Návrhy",
|
"suggestions": "Návrhy",
|
||||||
@@ -1755,7 +1808,7 @@
|
|||||||
"theme_selection": "Výběr motivu",
|
"theme_selection": "Výběr motivu",
|
||||||
"theme_selection_description": "Automatické nastavení světlého nebo tmavého motivu podle systémových preferencí prohlížeče",
|
"theme_selection_description": "Automatické nastavení světlého nebo tmavého motivu podle systémových preferencí prohlížeče",
|
||||||
"theme_setting_asset_list_storage_indicator_title": "Zobrazit indikátor úložiště na dlaždicích položek",
|
"theme_setting_asset_list_storage_indicator_title": "Zobrazit indikátor úložiště na dlaždicích položek",
|
||||||
"theme_setting_asset_list_tiles_per_row_title": "Počet položek na řádek ({})",
|
"theme_setting_asset_list_tiles_per_row_title": "Počet položek na řádek ({count})",
|
||||||
"theme_setting_colorful_interface_subtitle": "Použít hlavní barvu na povrchy pozadí.",
|
"theme_setting_colorful_interface_subtitle": "Použít hlavní barvu na povrchy pozadí.",
|
||||||
"theme_setting_colorful_interface_title": "Barevné rozhraní",
|
"theme_setting_colorful_interface_title": "Barevné rozhraní",
|
||||||
"theme_setting_image_viewer_quality_subtitle": "Přizpůsobení kvality detailů prohlížeče obrázků",
|
"theme_setting_image_viewer_quality_subtitle": "Přizpůsobení kvality detailů prohlížeče obrázků",
|
||||||
@@ -1779,7 +1832,6 @@
|
|||||||
"to_parent": "Přejít k rodiči",
|
"to_parent": "Přejít k rodiči",
|
||||||
"to_trash": "Vyhodit",
|
"to_trash": "Vyhodit",
|
||||||
"toggle_settings": "Přepnout nastavení",
|
"toggle_settings": "Přepnout nastavení",
|
||||||
"toggle_theme": "Přepnout tmavý motiv",
|
|
||||||
"total": "Celkem",
|
"total": "Celkem",
|
||||||
"total_usage": "Celkové využití",
|
"total_usage": "Celkové využití",
|
||||||
"trash": "Koš",
|
"trash": "Koš",
|
||||||
@@ -1790,15 +1842,18 @@
|
|||||||
"trash_no_results_message": "Zde se zobrazí odstraněné fotky a videa.",
|
"trash_no_results_message": "Zde se zobrazí odstraněné fotky a videa.",
|
||||||
"trash_page_delete_all": "Smazat všechny",
|
"trash_page_delete_all": "Smazat všechny",
|
||||||
"trash_page_empty_trash_dialog_content": "Chcete vyprázdnit svoje vyhozené položky? Tyto položky budou trvale odstraněny z aplikace",
|
"trash_page_empty_trash_dialog_content": "Chcete vyprázdnit svoje vyhozené položky? Tyto položky budou trvale odstraněny z aplikace",
|
||||||
"trash_page_info": "Vyhozené položky budou trvale smazány po {} dnech",
|
"trash_page_info": "Vyhozené položky budou trvale smazány po {days} dnech",
|
||||||
"trash_page_no_assets": "Žádné vyhozené položky",
|
"trash_page_no_assets": "Žádné vyhozené položky",
|
||||||
"trash_page_restore_all": "Obnovit všechny",
|
"trash_page_restore_all": "Obnovit všechny",
|
||||||
"trash_page_select_assets_btn": "Vybrat položky",
|
"trash_page_select_assets_btn": "Vybrat položky",
|
||||||
"trash_page_title": "Koš ({})",
|
"trash_page_title": "Koš ({count})",
|
||||||
"trashed_items_will_be_permanently_deleted_after": "Smazané položky budou trvale odstraněny po {days, plural, one {# dni} other {# dnech}}.",
|
"trashed_items_will_be_permanently_deleted_after": "Smazané položky budou trvale odstraněny po {days, plural, one {# dni} other {# dnech}}.",
|
||||||
"type": "Typ",
|
"type": "Typ",
|
||||||
|
"unable_to_change_pin_code": "Nelze změnit PIN kód",
|
||||||
|
"unable_to_setup_pin_code": "Nelze nastavit PIN kód",
|
||||||
"unarchive": "Odarchivovat",
|
"unarchive": "Odarchivovat",
|
||||||
"unarchived_count": "{count, plural, one {Odarchivována #} few {Odarchivovány #} other {Odarchivováno #}}",
|
"unarchived_count": "{count, plural, one {Odarchivována #} few {Odarchivovány #} other {Odarchivováno #}}",
|
||||||
|
"undo": "Vrátit zpět",
|
||||||
"unfavorite": "Zrušit oblíbení",
|
"unfavorite": "Zrušit oblíbení",
|
||||||
"unhide_person": "Zrušit skrytí osoby",
|
"unhide_person": "Zrušit skrytí osoby",
|
||||||
"unknown": "Neznámý",
|
"unknown": "Neznámý",
|
||||||
@@ -1817,9 +1872,8 @@
|
|||||||
"unselect_all_duplicates": "Zrušit výběr všech duplicit",
|
"unselect_all_duplicates": "Zrušit výběr všech duplicit",
|
||||||
"unstack": "Zrušit seskupení",
|
"unstack": "Zrušit seskupení",
|
||||||
"unstacked_assets_count": "{count, plural, one {Rozložená # položka} few {Rozložené # položky} other {Rozložených # položiek}}",
|
"unstacked_assets_count": "{count, plural, one {Rozložená # položka} few {Rozložené # položky} other {Rozložených # položiek}}",
|
||||||
"untracked_files": "Nesledované soubory",
|
|
||||||
"untracked_files_decription": "Tyto soubory nejsou aplikaci známy. Mohou být výsledkem neúspěšných přesunů, přerušeného nahrávání nebo mohou zůstat pozadu kvůli chybě",
|
|
||||||
"up_next": "To je prozatím vše",
|
"up_next": "To je prozatím vše",
|
||||||
|
"updated_at": "Aktualizováno",
|
||||||
"updated_password": "Heslo aktualizováno",
|
"updated_password": "Heslo aktualizováno",
|
||||||
"upload": "Nahrát",
|
"upload": "Nahrát",
|
||||||
"upload_concurrency": "Souběžnost nahrávání",
|
"upload_concurrency": "Souběžnost nahrávání",
|
||||||
@@ -1832,15 +1886,20 @@
|
|||||||
"upload_status_errors": "Chyby",
|
"upload_status_errors": "Chyby",
|
||||||
"upload_status_uploaded": "Nahráno",
|
"upload_status_uploaded": "Nahráno",
|
||||||
"upload_success": "Nahrání proběhlo úspěšně, obnovením stránky se zobrazí nově nahrané položky.",
|
"upload_success": "Nahrání proběhlo úspěšně, obnovením stránky se zobrazí nově nahrané položky.",
|
||||||
"upload_to_immich": "Nahrát do Immiche ({})",
|
"upload_to_immich": "Nahrát do Immich ({count})",
|
||||||
"uploading": "Nahrávání",
|
"uploading": "Nahrávání",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
"usage": "Využití",
|
"usage": "Využití",
|
||||||
|
"use_biometric": "Použít biometrické údaje",
|
||||||
"use_current_connection": "použít aktuální připojení",
|
"use_current_connection": "použít aktuální připojení",
|
||||||
"use_custom_date_range": "Použít vlastní rozsah dat",
|
"use_custom_date_range": "Použít vlastní rozsah dat",
|
||||||
"user": "Uživatel",
|
"user": "Uživatel",
|
||||||
|
"user_has_been_deleted": "Tento uživatel byl smazán.",
|
||||||
"user_id": "ID uživatele",
|
"user_id": "ID uživatele",
|
||||||
"user_liked": "Uživateli {user} se {type, select, photo {líbila tato fotka} video {líbilo toto video} asset {líbila tato položka} other {to líbilo}}",
|
"user_liked": "Uživateli {user} se {type, select, photo {líbila tato fotka} video {líbilo toto video} asset {líbila tato položka} other {to líbilo}}",
|
||||||
|
"user_pin_code_settings": "PIN kód",
|
||||||
|
"user_pin_code_settings_description": "Správa vašeho PIN kódu",
|
||||||
|
"user_privacy": "Ochrana soukromí uživatelů",
|
||||||
"user_purchase_settings": "Nákup",
|
"user_purchase_settings": "Nákup",
|
||||||
"user_purchase_settings_description": "Správa vašeho nákupu",
|
"user_purchase_settings_description": "Správa vašeho nákupu",
|
||||||
"user_role_set": "Uživatel {user} nastaven jako {role}",
|
"user_role_set": "Uživatel {user} nastaven jako {role}",
|
||||||
@@ -1856,11 +1915,6 @@
|
|||||||
"version": "Verze",
|
"version": "Verze",
|
||||||
"version_announcement_closing": "Váš přítel Alex",
|
"version_announcement_closing": "Váš přítel Alex",
|
||||||
"version_announcement_message": "Ahoj! K dispozici je nová verze aplikace Immich. Věnujte prosím chvíli přečtení <link>poznámek k vydání</link> a ujistěte se, že je vaše nastavení aktuální, abyste předešli případným chybným konfiguracím, zejména pokud používáte WatchTower nebo jiný mechanismus, který se stará o automatickou aktualizaci instance aplikace Immich.",
|
"version_announcement_message": "Ahoj! K dispozici je nová verze aplikace Immich. Věnujte prosím chvíli přečtení <link>poznámek k vydání</link> a ujistěte se, že je vaše nastavení aktuální, abyste předešli případným chybným konfiguracím, zejména pokud používáte WatchTower nebo jiný mechanismus, který se stará o automatickou aktualizaci instance aplikace Immich.",
|
||||||
"version_announcement_overlay_release_notes": "poznámky k vydání",
|
|
||||||
"version_announcement_overlay_text_1": "Ahoj, k dispozici je nová verze",
|
|
||||||
"version_announcement_overlay_text_2": "najděte si čas na návštěvu ",
|
|
||||||
"version_announcement_overlay_text_3": " a ujistěte se, že vaše konfigurace docker-compose a .env je aktuální, abyste předešli nesprávné konfiguraci, zvláště pokud používáte WatchTower nebo jakýkoli mechanismus, který podporuje automatické aktualizace serverových aplikací.",
|
|
||||||
"version_announcement_overlay_title": "K dispozici je nová verze serveru 🎉",
|
|
||||||
"version_history": "Historie verzí",
|
"version_history": "Historie verzí",
|
||||||
"version_history_item": "Nainstalováno {version} dne {date}",
|
"version_history_item": "Nainstalováno {version} dne {date}",
|
||||||
"video": "Video",
|
"video": "Video",
|
||||||
@@ -1880,6 +1934,7 @@
|
|||||||
"view_previous_asset": "Zobrazit předchozí položku",
|
"view_previous_asset": "Zobrazit předchozí položku",
|
||||||
"view_qr_code": "Zobrazit QR kód",
|
"view_qr_code": "Zobrazit QR kód",
|
||||||
"view_stack": "Zobrazit seskupení",
|
"view_stack": "Zobrazit seskupení",
|
||||||
|
"view_user": "Zobrazit uživatele",
|
||||||
"viewer_remove_from_stack": "Odstranit ze zásobníku",
|
"viewer_remove_from_stack": "Odstranit ze zásobníku",
|
||||||
"viewer_stack_use_as_main_asset": "Použít jako hlavní položku",
|
"viewer_stack_use_as_main_asset": "Použít jako hlavní položku",
|
||||||
"viewer_unstack": "Rozbalit zásobník",
|
"viewer_unstack": "Rozbalit zásobník",
|
||||||
@@ -1889,11 +1944,12 @@
|
|||||||
"week": "Týden",
|
"week": "Týden",
|
||||||
"welcome": "Vítejte",
|
"welcome": "Vítejte",
|
||||||
"welcome_to_immich": "Vítejte v Immichi",
|
"welcome_to_immich": "Vítejte v Immichi",
|
||||||
"wifi_name": "Název WiFi",
|
"wifi_name": "Název Wi-Fi",
|
||||||
|
"wrong_pin_code": "Chybný PIN kód",
|
||||||
"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",
|
||||||
"your_wifi_name": "Váš název WiFi",
|
"your_wifi_name": "Název vaší Wi-Fi",
|
||||||
"zoom_image": "Zvětšit obrázek"
|
"zoom_image": "Zvětšit obrázek"
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user