mirror of
https://github.com/immich-app/immich.git
synced 2025-12-15 17:21:16 -08:00
Compare commits
306 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86f5ceb80e | ||
|
|
06959a9ea5 | ||
|
|
acdc66413c | ||
|
|
816db700e1 | ||
|
|
9030b1f89f | ||
|
|
2e0c7abd65 | ||
|
|
1a633f3fca | ||
|
|
dda735ec51 | ||
|
|
f1c98ac9e6 | ||
|
|
7d07aaeba3 | ||
|
|
a0163d8df0 | ||
|
|
49ef86173f | ||
|
|
b6c6a7e403 | ||
|
|
672560f55b | ||
|
|
94cbbf3c4b | ||
|
|
40b802a5a9 | ||
|
|
a63f027bf7 | ||
|
|
1c02e1dadf | ||
|
|
63b6a71ebd | ||
|
|
0a9b632e48 | ||
|
|
7fcc5a5417 | ||
|
|
9cec6aaf46 | ||
|
|
a3206bf950 | ||
|
|
9c627920dd | ||
|
|
8a421eb778 | ||
|
|
14e681c954 | ||
|
|
095a5e0ffb | ||
|
|
b1d31a4567 | ||
|
|
b42ca61e1f | ||
|
|
197baf3473 | ||
|
|
3161eb7d16 | ||
|
|
bbbdd463fd | ||
|
|
73ad0d468f | ||
|
|
74d34b4f6c | ||
|
|
bf3b38a7f2 | ||
|
|
52d0c5fc73 | ||
|
|
fb20381f98 | ||
|
|
a678590ccd | ||
|
|
bd226e9e2c | ||
|
|
d023d5b6b4 | ||
|
|
9b30640e67 | ||
|
|
d17b24eea3 | ||
|
|
d38d0b8de0 | ||
|
|
f10b74f1e2 | ||
|
|
5c63d8f07a | ||
|
|
cb437829f3 | ||
|
|
7173af60e4 | ||
|
|
afccb37a3b | ||
|
|
c55ef7c383 | ||
|
|
47ea47ce14 | ||
|
|
fd6ade2b5d | ||
|
|
77e38abe91 | ||
|
|
5d1011b482 | ||
|
|
4b11e925d9 | ||
|
|
258b98c262 | ||
|
|
f1db257628 | ||
|
|
3edade6761 | ||
|
|
efcc66d63b | ||
|
|
ca96da22d0 | ||
|
|
85b98cf4c6 | ||
|
|
a404fb6cb5 | ||
|
|
3432b4625f | ||
|
|
b8777d7739 | ||
|
|
fb477627c7 | ||
|
|
fd78b89c92 | ||
|
|
45f9c52e7f | ||
|
|
0a24ff90bb | ||
|
|
e1eae00b35 | ||
|
|
15bfceb05a | ||
|
|
c1f4fe65bb | ||
|
|
a4a6a97aa8 | ||
|
|
608543da0b | ||
|
|
b4fa60d4fd | ||
|
|
b1467bd1da | ||
|
|
3cf0f5f11b | ||
|
|
454737ca79 | ||
|
|
26bc889f8d | ||
|
|
54775b896f | ||
|
|
9217fb4094 | ||
|
|
04d4a30471 | ||
|
|
90f9501902 | ||
|
|
f8d26bd865 | ||
|
|
816d040d81 | ||
|
|
2069293cc1 | ||
|
|
4bd77d5899 | ||
|
|
f8ff342852 | ||
|
|
67ac686704 | ||
|
|
4e5bf7ae2e | ||
|
|
b7fd5dcb4a | ||
|
|
bea287c5b3 | ||
|
|
46c716d450 | ||
|
|
9539a361e4 | ||
|
|
ca35e5557b | ||
|
|
a26ed3d1a6 | ||
|
|
c7d53a5006 | ||
|
|
41461e0d5d | ||
|
|
c0a48d7357 | ||
|
|
66cc744c22 | ||
|
|
58ae734fc2 | ||
|
|
54b2779b79 | ||
|
|
df26e12db6 | ||
|
|
343d89c032 | ||
|
|
49c2d4d115 | ||
|
|
58aefc928d | ||
|
|
70d8902737 | ||
|
|
78eeebf8e6 | ||
|
|
585330b179 | ||
|
|
5b1ac27058 | ||
|
|
bcc36d14a1 | ||
|
|
22f5e05060 | ||
|
|
e510e733cd | ||
|
|
d0a06739d8 | ||
|
|
26c43617d1 | ||
|
|
0a89c7ffc4 | ||
|
|
7097cf6319 | ||
|
|
912a13ea0d | ||
|
|
cb391342d7 | ||
|
|
305889f32b | ||
|
|
f1027d7807 | ||
|
|
2806ac6eb4 | ||
|
|
cc1fecfffd | ||
|
|
e02817362c | ||
|
|
1b0484fc46 | ||
|
|
6fe214a784 | ||
|
|
e18a9f84a4 | ||
|
|
59bb727636 | ||
|
|
20e0c03b39 | ||
|
|
6d1567cf44 | ||
|
|
dc3f53a973 | ||
|
|
dad7cf47b4 | ||
|
|
165b91b068 | ||
|
|
8211afb726 | ||
|
|
2cccef174a | ||
|
|
9bbef4a97b | ||
|
|
10c2bda3a9 | ||
|
|
cf9e04c8ec | ||
|
|
d6887117ac | ||
|
|
3b11be2859 | ||
|
|
d7f52739e8 | ||
|
|
71ea46d95e | ||
|
|
e2afc43506 | ||
|
|
6aed1180e7 | ||
|
|
476b735e3c | ||
|
|
7ad12c7f33 | ||
|
|
60729a091a | ||
|
|
d2bad1d553 | ||
|
|
3e31ad51be | ||
|
|
fbeb4664f7 | ||
|
|
4ee8a30a5a | ||
|
|
6243bce46c | ||
|
|
98b72fdb9b | ||
|
|
5e901e4d21 | ||
|
|
66490d5db4 | ||
|
|
2b839088c7 | ||
|
|
28d3d3e679 | ||
|
|
2de30e34f4 | ||
|
|
2ff71b0d27 | ||
|
|
cdb45364c3 | ||
|
|
8ba338fbe1 | ||
|
|
ce84f9c755 | ||
|
|
d1e74a28d9 | ||
|
|
78a2a9e666 | ||
|
|
53f5643994 | ||
|
|
4ee634766d | ||
|
|
bab739efbd | ||
|
|
8568ec838a | ||
|
|
4cbb18aabc | ||
|
|
3fb60aca4f | ||
|
|
19bbdebdf7 | ||
|
|
bc66b1a556 | ||
|
|
4762fd83d4 | ||
|
|
c27c12d975 | ||
|
|
0abbd85134 | ||
|
|
af1f00dff9 | ||
|
|
35b4c9d375 | ||
|
|
74da15e20d | ||
|
|
efc7fdb669 | ||
|
|
a75f368d5b | ||
|
|
a3b6095b61 | ||
|
|
7ca6f80ed2 | ||
|
|
f1b8a7ab54 | ||
|
|
079aa13edb | ||
|
|
67bac9ff59 | ||
|
|
0d80ae3a91 | ||
|
|
b1b215f083 | ||
|
|
f55c80eadf | ||
|
|
c81bb2b70a | ||
|
|
5fa9704a65 | ||
|
|
60d39a7d1f | ||
|
|
13564fbc17 | ||
|
|
77a5820c3c | ||
|
|
b790354f9a | ||
|
|
7948819e0c | ||
|
|
5cd13227ad | ||
|
|
36dc7bd924 | ||
|
|
6bd7c6c06d | ||
|
|
e9b0840f01 | ||
|
|
a8b01dc21a | ||
|
|
a815592954 | ||
|
|
f4475549d6 | ||
|
|
a6eb227330 | ||
|
|
343087e2b4 | ||
|
|
66b2ad7939 | ||
|
|
57a7103d75 | ||
|
|
23b836ffbb | ||
|
|
e54cf914d7 | ||
|
|
fa57853bd2 | ||
|
|
ddd4ec2d9e | ||
|
|
1812e8811b | ||
|
|
1d37d8cac0 | ||
|
|
19da705fcb | ||
|
|
6efc2ec9be | ||
|
|
d0c6c7cb33 | ||
|
|
b3b5f063cf | ||
|
|
3731cc4334 | ||
|
|
13df619ba9 | ||
|
|
3edb347666 | ||
|
|
c73832bd9c | ||
|
|
2f26a7edae | ||
|
|
deaf81e2a4 | ||
|
|
f1b92718d5 | ||
|
|
1f64649434 | ||
|
|
ff32506c5e | ||
|
|
68b5202730 | ||
|
|
c6abef186c | ||
|
|
e5bdf671b5 | ||
|
|
88e92332ee | ||
|
|
6da51deb83 | ||
|
|
b44f8d52ee | ||
|
|
fa03ed7dd7 | ||
|
|
5617b57b26 | ||
|
|
01210dceac | ||
|
|
e4e049d040 | ||
|
|
a405fba3bb | ||
|
|
b5844db0c7 | ||
|
|
28ab1d4551 | ||
|
|
050ee91289 | ||
|
|
5eb8d7e8b0 | ||
|
|
5e4403bb2e | ||
|
|
fb6591607f | ||
|
|
1cf3378499 | ||
|
|
a336aeb007 | ||
|
|
ee49f470b7 | ||
|
|
b9cda59172 | ||
|
|
ba71c83948 | ||
|
|
2835919931 | ||
|
|
310fab526d | ||
|
|
690b87e375 | ||
|
|
e53625b067 | ||
|
|
9e085c1071 | ||
|
|
5f9dfa9493 | ||
|
|
13051c1e5a | ||
|
|
a9cd3609dd | ||
|
|
e0a3e5a200 | ||
|
|
c587fb1df8 | ||
|
|
51cfe10c28 | ||
|
|
bc3f95c57c | ||
|
|
e368b9e50b | ||
|
|
95c75c289c | ||
|
|
23d3657ac2 | ||
|
|
74f04336bb | ||
|
|
54db2a48af | ||
|
|
cde56d5a22 | ||
|
|
3f1cf44717 | ||
|
|
2d83ac4125 | ||
|
|
7147486b6a | ||
|
|
89ddbac8bc | ||
|
|
e071b82e8a | ||
|
|
13b2b2fc4e | ||
|
|
fe9ef1a3ea | ||
|
|
afb0d0f54d | ||
|
|
26085ff82b | ||
|
|
2872886e77 | ||
|
|
a21112e4ab | ||
|
|
f3edf43158 | ||
|
|
1c5926553a | ||
|
|
05fa3092bf | ||
|
|
7d3ec8af37 | ||
|
|
8db008ef0b | ||
|
|
e493e05e99 | ||
|
|
b83e535010 | ||
|
|
111372edc1 | ||
|
|
625a899f64 | ||
|
|
aaf0496f74 | ||
|
|
4977926c88 | ||
|
|
f41e1159d1 | ||
|
|
670107373b | ||
|
|
e660f05c31 | ||
|
|
baf1ea313e | ||
|
|
ed64c91da6 | ||
|
|
c40aa4399b | ||
|
|
8f08100a30 | ||
|
|
337cd33042 | ||
|
|
1e8fc7266c | ||
|
|
7f35583c2c | ||
|
|
ace755f264 | ||
|
|
7b25c9d0a7 | ||
|
|
c0bee2a6b7 | ||
|
|
b48d5cab22 | ||
|
|
4f59e6c7ab | ||
|
|
82a5d54d2c | ||
|
|
5e6d830ecd | ||
|
|
f700f3427b | ||
|
|
0c07c0ba4e | ||
|
|
bc885f3644 | ||
|
|
6668964d92 |
12
.github/workflows/build-mobile.yml
vendored
12
.github/workflows/build-mobile.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
|||||||
build-sign-android:
|
build-sign-android:
|
||||||
name: Build and sign Android
|
name: Build and sign Android
|
||||||
# Skip when PR from a fork
|
# Skip when PR from a fork
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' }}
|
||||||
runs-on: macos-12
|
runs-on: macos-12
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
ref="${input_ref:-$github_ref}"
|
ref="${input_ref:-$github_ref}"
|
||||||
echo "ref=$ref" >> $GITHUB_OUTPUT
|
echo "ref=$ref" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{ steps.get-ref.outputs.ref }}
|
ref: ${{ steps.get-ref.outputs.ref }}
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ jobs:
|
|||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
channel: "stable"
|
channel: "stable"
|
||||||
flutter-version: "3.10.5"
|
flutter-version: "3.13.3"
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
- name: Create the Keystore
|
- name: Create the Keystore
|
||||||
@@ -64,10 +64,12 @@ jobs:
|
|||||||
ALIAS: ${{ secrets.ALIAS }}
|
ALIAS: ${{ secrets.ALIAS }}
|
||||||
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
|
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
|
||||||
ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
|
ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
|
||||||
run: flutter build apk --release
|
run: |
|
||||||
|
flutter build apk --release
|
||||||
|
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@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: release-apk-signed
|
name: release-apk-signed
|
||||||
path: mobile/build/app/outputs/flutter-apk/app-release.apk
|
path: mobile/build/app/outputs/flutter-apk/*.apk
|
||||||
|
|||||||
2
.github/workflows/cache-cleanup.yml
vendored
2
.github/workflows/cache-cleanup.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Cleanup
|
- name: Cleanup
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -42,7 +42,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
|
|||||||
4
.github/workflows/docker-cleanup.yml
vendored
4
.github/workflows/docker-cleanup.yml
vendored
@@ -38,7 +38,7 @@ jobs:
|
|||||||
-
|
-
|
||||||
name: Clean temporary images
|
name: Clean temporary images
|
||||||
if: "${{ env.TOKEN != '' }}"
|
if: "${{ env.TOKEN != '' }}"
|
||||||
uses: stumpylog/image-cleaner-action/ephemeral@v0.1.0
|
uses: stumpylog/image-cleaner-action/ephemeral@v0.3.0
|
||||||
with:
|
with:
|
||||||
token: "${{ env.TOKEN }}"
|
token: "${{ env.TOKEN }}"
|
||||||
owner: "immich-app"
|
owner: "immich-app"
|
||||||
@@ -70,7 +70,7 @@ jobs:
|
|||||||
-
|
-
|
||||||
name: Clean untagged images
|
name: Clean untagged images
|
||||||
if: "${{ env.TOKEN != '' }}"
|
if: "${{ env.TOKEN != '' }}"
|
||||||
uses: stumpylog/image-cleaner-action/untagged@v0.1.0
|
uses: stumpylog/image-cleaner-action/untagged@v0.3.0
|
||||||
with:
|
with:
|
||||||
token: "${{ env.TOKEN }}"
|
token: "${{ env.TOKEN }}"
|
||||||
owner: "immich-app"
|
owner: "immich-app"
|
||||||
|
|||||||
105
.github/workflows/docker.yml
vendored
105
.github/workflows/docker.yml
vendored
@@ -24,28 +24,25 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- context: "server"
|
|
||||||
image: "immich-server"
|
|
||||||
platforms: "linux/arm/v7,linux/amd64,linux/arm64"
|
|
||||||
- context: "web"
|
- context: "web"
|
||||||
image: "immich-web"
|
image: "immich-web"
|
||||||
platforms: "linux/arm/v7,linux/amd64,linux/arm64"
|
platforms: "linux/amd64,linux/arm64"
|
||||||
- context: "machine-learning"
|
- context: "machine-learning"
|
||||||
image: "immich-machine-learning"
|
image: "immich-machine-learning"
|
||||||
platforms: "linux/amd64,linux/arm64"
|
platforms: "linux/amd64,linux/arm64"
|
||||||
- context: "nginx"
|
- context: "nginx"
|
||||||
image: "immich-proxy"
|
image: "immich-proxy"
|
||||||
platforms: "linux/arm/v7,linux/amd64,linux/arm64"
|
platforms: "linux/amd64,linux/arm64"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2.2.0
|
uses: docker/setup-qemu-action@v3.0.0
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2.9.1
|
uses: docker/setup-buildx-action@v3.0.0
|
||||||
# Workaround to fix error:
|
# Workaround to fix error:
|
||||||
# failed to push: failed to copy: io: read/write on closed pipe
|
# failed to push: failed to copy: io: read/write on closed pipe
|
||||||
# See https://github.com/docker/build-push-action/issues/761
|
# See https://github.com/docker/build-push-action/issues/761
|
||||||
@@ -56,13 +53,13 @@ jobs:
|
|||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
# Only push to Docker Hub when making a release
|
# Only push to Docker Hub when making a release
|
||||||
if: ${{ github.event_name == 'release' }}
|
if: ${{ github.event_name == 'release' }}
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
# Skip when PR from a fork
|
# Skip when PR from a fork
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
with:
|
with:
|
||||||
@@ -72,7 +69,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Generate docker image tags
|
- name: Generate docker image tags
|
||||||
id: metadata
|
id: metadata
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
flavor: |
|
flavor: |
|
||||||
# Disable latest tag
|
# Disable latest tag
|
||||||
@@ -100,7 +97,91 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Build and push image
|
- name: Build and push image
|
||||||
uses: docker/build-push-action@v4.1.1
|
uses: docker/build-push-action@v5.0.0
|
||||||
|
with:
|
||||||
|
context: ${{ matrix.context }}
|
||||||
|
platforms: ${{ matrix.platforms }}
|
||||||
|
# Skip pushing when PR from a fork
|
||||||
|
push: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
|
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/immich-build-cache:${{matrix.image}}
|
||||||
|
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
||||||
|
tags: ${{ steps.metadata.outputs.tags }}
|
||||||
|
labels: ${{ steps.metadata.outputs.labels }}
|
||||||
|
|
||||||
|
build_and_push_server_arm_64:
|
||||||
|
runs-on: self-hosted
|
||||||
|
strategy:
|
||||||
|
# Prevent a failure in one image from stopping the other builds
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- context: "server"
|
||||||
|
image: "immich-server"
|
||||||
|
platforms: "linux/arm64,linux/amd64"
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3.0.0
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3.0.0
|
||||||
|
# Workaround to fix error:
|
||||||
|
# failed to push: failed to copy: io: read/write on closed pipe
|
||||||
|
# See https://github.com/docker/build-push-action/issues/761
|
||||||
|
with:
|
||||||
|
driver-opts: |
|
||||||
|
image=moby/buildkit:v0.10.6
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
# Only push to Docker Hub when making a release
|
||||||
|
if: ${{ github.event_name == 'release' }}
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
# Skip when PR from a fork
|
||||||
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Generate docker image tags
|
||||||
|
id: metadata
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
flavor: |
|
||||||
|
# Disable latest tag
|
||||||
|
latest=false
|
||||||
|
images: |
|
||||||
|
name=ghcr.io/${{ github.repository_owner }}/${{matrix.image}}
|
||||||
|
name=altran1502/${{matrix.image}},enable=${{ github.event_name == 'release' }}
|
||||||
|
tags: |
|
||||||
|
# Tag with branch name
|
||||||
|
type=ref,event=branch
|
||||||
|
# Tag with pr-number
|
||||||
|
type=ref,event=pr
|
||||||
|
# Tag with git tag on release
|
||||||
|
type=ref,event=tag
|
||||||
|
type=raw,value=release,enable=${{ github.event_name == 'release' }}
|
||||||
|
|
||||||
|
- name: Determine build cache output
|
||||||
|
id: cache-target
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||||
|
# Essentially just ignore the cache output (PR 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,mode=max,ref=ghcr.io/${{ github.repository_owner }}/immich-build-cache:${{ matrix.image }}" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build and push image
|
||||||
|
uses: docker/build-push-action@v5.0.0
|
||||||
with:
|
with:
|
||||||
context: ${{ matrix.context }}
|
context: ${{ matrix.context }}
|
||||||
platforms: ${{ matrix.platforms }}
|
platforms: ${{ matrix.platforms }}
|
||||||
|
|||||||
5
.github/workflows/prepare-release.yml
vendored
5
.github/workflows/prepare-release.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.ORG_RELEASE_TOKEN }}
|
token: ${{ secrets.ORG_RELEASE_TOKEN }}
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.ORG_RELEASE_TOKEN }}
|
token: ${{ secrets.ORG_RELEASE_TOKEN }}
|
||||||
|
|
||||||
@@ -83,4 +83,5 @@ jobs:
|
|||||||
files: |
|
files: |
|
||||||
docker/docker-compose.yml
|
docker/docker-compose.yml
|
||||||
docker/example.env
|
docker/example.env
|
||||||
|
docker/hwaccel.yml
|
||||||
*.apk
|
*.apk
|
||||||
|
|||||||
4
.github/workflows/static_analysis.yml
vendored
4
.github/workflows/static_analysis.yml
vendored
@@ -17,13 +17,13 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
channel: "stable"
|
channel: "stable"
|
||||||
flutter-version: "3.10.5"
|
flutter-version: "3.13.3"
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: dart pub get
|
run: dart pub get
|
||||||
|
|||||||
23
.github/workflows/test.yml
vendored
23
.github/workflows/test.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Run npm install
|
- name: Run npm install
|
||||||
run: npm ci
|
run: npm ci
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Run npm install
|
- name: Run npm install
|
||||||
run: npm ci
|
run: npm ci
|
||||||
@@ -59,7 +59,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Run npm install
|
- name: Run npm install
|
||||||
run: npm ci
|
run: npm ci
|
||||||
@@ -89,7 +89,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Run npm install
|
- name: Run npm install
|
||||||
run: npm ci
|
run: npm ci
|
||||||
@@ -115,7 +115,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Run npm install
|
- name: Run npm install
|
||||||
run: npm ci
|
run: npm ci
|
||||||
@@ -144,12 +144,12 @@ jobs:
|
|||||||
name: Run mobile unit tests
|
name: Run mobile unit tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
channel: "stable"
|
channel: "stable"
|
||||||
flutter-version: "3.10.5"
|
flutter-version: "3.13.3"
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
run: flutter test -j 1
|
run: flutter test -j 1
|
||||||
@@ -161,7 +161,7 @@ jobs:
|
|||||||
run:
|
run:
|
||||||
working-directory: ./machine-learning
|
working-directory: ./machine-learning
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install poetry
|
- name: Install poetry
|
||||||
run: pipx install poetry
|
run: pipx install poetry
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
@@ -171,6 +171,7 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
poetry install --with dev
|
poetry install --with dev
|
||||||
|
poetry run pip install --no-deps -r requirements.txt
|
||||||
- name: Lint with ruff
|
- name: Lint with ruff
|
||||||
run: |
|
run: |
|
||||||
poetry run ruff check --format=github app
|
poetry run ruff check --format=github app
|
||||||
@@ -188,7 +189,7 @@ jobs:
|
|||||||
name: Check generated files are up-to-date
|
name: Check generated files are up-to-date
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Run API generation
|
- name: Run API generation
|
||||||
run: npm --prefix server run api:generate
|
run: npm --prefix server run api:generate
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
@@ -223,7 +224,7 @@ jobs:
|
|||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install server dependencies
|
- name: Install server dependencies
|
||||||
run: npm --prefix server ci
|
run: npm --prefix server ci
|
||||||
- name: Run existing migrations
|
- name: Run existing migrations
|
||||||
@@ -248,7 +249,7 @@ jobs:
|
|||||||
# name: Run mobile end-to-end integration tests
|
# name: Run mobile end-to-end integration tests
|
||||||
# runs-on: macos-latest
|
# runs-on: macos-latest
|
||||||
# steps:
|
# steps:
|
||||||
# - uses: actions/checkout@v3
|
# - uses: actions/checkout@v4
|
||||||
# - uses: actions/setup-java@v3
|
# - uses: actions/setup-java@v3
|
||||||
# with:
|
# with:
|
||||||
# distribution: 'zulu'
|
# distribution: 'zulu'
|
||||||
|
|||||||
@@ -21,13 +21,17 @@
|
|||||||
<a href="README_zh_CN.md">中文</a>
|
<a href="README_zh_CN.md">中文</a>
|
||||||
<a href="README_tr_TR.md">Türkçe</a>
|
<a href="README_tr_TR.md">Türkçe</a>
|
||||||
<a href="README_ca_ES.md">Català</a>
|
<a href="README_ca_ES.md">Català</a>
|
||||||
|
<a href="README_es_ES.md">Español</a>
|
||||||
|
<a href="README_fr_FR.md">Français</a>
|
||||||
|
<a href="README_nl_NL.md">Nederlands</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
|
|
||||||
- ⚠️ The project is under **very active** development.
|
- ⚠️ The project is under **very active** development.
|
||||||
- ⚠️ Expect bugs and breaking changes.
|
- ⚠️ Expect bugs and breaking changes.
|
||||||
- ⚠️ **Do not use the app as the only way to store your photos and videos!**
|
- ⚠️ **Do not use the app as the only way to store your photos and videos.**
|
||||||
|
- ⚠️ Always follow [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) backup plan for your precious photos and videos!
|
||||||
|
|
||||||
## Content
|
## Content
|
||||||
|
|
||||||
@@ -83,7 +87,7 @@ Spec: Free-tier Oracle VM - Amsterdam - 2.4Ghz quad-core ARM64 CPU, 24GB RAM
|
|||||||
| User-defined storage structure | Yes | Yes |
|
| User-defined storage structure | Yes | Yes |
|
||||||
| Public Sharing | No | Yes |
|
| Public Sharing | No | Yes |
|
||||||
| Archive and Favorites | Yes | Yes |
|
| Archive and Favorites | Yes | Yes |
|
||||||
| Global Map | No | Yes |
|
| Global Map | Yes | Yes |
|
||||||
| Partner Sharing | Yes | Yes |
|
| Partner Sharing | Yes | Yes |
|
||||||
| Facial recognition and clustering | Yes | Yes |
|
| Facial recognition and clustering | Yes | Yes |
|
||||||
| Memories (x years ago) | Yes | Yes |
|
| Memories (x years ago) | Yes | Yes |
|
||||||
|
|||||||
@@ -21,6 +21,9 @@
|
|||||||
<a href="README.md">English</a>
|
<a href="README.md">English</a>
|
||||||
<a href="README_zh_CN.md">中文</a>
|
<a href="README_zh_CN.md">中文</a>
|
||||||
<a href="README_tr_TR.md">Türkçe</a>
|
<a href="README_tr_TR.md">Türkçe</a>
|
||||||
|
<a href="README_ca_ES.md">Español</a>
|
||||||
|
<a href="README_fr_FR.md">Français</a>
|
||||||
|
<a href="README_nl_NL.md">Nederlands</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## Avís legal
|
## Avís legal
|
||||||
|
|||||||
109
README_es_ES.md
Normal file
109
README_es_ES.md
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
<p align="center">
|
||||||
|
<br/>
|
||||||
|
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-green.svg?color=3F51B5&style=for-the-badge&label=License&logoColor=000000&labelColor=ececec" alt="Licencia: MIT"></a>
|
||||||
|
<a href="https://discord.gg/D8JsnBEuKb">
|
||||||
|
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" atl="Discord"/>
|
||||||
|
</a>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="design/immich-logo.svg" width="150" title="Iniciar sesión con URL personalizada">
|
||||||
|
</p>
|
||||||
|
<h3 align="center">Immich: Una solución Self-Hosted de copia de seguridad de fotos y videos de alto rendimiento</h3>
|
||||||
|
<br/>
|
||||||
|
<a href="https://immich.app">
|
||||||
|
<img src="design/immich-screenshots.png" title="Captura de pantalla principal">
|
||||||
|
</a>
|
||||||
|
<br/>
|
||||||
|
<p align="center">
|
||||||
|
<a href="README.md">English</a>
|
||||||
|
<a href="README_zh_CN.md">中文</a>
|
||||||
|
<a href="README_tr_TR.md">Türkçe</a>
|
||||||
|
<a href="README_ca_ES.md">Català</a>
|
||||||
|
<a href="README_fr_FR.md">Français</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## Descargo de responsabilidad
|
||||||
|
|
||||||
|
- ⚠️ El proyecto está en **desarrollo muy activo**.
|
||||||
|
- ⚠️ Es probable que haya errores y cambios disruptivos.
|
||||||
|
- ⚠️ **¡No utilices la aplicación como única forma de almacenar tus fotos y videos!**
|
||||||
|
|
||||||
|
## Contenido
|
||||||
|
|
||||||
|
- [Documentación oficial](https://immich.app/docs)
|
||||||
|
- [Hoja de ruta](https://github.com/orgs/immich-app/projects/1)
|
||||||
|
- [Demostración](#demo)
|
||||||
|
- [Funciones](#features)
|
||||||
|
- [Introducción](https://immich.app/docs/overview/introduction)
|
||||||
|
- [Instalación](https://immich.app/docs/install/requirements)
|
||||||
|
- [Directrices para contribuir](https://immich.app/docs/overview/support-the-project)
|
||||||
|
- [Apoya el proyecto](#support-the-project)
|
||||||
|
|
||||||
|
## Documentación
|
||||||
|
|
||||||
|
Puedes encontrar la documentación principal, incluidas las guías de instalación, en <https://immich.app/>.
|
||||||
|
|
||||||
|
## Demostración
|
||||||
|
|
||||||
|
Puedes acceder a la demostración web en <https://demo.immich.app>
|
||||||
|
|
||||||
|
Para la aplicación móvil, puedes usar `https://demo.immich.app/api` como `URL de la terminal del servidor`.
|
||||||
|
|
||||||
|
```bash title="Credenciales de la demostración"
|
||||||
|
Las credenciales son
|
||||||
|
correo electrónico: demo@immich.app
|
||||||
|
contraseña: demo
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
Especificaciones: VM de nivel gratuito de Oracle - Ámsterdam - CPU ARM64 de cuatro núcleos a 2.4 GHz, 24 GB de RAM
|
||||||
|
```
|
||||||
|
|
||||||
|
## Funcionalidades
|
||||||
|
|
||||||
|
| Funcionalidades | Móvil | Web |
|
||||||
|
| ----------------------------------------------------- | ------ | --- |
|
||||||
|
| Cargar y ver videos y fotos | Sí | Sí |
|
||||||
|
| Copia de seguridad automática al abrir la aplicación | Sí | N/D |
|
||||||
|
| Álbum(es) selectivo(s) para copia de seguridad | Sí | N/D |
|
||||||
|
| Descargar fotos y videos al dispositivo local | Sí | Sí |
|
||||||
|
| Soporte multiusuario | Sí | Sí |
|
||||||
|
| Álbum y álbumes compartidos | Sí | Sí |
|
||||||
|
| Barra de desplazamiento con función de búsqueda | Sí | Sí |
|
||||||
|
| Soporte para formatos RAW | Sí | Sí |
|
||||||
|
| Visualización de metadatos (EXIF, map) | Sí | Sí |
|
||||||
|
| Búsqueda por metadatos, objetos, rostros y CLIP | Sí | Sí |
|
||||||
|
| Funciones administrativas (gestión de usuarios) | No | Sí |
|
||||||
|
| Copia de seguridad en segundo plano | Sí | N/D |
|
||||||
|
| Desplazamiento virtual | Sí | Sí |
|
||||||
|
| Soporte de OAuth | Sí | Sí |
|
||||||
|
| Claves de API | N/D | Sí |
|
||||||
|
| Copia de seguridad y reproducción de LivePhoto | iOS | Sí |
|
||||||
|
| Estructura de almacenamiento definida por el usuario | Sí | Sí |
|
||||||
|
| Compartir públicamente | No | Sí |
|
||||||
|
| Archivar y marcar como favorito | Sí | Sí |
|
||||||
|
| Mapa global | No | Sí |
|
||||||
|
| Compartir con colaboradores | Sí | Sí |
|
||||||
|
| Reconocimiento facial y agrupación | Sí | Sí |
|
||||||
|
| Recuerdos (hace x años) | Sí | Sí |
|
||||||
|
| Soporte sin conexión | Sí | No |
|
||||||
|
| Galería de solo lectura | Sí | Sí |
|
||||||
|
|
||||||
|
## Apoya el proyecto
|
||||||
|
|
||||||
|
Me he comprometido con este proyecto, y no me detendré. Continuaré actualizando la documentación, agregando nuevas funcionalidades y corrigiendo errores. Pero no puedo hacerlo solo. Por eso, necesito tu ayuda para darme una motivación adicional para seguir adelante.
|
||||||
|
|
||||||
|
Como dijeron nuestros anfitriones en [selfhosted.show - En el episodio 'The-organization-must-not-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418), esto es una gran tarea de lo que el equipo y yo estamos haciendo. Y me encantaría poder dedicarme a esto a tiempo completo algún día, así que te pido tu ayuda para que eso sea posible.
|
||||||
|
|
||||||
|
Si consideras que esta es una causa justa y la aplicación es algo que te gustaría usar durante mucho tiempo, por favor, considera apoyar el proyecto con las siguientes opciones.
|
||||||
|
|
||||||
|
## Donación
|
||||||
|
|
||||||
|
- [Donación mensual](https://github.com/sponsors/alextran1502) a través de GitHub Sponsors
|
||||||
|
- [Donación única](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) a través de GitHub Sponsors
|
||||||
|
- [Librepay](https://liberapay.com/alex.tran1502/)
|
||||||
|
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
|
||||||
|
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
|
||||||
111
README_fr_FR.md
Normal file
111
README_fr_FR.md
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
<p align="center">
|
||||||
|
<br/>
|
||||||
|
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-green.svg?color=3F51B5&style=for-the-badge&label=License&logoColor=000000&labelColor=ececec" alt="License: MIT"></a>
|
||||||
|
<a href="https://discord.gg/D8JsnBEuKb">
|
||||||
|
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" atl="Discord"/>
|
||||||
|
</a>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="design/immich-logo.svg" width="150" title="Login With Custom URL">
|
||||||
|
</p>
|
||||||
|
<h3 align="center">Immich - Solution de sauvegarde performante et auto-hébergée des photos et des vidéos</h3>
|
||||||
|
<br/>
|
||||||
|
<a href="https://immich.app">
|
||||||
|
<img src="design/immich-screenshots.png" title="Main Screenshot">
|
||||||
|
</a>
|
||||||
|
<br/>
|
||||||
|
<p align="center">
|
||||||
|
<a href="README_zh_CN.md">中文</a>
|
||||||
|
<a href="README_tr_TR.md">Türkçe</a>
|
||||||
|
<a href="README_ca_ES.md">Català</a>
|
||||||
|
<a href="README_es_ES.md">Español</a>
|
||||||
|
<a href="README_fr_FR.md">Français</a>
|
||||||
|
<a href="README_nl_NL.md">Nederlands</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## Clause de non-responsabilité
|
||||||
|
|
||||||
|
- ⚠️ Le projet est en **très fort** développement.
|
||||||
|
- ⚠️ Attendez-vous à rencontrer des bugs et des changements importants.
|
||||||
|
- ⚠️ **N'utilisez pas cette application comme seule façon de sauvegarder vos photos et vos vidéos.**
|
||||||
|
- ⚠️ Ayez toujours un plan de sauvegarde en [3-2-1](https://www.seagate.com/fr/fr/blog/what-is-a-3-2-1-backup-strategy/) pour vos précieuses photos et vidéos !
|
||||||
|
|
||||||
|
## Sommaire
|
||||||
|
|
||||||
|
- [Documentation officielle](https://immich.app/docs)
|
||||||
|
- [Feuille de route](https://github.com/orgs/immich-app/projects/1)
|
||||||
|
- [Démo](#demo)
|
||||||
|
- [Fonctionnalités](#features)
|
||||||
|
- [Introduction](https://immich.app/docs/overview/introduction)
|
||||||
|
- [Installation](https://immich.app/docs/install/requirements)
|
||||||
|
- [Contribution](https://immich.app/docs/overview/support-the-project)
|
||||||
|
- [Soutenir le projet](#support-the-project)
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Vous pouvez trouver la documentation principale ainsi que les guides d'installation sur https://immich.app/.
|
||||||
|
|
||||||
|
## Démo
|
||||||
|
|
||||||
|
Vous pouvez accéder à la démo Web sur https://demo.immich.app
|
||||||
|
|
||||||
|
Pour l'application mobile, vous pouvez utiliser `https://demo.immich.app/api` dans le champ 'URL du point d'accès au serveur'
|
||||||
|
|
||||||
|
```bash title="Demo Credential"
|
||||||
|
Les identifiants
|
||||||
|
email: demo@immich.app
|
||||||
|
mot de passe: demo
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
Caractéristiques: Plan gratuit Oracle VM - Amsterdam - 2.4Ghz quatre-cœurs ARM64 CPU, 24GB RAM
|
||||||
|
```
|
||||||
|
|
||||||
|
# Fonctionnalités
|
||||||
|
|
||||||
|
| Fonctionnalités | Mobile | Web |
|
||||||
|
| ---------------------------------------------------------------- | ------ | --- |
|
||||||
|
| Téléverser et voir les vidéos et photos | Oui | Oui |
|
||||||
|
| Sauvegarde automatique quand l'application est ouverte | Oui | N/A |
|
||||||
|
| Sélection des albums à sauvegarder | Oui | N/A |
|
||||||
|
| Télécharger les photos et les vidéos sur l'appareil | Oui | Oui |
|
||||||
|
| Support multi-utilisateur | Oui | Oui |
|
||||||
|
| Albums et albums partagés | Oui | Oui |
|
||||||
|
| Barre de défilement mobile | Oui | Oui |
|
||||||
|
| Support des formats raw | Oui | Oui |
|
||||||
|
| Vue sur les métadonnées (EXIF, carte) | Oui | Oui |
|
||||||
|
| Rechercher par métadonnées, objets, faces et CLIP | Oui | Oui |
|
||||||
|
| Fonctions d'administration (gestion des utilisateurs) | Non | Oui |
|
||||||
|
| Sauvegarde en tâche de fond | Oui | N/A |
|
||||||
|
| Défilement virtuel | Oui | Oui |
|
||||||
|
| Support de l'OAuth | Oui | Oui |
|
||||||
|
| Clés d'API | N/A | Oui |
|
||||||
|
| Sauvegarde et lecture des LivePhotos | iOS | Oui |
|
||||||
|
| Structure de stockage définissable | Oui | Oui |
|
||||||
|
| Partage public | Non | Oui |
|
||||||
|
| Archives et favoris | Oui | Oui |
|
||||||
|
| Carte globale | Non | Oui |
|
||||||
|
| Partage entre utilisateurs | Oui | Oui |
|
||||||
|
| Reconnaissance et regroupement facial | Oui | Oui |
|
||||||
|
| Souvenirs (il y a x années) | Oui | Oui |
|
||||||
|
| Support hors-ligne | Oui | Non |
|
||||||
|
| Gallerie en lecture seule | Oui | Oui |
|
||||||
|
|
||||||
|
# Soutenir le projet
|
||||||
|
|
||||||
|
Je me suis engagé sur ce projet, et je ne compte pas m'arrêter. Je continuerai à mettre à jour les documentations, d'ajouter de nouvelles fonctionnalités et de résoudre des bugs. Mais je ne peux pas faire cela seul. Donc j'ai besoin de votre aide pour me donner encore plus de motivation et ainsi continuer.
|
||||||
|
|
||||||
|
Comme l'ont dit nos hôtes dans le [selfhosted.show - Dans l'épisode 'The-organization-must-not-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418), c'est un travail colossal ce que l'équipe et moi faisons. J'aimerais un jour être capable de faire ça à temps plein, c'est pourquoi je vous demande votre aide pour rendre cela possible.
|
||||||
|
|
||||||
|
Si vous estimez que c'est pour la bonne cause et que vous prévoyez d'utiliser l'application pour un moment, s'il-vous-plaît, pensez à soutenir le projet avec les moyens ci-dessous.
|
||||||
|
|
||||||
|
## Donation
|
||||||
|
|
||||||
|
- [Donation mensuelle](https://github.com/sponsors/alextran1502) via GitHub Sponsors
|
||||||
|
- [Donation occasionnelle](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) via GitHub Sponsors
|
||||||
|
- [Librepay](https://liberapay.com/alex.tran1502/)
|
||||||
|
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
|
||||||
|
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
|
||||||
111
README_nl_NL.md
Normal file
111
README_nl_NL.md
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
<p align="center">
|
||||||
|
<br/>
|
||||||
|
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-green.svg?color=3F51B5&style=for-the-badge&label=License&logoColor=000000&labelColor=ececec" alt="License: MIT"></a>
|
||||||
|
<a href="https://discord.gg/D8JsnBEuKb">
|
||||||
|
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" atl="Discord"/>
|
||||||
|
</a>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="design/immich-logo.svg" width="150" title="Login met aangepaste URL">
|
||||||
|
</p>
|
||||||
|
<h3 align="center">Immich - Hoogwaardige, self-hosted back-up oplossing voor foto's en video's</h3>
|
||||||
|
<br/>
|
||||||
|
<a href="https://immich.app">
|
||||||
|
<img src="design/immich-screenshots.png" title="Main Screenshot">
|
||||||
|
</a>
|
||||||
|
<br/>
|
||||||
|
<p align="center">
|
||||||
|
<a href="README_zh_CN.md">中文</a>
|
||||||
|
<a href="README_tr_TR.md">Türkçe</a>
|
||||||
|
<a href="README_ca_ES.md">Català</a>
|
||||||
|
<a href="README_es_ES.md">Español</a>
|
||||||
|
<a href="README_fr_FR.md">Français</a>
|
||||||
|
<a href="README_nl_NL.md">Nederlands</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
|
||||||
|
- ⚠️ Het project wordt momenteel **zeer actief** ontwikkeld.
|
||||||
|
- ⚠️ Verwacht bugs en ingrijpende wijzigingen.
|
||||||
|
- ⚠️ **Gebruik de app niet als de enige manier om uw foto's en video's op te slaan.**
|
||||||
|
- ⚠️ Volg altijd het [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) backup plan voor je kostbare foto's en video's!
|
||||||
|
|
||||||
|
## Inhoud
|
||||||
|
|
||||||
|
- [Officiële documentatie](https://immich.app/docs)
|
||||||
|
- [Roadmap](https://github.com/orgs/immich-app/projects/1)
|
||||||
|
- [Demo](#demo)
|
||||||
|
- [Functies](#functies)
|
||||||
|
- [Introductie](https://immich.app/docs/overview/introduction)
|
||||||
|
- [Installatie](https://immich.app/docs/install/requirements)
|
||||||
|
- [Richtlijnen voor bijdragen](https://immich.app/docs/overview/support-the-project)
|
||||||
|
- [Steun het project](#steun-het-project)
|
||||||
|
|
||||||
|
## Documentatie
|
||||||
|
|
||||||
|
De belangrijkste documentatie, inclusief installatie handleidingen, zijn te vinden op https://immich.app/.
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
De demo is te bekijken op https://demo.immich.app.
|
||||||
|
|
||||||
|
Voor de mobiele app kunt u gebruik maken van `https://demo.immich.app/api` voor de `Server Endpoint URL`
|
||||||
|
|
||||||
|
```bash title="Demo Credential"
|
||||||
|
De inloggegevens
|
||||||
|
email: demo@immich.app
|
||||||
|
wachtwoord: demo
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
Spec: Free-tier Oracle VM - Amsterdam - 2.4Ghz quad-core ARM64 CPU, 24GB RAM
|
||||||
|
```
|
||||||
|
|
||||||
|
# Functies
|
||||||
|
|
||||||
|
| Functies | Mobiel | Web |
|
||||||
|
|-----------------------------------------------------|--------|-----|
|
||||||
|
| Upload en bekijk video's en foto's | Ja | Ja |
|
||||||
|
| Automatische back-up wanneer de app wordt geopend | Ja | NVT |
|
||||||
|
| Selectieve album(s) voor back-up | Ja | NVT |
|
||||||
|
| Download foto's en video's naar een lokaal apparaat | Ja | Ja |
|
||||||
|
| Ondersteuning voor meerdere gebruikers | Ja | Ja |
|
||||||
|
| Album en gedeelde albums | Ja | Ja |
|
||||||
|
| Versleepbare scroll balk | Ja | Ja |
|
||||||
|
| Ondersteuning voor het RAW formaat | Ja | Ja |
|
||||||
|
| Metagegevensweergave (EXIF, kaart) | Ja | Ja |
|
||||||
|
| Zoek op metagegevens, objecten, gezichten en CLIP | Ja | Ja |
|
||||||
|
| Administratieve functies (gebruikersbeheer) | Nee | Ja |
|
||||||
|
| Back-up op de achtergrond | Ja | NVT |
|
||||||
|
| Virtueel scrollen | Ja | Ja |
|
||||||
|
| OAuth-ondersteuning | Ja | Ja |
|
||||||
|
| API-sleutels | NVT | Ja |
|
||||||
|
| LivePhoto-back-up en weergave | iOS | Ja |
|
||||||
|
| Door de gebruiker gedefinieerde opslagstructuur | Ja | Ja |
|
||||||
|
| Openbaar delen | Nee | Ja |
|
||||||
|
| Archief en Favorieten | Ja | Ja |
|
||||||
|
| Wereldkaart | Ja | Ja |
|
||||||
|
| Delen met partner | Ja | Ja |
|
||||||
|
| Gezichtsherkenning en groepering | Ja | Ja |
|
||||||
|
| Herinneringen (x jaar geleden) | Ja | Ja |
|
||||||
|
| Offline-ondersteuning | Ja | Nee |
|
||||||
|
| Alleen-lezen galerij | Ja | Ja |
|
||||||
|
|
||||||
|
# Steun het project
|
||||||
|
|
||||||
|
Ik ben trouw aan dit project en ik zal niet stoppen. Ik zal de documenten blijven bijwerken, nieuwe functies toevoegen en bugs oplossen. Maar ik kan het niet alleen. Ik heb dus jouw hulp nodig om mij extra motivatie te geven om door te gaan.
|
||||||
|
|
||||||
|
Als onze gastheren in de [selfhosted.show - In de aflevering 'The-organization-must-Neet-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418) zeiden, dit is een eNeerme onderneming van wat het team en ik doen. En ik zou dit graag fulltime willen doen, ik vraag jouw hulp om dat mogelijk te maken.
|
||||||
|
|
||||||
|
Als je denkt dat dit het juiste doel is en de app iets is dat je jezelf al heel lang ziet gebruiken, overweeg dan om het project te steunen met de onderstaande optie.
|
||||||
|
|
||||||
|
## Doneren
|
||||||
|
|
||||||
|
- [Maandelijkse donatie](https://github.com/sponsors/alextran1502) via GitHub Sponsors
|
||||||
|
- [Eenmalige donatie](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) via GitHub Sponsors
|
||||||
|
- [Librepay](https://liberapay.com/alex.tran1502/)
|
||||||
|
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
|
||||||
|
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
|
||||||
@@ -20,6 +20,10 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="README.md">English</a>
|
<a href="README.md">English</a>
|
||||||
<a href="README_zh_CN.md">中文</a>
|
<a href="README_zh_CN.md">中文</a>
|
||||||
|
<a href="README_ca_ES.md">Català</a>
|
||||||
|
<a href="README_es_ES.md">Español</a>
|
||||||
|
<a href="README_fr_FR.md">Français</a>
|
||||||
|
<a href="README_nl_NL.md">Nederlands</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## Feragatname
|
## Feragatname
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<h3 align="center">Immich - 高性能的自托管照片和视频备份方案</h3>
|
<h3 align="center">Immich - 高性能的自托管照片和视频备份方案</h3>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
请注意: 此README不是由Immich团队维护, 这意味着它在某一时间点不会被更新,因为我们是依靠贡献者来更新的。感谢理解。
|
请注意: 此 README 不是由 Immich 团队维护, 而是依靠贡献者来更新的,这意味着它可能并不会被及时更新。感谢理解。
|
||||||
</p>
|
</p>
|
||||||
<br/>
|
<br/>
|
||||||
<a href="https://immich.app">
|
<a href="https://immich.app">
|
||||||
@@ -24,34 +24,40 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="README.md">English</a>
|
<a href="README.md">English</a>
|
||||||
<a href="README_tr_TR.md">Türkçe</a>
|
<a href="README_tr_TR.md">Türkçe</a>
|
||||||
|
<a href="README_ca_ES.md">Català</a>
|
||||||
|
<a href="README_es_ES.md">Español</a>
|
||||||
|
<a href="README_fr_FR.md">Français</a>
|
||||||
|
<a href="README_nl_NL.md">Nederlands</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
## 免责声明
|
## 免责声明
|
||||||
|
|
||||||
- ⚠️ 本项目正在 **非常活跃** 的开发中。
|
- ⚠️ 本项目正在 **非常活跃** 地开发中。
|
||||||
- ⚠️ 可能存在bug或者重大变更。
|
- ⚠️ 可能存在 bug 或者随时有重大变更。
|
||||||
- ⚠️ **不要把本软件作为你存储照片或视频的唯一方式!**
|
- ⚠️ **不要把本软件作为您存储照片或视频的唯一方式。**
|
||||||
|
- ⚠️ 为了您宝贵的照片与视频,始终遵守 [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) 备份方案!
|
||||||
|
|
||||||
## 目录
|
## 目录
|
||||||
|
|
||||||
- [官方文档](https://immich.app/docs/overview/introduction)
|
- [官方文档](https://immich.app/docs)
|
||||||
|
- [路线图](https://github.com/orgs/immich-app/projects/1)
|
||||||
- [示例](#示例)
|
- [示例](#示例)
|
||||||
- [功能特性](#功能特性)
|
- [功能特性](#功能特性)
|
||||||
- [介绍](https://immich.app/docs/overview/introduction)
|
- [介绍](https://immich.app/docs/overview/introduction)
|
||||||
- [安装](https://immich.app/docs/install/requirements)
|
- [安装](https://immich.app/docs/install/requirements)
|
||||||
- [贡献指南](https://immich.app/docs/overview/support-the-project)
|
- [贡献指南](https://immich.app/docs/overview/support-the-project)
|
||||||
- [支持本项目](#support-the-project)
|
- [支持本项目](#支持本项目)
|
||||||
- [已知问题](#known-issues)
|
|
||||||
|
|
||||||
## 官方文档
|
## 官方文档
|
||||||
|
|
||||||
你可以在 https://immich.app/ 找到包含安装手册的官方文档.
|
您可以在 https://immich.app/ 找到官方文档(包含安装手册)。
|
||||||
|
|
||||||
## 示例
|
## 示例
|
||||||
|
|
||||||
你可以在 https://demo.immich.app 访问示例.
|
您可以在 https://demo.immich.app 访问示例。
|
||||||
|
|
||||||
在移动端, 你可以使用 `https://demo.immich.app/api`获取`服务终端链接`
|
在移动端, 您可以使用 `https://demo.immich.app/api` 获取 `服务终端链接`
|
||||||
|
|
||||||
```bash title="示例认证信息"
|
```bash title="示例认证信息"
|
||||||
认证信息
|
认证信息
|
||||||
@@ -60,57 +66,52 @@
|
|||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
规格: 甲骨文免费虚拟机套餐-阿姆斯特丹 4核 2.4Ghz ARM64 CPU, 24GB RAM。
|
规格: 甲骨文免费虚拟机套餐——阿姆斯特丹 4核 2.4Ghz ARM64 CPU, 24GB RAM。
|
||||||
```
|
```
|
||||||
|
|
||||||
# 功能特性
|
# 功能特性
|
||||||
|
|
||||||
| 功能特性 | 移动端 | 网页端 |
|
| 功能特性 | 移动端 | 网页端 |
|
||||||
| ------------------------------------------- | ------- | --- |
|
| ------------------------------------------- | ------- | --- |
|
||||||
| 上传并查看照片和视频 | 是 | 是 |
|
| 上传并查看照片和视频 | 是 | 是 |
|
||||||
| 软件运行时自动备份 | 是 | N/A |
|
| 软件运行时自动备份 | 是 | N/A |
|
||||||
| 选择需要备份的相册 | 是 | N/A |
|
| 选择需要备份的相册 | 是 | N/A |
|
||||||
| 下载照片和视频到本地 | 是 | 是 |
|
| 下载照片和视频到本地 | 是 | 是 |
|
||||||
| 多用户支持 | 是 | 是 |
|
| 多用户支持 | 是 | 是 |
|
||||||
| 相册 | 是 | 是 |
|
| 相册 | 是 | 是 |
|
||||||
| 共享相册 | 是 | 是 |
|
| 共享相册 | 是 | 是 |
|
||||||
| 可拖动的快速导航栏 | 是 | 是 |
|
| 可拖动的快速导航栏 | 是 | 是 |
|
||||||
| 支持RAW格式 (HEIC, HEIF, DNG, Apple ProRaw) | 是 | 是 |
|
| 支持RAW格式 (HEIC, HEIF, DNG, Apple ProRaw) | 是 | 是 |
|
||||||
| 元数据视图 (EXIF, 地图) | 是 | 是 |
|
| 元数据视图(EXIF, 地图) | 是 | 是 |
|
||||||
| 通过元数据、对象和标签进行搜索 | 是 | No |
|
| 通过元数据、对象和标签进行搜索 | 是 | 是 |
|
||||||
| 管理功能 (用户管理) | N/A | 是 |
|
| 管理功能(用户管理) | 否 | 是 |
|
||||||
| 后台备份 | Android | N/A |
|
| 后台备份 | 是 | N/A |
|
||||||
| 虚拟滚动 | 是 | 是 |
|
| 虚拟滚动 | 是 | 是 |
|
||||||
| OAuth支持 | 是 | 是 |
|
| OAuth 支持 | 是 | 是 |
|
||||||
| 实时照片备份和查看 (仅iOS) | 是 | 是 |
|
| API Keys|N/A|是|
|
||||||
|
| 实况照片备份和查看 | 仅 iOS | 是 |
|
||||||
|
|用户自定义存储结构|是|是|
|
||||||
|
|公共分享|否|是|
|
||||||
|
|归档与收藏功能|是|是|
|
||||||
|
|全局地图|否|是|
|
||||||
|
|好友分享|是|是|
|
||||||
|
|人像识别与分组|是|是|
|
||||||
|
|回忆(那年今日)|是|是|
|
||||||
|
|离线支持|是|否|
|
||||||
|
|只读相册|是|是|
|
||||||
|
|
||||||
# 支持本项目
|
# 支持本项目
|
||||||
|
|
||||||
我已经致力于本项目并且将我会持续更新文档、新增功能和修复问题。但是我不能一个人走下去,所以我需要你给予我走下去的动力。
|
我已经致力于本项目并且将我会持续更新文档、新增功能和修复问题。但是独木不成林,我需要您给予我坚持下去的动力。
|
||||||
|
|
||||||
就像我主页里面 [selfhosted.show - In the episode 'The-organization-must-not-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418) 说的一样,这是我和团队的一项艰巨的任务。我希望某一天我能够全职开发本项目,在此我希望你们能够助我梦想成真。
|
就像我在 [selfhosted.show - In the episode 'The-organization-must-not-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418) 节目里说的一样,这是我和团队的一项艰巨任务。并且我希望某一天我能够全职开发本项目,在此我请求您能够助我梦想成真。
|
||||||
|
|
||||||
如果你使用了本项目一段时间,并且觉得上面的话有道理,那么请你按照如下方式帮助我吧。
|
如果您使用了本项目一段时间,并且觉得上面的话有道理,那么请您考虑通过下列任一方式支持我吧。
|
||||||
|
|
||||||
## 捐赠
|
## 捐赠
|
||||||
|
|
||||||
- [按月捐赠](https://github.com/sponsors/alextran1502) via GitHub Sponsors
|
- 通过 GitHub Sponsors [按月捐赠](https://github.com/sponsors/alextran1502)
|
||||||
- [一次捐赠](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) via Github Sponsors
|
- 通过 Github Sponsors [单次捐赠](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502)
|
||||||
|
- [Librepay](https://liberapay.com/alex.tran1502/)
|
||||||
# 已知问题
|
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
|
||||||
|
- 比特币: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
|
||||||
## TensorFlow 构建问题
|
|
||||||
|
|
||||||
_这是一个针对于Proxmox的已知问题_
|
|
||||||
|
|
||||||
TensorFlow 不能运行在很旧的CPU架构上, 需要运行在AVX和AVX2指令集的CPU上。如果你在docker-compose的命令行中遇到了 `illegal instruction core dump`的错误, 通过如下命令检查你的CPU flag寄存器然后确保你能够看到`AVX`和`AVX2`的字样:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
more /proc/cpuinfo | grep flags
|
|
||||||
```
|
|
||||||
|
|
||||||
如果你在Proxmox中运行虚拟机, 虚拟机中没有启用flag寄存器。
|
|
||||||
|
|
||||||
你需要在虚拟机的硬件面板中把CPU类型从`kvm64`改为`host`。
|
|
||||||
|
|
||||||
`Hardware > Processors > Edit > Advanced > Type (dropdown menu) > host`
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
import type { Config } from 'jest';
|
|
||||||
|
|
||||||
const config: Config = {
|
|
||||||
preset: 'ts-jest',
|
|
||||||
setupFilesAfterEnv: ['jest-extended/all'],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default config;
|
|
||||||
222
cli/package-lock.json
generated
222
cli/package-lock.json
generated
@@ -8,9 +8,14 @@
|
|||||||
"name": "immich-cli",
|
"name": "immich-cli",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
|
"byte-size": "^8.1.1",
|
||||||
|
"cli-progress": "^3.12.0",
|
||||||
|
"commander": "^11.0.0",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"mime-types": "^2.1.35",
|
"glob": "^10.3.1",
|
||||||
"systeminformation": "^5.18.4"
|
"picomatch": "^2.3.1",
|
||||||
|
"systeminformation": "^5.18.4",
|
||||||
|
"yaml": "^2.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/byte-size": "^8.1.0",
|
"@types/byte-size": "^8.1.0",
|
||||||
@@ -22,28 +27,23 @@
|
|||||||
"@types/mock-fs": "^4.13.1",
|
"@types/mock-fs": "^4.13.1",
|
||||||
"@types/node": "^20.3.1",
|
"@types/node": "^20.3.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.60.1",
|
"@typescript-eslint/eslint-plugin": "^5.60.1",
|
||||||
"byte-size": "^8.1.1",
|
"@typescript-eslint/parser": "^5.48.1",
|
||||||
"chai": "^4.3.7",
|
"chai": "^4.3.7",
|
||||||
"cli-progress": "^3.12.0",
|
|
||||||
"commander": "^11.0.0",
|
|
||||||
"eslint": "^8.43.0",
|
"eslint": "^8.43.0",
|
||||||
"eslint-config-prettier": "^8.8.0",
|
"eslint-config-prettier": "^8.8.0",
|
||||||
"eslint-plugin-jest": "^27.2.2",
|
"eslint-plugin-jest": "^27.2.2",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"eslint-plugin-unicorn": "^47.0.0",
|
"eslint-plugin-unicorn": "^47.0.0",
|
||||||
"glob": "^10.3.1",
|
|
||||||
"jest": "^29.5.0",
|
"jest": "^29.5.0",
|
||||||
"jest-extended": "^4.0.0",
|
"jest-extended": "^4.0.0",
|
||||||
"jest-message-util": "^29.5.0",
|
"jest-message-util": "^29.5.0",
|
||||||
"jest-mock-axios": "^4.7.2",
|
"jest-mock-axios": "^4.7.2",
|
||||||
"jest-when": "^3.5.2",
|
"jest-when": "^3.5.2",
|
||||||
"mock-fs": "^5.2.0",
|
"mock-fs": "^5.2.0",
|
||||||
"picomatch": "^2.3.1",
|
|
||||||
"ts-jest": "^29.1.0",
|
"ts-jest": "^29.1.0",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"tslib": "^2.5.3",
|
"tslib": "^2.5.3",
|
||||||
"typescript": "^4.9.4",
|
"typescript": "^4.9.4"
|
||||||
"yaml": "^2.3.1"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@ampproject/remapping": {
|
"node_modules/@ampproject/remapping": {
|
||||||
@@ -111,9 +111,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/core/node_modules/semver": {
|
"node_modules/@babel/core/node_modules/semver": {
|
||||||
"version": "6.3.0",
|
"version": "6.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
@@ -154,9 +154,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-compilation-targets/node_modules/semver": {
|
"node_modules/@babel/helper-compilation-targets/node_modules/semver": {
|
||||||
"version": "6.3.0",
|
"version": "6.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
@@ -772,7 +772,6 @@
|
|||||||
"version": "8.0.2",
|
"version": "8.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||||
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
|
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"string-width": "^5.1.2",
|
"string-width": "^5.1.2",
|
||||||
"string-width-cjs": "npm:string-width@^4.2.0",
|
"string-width-cjs": "npm:string-width@^4.2.0",
|
||||||
@@ -789,7 +788,6 @@
|
|||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
|
||||||
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
|
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -801,7 +799,6 @@
|
|||||||
"version": "6.2.1",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -812,14 +809,12 @@
|
|||||||
"node_modules/@isaacs/cliui/node_modules/emoji-regex": {
|
"node_modules/@isaacs/cliui/node_modules/emoji-regex": {
|
||||||
"version": "9.2.2",
|
"version": "9.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@isaacs/cliui/node_modules/string-width": {
|
"node_modules/@isaacs/cliui/node_modules/string-width": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"eastasianwidth": "^0.2.0",
|
"eastasianwidth": "^0.2.0",
|
||||||
"emoji-regex": "^9.2.2",
|
"emoji-regex": "^9.2.2",
|
||||||
@@ -836,7 +831,6 @@
|
|||||||
"version": "7.1.0",
|
"version": "7.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^6.0.1"
|
"ansi-regex": "^6.0.1"
|
||||||
},
|
},
|
||||||
@@ -851,7 +845,6 @@
|
|||||||
"version": "8.1.0",
|
"version": "8.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
||||||
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-styles": "^6.1.0",
|
"ansi-styles": "^6.1.0",
|
||||||
"string-width": "^5.0.1",
|
"string-width": "^5.0.1",
|
||||||
@@ -1347,7 +1340,6 @@
|
|||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||||
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
|
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
@@ -1664,7 +1656,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.1.tgz",
|
||||||
"integrity": "sha512-pHWlc3alg2oSMGwsU/Is8hbm3XFbcrb6P5wIxcQW9NsYBfnrubl/GhVVD/Jm/t8HXhA2WncoIRfBtnCgRGV96Q==",
|
"integrity": "sha512-pHWlc3alg2oSMGwsU/Is8hbm3XFbcrb6P5wIxcQW9NsYBfnrubl/GhVVD/Jm/t8HXhA2WncoIRfBtnCgRGV96Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "5.60.1",
|
"@typescript-eslint/scope-manager": "5.60.1",
|
||||||
"@typescript-eslint/types": "5.60.1",
|
"@typescript-eslint/types": "5.60.1",
|
||||||
@@ -1692,7 +1683,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz",
|
||||||
"integrity": "sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ==",
|
"integrity": "sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "5.60.1",
|
"@typescript-eslint/types": "5.60.1",
|
||||||
"@typescript-eslint/visitor-keys": "5.60.1"
|
"@typescript-eslint/visitor-keys": "5.60.1"
|
||||||
@@ -1710,7 +1700,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.1.tgz",
|
||||||
"integrity": "sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==",
|
"integrity": "sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
},
|
},
|
||||||
@@ -1724,7 +1713,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz",
|
||||||
"integrity": "sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw==",
|
"integrity": "sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "5.60.1",
|
"@typescript-eslint/types": "5.60.1",
|
||||||
"@typescript-eslint/visitor-keys": "5.60.1",
|
"@typescript-eslint/visitor-keys": "5.60.1",
|
||||||
@@ -1752,7 +1740,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz",
|
||||||
"integrity": "sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==",
|
"integrity": "sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "5.60.1",
|
"@typescript-eslint/types": "5.60.1",
|
||||||
"eslint-visitor-keys": "^3.3.0"
|
"eslint-visitor-keys": "^3.3.0"
|
||||||
@@ -2036,7 +2023,6 @@
|
|||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
@@ -2045,7 +2031,6 @@
|
|||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-convert": "^2.0.1"
|
"color-convert": "^2.0.1"
|
||||||
},
|
},
|
||||||
@@ -2211,8 +2196,7 @@
|
|||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
@@ -2311,7 +2295,6 @@
|
|||||||
"version": "8.1.1",
|
"version": "8.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/byte-size/-/byte-size-8.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/byte-size/-/byte-size-8.1.1.tgz",
|
||||||
"integrity": "sha512-tUkzZWK0M/qdoLEqikxBWe4kumyuwjl3HO6zHTr4yEI23EojPtLYXdG1+AQY7MN0cGyNDvEaJ8wiYQm6P2bPxg==",
|
"integrity": "sha512-tUkzZWK0M/qdoLEqikxBWe4kumyuwjl3HO6zHTr4yEI23EojPtLYXdG1+AQY7MN0cGyNDvEaJ8wiYQm6P2bPxg==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.17"
|
"node": ">=12.17"
|
||||||
}
|
}
|
||||||
@@ -2464,7 +2447,6 @@
|
|||||||
"version": "3.12.0",
|
"version": "3.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz",
|
||||||
"integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==",
|
"integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"string-width": "^4.2.3"
|
"string-width": "^4.2.3"
|
||||||
},
|
},
|
||||||
@@ -2506,7 +2488,6 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-name": "~1.1.4"
|
"color-name": "~1.1.4"
|
||||||
},
|
},
|
||||||
@@ -2517,8 +2498,7 @@
|
|||||||
"node_modules/color-name": {
|
"node_modules/color-name": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/combined-stream": {
|
"node_modules/combined-stream": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
@@ -2535,7 +2515,6 @@
|
|||||||
"version": "11.0.0",
|
"version": "11.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz",
|
||||||
"integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==",
|
"integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16"
|
"node": ">=16"
|
||||||
}
|
}
|
||||||
@@ -2562,7 +2541,6 @@
|
|||||||
"version": "7.0.3",
|
"version": "7.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||||
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
|
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"path-key": "^3.1.0",
|
"path-key": "^3.1.0",
|
||||||
"shebang-command": "^2.0.0",
|
"shebang-command": "^2.0.0",
|
||||||
@@ -2675,8 +2653,7 @@
|
|||||||
"node_modules/eastasianwidth": {
|
"node_modules/eastasianwidth": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
|
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.4.440",
|
"version": "1.4.440",
|
||||||
@@ -2699,8 +2676,7 @@
|
|||||||
"node_modules/emoji-regex": {
|
"node_modules/emoji-regex": {
|
||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/error-ex": {
|
"node_modules/error-ex": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
@@ -2801,9 +2777,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-jest": {
|
"node_modules/eslint-plugin-jest": {
|
||||||
"version": "27.2.2",
|
"version": "27.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.2.3.tgz",
|
||||||
"integrity": "sha512-euzbp06F934Z7UDl5ZUaRPLAc9MKjh0rMPERrHT7UhlCEwgb25kBj37TvMgWeHZVkR5I9CayswrpoaqZU1RImw==",
|
"integrity": "sha512-sRLlSCpICzWuje66Gl9zvdF6mwD5X86I4u55hJyFBsxYOsBCmT5+kSUjf+fkFWVMMgpzNEupjW8WzUqi83hJAQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/utils": "^5.10.0"
|
"@typescript-eslint/utils": "^5.10.0"
|
||||||
@@ -2812,7 +2788,7 @@
|
|||||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
"@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0",
|
||||||
"eslint": "^7.0.0 || ^8.0.0",
|
"eslint": "^7.0.0 || ^8.0.0",
|
||||||
"jest": "*"
|
"jest": "*"
|
||||||
},
|
},
|
||||||
@@ -3203,7 +3179,6 @@
|
|||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
|
||||||
"integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
|
"integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cross-spawn": "^7.0.0",
|
"cross-spawn": "^7.0.0",
|
||||||
"signal-exit": "^4.0.1"
|
"signal-exit": "^4.0.1"
|
||||||
@@ -3219,7 +3194,6 @@
|
|||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz",
|
||||||
"integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==",
|
"integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
},
|
},
|
||||||
@@ -3318,7 +3292,6 @@
|
|||||||
"version": "10.3.1",
|
"version": "10.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.1.tgz",
|
||||||
"integrity": "sha512-9BKYcEeIs7QwlCYs+Y3GBvqAMISufUS0i2ELd11zpZjxI5V9iyRj0HgzB5/cLf2NY4vcYBTYzJ7GIui7j/4DOw==",
|
"integrity": "sha512-9BKYcEeIs7QwlCYs+Y3GBvqAMISufUS0i2ELd11zpZjxI5V9iyRj0HgzB5/cLf2NY4vcYBTYzJ7GIui7j/4DOw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"foreground-child": "^3.1.0",
|
"foreground-child": "^3.1.0",
|
||||||
"jackspeak": "^2.0.3",
|
"jackspeak": "^2.0.3",
|
||||||
@@ -3352,7 +3325,6 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^1.0.0"
|
"balanced-match": "^1.0.0"
|
||||||
}
|
}
|
||||||
@@ -3361,7 +3333,6 @@
|
|||||||
"version": "9.0.2",
|
"version": "9.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.2.tgz",
|
||||||
"integrity": "sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg==",
|
"integrity": "sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"brace-expansion": "^2.0.1"
|
"brace-expansion": "^2.0.1"
|
||||||
},
|
},
|
||||||
@@ -3458,6 +3429,12 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/hosted-git-info": {
|
||||||
|
"version": "2.8.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
|
||||||
|
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"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",
|
||||||
@@ -3597,7 +3574,6 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
@@ -3656,8 +3632,7 @@
|
|||||||
"node_modules/isexe": {
|
"node_modules/isexe": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/istanbul-lib-coverage": {
|
"node_modules/istanbul-lib-coverage": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
@@ -3685,9 +3660,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/istanbul-lib-instrument/node_modules/semver": {
|
"node_modules/istanbul-lib-instrument/node_modules/semver": {
|
||||||
"version": "6.3.0",
|
"version": "6.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
@@ -3750,7 +3725,6 @@
|
|||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz",
|
||||||
"integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==",
|
"integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@isaacs/cliui": "^8.0.2"
|
"@isaacs/cliui": "^8.0.2"
|
||||||
},
|
},
|
||||||
@@ -3933,24 +3907,6 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jest-config/node_modules/parse-json": {
|
|
||||||
"version": "5.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
|
||||||
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/code-frame": "^7.0.0",
|
|
||||||
"error-ex": "^1.3.1",
|
|
||||||
"json-parse-even-better-errors": "^2.3.0",
|
|
||||||
"lines-and-columns": "^1.1.6"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/jest-diff": {
|
"node_modules/jest-diff": {
|
||||||
"version": "29.5.0",
|
"version": "29.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz",
|
||||||
@@ -4571,9 +4527,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/make-dir/node_modules/semver": {
|
"node_modules/make-dir/node_modules/semver": {
|
||||||
"version": "6.3.0",
|
"version": "6.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
@@ -4675,7 +4631,6 @@
|
|||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz",
|
||||||
"integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==",
|
"integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16 || 14 >=14.17"
|
"node": ">=16 || 14 >=14.17"
|
||||||
}
|
}
|
||||||
@@ -4719,6 +4674,27 @@
|
|||||||
"integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==",
|
"integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/normalize-package-data": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
|
||||||
|
"integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"hosted-git-info": "^2.1.4",
|
||||||
|
"resolve": "^1.10.0",
|
||||||
|
"semver": "2 || 3 || 4 || 5",
|
||||||
|
"validate-npm-package-license": "^3.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/normalize-package-data/node_modules/semver": {
|
||||||
|
"version": "5.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||||
|
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/normalize-path": {
|
"node_modules/normalize-path": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
@@ -4838,6 +4814,24 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/parse-json": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/code-frame": "^7.0.0",
|
||||||
|
"error-ex": "^1.3.1",
|
||||||
|
"json-parse-even-better-errors": "^2.3.0",
|
||||||
|
"lines-and-columns": "^1.1.6"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"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",
|
||||||
@@ -4860,7 +4854,6 @@
|
|||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
@@ -4875,7 +4868,6 @@
|
|||||||
"version": "1.10.0",
|
"version": "1.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.0.tgz",
|
||||||
"integrity": "sha512-tZFEaRQbMLjwrsmidsGJ6wDMv0iazJWk6SfIKnY4Xru8auXgmJkOBa5DUbYFcFD2Rzk2+KDlIiF0GVXNCbgC7g==",
|
"integrity": "sha512-tZFEaRQbMLjwrsmidsGJ6wDMv0iazJWk6SfIKnY4Xru8auXgmJkOBa5DUbYFcFD2Rzk2+KDlIiF0GVXNCbgC7g==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lru-cache": "^9.1.1 || ^10.0.0",
|
"lru-cache": "^9.1.1 || ^10.0.0",
|
||||||
"minipass": "^5.0.0 || ^6.0.2"
|
"minipass": "^5.0.0 || ^6.0.2"
|
||||||
@@ -4891,7 +4883,6 @@
|
|||||||
"version": "10.0.0",
|
"version": "10.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.0.tgz",
|
||||||
"integrity": "sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==",
|
"integrity": "sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "14 || >=16.14"
|
"node": "14 || >=16.14"
|
||||||
}
|
}
|
||||||
@@ -4924,7 +4915,6 @@
|
|||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
},
|
},
|
||||||
@@ -5239,51 +5229,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/read-pkg/node_modules/hosted-git-info": {
|
|
||||||
"version": "2.8.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
|
|
||||||
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/read-pkg/node_modules/normalize-package-data": {
|
|
||||||
"version": "2.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
|
|
||||||
"integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"hosted-git-info": "^2.1.4",
|
|
||||||
"resolve": "^1.10.0",
|
|
||||||
"semver": "2 || 3 || 4 || 5",
|
|
||||||
"validate-npm-package-license": "^3.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/read-pkg/node_modules/parse-json": {
|
|
||||||
"version": "5.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
|
||||||
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/code-frame": "^7.0.0",
|
|
||||||
"error-ex": "^1.3.1",
|
|
||||||
"json-parse-even-better-errors": "^2.3.0",
|
|
||||||
"lines-and-columns": "^1.1.6"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/read-pkg/node_modules/semver": {
|
|
||||||
"version": "5.7.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
|
||||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
|
||||||
"semver": "bin/semver"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/read-pkg/node_modules/type-fest": {
|
"node_modules/read-pkg/node_modules/type-fest": {
|
||||||
"version": "0.6.0",
|
"version": "0.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
|
||||||
@@ -5502,7 +5447,6 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"shebang-regex": "^3.0.0"
|
"shebang-regex": "^3.0.0"
|
||||||
},
|
},
|
||||||
@@ -5514,7 +5458,6 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
@@ -5635,7 +5578,6 @@
|
|||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"emoji-regex": "^8.0.0",
|
"emoji-regex": "^8.0.0",
|
||||||
"is-fullwidth-code-point": "^3.0.0",
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
@@ -5650,7 +5592,6 @@
|
|||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"emoji-regex": "^8.0.0",
|
"emoji-regex": "^8.0.0",
|
||||||
"is-fullwidth-code-point": "^3.0.0",
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
@@ -5664,7 +5605,6 @@
|
|||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^5.0.1"
|
"ansi-regex": "^5.0.1"
|
||||||
},
|
},
|
||||||
@@ -5677,7 +5617,6 @@
|
|||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^5.0.1"
|
"ansi-regex": "^5.0.1"
|
||||||
},
|
},
|
||||||
@@ -6111,7 +6050,6 @@
|
|||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"isexe": "^2.0.0"
|
"isexe": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -6123,9 +6061,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/word-wrap": {
|
"node_modules/word-wrap": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
|
||||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
"integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@@ -6153,7 +6091,6 @@
|
|||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-styles": "^4.0.0",
|
"ansi-styles": "^4.0.0",
|
||||||
"string-width": "^4.1.0",
|
"string-width": "^4.1.0",
|
||||||
@@ -6204,7 +6141,6 @@
|
|||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz",
|
||||||
"integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==",
|
"integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 14"
|
"node": ">= 14"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,14 @@
|
|||||||
"name": "immich-cli",
|
"name": "immich-cli",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
|
"byte-size": "^8.1.1",
|
||||||
|
"cli-progress": "^3.12.0",
|
||||||
|
"commander": "^11.0.0",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"mime-types": "^2.1.35",
|
"glob": "^10.3.1",
|
||||||
"systeminformation": "^5.18.4"
|
"picomatch": "^2.3.1",
|
||||||
|
"systeminformation": "^5.18.4",
|
||||||
|
"yaml": "^2.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/byte-size": "^8.1.0",
|
"@types/byte-size": "^8.1.0",
|
||||||
@@ -16,34 +21,48 @@
|
|||||||
"@types/mock-fs": "^4.13.1",
|
"@types/mock-fs": "^4.13.1",
|
||||||
"@types/node": "^20.3.1",
|
"@types/node": "^20.3.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.60.1",
|
"@typescript-eslint/eslint-plugin": "^5.60.1",
|
||||||
"byte-size": "^8.1.1",
|
"@typescript-eslint/parser": "^5.48.1",
|
||||||
"chai": "^4.3.7",
|
"chai": "^4.3.7",
|
||||||
"cli-progress": "^3.12.0",
|
|
||||||
"commander": "^11.0.0",
|
|
||||||
"eslint": "^8.43.0",
|
"eslint": "^8.43.0",
|
||||||
"eslint-config-prettier": "^8.8.0",
|
"eslint-config-prettier": "^8.8.0",
|
||||||
"eslint-plugin-jest": "^27.2.2",
|
"eslint-plugin-jest": "^27.2.2",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"eslint-plugin-unicorn": "^47.0.0",
|
"eslint-plugin-unicorn": "^47.0.0",
|
||||||
"glob": "^10.3.1",
|
|
||||||
"jest": "^29.5.0",
|
"jest": "^29.5.0",
|
||||||
"jest-extended": "^4.0.0",
|
"jest-extended": "^4.0.0",
|
||||||
"jest-message-util": "^29.5.0",
|
"jest-message-util": "^29.5.0",
|
||||||
"jest-mock-axios": "^4.7.2",
|
"jest-mock-axios": "^4.7.2",
|
||||||
"jest-when": "^3.5.2",
|
"jest-when": "^3.5.2",
|
||||||
"mock-fs": "^5.2.0",
|
"mock-fs": "^5.2.0",
|
||||||
"picomatch": "^2.3.1",
|
|
||||||
"ts-jest": "^29.1.0",
|
"ts-jest": "^29.1.0",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"tslib": "^2.5.3",
|
"tslib": "^2.5.3",
|
||||||
"typescript": "^4.9.4",
|
"typescript": "^4.9.4"
|
||||||
"yaml": "^2.3.1"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"build": "tsc --project tsconfig.build.json",
|
||||||
"lint": "eslint \"src/**/*.ts\" --max-warnings 0",
|
"lint": "eslint \"src/**/*.ts\" --max-warnings 0",
|
||||||
"prepack": "yarn build ",
|
"prepack": "yarn build ",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:cov": "jest --coverage",
|
"test:cov": "jest --coverage",
|
||||||
"format": "prettier --check ."
|
"format": "prettier --check ."
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"clearMocks": true,
|
||||||
|
"moduleFileExtensions": [
|
||||||
|
"js",
|
||||||
|
"json",
|
||||||
|
"ts"
|
||||||
|
],
|
||||||
|
"rootDir": ".",
|
||||||
|
"testRegex": ".*\\.spec\\.ts$",
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.ts$": "ts-jest"
|
||||||
|
},
|
||||||
|
"collectCoverageFrom": [
|
||||||
|
"<rootDir>/src/**/*.(t|j)s"
|
||||||
|
],
|
||||||
|
"coverageDirectory": "./coverage",
|
||||||
|
"testEnvironment": "node"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4862
cli/src/api/open-api/api.ts
generated
4862
cli/src/api/open-api/api.ts
generated
File diff suppressed because it is too large
Load Diff
2
cli/src/api/open-api/base.ts
generated
2
cli/src/api/open-api/base.ts
generated
@@ -4,7 +4,7 @@
|
|||||||
* Immich
|
* Immich
|
||||||
* Immich API
|
* Immich API
|
||||||
*
|
*
|
||||||
* The version of the OpenAPI document: 1.68.0
|
* The version of the OpenAPI document: 1.79.0
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|||||||
2
cli/src/api/open-api/common.ts
generated
2
cli/src/api/open-api/common.ts
generated
@@ -4,7 +4,7 @@
|
|||||||
* Immich
|
* Immich
|
||||||
* Immich API
|
* Immich API
|
||||||
*
|
*
|
||||||
* The version of the OpenAPI document: 1.68.0
|
* The version of the OpenAPI document: 1.79.0
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|||||||
2
cli/src/api/open-api/configuration.ts
generated
2
cli/src/api/open-api/configuration.ts
generated
@@ -4,7 +4,7 @@
|
|||||||
* Immich
|
* Immich
|
||||||
* Immich API
|
* Immich API
|
||||||
*
|
*
|
||||||
* The version of the OpenAPI document: 1.68.0
|
* The version of the OpenAPI document: 1.79.0
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|||||||
2
cli/src/api/open-api/index.ts
generated
2
cli/src/api/open-api/index.ts
generated
@@ -4,7 +4,7 @@
|
|||||||
* Immich
|
* Immich
|
||||||
* Immich API
|
* Immich API
|
||||||
*
|
*
|
||||||
* The version of the OpenAPI document: 1.68.0
|
* The version of the OpenAPI document: 1.79.0
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ import { SessionService } from '../services/session.service';
|
|||||||
import { LoginError } from '../cores/errors/login-error';
|
import { LoginError } from '../cores/errors/login-error';
|
||||||
import { exit } from 'node:process';
|
import { exit } from 'node:process';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import { ServerVersionReponseDto, UserResponseDto } from 'src/api/open-api';
|
import { ServerVersionResponseDto, UserResponseDto } from 'src/api/open-api';
|
||||||
|
|
||||||
export abstract class BaseCommand {
|
export abstract class BaseCommand {
|
||||||
protected sessionService!: SessionService;
|
protected sessionService!: SessionService;
|
||||||
protected immichApi!: ImmichApi;
|
protected immichApi!: ImmichApi;
|
||||||
protected deviceId!: string;
|
protected deviceId!: string;
|
||||||
protected user!: UserResponseDto;
|
protected user!: UserResponseDto;
|
||||||
protected serverVersion!: ServerVersionReponseDto;
|
protected serverVersion!: ServerVersionResponseDto;
|
||||||
|
|
||||||
protected configDir;
|
protected configDir;
|
||||||
protected authPath;
|
protected authPath;
|
||||||
|
|||||||
@@ -70,11 +70,13 @@ export default class Upload extends BaseCommand {
|
|||||||
if (options.import) {
|
if (options.import) {
|
||||||
const importData = {
|
const importData = {
|
||||||
assetPath: asset.path,
|
assetPath: asset.path,
|
||||||
|
sidecarPath: asset.sidecarPath,
|
||||||
deviceAssetId: asset.deviceAssetId,
|
deviceAssetId: asset.deviceAssetId,
|
||||||
deviceId: this.deviceId,
|
deviceId: this.deviceId,
|
||||||
fileCreatedAt: asset.fileCreatedAt,
|
fileCreatedAt: asset.fileCreatedAt,
|
||||||
fileModifiedAt: asset.fileModifiedAt,
|
fileModifiedAt: asset.fileModifiedAt,
|
||||||
isFavorite: false,
|
isFavorite: false,
|
||||||
|
isReadOnly: options.readOnly,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!this.dryRun) {
|
if (!this.dryRun) {
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ export class UploadOptionsDto {
|
|||||||
skipHash = false;
|
skipHash = false;
|
||||||
delete = false;
|
delete = false;
|
||||||
import = false;
|
import = false;
|
||||||
|
readOnly = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,9 +35,11 @@ program
|
|||||||
.default(false),
|
.default(false),
|
||||||
)
|
)
|
||||||
.addOption(new Option('-i, --ignore [paths...]', 'Paths to ignore').env('IMMICH_IGNORE_PATHS').default(false))
|
.addOption(new Option('-i, --ignore [paths...]', 'Paths to ignore').env('IMMICH_IGNORE_PATHS').default(false))
|
||||||
.argument('[paths...]', 'One or more paths to assets to be uploaded')
|
.addOption(new Option('--no-read-only', 'Import files without read-only protection, allowing Immich to manage them'))
|
||||||
|
.argument('[paths...]', 'One or more paths to assets to be imported')
|
||||||
.action((paths, options) => {
|
.action((paths, options) => {
|
||||||
options.import = true;
|
options.import = true;
|
||||||
|
options.excludePatterns = options.ignore;
|
||||||
new Upload().run(paths, options);
|
new Upload().run(paths, options);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "../tsconfig",
|
|
||||||
"compilerOptions": {
|
|
||||||
"noEmit": true
|
|
||||||
},
|
|
||||||
"references": [{ "path": ".." }]
|
|
||||||
}
|
|
||||||
4
cli/tsconfig.build.json
Normal file
4
cli/tsconfig.build.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"exclude": ["dist", "node_modules", "upload", "test", "**/*spec.ts"]
|
||||||
|
}
|
||||||
@@ -31,11 +31,10 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: ../machine-learning
|
context: ../machine-learning
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
command: python main.py
|
|
||||||
ports:
|
ports:
|
||||||
- 3003:3003
|
- 3003:3003
|
||||||
volumes:
|
volumes:
|
||||||
- ../machine-learning/app:/usr/src/app
|
- ../machine-learning:/usr/src/app
|
||||||
- model-cache:/cache
|
- model-cache:/cache
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
@@ -48,6 +47,9 @@ services:
|
|||||||
immich-microservices:
|
immich-microservices:
|
||||||
container_name: immich_microservices
|
container_name: immich_microservices
|
||||||
image: immich-microservices:latest
|
image: immich-microservices:latest
|
||||||
|
# extends:
|
||||||
|
# file: hwaccel.yml
|
||||||
|
# service: hwaccel
|
||||||
build:
|
build:
|
||||||
context: ../server
|
context: ../server
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
@@ -98,8 +100,8 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- TYPESENSE_API_KEY=${TYPESENSE_API_KEY}
|
- TYPESENSE_API_KEY=${TYPESENSE_API_KEY}
|
||||||
- TYPESENSE_DATA_DIR=/data
|
- TYPESENSE_DATA_DIR=/data
|
||||||
logging:
|
# remove this to get debug messages
|
||||||
driver: none
|
- GLOG_minloglevel=1
|
||||||
volumes:
|
volumes:
|
||||||
- tsdata:/data
|
- tsdata:/data
|
||||||
|
|
||||||
@@ -116,7 +118,6 @@ services:
|
|||||||
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}
|
||||||
PG_DATA: /var/lib/postgresql/data
|
|
||||||
volumes:
|
volumes:
|
||||||
- pgdata:/var/lib/postgresql/data
|
- pgdata:/var/lib/postgresql/data
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ services:
|
|||||||
immich-microservices:
|
immich-microservices:
|
||||||
container_name: immich_microservices
|
container_name: immich_microservices
|
||||||
image: immich-microservices:latest
|
image: immich-microservices:latest
|
||||||
|
# extends:
|
||||||
|
# file: hwaccel.yml
|
||||||
|
# service: hwaccel
|
||||||
build:
|
build:
|
||||||
context: ../server
|
context: ../server
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
@@ -65,8 +68,8 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- TYPESENSE_API_KEY=${TYPESENSE_API_KEY}
|
- TYPESENSE_API_KEY=${TYPESENSE_API_KEY}
|
||||||
- TYPESENSE_DATA_DIR=/data
|
- TYPESENSE_DATA_DIR=/data
|
||||||
logging:
|
# remove this to get debug messages
|
||||||
driver: none
|
- GLOG_minloglevel=1
|
||||||
volumes:
|
volumes:
|
||||||
- tsdata:/data
|
- tsdata:/data
|
||||||
restart: always
|
restart: always
|
||||||
@@ -85,7 +88,6 @@ services:
|
|||||||
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}
|
||||||
PG_DATA: /var/lib/postgresql/data
|
|
||||||
volumes:
|
volumes:
|
||||||
- pgdata:/var/lib/postgresql/data
|
- pgdata:/var/lib/postgresql/data
|
||||||
restart: always
|
restart: always
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ services:
|
|||||||
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}
|
||||||
PG_DATA: /var/lib/postgresql/data
|
|
||||||
volumes:
|
volumes:
|
||||||
- /var/lib/postgresql/data
|
- /var/lib/postgresql/data
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ services:
|
|||||||
immich-microservices:
|
immich-microservices:
|
||||||
container_name: immich_microservices
|
container_name: immich_microservices
|
||||||
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
|
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
|
||||||
|
# extends:
|
||||||
|
# file: hwaccel.yml
|
||||||
|
# service: hwaccel
|
||||||
command: [ "start.sh", "microservices" ]
|
command: [ "start.sh", "microservices" ]
|
||||||
volumes:
|
volumes:
|
||||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||||
@@ -51,6 +54,8 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- TYPESENSE_API_KEY=${TYPESENSE_API_KEY}
|
- TYPESENSE_API_KEY=${TYPESENSE_API_KEY}
|
||||||
- TYPESENSE_DATA_DIR=/data
|
- TYPESENSE_DATA_DIR=/data
|
||||||
|
# remove this to get debug messages
|
||||||
|
- GLOG_minloglevel=1
|
||||||
volumes:
|
volumes:
|
||||||
- tsdata:/data
|
- tsdata:/data
|
||||||
restart: always
|
restart: always
|
||||||
@@ -69,7 +74,6 @@ services:
|
|||||||
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}
|
||||||
PG_DATA: /var/lib/postgresql/data
|
|
||||||
volumes:
|
volumes:
|
||||||
- pgdata:/var/lib/postgresql/data
|
- pgdata:/var/lib/postgresql/data
|
||||||
restart: always
|
restart: always
|
||||||
|
|||||||
@@ -1,116 +1,19 @@
|
|||||||
###################################################################################
|
# You can find documentation for all the supported env variables at https://immich.app/docs/install/environment-variables
|
||||||
# Database
|
|
||||||
###################################################################################
|
|
||||||
|
|
||||||
# NOTE: The following four database variables support Docker secrets by adding a *_FILE suffix to the variable name
|
# The location where your uploaded files are stored
|
||||||
# See the docker-compose documentation on secrets for additional details: https://docs.docker.com/compose/compose-file/compose-file-v3/#secrets
|
UPLOAD_LOCATION=./library
|
||||||
|
|
||||||
|
# The Immich version to use. You can pin this to a specific version like "v1.71.0"
|
||||||
|
IMMICH_VERSION=release
|
||||||
|
|
||||||
|
# Connection secrets for postgres and typesense. You should change these to random passwords
|
||||||
|
TYPESENSE_API_KEY=some-random-text
|
||||||
|
DB_PASSWORD=postgres
|
||||||
|
|
||||||
|
# The values below this line do not need to be changed
|
||||||
|
###################################################################################
|
||||||
DB_HOSTNAME=immich_postgres
|
DB_HOSTNAME=immich_postgres
|
||||||
DB_USERNAME=postgres
|
DB_USERNAME=postgres
|
||||||
DB_PASSWORD=postgres
|
|
||||||
DB_DATABASE_NAME=immich
|
DB_DATABASE_NAME=immich
|
||||||
|
|
||||||
# Optional Database settings:
|
|
||||||
# DB_PORT=5432
|
|
||||||
|
|
||||||
###################################################################################
|
|
||||||
# Redis
|
|
||||||
###################################################################################
|
|
||||||
|
|
||||||
REDIS_HOSTNAME=immich_redis
|
REDIS_HOSTNAME=immich_redis
|
||||||
|
|
||||||
# REDIS_URL will be used to pass custom options to ioredis.
|
|
||||||
# Example for Sentinel
|
|
||||||
# {"sentinels":[{"host":"redis-sentinel-node-0","port":26379},{"host":"redis-sentinel-node-1","port":26379},{"host":"redis-sentinel-node-2","port":26379}],"name":"redis-sentinel"}
|
|
||||||
# REDIS_URL=ioredis://eyJzZW50aW5lbHMiOlt7Imhvc3QiOiJyZWRpcy1zZW50aW5lbDEiLCJwb3J0IjoyNjM3OX0seyJob3N0IjoicmVkaXMtc2VudGluZWwyIiwicG9ydCI6MjYzNzl9XSwibmFtZSI6Im15bWFzdGVyIn0=
|
|
||||||
|
|
||||||
# Optional Redis settings:
|
|
||||||
|
|
||||||
# Note: these parameters are not automatically passed to the Redis Container
|
|
||||||
# to do so, please edit the docker-compose.yml file as well. Redis is not configured
|
|
||||||
# via environment variables, only redis.conf or the command line
|
|
||||||
|
|
||||||
# REDIS_PORT=6379
|
|
||||||
# REDIS_DBINDEX=0
|
|
||||||
# REDIS_USERNAME=
|
|
||||||
# REDIS_PASSWORD=
|
|
||||||
# REDIS_SOCKET=
|
|
||||||
|
|
||||||
###################################################################################
|
|
||||||
# Upload File Location
|
|
||||||
#
|
|
||||||
# This is the location where uploaded files are stored.
|
|
||||||
###################################################################################
|
|
||||||
|
|
||||||
UPLOAD_LOCATION=absolute_location_on_your_machine_where_you_want_to_store_the_backup
|
|
||||||
|
|
||||||
|
|
||||||
###################################################################################
|
|
||||||
# Typesense
|
|
||||||
###################################################################################
|
|
||||||
TYPESENSE_API_KEY=some-random-text
|
|
||||||
# TYPESENSE_ENABLED=false
|
|
||||||
# TYPESENSE_URL uses base64 encoding for the nodes json.
|
|
||||||
# Example JSON that was used:
|
|
||||||
# [
|
|
||||||
# { "host": "typesense-1.example.net", "port": "443", "protocol": "https" },
|
|
||||||
# { "host": "typesense-2.example.net", "port": "443", "protocol": "https" },
|
|
||||||
# { "host": "typesense-3.example.net", "port": "443", "protocol": "https" },
|
|
||||||
# ]
|
|
||||||
# TYPESENSE_URL=ha://WwogIHsgImhvc3QiOiAidHlwZXNlbnNlLTEuZXhhbXBsZS5uZXQiLCAicG9ydCI6ICI0NDMiLCAicHJvdG9jb2wiOiAiaHR0cHMiIH0sCiAgeyAiaG9zdCI6ICJ0eXBlc2Vuc2UtMi5leGFtcGxlLm5ldCIsICJwb3J0IjogIjQ0MyIsICJwcm90b2NvbCI6ICJodHRwcyIgfSwKICB7ICJob3N0IjogInR5cGVzZW5zZS0zLmV4YW1wbGUubmV0IiwgInBvcnQiOiAiNDQzIiwgInByb3RvY29sIjogImh0dHBzIiB9Cl0=
|
|
||||||
|
|
||||||
###################################################################################
|
|
||||||
# Reverse Geocoding
|
|
||||||
#
|
|
||||||
# Reverse geocoding is done locally which has a small impact on memory usage
|
|
||||||
# This memory usage can be altered by changing the REVERSE_GEOCODING_PRECISION variable
|
|
||||||
# This ranges from 0-3 with 3 being the most precise
|
|
||||||
# 3 - Cities > 500 population: ~200MB RAM
|
|
||||||
# 2 - Cities > 1000 population: ~150MB RAM
|
|
||||||
# 1 - Cities > 5000 population: ~80MB RAM
|
|
||||||
# 0 - Cities > 15000 population: ~40MB RAM
|
|
||||||
####################################################################################
|
|
||||||
|
|
||||||
# DISABLE_REVERSE_GEOCODING=false
|
|
||||||
# REVERSE_GEOCODING_PRECISION=3
|
|
||||||
|
|
||||||
####################################################################################
|
|
||||||
# WEB - Optional
|
|
||||||
#
|
|
||||||
# Custom message on the login page, should be written in HTML form.
|
|
||||||
# For example:
|
|
||||||
# PUBLIC_LOGIN_PAGE_MESSAGE="This is a demo instance of Immich.<br><br>Email: <i>demo@demo.de</i><br>Password: <i>demo</i>"
|
|
||||||
####################################################################################
|
|
||||||
|
|
||||||
PUBLIC_LOGIN_PAGE_MESSAGE=
|
|
||||||
|
|
||||||
####################################################################################
|
|
||||||
# Alternative Service Addresses - Optional
|
|
||||||
#
|
|
||||||
# This is an advanced feature for users who may be running their immich services on different hosts.
|
|
||||||
# It will not change which address or port that services bind to within their containers, but it will change where other services look for their peers.
|
|
||||||
# Note: immich-microservices is bound to 3002, but no references are made
|
|
||||||
####################################################################################
|
|
||||||
|
|
||||||
IMMICH_WEB_URL=http://immich-web:3000
|
|
||||||
IMMICH_SERVER_URL=http://immich-server:3001
|
|
||||||
IMMICH_MACHINE_LEARNING_URL=http://immich-machine-learning:3003
|
|
||||||
|
|
||||||
####################################################################################
|
|
||||||
# Alternative API's External Address - Optional
|
|
||||||
#
|
|
||||||
# This is an advanced feature used to control the public server endpoint returned to clients during Well-known discovery.
|
|
||||||
# You should only use this if you want mobile apps to access the immich API over a custom URL. Do not include trailing slash.
|
|
||||||
# NOTE: At this time, the web app will not be affected by this setting and will continue to use the relative path: /api
|
|
||||||
# Examples: http://localhost:3001, http://immich-api.example.com, etc
|
|
||||||
####################################################################################
|
|
||||||
|
|
||||||
#IMMICH_API_URL_EXTERNAL=http://localhost:3001
|
|
||||||
|
|
||||||
###################################################################################
|
|
||||||
# Immich Version - Optional
|
|
||||||
#
|
|
||||||
# This allows all immich docker images to be pinned to a specific version. By default,
|
|
||||||
# the version is "release" but could be a specific version, like "v1.59.0".
|
|
||||||
###################################################################################
|
|
||||||
|
|
||||||
#IMMICH_VERSION=
|
|
||||||
|
|||||||
23
docker/hwaccel.yml
Normal file
23
docker/hwaccel.yml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
# Hardware acceleration for transcoding - Optional
|
||||||
|
# This is only needed if you want to use hardware acceleration for transcoding.
|
||||||
|
# Depending on your hardware, you should uncomment the relevant lines below.
|
||||||
|
|
||||||
|
services:
|
||||||
|
hwaccel:
|
||||||
|
# devices:
|
||||||
|
# - /dev/dri:/dev/dri # If using Intel QuickSync or VAAPI
|
||||||
|
# volumes:
|
||||||
|
# - /usr/lib/wsl:/usr/lib/wsl # If using VAAPI in WSL2
|
||||||
|
# environment:
|
||||||
|
# - NVIDIA_DRIVER_CAPABILITIES=all # If using NVIDIA GPU
|
||||||
|
# - LD_LIBRARY_PATH=/usr/lib/wsl/lib # If using VAAPI in WSL2
|
||||||
|
# - LIBVA_DRIVER_NAME=d3d12 # If using VAAPI in WSL2
|
||||||
|
# deploy: # Uncomment this section if using NVIDIA GPU
|
||||||
|
# resources:
|
||||||
|
# reservations:
|
||||||
|
# devices:
|
||||||
|
# - driver: nvidia
|
||||||
|
# count: 1
|
||||||
|
# capabilities: [gpu,video]
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: June 2023 update
|
title: Immich Update - June 2023
|
||||||
authors: [alextran]
|
authors: [alextran]
|
||||||
tags: [update]
|
tags: [update]
|
||||||
---
|
---
|
||||||
|
|||||||
BIN
docs/blog/2023/07-29/images/web-shortcuts-panel.png
Normal file
BIN
docs/blog/2023/07-29/images/web-shortcuts-panel.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
151
docs/blog/2023/07-29/update.mdx
Normal file
151
docs/blog/2023/07-29/update.mdx
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
---
|
||||||
|
title: Immich Update - July 2023
|
||||||
|
authors: [alextran]
|
||||||
|
tags: [update, v1.64.0-v1.71.0]
|
||||||
|
---
|
||||||
|
|
||||||
|
Hello, Immich fans, another month, another milestone. We hope you are staying cool and safe in this scorching hot summer across the globe.
|
||||||
|
|
||||||
|
Immich recently got some good recognition when getting to the front page of HackerNews, which helped to let more people know about the project's existence. The project will help more and more people find a solution to control the privacy of their most precious moments. And with the gain in popularity and recognition, we have gotten new users and more questions from the community than ever.
|
||||||
|
|
||||||
|
I want to express my gratitude to all the contributors and the community who have been tremendously helpful to new users' questions and provided technical support.
|
||||||
|
|
||||||
|
Below are the highlights of new features we added to the application over the past month, along with countless bug fixes and improvements across the board, from developer experience to resource optimization and UI/UX improvement. I hope you find these topics as exciting as I am.
|
||||||
|
|
||||||
|
## Highlights
|
||||||
|
|
||||||
|
- Memories feature.
|
||||||
|
- Facial recognition improvements.
|
||||||
|
- Improvements on multi selection behavior on the web.
|
||||||
|
- Shortcuts for common actions on the web.
|
||||||
|
- Support viewer for 360-panorama photos.
|
||||||
|
|
||||||
|
<!--truncate-->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Memories feature
|
||||||
|
|
||||||
|
We've added the memory feature on the mobile app, so you can reminisce about your past memories.
|
||||||
|
|
||||||
|
<iframe
|
||||||
|
width="560"
|
||||||
|
height="315"
|
||||||
|
src="https://youtube.com/embed/c7OTl-RqNRE"
|
||||||
|
title="YouTube video player"
|
||||||
|
frameborder="0"
|
||||||
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||||
|
allowfullscreen
|
||||||
|
></iframe>
|
||||||
|
|
||||||
|
### Facial recognition improvements
|
||||||
|
|
||||||
|
Over the past few releases, we have added many UI improvements to the facial recognition feature to help you manage the recognized people better. Some of the highlights:
|
||||||
|
|
||||||
|
#### Choose a new feature photo for a person.
|
||||||
|
|
||||||
|
<iframe
|
||||||
|
width="560"
|
||||||
|
height="315"
|
||||||
|
src="https://youtube.com/embed/PmJp8DmSh1U"
|
||||||
|
title="YouTube video player"
|
||||||
|
frameborder="0"
|
||||||
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||||
|
allowfullscreen
|
||||||
|
></iframe>
|
||||||
|
|
||||||
|
#### Hide and show faces.
|
||||||
|
|
||||||
|
You can now select irrelevant faces to hide them. The hidden faces won’t be displayed in search results and the people section in the info panel.
|
||||||
|
|
||||||
|
#### Merge faces.
|
||||||
|
|
||||||
|
This is useful when you have multiple faces of the same person in your photos, and you want to merge them into one.
|
||||||
|
|
||||||
|
<iframe
|
||||||
|
width="560"
|
||||||
|
height="315"
|
||||||
|
src="https://youtube.com/embed/-Xskhw-vpc4"
|
||||||
|
title="YouTube video player"
|
||||||
|
frameborder="0"
|
||||||
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||||
|
allowfullscreen
|
||||||
|
></iframe>
|
||||||
|
|
||||||
|
We also added a nifty mechanism that when naming a face, similar names will prompt you a merge face option for the convenience.
|
||||||
|
|
||||||
|
<iframe
|
||||||
|
width="560"
|
||||||
|
height="315"
|
||||||
|
src="https://youtube.com/embed/XzE6wficbl4"
|
||||||
|
title="YouTube video player"
|
||||||
|
frameborder="0"
|
||||||
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||||
|
allowfullscreen
|
||||||
|
></iframe>
|
||||||
|
|
||||||
|
### Improvements on multi selection behavior on the web
|
||||||
|
|
||||||
|
We have added a new multi selection behavior on the web to help you select multiple items easier. You can now select a range of photos and videos by holding the `Shift` key.
|
||||||
|
|
||||||
|
<iframe
|
||||||
|
width="560"
|
||||||
|
height="315"
|
||||||
|
src="https://youtube.com/embed/e_SiuHpVnmM"
|
||||||
|
title="YouTube video player"
|
||||||
|
frameborder="0"
|
||||||
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||||
|
allowfullscreen
|
||||||
|
></iframe>
|
||||||
|
|
||||||
|
### Shortcuts for common actions on the web.
|
||||||
|
|
||||||
|
Some of us only navigate the world and the web with a keyboard (looking at you, Vim and Emacs users). So it would take away the sacred weapon of choice to require many clicks to perform repetitive actions. So we added quick shortcuts for the following action on the web.
|
||||||
|
|
||||||
|
<img
|
||||||
|
src={require('./images/web-shortcuts-panel.png').default}
|
||||||
|
width="100%"
|
||||||
|
style={{ borderRadius: '25px' }}
|
||||||
|
alt="Dot Env Example"
|
||||||
|
/>
|
||||||
|
|
||||||
|
### Support viewer for 360-panorama photos.
|
||||||
|
|
||||||
|
Photos with the EXIF property of `ProjectionType` will now have a special viewer on the web to view all the angles of the panorama.
|
||||||
|
|
||||||
|
The thumbnail of the 360 degrees panoramas will have a special icon on the top right of the thumbnail
|
||||||
|
|
||||||
|
<img
|
||||||
|
src="https://github.com/immich-app/immich/assets/61410067/728ca1b0-375c-4631-8081-a609843e702f"
|
||||||
|
width="50%"
|
||||||
|
style={{ borderRadius: '25px' }}
|
||||||
|
alt="Dot Env Example"
|
||||||
|
/>
|
||||||
|
|
||||||
|
Panorama in the detail view
|
||||||
|
|
||||||
|
<img
|
||||||
|
src="https://github.com/immich-app/immich/assets/61410067/3c89dac4-395d-45fa-9bc5-98a6248fd476"
|
||||||
|
width="50%"
|
||||||
|
style={{ borderRadius: '25px' }}
|
||||||
|
alt="Dot Env Example"
|
||||||
|
/>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Thank you, and I am asking for your support for the project. I hope to be a full-time maintainer of Immich one day to dedicate myself to the project as my life's work for the community and my family. You can find the support channels below:
|
||||||
|
|
||||||
|
- Monthly donation via [GitHub Sponsors](https://github.com/sponsors/alextran1502)
|
||||||
|
- One-time donation via [GitHub Sponsors](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502)
|
||||||
|
- [Liberapay](https://liberapay.com/alex.tran1502/)
|
||||||
|
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
|
||||||
|
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
|
||||||
|
- Give a project a star - the contributors love gazing at the stars and seeing their creations shining in the sky.
|
||||||
|
|
||||||
|
Join our friendly [Discord](https://discord.gg/D8JsnBEuKb) to talk and discuss Immich, tech, or anything
|
||||||
|
|
||||||
|
Cheer!
|
||||||
|
|
||||||
|
Until next time!
|
||||||
|
|
||||||
|
Alex
|
||||||
@@ -39,15 +39,40 @@ This often happens when using a reverse proxy or cloudflare tunnel in front of I
|
|||||||
|
|
||||||
### Why is Immich slow on low-memory systems like the Raspberry Pi?
|
### Why is Immich slow on low-memory systems like the Raspberry Pi?
|
||||||
|
|
||||||
Immich uses optional machine-learning features to enhance search results. This feature, however, can be too heavy to run on a Raspberry Pi. To disable machine learning, comment out the `immich-machine-learning` section of your docker-compose.yml and set `IMMICH_MACHINE_LEARNING_URL=false` in your .env file.
|
Immich optionally uses machine learning for several features. However, it can be too heavy to run on a Raspberry Pi. You can [mitigate](/docs/FAQ#how-can-i-lower-immichs-cpu-usage) this or [disable](/docs/FAQ.md#how-can-i-disable-machine-learning) machine learning entirely.
|
||||||
|
|
||||||
### How to disable machine-learning and TypeSense?
|
### How can I lower Immich's CPU usage?
|
||||||
|
|
||||||
:::warning
|
The initial backup is the most intensive due to the number of jobs running. The most CPU-intensive ones are transcoding and machine learning jobs (Tag Images, Encode CLIP, Recognize Faces), and to a lesser extent thumbnail generation. Here are some ways to lower their CPU usage:
|
||||||
Disabling both will result in poor search experience and typesense utilizes CLIP embeddings which are generated by machine-learning.
|
|
||||||
|
- Lower the job concurrency for these jobs to 1.
|
||||||
|
- Under Settings > Transcoding Settings > Threads, set the number of threads to a low number like 1 or 2.
|
||||||
|
- Set the `TYPESENSE_THREAD_POOL_SIZE` environmental variable and restart the Typesense container. For instance, `TYPESENSE_THREAD_POOL_SIZE=8` will limit it to 8 threads.
|
||||||
|
- Under Settings > Machine Learning Settings > Facial Recognition > Model Name, you can change the facial recognition model to `buffalo_s` instead of `buffalo_l`. The former is a smaller and faster model, albeit not as good.
|
||||||
|
- You _must_ re-run the Recognize Faces job for all images after this for facial recognition on new images to work properly.
|
||||||
|
- If these changes are not enough, see [below](/docs/FAQ.md#how-can-i-disable-machine-learning) for how you can disable machine learning.
|
||||||
|
|
||||||
|
### How can I disable machine learning?
|
||||||
|
|
||||||
|
:::info
|
||||||
|
Disabling machine learning will result in a poor experience for searching and the 'Explore' page, as these are reliant on it to work as intended.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
These features can be disabled by commenting out `immich-typesense` and `immich-machine-learning` sections of the docker-compose.yml and setting `IMMICH_MACHINE_LEARNING_URL=false` & `TYPESENSE_ENABLED=false` in your .env file.
|
Machine learning can be disabled under Settings > Machine Learning Settings, either entirely or by model type. For instance, you can choose to disable smart search with CLIP, but keep facial recognition enabled. This means that the machine learning service will only process the enabled jobs.
|
||||||
|
|
||||||
|
However, disabling all jobs will not disable the machine learning service itself. To prevent it from starting up at all in this case, you can comment out the `immich-machine-learning` section of the docker-compose.yml.
|
||||||
|
|
||||||
|
### How can I disable TypeSense?
|
||||||
|
|
||||||
|
:::info
|
||||||
|
Disabling Typesense will result in a poor search experience since searching is reliant on it.
|
||||||
|
:::
|
||||||
|
|
||||||
|
You can disable Typesense by commenting out the `immich-typesense` section of the docker-compose.yml and setting `TYPESENSE_ENABLED=false` in your .env file.
|
||||||
|
|
||||||
|
### I'm getting errors about models being corrupt or failing to download. What do I do?
|
||||||
|
|
||||||
|
You can delete the model cache volume, which is where models are downloaded. This will give the service a clean environment to download the model again.
|
||||||
|
|
||||||
### What happens to existing files after I choose a new [Storage Template](/docs/administration/storage-template.mdx)?
|
### What happens to existing files after I choose a new [Storage Template](/docs/administration/storage-template.mdx)?
|
||||||
|
|
||||||
@@ -59,7 +84,7 @@ This is fixed by running the storage migration job.
|
|||||||
|
|
||||||
### Why is object detection not very good?
|
### Why is object detection not very good?
|
||||||
|
|
||||||
The model we used for machine learning is a prebuilt model, so the accuracy is not very good. It will hopefully be replaced with a better solution in the future.
|
The default image tagging model is relatively small. You can change this for a larger model like `google/vit-base-patch16-224` by setting the model name under Settings > Machine Learning Settings > Image Tagging. You can then re-run the Image Tagging job to get improved tags.
|
||||||
|
|
||||||
### How can I see Immich logs?
|
### How can I see Immich logs?
|
||||||
|
|
||||||
@@ -96,6 +121,28 @@ docker-compose down -v
|
|||||||
|
|
||||||
After removing the containers and volumes, the **Files** can be cleaned up (if necessary) from the `UPLOAD_LOCATION` by simply deleting an unwanted files or folders.
|
After removing the containers and volumes, the **Files** can be cleaned up (if necessary) from the `UPLOAD_LOCATION` by simply deleting an unwanted files or folders.
|
||||||
|
|
||||||
### Why iOS app shows duplicate photos on the timeline while the web doesn't?
|
### How can I move all data (photos, persons, albums) from one user to another?
|
||||||
|
|
||||||
If you are using `My Photo Stream`, the Photos app temporarily creates duplicates of photos taken in the last 30 days. These photos are included in the `Recents` album and thus shown up twice. To fix this, you can disable `My Photo Stream` in the native Photos app or choose a different album in the backup screen in Immich.
|
This requires some database queries. You can do this on the command line (in the PostgreSQL container using the psql command), or you can add for example an [Adminer](https://www.adminer.org/) container to the `docker-compose.yml` file, so that you can use a web-interface.
|
||||||
|
|
||||||
|
:::warning
|
||||||
|
This is an advanced operation. If you can't to do it with the steps described here, this is not for you.
|
||||||
|
:::
|
||||||
|
|
||||||
|
1. **MAKE A BACKUP** - See [backup and restore](/docs/administration/backup-and-restore.md).
|
||||||
|
2. Find the id of both the 'source' and the 'destination' user (it's the id column in the users table)
|
||||||
|
3. Three tables need to be updated:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
// reassign albums
|
||||||
|
update albums set "ownerId" = '<destinationId>' where "ownerId" = '<sourceId>';
|
||||||
|
|
||||||
|
// reassign people
|
||||||
|
update person set "ownerId" = '<destinationId>' where "ownerId" = '<sourceId>';
|
||||||
|
|
||||||
|
// reassign assets
|
||||||
|
update assets set "ownerId" = '<destinationId>' where "ownerId" = '<sourceId>'
|
||||||
|
and checksum not in (select checksum from assets where "ownerId" = '<destinationId>');
|
||||||
|
```
|
||||||
|
|
||||||
|
4. There might be left-over assets in the 'source' user's library if they are skipped by the last query because of duplicate checksums. These are probably duplicates anyway, and can probably be removed.
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
## Database
|
## Database
|
||||||
|
|
||||||
|
:::caution
|
||||||
|
Immich saves [file paths in the database](https://github.com/immich-app/immich/discussions/3299), it does not scan the library folder to update the database so backups are crucial.
|
||||||
|
:::
|
||||||
|
|
||||||
:::info
|
:::info
|
||||||
Refer to the official [postgres documentation](https://www.postgresql.org/docs/current/backup.html) for details about backing up and restoring a postgres database.
|
Refer to the official [postgres documentation](https://www.postgresql.org/docs/current/backup.html) for details about backing up and restoring a postgres database.
|
||||||
:::
|
:::
|
||||||
|
|||||||
@@ -12,10 +12,12 @@ The `immich-server` docker image comes preinstalled with an administrative CLI (
|
|||||||
|
|
||||||
## How to run a command
|
## How to run a command
|
||||||
|
|
||||||
To run a command, [connect](/docs/guides/docker-help.md#attach-to-a-container) to the `immich_server` container and then execute the command via `immich <command>`.
|
To run a command, [connect](/docs/guides/docker-help.md#attach-to-a-container) to the `immich_server` container and then execute the command via `immich-admin <command>`.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
Note that the commands below should begin with `immich-admin`.
|
||||||
|
|
||||||
Reset Admin Password
|
Reset Admin Password
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -4,38 +4,113 @@ sidebar_position: 1
|
|||||||
|
|
||||||
# Architecture
|
# Architecture
|
||||||
|
|
||||||
|
Immich uses a traditional client-server design, with a dedicated database for data persistence. The frontend clients communicate with backend services over HTTP using REST APIs.
|
||||||
|
|
||||||
## High Level Diagram
|
## High Level Diagram
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Technology
|
The diagram shows clients communicating with the server via REST, as well as the flow of database between backend services.
|
||||||
|
|
||||||
Immich is a full-stack [TypeScript](https://www.typescriptlang.org/) application, with a [Flutter](https://flutter.dev/) mobile app.
|
## Clients
|
||||||
|
|
||||||
### Mobile
|
Immich has three main clients:
|
||||||
|
|
||||||
- [Flutter](https://flutter.dev/)
|
1. Mobile app - Android, iOS
|
||||||
- [Riverpod](https://riverpod.dev/) for state management.
|
2. Web app - Responsive website
|
||||||
|
3. CLI - Command-line utility for bulk upload
|
||||||
|
|
||||||
### Web
|
:::info
|
||||||
|
All three clients use [OpenAPI](./open-api.md) to auto-generate rest clients for easy integration. For more information about this process, see [OpenAPI](./open-api.md).
|
||||||
|
:::
|
||||||
|
|
||||||
- [SvelteKit](https://kit.svelte.dev/)
|
### Mobile App
|
||||||
- [Tailwindcss](https://tailwindcss.com/)
|
|
||||||
|
|
||||||
### Server
|
The mobile app is written in [Flutter](https://flutter.dev/). It uses [Isar Database](https://isar.dev/) for a local database and [Riverpod](https://riverpod.dev/) for state management.
|
||||||
|
|
||||||
- [Node.js](https://nodejs.org/)
|
### Web Client
|
||||||
- [Nest.js](https://nestjs.com/)
|
|
||||||
- [TypeORM](https://typeorm.io/) for database management.
|
|
||||||
- [Jest](https://jestjs.io/) for testing.
|
|
||||||
- [Python](https://www.python.org/) for Machine Learning.
|
|
||||||
|
|
||||||
### Database
|
The web app is a [TypeScript](https://www.typescriptlang.org/) project that uses [SvelteKit](https://kit.svelte.dev) and [Tailwindcss](https://tailwindcss.com/).
|
||||||
|
|
||||||
- [PostgreSQL](https://www.postgresql.org/)
|
### CLI
|
||||||
- [Redis](https://redis.io/) for job queuing.
|
|
||||||
- [Typesense](https://typesense.org/) for search.
|
|
||||||
|
|
||||||
### Web Server
|
The CLI is a [TypeScript](https://www.typescriptlang.org/) project that parses command line arguments to programmatically upload/import assets to an Immich server. See [Bulk Upload](/docs/features/bulk-upload.md) for more information about its usage.
|
||||||
|
|
||||||
- [NGINX](https://www.nginx.com/) for internal communication between containers and load balancing when scaling.
|
## Server
|
||||||
|
|
||||||
|
The Immich backend is divided into several services, which are run as individual docker containers.
|
||||||
|
|
||||||
|
1. `immich-server` - Handle and respond to REST API requests
|
||||||
|
1. `immich-microservices` - Execute background jobs (thumbnail generation, metadata extraction, transcoding, etc.)
|
||||||
|
1. `immich-machine-learning` - Execute machine learning models
|
||||||
|
1. `postgres` - Persistent data storage
|
||||||
|
1. `redis`- Queue management for `immich-microservices`
|
||||||
|
1. `typesense`- Specialized database for search, specifically with vector comparison features
|
||||||
|
|
||||||
|
### Immich Server
|
||||||
|
|
||||||
|
The Immich Server is a [TypeScript](https://www.typescriptlang.org/) project written for [Node.js](https://nodejs.org/). It uses the [Nest.js](https://nestjs.com) framework, with [TypeORM](https://typeorm.io/) for database management. The server codebase also loosely follows the [Hexagonal Architecture](<https://en.wikipedia.org/wiki/Hexagonal_architecture_(software)>). Specifically, we aim to separate technology specific implementations (`infra/`) from core business logic (`domain/`).
|
||||||
|
|
||||||
|
#### REST Endpoints
|
||||||
|
|
||||||
|
The server is a list of HTTP endpoints and associated handlers (controllers). Each controller usually implements the following CRUD operations:
|
||||||
|
|
||||||
|
- `POST` `/<type>` - **Create**
|
||||||
|
- `GET` `/<type>` - **Read** (all)
|
||||||
|
- `GET` `/<type>/:id` - **Read** (by id)
|
||||||
|
- `PUT` `/<type>/:id` - **Updated** (by id)
|
||||||
|
- `DELETE` `/<type>/:id` - **Delete** (by id)
|
||||||
|
|
||||||
|
#### DTOs
|
||||||
|
|
||||||
|
The server uses [Domain Transfer Objects](https://en.wikipedia.org/wiki/Data_transfer_object) as public interfaces for the inputs (query, params, and body) and outputs (response) for each endpoint. DTOs translate to [OpenAPI](./open-api.md) schemas and control the generated code used by each client.
|
||||||
|
|
||||||
|
### Microservices
|
||||||
|
|
||||||
|
The Immich Microservices image uses the same `Dockerfile` as the Immich Server, but with a different entrypoint. The Immich Microservices service mainly handles executing jobs, which include the following:
|
||||||
|
|
||||||
|
- Thumbnail Generation
|
||||||
|
- Metadata Extraction
|
||||||
|
- Video Transcoding
|
||||||
|
- Object Tagging
|
||||||
|
- Facial Recognition
|
||||||
|
- Storage Template Migration
|
||||||
|
- Search (Typesense synchronization)
|
||||||
|
- Sidecar (see [XMP Sidecars](/docs/features/xmp-sidecars.md))
|
||||||
|
- Background jobs (file deletion, user deletion)
|
||||||
|
|
||||||
|
:::info
|
||||||
|
This list closely matches what is available on the [Administration > Jobs](/docs/administration/jobs.md) page, which provides some remote queue management capabilities.
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Machine Learning
|
||||||
|
|
||||||
|
The machine learning service is written in [Python](https://www.python.org/) and uses [FastAPI](https://fastapi.tiangolo.com/) for HTTP communication.
|
||||||
|
|
||||||
|
All machine learning related operations have been externalized to this service, `immich-machine-learning`. Python is a natural choice for AI and machine learning. It also has some pretty specific hardware requirements. Running it as a separate container makes it possible to run the container on a separate machine, or easily disable it entirely.
|
||||||
|
|
||||||
|
Each request to the machine learning service contains the relevant metadata for the model task, model name, and so on. These settings are stored in Postgres along with other system configs. For each request, the microservices container fetches these settings in order to attach them to the request.
|
||||||
|
|
||||||
|
Internally, the machine learning service downloads, loads and configures the specified model for a given request before processing the text or image payload with it. Models that have been loaded are cached and reused across requests. A thread pool is used to process each request in a different thread so as not to block the async event loop.
|
||||||
|
|
||||||
|
All models are in ONNX format. This format has wide industry support, meaning that most other model formats can be exported to it and many hardware APIs support it. It's also quite fast.
|
||||||
|
|
||||||
|
Machine learning models are also quite _large_, requiring _quite a bit_ of memory. We are always looking for ways to improve and optimize this aspect of this container specifically.
|
||||||
|
|
||||||
|
### Postgres
|
||||||
|
|
||||||
|
Immich persists data in Postgres, which includes information about access and authorization, users, albums, asset, sharing settings, etc.
|
||||||
|
|
||||||
|
:::info
|
||||||
|
See [Database Migrations](./database-migrations.md) for more information about how to modify the database to create an index, modify a table, add a new column, etc.
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Redis
|
||||||
|
|
||||||
|
Immich uses [Redis](https://redis.com/) via [BullMQ](https://docs.bullmq.io/) to manage job queues. Some jobs trigger subsequent jobs. For example, object detection relies on thumbnail generation and automatically run after one is generated.
|
||||||
|
|
||||||
|
### Typesense
|
||||||
|
|
||||||
|
Immich synchronizes some of the Postgres data into Typesense, so it can execute vector related queries in order to implement certain features including, facial recognition and CLIP search.
|
||||||
|
|
||||||
|
<!-- - [NGINX](https://www.nginx.com/) for internal communication between containers and load balancing when scaling. -->
|
||||||
|
|||||||
22
docs/docs/developer/directories.md
Normal file
22
docs/docs/developer/directories.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: Directories
|
||||||
|
---
|
||||||
|
|
||||||
|
# Repository Folder Structure
|
||||||
|
|
||||||
|
Our [GitHub Repository](https://github.com/immich-app/immich) is a [monorepo](https://en.wikipedia.org/wiki/Monorepo) and includes the following folders:
|
||||||
|
|
||||||
|
| Folder | Description |
|
||||||
|
| :------------------ | :------------------------------------------------------------------- |
|
||||||
|
| `.github/` | Github templates and action workflows |
|
||||||
|
| `.vscode/` | VSCode debug launch profiles |
|
||||||
|
| `cli/` | Source code for the work-in-progress CLI rewrite |
|
||||||
|
| `docker/` | Docker compose resources for dev, test, production |
|
||||||
|
| `design/` | Screenshots and logos for the README |
|
||||||
|
| `docs/` | Source code for the [https://immich.app](https://immich.app) website |
|
||||||
|
| `machine-learning/` | Source code for the `immich-machine-learning` docker image |
|
||||||
|
| `misc/release/` | Scripts for version pumps and draft releases |
|
||||||
|
| `mobile/` | Source code for the mobile app, both Android and iOS |
|
||||||
|
| `nginx/` | Source code for the `immich-proxy` docker image |
|
||||||
|
| `server/` | Source code for the `immich-server` docker image |
|
||||||
|
| `web/` | Source code for the `immich-web` docker image |
|
||||||
@@ -56,8 +56,6 @@ The API key can be obtained in the user setting panel on the web interface.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Uploading existing libraries
|
|
||||||
|
|
||||||
### Run via Docker
|
### Run via Docker
|
||||||
|
|
||||||
You can run the CLI inside of a docker container to avoid needing to install anything.
|
You can run the CLI inside of a docker container to avoid needing to install anything.
|
||||||
@@ -68,16 +66,16 @@ Be aware that as this runs inside a container, you need to mount the folder from
|
|||||||
|
|
||||||
```bash title="Upload current directory"
|
```bash title="Upload current directory"
|
||||||
cd /DIRECTORY/WITH/IMAGES
|
cd /DIRECTORY/WITH/IMAGES
|
||||||
docker run -it --rm -v "$(pwd):/import" ghcr.io/immich-app/immich-cli:latest upload --key HFEJ38DNSDUEG --server http://192.168.1.216:2283/api
|
docker run -it --rm -v "$(pwd):/import" ghcr.io/immich-app/immich-cli:latest upload --recursive --key HFEJ38DNSDUEG --server http://192.168.1.216:2283/api
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash title="Upload target directory"
|
```bash title="Upload target directory"
|
||||||
docker run -it --rm -v "/DIRECTORY/WITH/IMAGES:/import" ghcr.io/immich-app/immich-cli:latest upload --key HFEJ38DNSDUEG --server http://192.168.1.216:2283/api
|
docker run -it --rm -v "/DIRECTORY/WITH/IMAGES:/import" ghcr.io/immich-app/immich-cli:latest upload --recursive --key HFEJ38DNSDUEG --server http://192.168.1.216:2283/api
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash title="Create an alias"
|
```bash title="Create an alias"
|
||||||
alias immich='docker run -it --rm -v "$(pwd):/import" ghcr.io/immich-app/immich-cli:latest'
|
alias immich='docker run -it --rm -v "$(pwd):/import" ghcr.io/immich-app/immich-cli:latest'
|
||||||
immich upload --key HFEJ38DNSDUEG --server http://192.168.1.216:2283/api
|
immich upload --recursive --key HFEJ38DNSDUEG --server http://192.168.1.216:2283/api
|
||||||
```
|
```
|
||||||
|
|
||||||
:::tip Internal networking
|
:::tip Internal networking
|
||||||
@@ -88,7 +86,7 @@ If you are running the CLI container on the same machine as your Immich server,
|
|||||||
3. Use `--server http://immich-server:3001` for the upload command instead of the external address.
|
3. Use `--server http://immich-server:3001` for the upload command instead of the external address.
|
||||||
|
|
||||||
```bash title="Upload to internal address"
|
```bash title="Upload to internal address"
|
||||||
docker run --network immich_default -it --rm -v "$(pwd):/import" ghcr.io/immich-app/immich-cli:latest upload --key HFEJ38DNSDUEG --server http://immich-server:3001
|
docker run --network immich_default -it --rm -v "$(pwd):/import" ghcr.io/immich-app/immich-cli:latest upload --recursive --key HFEJ38DNSDUEG --server http://immich-server:3001
|
||||||
```
|
```
|
||||||
|
|
||||||
:::
|
:::
|
||||||
@@ -170,4 +168,10 @@ The proper command for above would be as shown below. You should have access to
|
|||||||
immich upload --key HFEJ38DNSDUEG --server http://192.168.1.216:2283/api --recursive /path/to/media --import
|
immich upload --key HFEJ38DNSDUEG --server http://192.168.1.216:2283/api --recursive /path/to/media --import
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you are running the import using the docker command, please note that the volumes should point to the `/path/to/media` exactly on the environment the CLI command is being run on
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -it --rm -v "/path/to/media:/path/to/media" ghcr.io/immich-app/immich-cli:latest upload --key HFEJ38DNSDUEG --server http://192.168.1.216:2283/api --recursive /path/to/media --import
|
||||||
|
```
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|||||||
60
docs/docs/features/hardware-transcoding.md
Normal file
60
docs/docs/features/hardware-transcoding.md
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# Hardware Transcoding [Experimental]
|
||||||
|
|
||||||
|
This feature allows you to use a GPU or Intel Quick Sync to accelerate transcoding and reduce CPU load.
|
||||||
|
Note that hardware transcoding is much less efficient for file sizes.
|
||||||
|
As this is a new feature, it is still experimental and may not work on all systems.
|
||||||
|
|
||||||
|
## Supported APIs
|
||||||
|
|
||||||
|
- NVENC
|
||||||
|
- NVIDIA GPUs
|
||||||
|
- Quick Sync
|
||||||
|
- Intel CPUs
|
||||||
|
- VAAPI
|
||||||
|
- GPUs
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
|
||||||
|
- The instructions and configurations here are specific to Docker Compose. Other container engines may require different configuration.
|
||||||
|
- Only Linux and Windows (through WSL2) servers are supported.
|
||||||
|
- WSL2 does not support Quick Sync.
|
||||||
|
- Raspberry Pi is currently not supported.
|
||||||
|
- Two-pass mode is only supported for NVENC. Other APIs will ignore this setting.
|
||||||
|
- Only encoding is currently hardware accelerated, so the CPU is still used for software decoding.
|
||||||
|
- This is mainly because the original video may not be hardware-decodable.
|
||||||
|
- Hardware dependent
|
||||||
|
- Codec support varies, but H.264 and HEVC are usually supported.
|
||||||
|
- Notably, NVIDIA and AMD GPUs do not support VP9 encoding.
|
||||||
|
- Newer devices tend to have higher transcoding quality.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
#### NVENC
|
||||||
|
|
||||||
|
- You must have the official NVIDIA driver installed on the server.
|
||||||
|
- On Linux (except for WSL2), you also need to have [NVIDIA Container Runtime][nvcr] installed.
|
||||||
|
|
||||||
|
#### QSV
|
||||||
|
|
||||||
|
- For VP9 to work:
|
||||||
|
- You must have a 9th gen Intel CPU or newer
|
||||||
|
- If you have an 11th gen CPU or older, then you may need to follow [these][jellyfin-lp] instructions as Low-Power mode is required
|
||||||
|
- Additionally, if the server specifically has an 11th gen CPU and is running kernel 5.15 (shipped with Ubuntu 22.04 LTS), then you will need to upgrade this kernel (from [Jellyfin docs][jellyfin-kernel-bug])
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
1. If you do not already have it, download the latest [`hwaccel.yml`][hw-file] file and ensure it's in the same folder as the `docker-compose.yml`.
|
||||||
|
2. Uncomment the lines that apply to your system and desired usage.
|
||||||
|
3. In the `docker-compose.yml` under `immich-microservices`, uncomment the lines relating to the `hwaccel.yml` file.
|
||||||
|
4. Redeploy the `immich-microservices` container with these updated settings.
|
||||||
|
5. In the Admin page under `FFmpeg settings`, change the hardware acceleration setting to the appropriate option and save.
|
||||||
|
|
||||||
|
## Tips
|
||||||
|
|
||||||
|
- You may want to choose a slower preset than for software transcoding to maintain quality and efficiency
|
||||||
|
- While you can use VAAPI with Nvidia GPUs and Intel CPUs, prefer the more specific APIs since they're more optimized for their respective devices
|
||||||
|
|
||||||
|
[hw-file]: https://github.com/immich-app/immich/releases/latest/download/hwaccel.yml
|
||||||
|
[nvcr]: https://github.com/NVIDIA/nvidia-container-runtime/
|
||||||
|
[jellyfin-lp]: https://jellyfin.org/docs/general/administration/hardware-acceleration/intel/#configure-and-verify-lp-mode-on-linux
|
||||||
|
[jellyfin-kernel-bug]: https://jellyfin.org/docs/general/administration/hardware-acceleration/intel/#known-issues-and-limitations
|
||||||
148
docs/docs/features/libraries.md
Normal file
148
docs/docs/features/libraries.md
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
# Libraries
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Immich supports the creation of libraries which is a top-level asset container. Currently, there are two types of libraries: traditional upload libraries that can sync with a mobile device, and external libraries, that keeps up to date with files on disk. Libraries are different from albums in that an asset can belong to multiple albums but only one library, and deleting a library deletes all assets contained within. As of August 2023, this is a new feature and libraries have a lot of potential for future development beyond what is documented here. This document attempts to describe the current state of libraries.
|
||||||
|
|
||||||
|
## The Upload Library
|
||||||
|
|
||||||
|
Immich comes preconfigured with an upload library for each user. All assets uploaded to Immich are added to this library. This library can be renamed, but not deleted. The upload library is the only library that can be synced with a mobile device. No items in an upload library is allowed to have the same sha1 hash as another item in the same library in order to prevent duplicates.
|
||||||
|
|
||||||
|
## External Libraries
|
||||||
|
|
||||||
|
External libraries tracks assets stored outside of immich, i.e. in the file system. Immich will only read data from the files, and will not modify them in any way. Therefore, the delete button is disabled for external assets. When the external library is scanned, immich will read the metadata from the file and create an asset in the library for each image or video file. These items will then be shown in the main timeline, and they will look and behave like any other asset, including viewing on the map, adding to albums, etc.
|
||||||
|
|
||||||
|
If a file is modified outside of Immich, the changes will not be reflected in immich until the library is scanned again. There are different ways to scan a library depending on the use case:
|
||||||
|
|
||||||
|
- Scan Library Files: This is the default scan method and also the quickest. It will scan all files in the library and add new files to the library. It will notice if any files are missing (see below) but not check existing assets
|
||||||
|
- Scan All Library Files: Same as above, but will check each existing asset to see if the modification time has changed. If it has, the asset will be updated. Since it has to check each asset, this is slower than Scan Library Files.
|
||||||
|
- Force Scan All Library Files: Same as above, but will read each asset from disk no matter the modification time. This is useful in some cases where an asset has been modified externally but the modification time has not changed. This is the slowest way to scan because it reads each asset from disk.
|
||||||
|
|
||||||
|
:::caution
|
||||||
|
|
||||||
|
Due to aggressive caching it can take some time for a refreshed asset to appear correctly in the web view. You need to clear the cache in your browser to see the changes. This is a known issue and will be fixed in a future release. In Chrome, you need to open the developer console with F12, then reload the page with F5, and finally right click on the reload button and select "Empty Cache and Hard Reload".
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
In external libraries, the file path is used for duplicate detection. This means that if a file is moved to a different location, it will be added as a new asset. If the file is moved back to its original location, it will be added as a new asset. In contrast to upload libraries, two identical files can be uploaded if they are in different locations. This is a deliberate design choice to make Immich reflect the file system as closely as possible. Remember that duplication detection is only done within the same library, so if you have multiple external libraries, the same file can be added to multiple libraries.
|
||||||
|
|
||||||
|
:::caution
|
||||||
|
|
||||||
|
If you add assets from an external library to an album and then move the asset to another location within the library, the asset will be removed from the album upon rescan. This is because the asset is considered a new asset after the move. This is a known issue and will be fixed in a future release.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Deleted External Assets
|
||||||
|
|
||||||
|
In all above scan methods, Immich will check if any files are missing. This can happen if files are deleted, or if they are on a storage location that is currently unavailable, like a network drive that is not mounted, or a USB drive that has been unplugged. In order to prevent accidental deletion of assets, Immich will not immediately delete an asset from the library if the file is missing. Instead, the asset will be internally marked as offline and will still be visible in the main timeline. If the file is moved back to its original location and the library is scanned again, the asset will be restored.
|
||||||
|
|
||||||
|
Finally, files can be deleted from Immich via the `Remove Offline Files` job. Any assets marked as offline will then be removed from Immich. Run this job whenever files have been deleted from the file system and you want to remove them from Immich. Note that a library scan must be performed first to mark the assets as offline.
|
||||||
|
|
||||||
|
### Import Paths
|
||||||
|
|
||||||
|
External libraries use import paths to determine which files to scan. Each library can have multiple import paths so that files from different locations can be added to the same library. Import paths are scanned recursively, and if a file is in multiple import paths, it will only be added once. If the import paths are edited in a way that an external file is no longer in any import path, it will be removed from the library in the same way a deleted file would. If the file is moved back to an import path, it will be added again as if it was a new file.
|
||||||
|
|
||||||
|
### Security Considerations
|
||||||
|
|
||||||
|
For security purposes, each Immich user is disallowed to add external files by default. This is to prevent devastating [path traversal attacks](https://owasp.org/www-community/attacks/Path_Traversal). An admin can allow individual users to use external path feature via the `external path` setting found in the admin panel. Without the external path restriction, a user can add any image or video file on the Immich host filesystem to be imported into Immich, potentially allowing sensitive data to be accessed. If you are running Immich as root in your Docker setup (which is the default), all external file reads are done with root privileges. This is particularly dangerous if the Immich host is a shared server.
|
||||||
|
|
||||||
|
With the `external path` set, a user is restricted to accessing external files to files or directories within that path. The Immich admin should still be careful not set the external path too generously. For example, `user1` wants to read their photos in to `/home/user1`. A lazy admin sets that user's external path to `/home/` since it "gets the job done". However, that user will then be able to read all photos in `/home/user2/private-photos`, too! Please set the external path as specific as possible. If multiple folders must be added, do this using the docker volume mount feature described below.
|
||||||
|
|
||||||
|
### Exclusion Patterns and Scan Settings
|
||||||
|
|
||||||
|
By default, all files in the import paths will be added to the library. If there are files that should not be added, exclusion patterns can be used to exclude them. Exclusion patterns are glob patterns are matched against the full file path. If a file matches an exclusion pattern, it will not be added to the library. Exclusion patterns can be added in the Scan Settings page for each library. Under the hood, Immich uses the [glob](https://www.npmjs.com/package/glob) package to match patterns, so please refer to [their documentation](https://github.com/isaacs/node-glob#glob-primer) to see what patterns are supported.
|
||||||
|
|
||||||
|
Some basic examples:
|
||||||
|
|
||||||
|
- `*.tif` will exclude all files with the extension `.tif`
|
||||||
|
- `hidden.jpg` will exclude all files named `hidden.jpg`
|
||||||
|
- `**/Raw/**` will exclude all files in any directory named `Raw`
|
||||||
|
- `*.(tif,jpg)` will exclude all files with the extension `.tif` or `.jpg`
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Let's show a concrete example where we add an existing gallery to Immich. Here, we have the following folders we want to add:
|
||||||
|
|
||||||
|
- `/home/user/old-pics`: a folder contining childhood photos.
|
||||||
|
- `/mnt/nas/christmas-trip`: photos from a christmas trip. The subfolder `/mnt/nas/christmas-trip/Raw` contains the raw files directly from the DSLR. We don't want to import the raw files to Immich
|
||||||
|
- `/mnt/media/videos`: Videos from the same christmas trip.
|
||||||
|
|
||||||
|
First, we need to plan how we want to organize the libraries. The christmas trip photos should belong to its own library since we want to exclude the raw files. The videos and old photos can be in the same library since we want to import all files. We could also add all three folders to the same library if there are no files matching the Raw exclusion pattern in the other folders.
|
||||||
|
|
||||||
|
### Mount Docker Volumes
|
||||||
|
|
||||||
|
`immich-server` and `immich-microservices` containers will need access to the gallery. Modify your docker compose file as follows
|
||||||
|
|
||||||
|
```diff title="docker-compose.yml"
|
||||||
|
immich-server:
|
||||||
|
volumes:
|
||||||
|
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||||
|
+ - /mnt/nas/christmas-trip:/mnt/media/christmas-trip:ro
|
||||||
|
+ - /home/user/old-pics:/mnt/media/old-pics:ro
|
||||||
|
+ - /mnt/media/videos:/mnt/media/videos:ro
|
||||||
|
|
||||||
|
|
||||||
|
immich-microservices:
|
||||||
|
volumes:
|
||||||
|
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||||
|
+ - /mnt/nas/christmas-trip:/mnt/media/christmas-trip:ro
|
||||||
|
+ - /home/user/old-pics:/mnt/media/old-pics:ro
|
||||||
|
+ - /mnt/media/videos:/mnt/media/videos:ro
|
||||||
|
```
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
The `ro` flag at the end only gives read-only access to the volumes. While Immich does not modify files, it's a good practice to mount read-only.
|
||||||
|
:::
|
||||||
|
|
||||||
|
_Remember to bring the container down/up to register the changes. Make sure you can see the mounted path in the container._
|
||||||
|
|
||||||
|
### Set External Path
|
||||||
|
|
||||||
|
Only an admin can do this.
|
||||||
|
|
||||||
|
- Navigate to `Administration > Users` page on the web.
|
||||||
|
- Click on the user edit button.
|
||||||
|
- Set `/mnt/media` to be the external path. This folder will only contain the three folders that we want to import, so nothing else can be accessed.
|
||||||
|
|
||||||
|
### Create External Libraries
|
||||||
|
|
||||||
|
- Click on your user name in the top right corner -> Account Settings
|
||||||
|
- Click on Libraries
|
||||||
|
- Click on Create External Library
|
||||||
|
- Click the drop-down menu on the newly created library
|
||||||
|
- Click on Rename Library and rename it to "Christmas Trip"
|
||||||
|
- Click Edit Import Paths
|
||||||
|
- Click on Add Path
|
||||||
|
- Enter `/mnt/media/christmas-trip` then click Add
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Next, we'll add an exclusion pattern to filter out raw files.
|
||||||
|
|
||||||
|
- Click the drop-down menu on the newly christmas library
|
||||||
|
- Click on Manage
|
||||||
|
- Click on Scan Settings
|
||||||
|
- Click on Add Exclusion Pattern
|
||||||
|
- Enter `**/Raw/**` and click save.
|
||||||
|
- Click save
|
||||||
|
- Click the drop-down menu on the newly created library
|
||||||
|
- Click on Scan Library Files
|
||||||
|
|
||||||
|
The christmas trip library will now be scanned in the background. In the meantime, let's add the videos and old photos to another library.
|
||||||
|
|
||||||
|
- Click on Create External Library.
|
||||||
|
|
||||||
|
:::info Note
|
||||||
|
If you get an error here, please rename the other external library to something else. This is a bug that will be fixed in a future release.
|
||||||
|
:::
|
||||||
|
|
||||||
|
- Click the drop-down menu on the newly created library
|
||||||
|
- Click Edit Import Paths
|
||||||
|
- Click on Add Path
|
||||||
|
- Enter `/mnt/media/old-pics` then click Add
|
||||||
|
- Click on Add Path
|
||||||
|
- Enter `/mnt/media/videos` then click Add
|
||||||
|
- Click Save
|
||||||
|
- Click on Scan Library Files
|
||||||
|
|
||||||
|
Within seconds, the assets from the old-pics and videos folders should show up in the main timeline.
|
||||||
81
docs/docs/guides/database-queries.md
Normal file
81
docs/docs/guides/database-queries.md
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
# Database Queries
|
||||||
|
|
||||||
|
:::danger
|
||||||
|
Keep in mind that mucking around in the database might set the moon on fire. Avoid modifying the database directly when possible, and always have current backups.
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
Run `docker exec -it immich_postgres psql immich <DB_USERNAME>` to connect to the database via the container directly.
|
||||||
|
|
||||||
|
(Replace `<DB_USERNAME>` wit the value from your [`.env` file](/docs/install/environment-variables#database)).
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Assets
|
||||||
|
|
||||||
|
:::note
|
||||||
|
The `"originalFileName"` column is the name of the uploaded file _without_ the extension.
|
||||||
|
:::
|
||||||
|
|
||||||
|
```sql title="Find by original filename"
|
||||||
|
SELECT * FROM "assets" WHERE "originalFileName" = 'PXL_20230903_232542848';
|
||||||
|
SELECT * FROM "assets" WHERE "originalFileName" LIKE 'PXL_%'; -- all files starting with PXL_
|
||||||
|
SELECT * FROM "assets" WHERE "originalFileName" LIKE '%_2023_%'; -- all files with _2023_ in the middle
|
||||||
|
```
|
||||||
|
|
||||||
|
```sql title="Find by path"
|
||||||
|
SELECT * FROM "assets" WHERE "originalPath" = 'upload/library/admin/2023/2023-09-03/PXL_20230903_232542848.jpg';
|
||||||
|
SELECT * FROM "assets" WHERE "originalPath" LIKE 'upload/library/admin/2023/%';
|
||||||
|
```
|
||||||
|
|
||||||
|
```sql title="Find by checksum" (sha1)
|
||||||
|
SELECT encode("checksum", 'hex') FROM "assets";
|
||||||
|
SELECT * FROM "assets" WHERE "checksum" = decode('69de19c87658c4c15d9cacb9967b8e033bf74dd1', 'hex');
|
||||||
|
```
|
||||||
|
|
||||||
|
```sql title="Live photos"
|
||||||
|
SELECT * FROM "assets" where "livePhotoVideoId" IS NOT NULL;
|
||||||
|
```
|
||||||
|
|
||||||
|
```sql title="Without metadata"
|
||||||
|
SELECT "assets".* FROM "exif" LEFT JOIN "assets" ON "assets"."id" = "exif"."assetId" WHERE "exif"."assetId" IS NULL;
|
||||||
|
```
|
||||||
|
|
||||||
|
```sql title="Without thumbnails"
|
||||||
|
SELECT * FROM "assets" WHERE "assets"."resizePath" IS NULL OR "assets"."webpPath" IS NULL;
|
||||||
|
```
|
||||||
|
|
||||||
|
```sql title="By type"
|
||||||
|
SELECT * FROM "assets" WHERE "assets"."type" = 'VIDEO';
|
||||||
|
SELECT * FROM "assets" WHERE "assets"."type" = 'IMAGE';
|
||||||
|
```
|
||||||
|
|
||||||
|
```sql title="Count by type"
|
||||||
|
SELECT "assets"."type", count(*) FROM "assets" GROUP BY "assets"."type";
|
||||||
|
```
|
||||||
|
|
||||||
|
```sql title="Count by type (per user)"
|
||||||
|
SELECT
|
||||||
|
"users"."email", "assets"."type", COUNT(*)
|
||||||
|
FROM
|
||||||
|
"assets"
|
||||||
|
JOIN
|
||||||
|
"users" ON "assets"."ownerId" = "users"."id"
|
||||||
|
GROUP BY
|
||||||
|
"assets"."type", "users"."email"
|
||||||
|
ORDER BY
|
||||||
|
"users"."email";
|
||||||
|
```
|
||||||
|
|
||||||
|
## Users
|
||||||
|
|
||||||
|
```sql title="List"
|
||||||
|
SELECT * FROM "users";
|
||||||
|
```
|
||||||
|
|
||||||
|
## System Config
|
||||||
|
|
||||||
|
```sql title="Custom settings"
|
||||||
|
SELECT "key", "value" FROM "system_config";
|
||||||
|
```
|
||||||
|
|
||||||
|
(Only used when not using the [config file](/docs/install/config-file))
|
||||||
@@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
sidebar_position: 1
|
|
||||||
---
|
|
||||||
|
|
||||||
# Docker Help
|
# Docker Help
|
||||||
|
|
||||||
## Containers
|
## Containers
|
||||||
|
|||||||
26
docs/docs/guides/machine-learning.md
Normal file
26
docs/docs/guides/machine-learning.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Remote Machine Learning
|
||||||
|
|
||||||
|
To alleviate [performance issues on low-memory systems](/docs/FAQ.md#why-is-immich-slow-on-low-memory-systems-like-the-raspberry-pi) like the Raspberry Pi, you may also host Immich's machine-learning container on a more powerful system (e.g. your laptop or desktop computer):
|
||||||
|
|
||||||
|
- Set `IMMICH_MACHINE_LEARNING_URL` to point to the designated ML system, e.g. `http://workstation:3003`.
|
||||||
|
- Copy the following `docker-compose.yml` to your ML system.
|
||||||
|
- Start the container by running `docker-compose up -d` or `docker compose up -d` (depending on your Docker version).
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
immich-machine-learning:
|
||||||
|
container_name: immich_machine_learning
|
||||||
|
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
|
||||||
|
volumes:
|
||||||
|
- model-cache:/cache
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 3003:3003
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
model-cache:
|
||||||
|
```
|
||||||
|
|
||||||
|
Please note that version mismatches between both hosts may cause instabilities and bugs, so make sure to always perform updates together.
|
||||||
113
docs/docs/install/config-file.md
Normal file
113
docs/docs/install/config-file.md
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
# Config File
|
||||||
|
|
||||||
|
A config file can be provided as an alternative to the UI configuration.
|
||||||
|
|
||||||
|
### Step 1 - Create a new config file
|
||||||
|
|
||||||
|
In JSON format, create a new config file (e.g. `immich.config`) and put it in a location that can be accessed by Immich.
|
||||||
|
The default configuration looks like this:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ffmpeg": {
|
||||||
|
"crf": 23,
|
||||||
|
"threads": 0,
|
||||||
|
"preset": "ultrafast",
|
||||||
|
"targetVideoCodec": "h264",
|
||||||
|
"targetAudioCodec": "aac",
|
||||||
|
"targetResolution": "720",
|
||||||
|
"maxBitrate": "0",
|
||||||
|
"twoPass": false,
|
||||||
|
"transcode": "required",
|
||||||
|
"tonemap": "hable",
|
||||||
|
"accel": "disabled"
|
||||||
|
},
|
||||||
|
"job": {
|
||||||
|
"backgroundTask": {
|
||||||
|
"concurrency": 5
|
||||||
|
},
|
||||||
|
"clipEncoding": {
|
||||||
|
"concurrency": 2
|
||||||
|
},
|
||||||
|
"metadataExtraction": {
|
||||||
|
"concurrency": 5
|
||||||
|
},
|
||||||
|
"objectTagging": {
|
||||||
|
"concurrency": 2
|
||||||
|
},
|
||||||
|
"recognizeFaces": {
|
||||||
|
"concurrency": 2
|
||||||
|
},
|
||||||
|
"search": {
|
||||||
|
"concurrency": 5
|
||||||
|
},
|
||||||
|
"sidecar": {
|
||||||
|
"concurrency": 5
|
||||||
|
},
|
||||||
|
"storageTemplateMigration": {
|
||||||
|
"concurrency": 5
|
||||||
|
},
|
||||||
|
"thumbnailGeneration": {
|
||||||
|
"concurrency": 5
|
||||||
|
},
|
||||||
|
"videoConversion": {
|
||||||
|
"concurrency": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"machineLearning": {
|
||||||
|
"classification": {
|
||||||
|
"minScore": 0.7,
|
||||||
|
"enabled": true,
|
||||||
|
"modelName": "microsoft/resnet-50"
|
||||||
|
},
|
||||||
|
"enabled": true,
|
||||||
|
"url": "http://immich-machine-learning:3003",
|
||||||
|
"clip": {
|
||||||
|
"enabled": true,
|
||||||
|
"modelName": "ViT-B-32::openai"
|
||||||
|
},
|
||||||
|
"facialRecognition": {
|
||||||
|
"enabled": true,
|
||||||
|
"modelName": "buffalo_l",
|
||||||
|
"minScore": 0.7,
|
||||||
|
"maxDistance": 0.6,
|
||||||
|
"minFaces": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oauth": {
|
||||||
|
"enabled": false,
|
||||||
|
"issuerUrl": "",
|
||||||
|
"clientId": "",
|
||||||
|
"clientSecret": "",
|
||||||
|
"mobileOverrideEnabled": false,
|
||||||
|
"mobileRedirectUri": "",
|
||||||
|
"scope": "openid email profile",
|
||||||
|
"storageLabelClaim": "preferred_username",
|
||||||
|
"buttonText": "Login with OAuth",
|
||||||
|
"autoRegister": true,
|
||||||
|
"autoLaunch": false
|
||||||
|
},
|
||||||
|
"passwordLogin": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"storageTemplate": {
|
||||||
|
"template": "{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}"
|
||||||
|
},
|
||||||
|
"thumbnail": {
|
||||||
|
"webpSize": 250,
|
||||||
|
"jpegSize": 1440,
|
||||||
|
"quality": 90,
|
||||||
|
"colorspace": "p3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
In Administration > Settings is a button to copy the current configuration to your clipboard.
|
||||||
|
So you can just grab it from there, paste it into a file and you're pretty much good to go.
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Step 2 - Specify the file location
|
||||||
|
|
||||||
|
In your `.env` file, set the variable `IMMICH_CONFIG_FILE` to the path of your config.
|
||||||
|
For more information, refer to the [Environment Variables](https://docs.immich.app/docs/install/environment-variables) section.
|
||||||
@@ -25,10 +25,18 @@ wget https://github.com/immich-app/immich/releases/latest/download/docker-compos
|
|||||||
wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.env
|
wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.env
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```bash title="(Optional) Get hwaccel.yml file"
|
||||||
|
wget https://github.com/immich-app/immich/releases/latest/download/hwaccel.yml
|
||||||
|
```
|
||||||
|
|
||||||
or by downloading from your browser and moving the files to the directory that you created.
|
or by downloading from your browser and moving the files to the directory that you created.
|
||||||
|
|
||||||
Note: If you downloaded the files from your browser, also ensure that you rename `example.env` to `.env`.
|
Note: If you downloaded the files from your browser, also ensure that you rename `example.env` to `.env`.
|
||||||
|
|
||||||
|
:::info
|
||||||
|
Optionally, you can use the [`hwaccel.yml`][hw-file] file to enable hardware acceleration for transcoding. See the [Hardware Transcoding](/docs/features/hardware-transcoding.md) guide for info on how to set this up.
|
||||||
|
:::
|
||||||
|
|
||||||
### Step 2 - Populate the .env file with custom values
|
### Step 2 - Populate the .env file with custom values
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@@ -124,7 +132,6 @@ PUBLIC_LOGIN_PAGE_MESSAGE="My Family Photos and Videos Backup Server"
|
|||||||
|
|
||||||
IMMICH_WEB_URL=http://immich-web:3000
|
IMMICH_WEB_URL=http://immich-web:3000
|
||||||
IMMICH_SERVER_URL=http://immich-server:3001
|
IMMICH_SERVER_URL=http://immich-server:3001
|
||||||
IMMICH_MACHINE_LEARNING_URL=http://immich-machine-learning:3003
|
|
||||||
|
|
||||||
####################################################################################
|
####################################################################################
|
||||||
# Alternative API's External Address - Optional
|
# Alternative API's External Address - Optional
|
||||||
@@ -166,6 +173,10 @@ docker-compose up -d # or `docker compose up -d` based on your docker-compos
|
|||||||
For more information on how to use the application, please refer to the [Post Installation](/docs/install/post-install.mdx) guide.
|
For more information on how to use the application, please refer to the [Post Installation](/docs/install/post-install.mdx) guide.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
Note that downloading container images might require you to authenticate to the GitHub Container Registry ([steps here](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#authenticating-to-the-container-registry)).
|
||||||
|
:::
|
||||||
|
|
||||||
### Step 4 - Upgrading
|
### Step 4 - Upgrading
|
||||||
|
|
||||||
If `IMMICH_VERSION` is set, it will need to be updated to the latest or desired version.
|
If `IMMICH_VERSION` is set, it will need to be updated to the latest or desired version.
|
||||||
@@ -182,4 +193,5 @@ Immich is currently under heavy development, which means you can expect breaking
|
|||||||
|
|
||||||
[compose-file]: https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
[compose-file]: https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
||||||
[env-file]: https://github.com/immich-app/immich/releases/latest/download/example.env
|
[env-file]: https://github.com/immich-app/immich/releases/latest/download/example.env
|
||||||
|
[hw-file]: https://github.com/immich-app/immich/releases/latest/download/hwaccel.yml
|
||||||
[watchtower]: https://containrrr.dev/watchtower/
|
[watchtower]: https://containrrr.dev/watchtower/
|
||||||
|
|||||||
@@ -1,5 +1,20 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 90
|
||||||
|
---
|
||||||
|
|
||||||
# Environment Variables
|
# Environment Variables
|
||||||
|
|
||||||
|
:::caution
|
||||||
|
|
||||||
|
To change environment variables, you must recreate the Immich containers.
|
||||||
|
Just restarting the containers does not replace the environment within the container!
|
||||||
|
|
||||||
|
In order to recreate the container using docker compose, run `docker compose up -d`.
|
||||||
|
In most cases docker will recognize that the `.env` file has changed and recreate the affected containers.
|
||||||
|
If this should not work, try running `docker compose up -d --force-recreate`.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
## Docker Compose
|
## Docker Compose
|
||||||
|
|
||||||
| Variable | Description | Default | Services |
|
| Variable | Description | Default | Services |
|
||||||
@@ -22,6 +37,7 @@ These environment variables are used by the `docker-compose.yml` file and do **N
|
|||||||
| `LOG_LEVEL` | Log Level (verbose, debug, log, warn, error) | `log` | server, microservices |
|
| `LOG_LEVEL` | Log Level (verbose, debug, log, warn, error) | `log` | server, microservices |
|
||||||
| `IMMICH_MEDIA_LOCATION` | Media Location | `./upload` | server, microservices |
|
| `IMMICH_MEDIA_LOCATION` | Media Location | `./upload` | server, microservices |
|
||||||
| `PUBLIC_LOGIN_PAGE_MESSAGE` | Public Login Page Message | | web |
|
| `PUBLIC_LOGIN_PAGE_MESSAGE` | Public Login Page Message | | web |
|
||||||
|
| `IMMICH_CONFIG_FILE` | Path to config file | | server |
|
||||||
|
|
||||||
:::tip
|
:::tip
|
||||||
|
|
||||||
@@ -41,22 +57,22 @@ These environment variables are used by the `docker-compose.yml` file and do **N
|
|||||||
|
|
||||||
## Ports
|
## Ports
|
||||||
|
|
||||||
| Variable | Description | Default | Services |
|
| Variable | Description | Default | Services |
|
||||||
| :---------------------- | :-------------------- | :-----: | :--------------- |
|
| :---------------------- | :-------------------- | :-------: | :--------------- |
|
||||||
| `PORT` | Web Port | `3000` | web |
|
| `PORT` | Web Port | `3000` | web |
|
||||||
| `SERVER_PORT` | Server Port | `3001` | server |
|
| `SERVER_PORT` | Server Port | `3001` | server |
|
||||||
| `MICROSERVICES_PORT` | Microservices Port | `3002` | microservices |
|
| `MICROSERVICES_PORT` | Microservices Port | `3002` | microservices |
|
||||||
| `MACHINE_LEARNING_PORT` | Machine Learning Port | `3003` | machine learning |
|
| `MACHINE_LEARNING_HOST` | Machine Learning Host | `0.0.0.0` | machine learning |
|
||||||
|
| `MACHINE_LEARNING_PORT` | Machine Learning Port | `3003` | machine learning |
|
||||||
|
|
||||||
## URLs
|
## URLs
|
||||||
|
|
||||||
| Variable | Description | Default | Services |
|
| Variable | Description | Default | Services |
|
||||||
| :---------------------------- | :------------------------------------------------------- | :-----------------------------------: | :-------------------- |
|
| :------------------------- | :---------------------- | :-------------------------: | :--------- |
|
||||||
| `IMMICH_WEB_URL` | Immich Web URL | `http://immich-web:3000` | proxy |
|
| `IMMICH_WEB_URL` | Immich Web URL | `http://immich-web:3000` | proxy |
|
||||||
| `IMMICH_SERVER_URL` | Immich Server URL | `http://immich-server:3001` | web, proxy |
|
| `IMMICH_SERVER_URL` | Immich Server URL | `http://immich-server:3001` | web, proxy |
|
||||||
| `IMMICH_MACHINE_LEARNING_URL` | Immich Machine Learning URL, set `"false"` to disable ML | `http://immich-machine-learning:3003` | server, microservices |
|
| `PUBLIC_IMMICH_SERVER_URL` | Public Immich URL | `http://immich-server:3001` | web |
|
||||||
| `PUBLIC_IMMICH_SERVER_URL` | Public Immich URL | `http://immich-server:3001` | web |
|
| `IMMICH_API_URL_EXTERNAL` | Immich API URL External | `/api` | web |
|
||||||
| `IMMICH_API_URL_EXTERNAL` | Immich API URL External | `/api` | web |
|
|
||||||
|
|
||||||
:::info
|
:::info
|
||||||
|
|
||||||
@@ -172,15 +188,45 @@ Typesense URL example JSON before encoding:
|
|||||||
|
|
||||||
## Machine Learning
|
## Machine Learning
|
||||||
|
|
||||||
| Variable | Description | Default | Services |
|
| Variable | Description | Default | Services |
|
||||||
| :------------------------------------------ | :----------------------------- | :-------------------: | :--------------- |
|
| :----------------------------------------------- | :---------------------------------------------------------------- | :-----------------: | :--------------- |
|
||||||
| `MACHINE_LEARNING_MIN_FACE_SCORE` | Minimum Face Score | `0.7` | machine learning |
|
| `MACHINE_LEARNING_MODEL_TTL`<sup>\*1</sup> | Inactivity time (s) before a model is unloaded (disabled if <= 0) | `0` | machine learning |
|
||||||
| `MACHINE_LEARNING_MODEL_TTL` | Model TTL | `300` | machine learning |
|
| `MACHINE_LEARNING_CACHE_FOLDER` | Directory where models are downloaded | `/cache` | machine learning |
|
||||||
| `MACHINE_LEARNING_EAGER_STARTUP` | Eager Startup | `true` | machine learning |
|
| `MACHINE_LEARNING_REQUEST_THREADS`<sup>\*2</sup> | Thread count of the request thread pool (disabled if <= 0) | number of CPU cores | machine learning |
|
||||||
| `MACHINE_LEARNING_MIN_TAG_SCORE` | Minimum Tag Score | `0.9` | machine learning |
|
| `MACHINE_LEARNING_MODEL_INTER_OP_THREADS` | Number of parallel model operations | `1` | machine learning |
|
||||||
| `MACHINE_LEARNING_FACIAL_RECOGNITION_MODEL` | Facial Recognition Model | `buffalo_l` | machine learning |
|
| `MACHINE_LEARNING_MODEL_INTRA_OP_THREADS` | Number of threads for each model operation | `2` | machine learning |
|
||||||
| `MACHINE_LEARNING_CLIP_TEXT_MODEL` | Clip Text Model | `clip-ViT-B-32` | machine learning |
|
| `MACHINE_LEARNING_WORKERS`<sup>\*3</sup> | Number of worker processes to spawn | `1` | machine learning |
|
||||||
| `MACHINE_LEARNING_CLIP_IMAGE_MODEL` | Clip Image Model | `clip-ViT-B-32` | machine learning |
|
| `MACHINE_LEARNING_WORKER_TIMEOUT` | Maximum time (s) of unresponsiveness before a worker is killed | `120` | machine learning |
|
||||||
| `MACHINE_LEARNING_CLASSIFICATION_MODEL` | Classification Model | `microsoft/resnet-50` | machine learning |
|
|
||||||
| `MACHINE_LEARNING_CACHE_FOLDER` | ML Cache Location | `/cache` | machine learning |
|
\*1: This is an experimental feature. It may result in increased memory use over time when loading models repeatedly.
|
||||||
| `TRANSFORMERS_CACHE` | ML Transformers Cache Location | `/cache` | machine learning |
|
|
||||||
|
\*2: It is recommended to begin with this parameter when changing the concurrency levels of the machine learning service and then tune the other ones.
|
||||||
|
|
||||||
|
\*3: Since each process duplicates models in memory, changing this is not recommended unless you have abundant memory to go around.
|
||||||
|
|
||||||
|
:::info
|
||||||
|
|
||||||
|
Other machine learning parameters can be tuned from the admin UI.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Docker Secrets
|
||||||
|
|
||||||
|
The following variables support the use of [Docker secrets](https://docs.docker.com/engine/swarm/secrets/) for additional security.
|
||||||
|
|
||||||
|
To use any of these, replace the regular environment variable with the equivalent `_FILE` environment variable. The value of
|
||||||
|
the `_FILE` variable should be set to the path of a file containing the variable value.
|
||||||
|
|
||||||
|
| Regular Variable | Equivalent Docker Secrets '\_FILE' Variable |
|
||||||
|
| :----------------: | :-----------------------------------------: |
|
||||||
|
| `DB_HOSTNAME` | `DB_HOSTNAME_FILE`<sup>\*1</sup> |
|
||||||
|
| `DB_DATABASE_NAME` | `DB_DATABASE_NAME_FILE`<sup>\*1</sup> |
|
||||||
|
| `DB_USERNAME` | `DB_USERNAME_FILE`<sup>\*1</sup> |
|
||||||
|
| `DB_PASSWORD` | `DB_PASSWORD_FILE`<sup>\*1</sup> |
|
||||||
|
| `REDIS_PASSWORD` | `REDIS_PASSWORD_FILE`<sup>\*2</sup> |
|
||||||
|
|
||||||
|
\*1: See the [official documentation](https://github.com/docker-library/docs/tree/master/postgres#docker-secrets) for
|
||||||
|
details on how to use Docker Secrets in the Postgres image.
|
||||||
|
|
||||||
|
\*2: See [this comment](https://github.com/docker-library/redis/issues/46#issuecomment-335326234) for an example of how
|
||||||
|
to use use a Docker secret for the password in the Redis container.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
sidebar_position: 100
|
sidebar_position: 80
|
||||||
---
|
---
|
||||||
|
|
||||||
import RegisterAdminUser from '../partials/_register-admin.md';
|
import RegisterAdminUser from '../partials/_register-admin.md';
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ I really like the Japanese culture, especially the books, history, and food. The
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
One of my favorite books is [Taikō](https://www.goodreads.com/book/show/336228.Taiko), it is a story about a prominent figure in the history of Japan, [Toyotomy Hideyoshi](https://www.britannica.com/biography/Toyotomi-Hideyoshi). He came from nothing, and through his resilience and wonderful mind, he has become one of the most powerful rulers in Japan's history. I enjoy his personality and the way he moved through life.
|
One of my favorite books is [Taikō](https://www.goodreads.com/book/show/336228.Taiko), it is a story about a prominent figure in the history of Japan, [Toyotomi Hideyoshi](https://www.britannica.com/biography/Toyotomi-Hideyoshi). He came from nothing, and through his resilience and wonderful mind, he has become one of the most powerful rulers in Japan's history. I enjoy his personality and the way he moved through life.
|
||||||
|
|
||||||
The color is an adaptation of **_App-Which-Must-Not-Be-Named_**'s color scheme, with an extra color (pink) to complete the flower's fifth petal. The petal layers are the same color scheme as the main layer rotating back and forth to "bring the flower to life."
|
The color is an adaptation of **_App-Which-Must-Not-Be-Named_**'s color scheme, with an extra color (pink) to complete the flower's fifth petal. The petal layers are the same color scheme as the main layer rotating back and forth to "bring the flower to life."
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
Immich allows the admin user to set the uploaded filename pattern. Both at the directory and filename level.
|
Immich allows the admin user to set the uploaded filename pattern. Both at the directory and filename level.
|
||||||
|
|
||||||
The admin user can set the template by using the template builder in the `Administration -> Settings -> Storage Template`. Immich provides a set of variables that you can use in constructing the template, along with additional custom text.
|
The admin user can set the template by using the template builder in the `Administration -> Settings -> Storage Template`. Immich provides a set of variables that you can use in constructing the template, along with additional custom text. If the template produces [multiple files with the same filename, they won't be overwritten](https://github.com/immich-app/immich/discussions/3324) as a sequence number is appended to the filename.
|
||||||
|
|
||||||
```bash title="Default template"
|
```bash title="Default template"
|
||||||
Year/Year-Month-Day/Filename.Extension
|
Year/Year-Month-Day/Filename.Extension
|
||||||
@@ -8,4 +8,4 @@ Year/Year-Month-Day/Filename.Extension
|
|||||||
|
|
||||||
<img src={require('./img/storage-template.png').default} width="100%" title="Storage Template Setting" />
|
<img src={require('./img/storage-template.png').default} width="100%" title="Storage Template Setting" />
|
||||||
|
|
||||||
Immich also provides a mechanism to migrate between template so that if the template you set now doesn't work in the future, you can always migrate all the existing files to the new template. The mechanism is run as a job in the Job page.
|
Immich also provides a mechanism to migrate between templates so that if the template you set now doesn't work in the future, you can always migrate all the existing files to the new template. The mechanism is run as a job on the Job page.
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ const config = {
|
|||||||
prism: {
|
prism: {
|
||||||
theme: lightCodeTheme,
|
theme: lightCodeTheme,
|
||||||
darkTheme: darkCodeTheme,
|
darkTheme: darkCodeTheme,
|
||||||
|
additionalLanguages: ['sql'],
|
||||||
},
|
},
|
||||||
image: 'overview/img/feature-panel.png',
|
image: 'overview/img/feature-panel.png',
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM python:3.11.4-bullseye@sha256:5b401676aff858495a5c9c726c60b8b73fe52833e9e16eccdb59e93d52741727 as builder
|
FROM python:3.11-bookworm as builder
|
||||||
|
|
||||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||||
PYTHONUNBUFFERED=1 \
|
PYTHONUNBUFFERED=1 \
|
||||||
@@ -10,10 +10,13 @@ RUN poetry config installer.max-workers 10 && \
|
|||||||
RUN python -m venv /opt/venv
|
RUN python -m venv /opt/venv
|
||||||
ENV VIRTUAL_ENV="/opt/venv" PATH="/opt/venv/bin:${PATH}"
|
ENV VIRTUAL_ENV="/opt/venv" PATH="/opt/venv/bin:${PATH}"
|
||||||
|
|
||||||
COPY poetry.lock pyproject.toml ./
|
COPY poetry.lock pyproject.toml requirements.txt ./
|
||||||
RUN poetry install --sync --no-interaction --no-ansi --no-root --only main
|
RUN poetry install --sync --no-interaction --no-ansi --no-root --only main
|
||||||
|
RUN pip install --no-deps -r requirements.txt
|
||||||
|
|
||||||
FROM python:3.11.4-slim-bullseye@sha256:91d194f58f50594cda71dcd2e8fdefd90e7ecc57d07823813b67c8521e565dcd
|
FROM python:3.11-slim-bookworm
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends tini libmimalloc2.0 && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
ENV NODE_ENV=production \
|
ENV NODE_ENV=production \
|
||||||
@@ -24,5 +27,7 @@ ENV NODE_ENV=production \
|
|||||||
PYTHONPATH=/usr/src
|
PYTHONPATH=/usr/src
|
||||||
|
|
||||||
COPY --from=builder /opt/venv /opt/venv
|
COPY --from=builder /opt/venv /opt/venv
|
||||||
|
COPY start.sh log_conf.json ./
|
||||||
COPY app .
|
COPY app .
|
||||||
ENTRYPOINT ["python", "-m", "app.main"]
|
ENTRYPOINT ["tini", "--"]
|
||||||
|
CMD ["./start.sh"]
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ Be sure to commit the `poetry.lock` and `pyproject.toml` files to reflect any ch
|
|||||||
|
|
||||||
To measure inference throughput and latency, you can use [Locust](https://locust.io/) using the provided `locustfile.py`.
|
To measure inference throughput and latency, you can use [Locust](https://locust.io/) using the provided `locustfile.py`.
|
||||||
Locust works by querying the model endpoints and aggregating their statistics, meaning the app must be deployed.
|
Locust works by querying the model endpoints and aggregating their statistics, meaning the app must be deployed.
|
||||||
You can run `load_test.sh` to automatically deploy the app locally and start Locust, optionally adjusting its env variables as needed.
|
You can change the models or adjust options like score thresholds through the Locust UI.
|
||||||
|
|
||||||
Alternatively, for more custom testing, you may also run `locust` directly: see the [documentation](https://docs.locust.io/en/stable/index.html). Note that in Locust's jargon, concurrency is measured in `users`, and each user runs one task at a time. To achieve a particular per-endpoint concurrency, multiply that number by the number of endpoints to be queried. For example, if there are 3 endpoints and you want each of them to receive 8 requests at a time, you should set the number of users to 24.
|
To get started, you can simply run `locust --web-host 127.0.0.1` and open `localhost:8089` in a browser to access the UI. See the [Locust documentation](https://docs.locust.io/en/stable/index.html) for more info on running Locust.
|
||||||
|
|
||||||
|
Note that in Locust's jargon, concurrency is measured in `users`, and each user runs one task at a time. To achieve a particular per-endpoint concurrency, multiply that number by the number of endpoints to be queried. For example, if there are 3 endpoints and you want each of them to receive 8 requests at a time, you should set the number of users to 24.
|
||||||
21
machine-learning/README_es_ES.md
Normal file
21
machine-learning/README_es_ES.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Immich Machine Learning
|
||||||
|
|
||||||
|
- Clasificación de imágenes
|
||||||
|
- Incorporación de CLIP
|
||||||
|
- Reconocimiento facial
|
||||||
|
|
||||||
|
# Configuración
|
||||||
|
|
||||||
|
Este proyecto utiliza [Poetry](https://python-poetry.org/docs/#installation), así que asegúrate de instalarlo primero.
|
||||||
|
Ejecutar `poetry install --no-root --with dev` instalará todo lo necesario en un entorno virtual aislado.
|
||||||
|
|
||||||
|
Para agregar o eliminar dependencias, puedes utilizar los comandos `poetry add $PACKAGE_NAME` y `poetry remove $PACKAGE_NAME`, respectivamente.
|
||||||
|
Asegúrate de hacer commit de los archivos `poetry.lock` y `pyproject.toml` para reflejar cualquier cambio en las dependencias.
|
||||||
|
|
||||||
|
# Pruebas de carga
|
||||||
|
|
||||||
|
Para medir la velocidad y latencia de inferencia, puedes utilizar [Locust](https://locust.io/) con el archivo `locustfile.py` proporcionado.
|
||||||
|
Locust funciona haciendo consultas a los puntos finales del modelo y agregando estadísticas, lo que significa que la aplicación debe estar desplegada.
|
||||||
|
Puedes ejecutar `load_test.sh` para implementar automáticamente la aplicación localmente e iniciar Locust, ajustando opcionalmente sus variables de entorno según sea necesario.
|
||||||
|
|
||||||
|
Alternativamente, para pruebas más personalizadas, también puedes ejecutar `locust` directamente: consulta la [documentación](https://docs.locust.io/en/stable/index.html). Ten en cuenta que, en la jerga de Locust, la concurrencia se mide en `usuarios`, y cada usuario ejecuta una tarea a la vez. Para lograr una concurrencia específica por punto final, multiplica ese número por la cantidad de puntos finales que se desean consultar. Por ejemplo, si hay 3 puntos finales y deseas que cada uno de ellos reciba 8 solicitudes al mismo tiempo, debes configurar el número de usuarios en 24.
|
||||||
22
machine-learning/README_fr_FR.md
Normal file
22
machine-learning/README_fr_FR.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Immich Apprentissage machine
|
||||||
|
|
||||||
|
- Classification d'images
|
||||||
|
- Embarquement de CLIP
|
||||||
|
- Reconnaissance faciale
|
||||||
|
|
||||||
|
# Mise en place
|
||||||
|
|
||||||
|
Ce projet utilise [Poetry](https://python-poetry.org/docs/#installation), donc soyez certain de l'installer en premier.
|
||||||
|
Exécuter `poetry install --no-root --with dev` installera tout ce dont vous avez besoin dans un environnement virtuel isolé.
|
||||||
|
|
||||||
|
Pour ajouter ou supprimer des dépendances, vous pouvez utiliser les commandes `poetry add $PACKAGE_NAME` et `poetry remove $PACKAGE_NAME` respectivement.
|
||||||
|
Soyez sûr de commit les fichiers `poetry.lock` et `pyproject.toml` pour refléter les changements de dépendances.
|
||||||
|
|
||||||
|
|
||||||
|
# Test de charge
|
||||||
|
|
||||||
|
Pour mesurer le débit d'inférence et la latence, vous pouvez utiliser [Locust](https://locust.io/) avec le fichier fourni `locustfile.py`.
|
||||||
|
Locust fonctionne en interrogeant les endpoints des modèles et en aggrégeant leurs statistiques, signifiant que l'application doit être déployée.
|
||||||
|
Vous pouvez exécuter `load_test.sh` pour automatiquement déployer l'application localement et démarrer Locust, en ajustant si besoin ses variables d'environnement.
|
||||||
|
|
||||||
|
En alternative, pour réaliser plus de tests customisés, vous pourriez aussi exécuter `locust` directement : voir la [documentation](https://docs.locust.io/en/stable/index.html). Notez que dans le jargon de Locust, la concurrence est mesurée en `users` et que chaque user exécute une tâche après l'autre. Pour parvenir à une concurrence par endpoint, multipliez ce nombre par le nombre d'endpoints à interroger. Par exemple, s'il y a 3 endpoints et que vous voulez que chacun d'entre eux reçoive 8 requêtes à la fois, vous devrez mettre ce nombre d'users à 24.
|
||||||
@@ -1,32 +1,69 @@
|
|||||||
|
import logging
|
||||||
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import gunicorn
|
||||||
|
import starlette
|
||||||
from pydantic import BaseSettings
|
from pydantic import BaseSettings
|
||||||
|
from rich.console import Console
|
||||||
|
from rich.logging import RichHandler
|
||||||
|
|
||||||
from .schemas import ModelType
|
from .schemas import ModelType
|
||||||
|
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
cache_folder: str = "/cache"
|
cache_folder: str = "/cache"
|
||||||
classification_model: str = "microsoft/resnet-50"
|
|
||||||
clip_image_model: str = "clip-ViT-B-32"
|
|
||||||
clip_text_model: str = "clip-ViT-B-32"
|
|
||||||
facial_recognition_model: str = "buffalo_l"
|
|
||||||
min_tag_score: float = 0.9
|
|
||||||
eager_startup: bool = True
|
|
||||||
model_ttl: int = 0
|
model_ttl: int = 0
|
||||||
host: str = "0.0.0.0"
|
host: str = "0.0.0.0"
|
||||||
port: int = 3003
|
port: int = 3003
|
||||||
workers: int = 1
|
workers: int = 1
|
||||||
min_face_score: float = 0.7
|
|
||||||
test_full: bool = False
|
test_full: bool = False
|
||||||
|
request_threads: int = os.cpu_count() or 4
|
||||||
|
model_inter_op_threads: int = 1
|
||||||
|
model_intra_op_threads: int = 2
|
||||||
|
|
||||||
class Config(BaseSettings.Config):
|
class Config:
|
||||||
env_prefix = "MACHINE_LEARNING_"
|
env_prefix = "MACHINE_LEARNING_"
|
||||||
case_sensitive = False
|
case_sensitive = False
|
||||||
|
|
||||||
|
|
||||||
def get_cache_dir(model_name: str, model_type: ModelType) -> Path:
|
class LogSettings(BaseSettings):
|
||||||
return Path(settings.cache_folder, model_type.value, model_name)
|
log_level: str = "info"
|
||||||
|
no_color: bool = False
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
case_sensitive = False
|
||||||
|
|
||||||
|
|
||||||
|
_clean_name = str.maketrans(":\\/", "___", ".")
|
||||||
|
|
||||||
|
|
||||||
|
def get_cache_dir(model_name: str, model_type: ModelType) -> Path:
|
||||||
|
return Path(settings.cache_folder) / model_type.value / model_name.translate(_clean_name)
|
||||||
|
|
||||||
|
|
||||||
|
LOG_LEVELS: dict[str, int] = {
|
||||||
|
"critical": logging.ERROR,
|
||||||
|
"error": logging.ERROR,
|
||||||
|
"warning": logging.WARNING,
|
||||||
|
"warn": logging.WARNING,
|
||||||
|
"info": logging.INFO,
|
||||||
|
"log": logging.INFO,
|
||||||
|
"debug": logging.DEBUG,
|
||||||
|
"verbose": logging.DEBUG,
|
||||||
|
}
|
||||||
|
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
|
log_settings = LogSettings()
|
||||||
|
|
||||||
|
|
||||||
|
class CustomRichHandler(RichHandler):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
console = Console(color_system="standard", no_color=log_settings.no_color)
|
||||||
|
super().__init__(
|
||||||
|
show_path=False, omit_repeated_times=False, console=console, tracebacks_suppress=[gunicorn, starlette]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger("gunicorn.access")
|
||||||
|
log.setLevel(LOG_LEVELS.get(log_settings.log_level.lower(), logging.INFO))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from types import SimpleNamespace
|
import json
|
||||||
from typing import Any, Iterator, TypeAlias
|
from typing import Any, Iterator, TypeAlias
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
@@ -22,91 +22,6 @@ def cv_image(pil_image: Image.Image) -> ndarray:
|
|||||||
return np.asarray(pil_image)[:, :, ::-1] # PIL uses RGB while cv2 uses BGR
|
return np.asarray(pil_image)[:, :, ::-1] # PIL uses RGB while cv2 uses BGR
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_classifier_pipeline() -> Iterator[mock.Mock]:
|
|
||||||
with mock.patch("app.models.image_classification.pipeline") as model:
|
|
||||||
classifier_preds = [
|
|
||||||
{"label": "that's an image alright", "score": 0.8},
|
|
||||||
{"label": "well it ends with .jpg", "score": 0.1},
|
|
||||||
{"label": "idk, im just seeing bytes", "score": 0.05},
|
|
||||||
{"label": "not sure", "score": 0.04},
|
|
||||||
{"label": "probably a virus", "score": 0.01},
|
|
||||||
]
|
|
||||||
|
|
||||||
def forward(
|
|
||||||
inputs: Image.Image | list[Image.Image], **kwargs: Any
|
|
||||||
) -> list[dict[str, Any]] | list[list[dict[str, Any]]]:
|
|
||||||
if isinstance(inputs, list) and not all([isinstance(img, Image.Image) for img in inputs]):
|
|
||||||
raise TypeError
|
|
||||||
elif not isinstance(inputs, Image.Image):
|
|
||||||
raise TypeError
|
|
||||||
|
|
||||||
if isinstance(inputs, list):
|
|
||||||
return [classifier_preds] * len(inputs)
|
|
||||||
|
|
||||||
return classifier_preds
|
|
||||||
|
|
||||||
model.return_value = forward
|
|
||||||
yield model
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_st() -> Iterator[mock.Mock]:
|
|
||||||
with mock.patch("app.models.clip.SentenceTransformer") as model:
|
|
||||||
embedding = np.random.rand(512).astype(np.float32)
|
|
||||||
|
|
||||||
def encode(inputs: Image.Image | list[Image.Image], **kwargs: Any) -> ndarray | list[ndarray]:
|
|
||||||
# mypy complains unless isinstance(inputs, list) is used explicitly
|
|
||||||
img_batch = isinstance(inputs, list) and all([isinstance(inst, Image.Image) for inst in inputs])
|
|
||||||
text_batch = isinstance(inputs, list) and all([isinstance(inst, str) for inst in inputs])
|
|
||||||
if isinstance(inputs, list) and not any([img_batch, text_batch]):
|
|
||||||
raise TypeError
|
|
||||||
|
|
||||||
if isinstance(inputs, list):
|
|
||||||
return np.stack([embedding] * len(inputs))
|
|
||||||
|
|
||||||
return embedding
|
|
||||||
|
|
||||||
mocked = mock.Mock()
|
|
||||||
mocked.encode = encode
|
|
||||||
model.return_value = mocked
|
|
||||||
yield model
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_faceanalysis() -> Iterator[mock.Mock]:
|
|
||||||
with mock.patch("app.models.facial_recognition.FaceAnalysis") as model:
|
|
||||||
face_preds = [
|
|
||||||
SimpleNamespace( # this is so these fields can be accessed through dot notation
|
|
||||||
**{
|
|
||||||
"bbox": np.random.rand(4).astype(np.float32),
|
|
||||||
"kps": np.random.rand(5, 2).astype(np.float32),
|
|
||||||
"det_score": np.array([0.67]).astype(np.float32),
|
|
||||||
"normed_embedding": np.random.rand(512).astype(np.float32),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
SimpleNamespace(
|
|
||||||
**{
|
|
||||||
"bbox": np.random.rand(4).astype(np.float32),
|
|
||||||
"kps": np.random.rand(5, 2).astype(np.float32),
|
|
||||||
"det_score": np.array([0.4]).astype(np.float32),
|
|
||||||
"normed_embedding": np.random.rand(512).astype(np.float32),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
def get(image: np.ndarray[int, np.dtype[np.float32]], **kwargs: Any) -> list[SimpleNamespace]:
|
|
||||||
if not isinstance(image, np.ndarray):
|
|
||||||
raise TypeError
|
|
||||||
|
|
||||||
return face_preds
|
|
||||||
|
|
||||||
mocked = mock.Mock()
|
|
||||||
mocked.get = get
|
|
||||||
model.return_value = mocked
|
|
||||||
yield model
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_get_model() -> Iterator[mock.Mock]:
|
def mock_get_model() -> Iterator[mock.Mock]:
|
||||||
with mock.patch("app.models.cache.InferenceModel.from_model_type", autospec=True) as mocked:
|
with mock.patch("app.models.cache.InferenceModel.from_model_type", autospec=True) as mocked:
|
||||||
@@ -117,3 +32,8 @@ def mock_get_model() -> Iterator[mock.Mock]:
|
|||||||
def deployed_app() -> TestClient:
|
def deployed_app() -> TestClient:
|
||||||
init_state()
|
init_state()
|
||||||
return TestClient(app)
|
return TestClient(app)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def responses() -> dict[str, Any]:
|
||||||
|
return json.load(open("responses.json", "r"))
|
||||||
|
|||||||
@@ -1,62 +1,46 @@
|
|||||||
import os
|
import asyncio
|
||||||
from io import BytesIO
|
import threading
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
from zipfile import BadZipFile
|
||||||
|
|
||||||
import cv2
|
import orjson
|
||||||
import numpy as np
|
from fastapi import FastAPI, Form, HTTPException, UploadFile
|
||||||
import uvicorn
|
from fastapi.responses import ORJSONResponse
|
||||||
from fastapi import Body, Depends, FastAPI
|
from onnxruntime.capi.onnxruntime_pybind11_state import InvalidProtobuf, NoSuchFile # type: ignore
|
||||||
from PIL import Image
|
from starlette.formparsers import MultiPartParser
|
||||||
|
|
||||||
from .config import settings
|
from app.models.base import InferenceModel
|
||||||
from .models.base import InferenceModel
|
|
||||||
|
from .config import log, settings
|
||||||
from .models.cache import ModelCache
|
from .models.cache import ModelCache
|
||||||
from .schemas import (
|
from .schemas import (
|
||||||
EmbeddingResponse,
|
|
||||||
FaceResponse,
|
|
||||||
MessageResponse,
|
MessageResponse,
|
||||||
ModelType,
|
ModelType,
|
||||||
TagResponse,
|
|
||||||
TextModelRequest,
|
|
||||||
TextResponse,
|
TextResponse,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
MultiPartParser.max_file_size = 2**24 # spools to disk if payload is 16 MiB or larger
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
def init_state() -> None:
|
def init_state() -> None:
|
||||||
app.state.model_cache = ModelCache(ttl=settings.model_ttl, revalidate=settings.model_ttl > 0)
|
app.state.model_cache = ModelCache(ttl=settings.model_ttl, revalidate=settings.model_ttl > 0)
|
||||||
|
log.info(
|
||||||
|
(
|
||||||
async def load_models() -> None:
|
"Created in-memory cache with unloading "
|
||||||
models = [
|
f"{f'after {settings.model_ttl}s of inactivity' if settings.model_ttl > 0 else 'disabled'}."
|
||||||
(settings.classification_model, ModelType.IMAGE_CLASSIFICATION),
|
)
|
||||||
(settings.clip_image_model, ModelType.CLIP),
|
)
|
||||||
(settings.clip_text_model, ModelType.CLIP),
|
# asyncio is a huge bottleneck for performance, so we use a thread pool to run blocking code
|
||||||
(settings.facial_recognition_model, ModelType.FACIAL_RECOGNITION),
|
app.state.thread_pool = ThreadPoolExecutor(settings.request_threads) if settings.request_threads > 0 else None
|
||||||
]
|
app.state.locks = {model_type: threading.Lock() for model_type in ModelType}
|
||||||
|
log.info(f"Initialized request thread pool with {settings.request_threads} threads.")
|
||||||
# Get all models
|
|
||||||
for model_name, model_type in models:
|
|
||||||
if settings.eager_startup:
|
|
||||||
await app.state.model_cache.get(model_name, model_type)
|
|
||||||
else:
|
|
||||||
InferenceModel.from_model_type(model_type, model_name)
|
|
||||||
|
|
||||||
|
|
||||||
@app.on_event("startup")
|
@app.on_event("startup")
|
||||||
async def startup_event() -> None:
|
async def startup_event() -> None:
|
||||||
init_state()
|
init_state()
|
||||||
await load_models()
|
|
||||||
|
|
||||||
|
|
||||||
def dep_pil_image(byte_image: bytes = Body(...)) -> Image.Image:
|
|
||||||
return Image.open(BytesIO(byte_image))
|
|
||||||
|
|
||||||
|
|
||||||
def dep_cv_image(byte_image: bytes = Body(...)) -> cv2.Mat:
|
|
||||||
byte_image_np = np.frombuffer(byte_image, np.uint8)
|
|
||||||
return cv2.imdecode(byte_image_np, cv2.IMREAD_COLOR)
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/", response_model=MessageResponse)
|
@app.get("/", response_model=MessageResponse)
|
||||||
@@ -69,62 +53,63 @@ def ping() -> str:
|
|||||||
return "pong"
|
return "pong"
|
||||||
|
|
||||||
|
|
||||||
@app.post(
|
@app.post("/predict")
|
||||||
"/image-classifier/tag-image",
|
async def predict(
|
||||||
response_model=TagResponse,
|
model_name: str = Form(alias="modelName"),
|
||||||
status_code=200,
|
model_type: ModelType = Form(alias="modelType"),
|
||||||
)
|
options: str = Form(default="{}"),
|
||||||
async def image_classification(
|
text: str | None = Form(default=None),
|
||||||
image: Image.Image = Depends(dep_pil_image),
|
image: UploadFile | None = None,
|
||||||
) -> list[str]:
|
) -> Any:
|
||||||
model = await app.state.model_cache.get(settings.classification_model, ModelType.IMAGE_CLASSIFICATION)
|
if image is not None:
|
||||||
labels = model.predict(image)
|
inputs: str | bytes = await image.read()
|
||||||
return labels
|
elif text is not None:
|
||||||
|
inputs = text
|
||||||
|
else:
|
||||||
|
raise HTTPException(400, "Either image or text must be provided")
|
||||||
|
try:
|
||||||
|
kwargs = orjson.loads(options)
|
||||||
|
except orjson.JSONDecodeError:
|
||||||
|
raise HTTPException(400, f"Invalid options JSON: {options}")
|
||||||
|
|
||||||
|
model = await load(await app.state.model_cache.get(model_name, model_type, **kwargs))
|
||||||
|
model.configure(**kwargs)
|
||||||
|
outputs = await run(model, inputs)
|
||||||
|
return ORJSONResponse(outputs)
|
||||||
|
|
||||||
|
|
||||||
@app.post(
|
async def run(model: InferenceModel, inputs: Any) -> Any:
|
||||||
"/sentence-transformer/encode-image",
|
if app.state.thread_pool is None:
|
||||||
response_model=EmbeddingResponse,
|
return model.predict(inputs)
|
||||||
status_code=200,
|
|
||||||
)
|
return await asyncio.get_running_loop().run_in_executor(app.state.thread_pool, model.predict, inputs)
|
||||||
async def clip_encode_image(
|
|
||||||
image: Image.Image = Depends(dep_pil_image),
|
|
||||||
) -> list[float]:
|
|
||||||
model = await app.state.model_cache.get(settings.clip_image_model, ModelType.CLIP)
|
|
||||||
embedding = model.predict(image)
|
|
||||||
return embedding
|
|
||||||
|
|
||||||
|
|
||||||
@app.post(
|
async def load(model: InferenceModel) -> InferenceModel:
|
||||||
"/sentence-transformer/encode-text",
|
if model.loaded:
|
||||||
response_model=EmbeddingResponse,
|
return model
|
||||||
status_code=200,
|
|
||||||
)
|
|
||||||
async def clip_encode_text(payload: TextModelRequest) -> list[float]:
|
|
||||||
model = await app.state.model_cache.get(settings.clip_text_model, ModelType.CLIP)
|
|
||||||
embedding = model.predict(payload.text)
|
|
||||||
return embedding
|
|
||||||
|
|
||||||
|
def _load() -> None:
|
||||||
|
with app.state.locks[model.model_type]:
|
||||||
|
model.load()
|
||||||
|
|
||||||
@app.post(
|
loop = asyncio.get_running_loop()
|
||||||
"/facial-recognition/detect-faces",
|
try:
|
||||||
response_model=FaceResponse,
|
if app.state.thread_pool is None:
|
||||||
status_code=200,
|
model.load()
|
||||||
)
|
else:
|
||||||
async def facial_recognition(
|
await loop.run_in_executor(app.state.thread_pool, _load)
|
||||||
image: cv2.Mat = Depends(dep_cv_image),
|
return model
|
||||||
) -> list[dict[str, Any]]:
|
except (OSError, InvalidProtobuf, BadZipFile, NoSuchFile):
|
||||||
model = await app.state.model_cache.get(settings.facial_recognition_model, ModelType.FACIAL_RECOGNITION)
|
log.warn(
|
||||||
faces = model.predict(image)
|
(
|
||||||
return faces
|
f"Failed to load {model.model_type.replace('_', ' ')} model '{model.model_name}'."
|
||||||
|
"Clearing cache and retrying."
|
||||||
|
)
|
||||||
if __name__ == "__main__":
|
)
|
||||||
is_dev = os.getenv("NODE_ENV") == "development"
|
model.clear_cache()
|
||||||
uvicorn.run(
|
if app.state.thread_pool is None:
|
||||||
"app.main:app",
|
model.load()
|
||||||
host=settings.host,
|
else:
|
||||||
port=settings.port,
|
await loop.run_in_executor(app.state.thread_pool, _load)
|
||||||
reload=is_dev,
|
return model
|
||||||
workers=settings.workers,
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
from .clip import CLIPSTEncoder
|
from .clip import CLIPEncoder
|
||||||
from .facial_recognition import FaceRecognizer
|
from .facial_recognition import FaceRecognizer
|
||||||
from .image_classification import ImageClassifier
|
from .image_classification import ImageClassifier
|
||||||
|
|||||||
@@ -1,35 +1,89 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import pickle
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from shutil import rmtree
|
from shutil import rmtree
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from onnxruntime.capi.onnxruntime_pybind11_state import InvalidProtobuf # type: ignore
|
import onnxruntime as ort
|
||||||
|
|
||||||
from ..config import get_cache_dir
|
from ..config import get_cache_dir, log, settings
|
||||||
from ..schemas import ModelType
|
from ..schemas import ModelType
|
||||||
|
|
||||||
|
|
||||||
class InferenceModel(ABC):
|
class InferenceModel(ABC):
|
||||||
_model_type: ModelType
|
_model_type: ModelType
|
||||||
|
|
||||||
def __init__(self, model_name: str, cache_dir: Path | str | None = None, **model_kwargs: Any) -> None:
|
def __init__(
|
||||||
|
self,
|
||||||
|
model_name: str,
|
||||||
|
cache_dir: Path | str | None = None,
|
||||||
|
inter_op_num_threads: int = settings.model_inter_op_threads,
|
||||||
|
intra_op_num_threads: int = settings.model_intra_op_threads,
|
||||||
|
**model_kwargs: Any,
|
||||||
|
) -> None:
|
||||||
self.model_name = model_name
|
self.model_name = model_name
|
||||||
|
self.loaded = False
|
||||||
self._cache_dir = Path(cache_dir) if cache_dir is not None else get_cache_dir(model_name, self.model_type)
|
self._cache_dir = Path(cache_dir) if cache_dir is not None else get_cache_dir(model_name, self.model_type)
|
||||||
|
self.providers = model_kwargs.pop("providers", ["CPUExecutionProvider"])
|
||||||
|
# don't pre-allocate more memory than needed
|
||||||
|
self.provider_options = model_kwargs.pop(
|
||||||
|
"provider_options", [{"arena_extend_strategy": "kSameAsRequested"}] * len(self.providers)
|
||||||
|
)
|
||||||
|
log.debug(
|
||||||
|
(
|
||||||
|
f"Setting '{self.model_name}' execution providers to {self.providers}"
|
||||||
|
"in descending order of preference"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
log.debug(f"Setting execution provider options to {self.provider_options}")
|
||||||
|
self.sess_options = PicklableSessionOptions()
|
||||||
|
# avoid thread contention between models
|
||||||
|
if inter_op_num_threads > 1:
|
||||||
|
self.sess_options.execution_mode = ort.ExecutionMode.ORT_PARALLEL
|
||||||
|
|
||||||
try:
|
log.debug(f"Setting execution_mode to {self.sess_options.execution_mode.name}")
|
||||||
self.load(**model_kwargs)
|
log.debug(f"Setting inter_op_num_threads to {inter_op_num_threads}")
|
||||||
except (OSError, InvalidProtobuf):
|
log.debug(f"Setting intra_op_num_threads to {intra_op_num_threads}")
|
||||||
self.clear_cache()
|
self.sess_options.inter_op_num_threads = inter_op_num_threads
|
||||||
self.load(**model_kwargs)
|
self.sess_options.intra_op_num_threads = intra_op_num_threads
|
||||||
|
self.sess_options.enable_cpu_mem_arena = False
|
||||||
|
|
||||||
|
def download(self) -> None:
|
||||||
|
if not self.cached:
|
||||||
|
log.info(
|
||||||
|
(f"Downloading {self.model_type.replace('-', ' ')} model '{self.model_name}'." "This may take a while.")
|
||||||
|
)
|
||||||
|
self._download()
|
||||||
|
|
||||||
|
def load(self) -> None:
|
||||||
|
if self.loaded:
|
||||||
|
return
|
||||||
|
self.download()
|
||||||
|
log.info(f"Loading {self.model_type.replace('-', ' ')} model '{self.model_name}'")
|
||||||
|
self._load()
|
||||||
|
self.loaded = True
|
||||||
|
|
||||||
|
def predict(self, inputs: Any, **model_kwargs: Any) -> Any:
|
||||||
|
self.load()
|
||||||
|
if model_kwargs:
|
||||||
|
self.configure(**model_kwargs)
|
||||||
|
return self._predict(inputs)
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def load(self, **model_kwargs: Any) -> None:
|
def _predict(self, inputs: Any) -> Any:
|
||||||
|
...
|
||||||
|
|
||||||
|
def configure(self, **model_kwargs: Any) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def _download(self) -> None:
|
||||||
...
|
...
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def predict(self, inputs: Any) -> Any:
|
def _load(self) -> None:
|
||||||
...
|
...
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -44,6 +98,10 @@ class InferenceModel(ABC):
|
|||||||
def cache_dir(self, cache_dir: Path) -> None:
|
def cache_dir(self, cache_dir: Path) -> None:
|
||||||
self._cache_dir = cache_dir
|
self._cache_dir = cache_dir
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cached(self) -> bool:
|
||||||
|
return self.cache_dir.exists() and any(self.cache_dir.iterdir())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_model_type(cls, model_type: ModelType, model_name: str, **model_kwargs: Any) -> InferenceModel:
|
def from_model_type(cls, model_type: ModelType, model_name: str, **model_kwargs: Any) -> InferenceModel:
|
||||||
subclasses = {subclass._model_type: subclass for subclass in cls.__subclasses__()}
|
subclasses = {subclass._model_type: subclass for subclass in cls.__subclasses__()}
|
||||||
@@ -54,8 +112,33 @@ class InferenceModel(ABC):
|
|||||||
|
|
||||||
def clear_cache(self) -> None:
|
def clear_cache(self) -> None:
|
||||||
if not self.cache_dir.exists():
|
if not self.cache_dir.exists():
|
||||||
|
log.warn(
|
||||||
|
f"Attempted to clear cache for model '{self.model_name}' but cache directory does not exist.",
|
||||||
|
)
|
||||||
return
|
return
|
||||||
elif not rmtree.avoids_symlink_attacks:
|
if not rmtree.avoids_symlink_attacks:
|
||||||
raise RuntimeError("Attempted to clear cache, but rmtree is not safe on this platform.")
|
raise RuntimeError("Attempted to clear cache, but rmtree is not safe on this platform.")
|
||||||
|
|
||||||
rmtree(self.cache_dir)
|
if self.cache_dir.is_dir():
|
||||||
|
log.info(f"Cleared cache directory for model '{self.model_name}'.")
|
||||||
|
rmtree(self.cache_dir)
|
||||||
|
else:
|
||||||
|
log.warn(
|
||||||
|
(
|
||||||
|
f"Encountered file instead of directory at cache path "
|
||||||
|
f"for '{self.model_name}'. Removing file and replacing with a directory."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
self.cache_dir.unlink()
|
||||||
|
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
# HF deep copies configs, so we need to make session options picklable
|
||||||
|
class PicklableSessionOptions(ort.SessionOptions):
|
||||||
|
def __getstate__(self) -> bytes:
|
||||||
|
return pickle.dumps([(attr, getattr(self, attr)) for attr in dir(self) if not callable(getattr(self, attr))])
|
||||||
|
|
||||||
|
def __setstate__(self, state: Any) -> None:
|
||||||
|
self.__init__() # type: ignore
|
||||||
|
for attr, val in pickle.loads(state):
|
||||||
|
setattr(self, attr, val)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class ModelCache:
|
|||||||
revalidate: bool = False,
|
revalidate: bool = False,
|
||||||
timeout: int | None = None,
|
timeout: int | None = None,
|
||||||
profiling: bool = False,
|
profiling: bool = False,
|
||||||
):
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
ttl: Unloads model after this duration. Disabled if None. Defaults to None.
|
ttl: Unloads model after this duration. Disabled if None. Defaults to None.
|
||||||
@@ -46,7 +46,7 @@ class ModelCache:
|
|||||||
model: The requested model.
|
model: The requested model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = self.cache.build_key(model_name, model_type.value)
|
key = f"{model_name}{model_type.value}{model_kwargs.get('mode', '')}"
|
||||||
async with OptimisticLock(self.cache, key) as lock:
|
async with OptimisticLock(self.cache, key) as lock:
|
||||||
model = await self.cache.get(key)
|
model = await self.cache.get(key)
|
||||||
if model is None:
|
if model is None:
|
||||||
|
|||||||
@@ -1,22 +1,154 @@
|
|||||||
from pathlib import Path
|
import os
|
||||||
from typing import Any
|
import zipfile
|
||||||
|
from io import BytesIO
|
||||||
|
from typing import Any, Literal
|
||||||
|
|
||||||
from PIL.Image import Image
|
import onnxruntime as ort
|
||||||
from sentence_transformers import SentenceTransformer
|
import torch
|
||||||
|
from clip_server.model.clip import BICUBIC, _convert_image_to_rgb
|
||||||
|
from clip_server.model.clip_onnx import _MODELS, _S3_BUCKET_V2, CLIPOnnxModel, download_model
|
||||||
|
from clip_server.model.pretrained_models import _VISUAL_MODEL_IMAGE_SIZE
|
||||||
|
from clip_server.model.tokenization import Tokenizer
|
||||||
|
from PIL import Image
|
||||||
|
from torchvision.transforms import CenterCrop, Compose, Normalize, Resize, ToTensor
|
||||||
|
|
||||||
|
from ..config import log
|
||||||
from ..schemas import ModelType
|
from ..schemas import ModelType
|
||||||
from .base import InferenceModel
|
from .base import InferenceModel
|
||||||
|
|
||||||
|
_ST_TO_JINA_MODEL_NAME = {
|
||||||
|
"clip-ViT-B-16": "ViT-B-16::openai",
|
||||||
|
"clip-ViT-B-32": "ViT-B-32::openai",
|
||||||
|
"clip-ViT-B-32-multilingual-v1": "M-CLIP/XLM-Roberta-Large-Vit-B-32",
|
||||||
|
"clip-ViT-L-14": "ViT-L-14::openai",
|
||||||
|
}
|
||||||
|
|
||||||
class CLIPSTEncoder(InferenceModel):
|
|
||||||
|
class CLIPEncoder(InferenceModel):
|
||||||
_model_type = ModelType.CLIP
|
_model_type = ModelType.CLIP
|
||||||
|
|
||||||
def load(self, **model_kwargs: Any) -> None:
|
def __init__(
|
||||||
self.model = SentenceTransformer(
|
self,
|
||||||
self.model_name,
|
model_name: str,
|
||||||
cache_folder=self.cache_dir.as_posix(),
|
cache_dir: str | None = None,
|
||||||
**model_kwargs,
|
mode: Literal["text", "vision"] | None = None,
|
||||||
)
|
**model_kwargs: Any,
|
||||||
|
) -> None:
|
||||||
|
if mode is not None and mode not in ("text", "vision"):
|
||||||
|
raise ValueError(f"Mode must be 'text', 'vision', or omitted; got '{mode}'")
|
||||||
|
if "vit-b" not in model_name.lower():
|
||||||
|
raise ValueError(f"Only ViT-B models are currently supported; got '{model_name}'")
|
||||||
|
self.mode = mode
|
||||||
|
jina_model_name = self._get_jina_model_name(model_name)
|
||||||
|
super().__init__(jina_model_name, cache_dir, **model_kwargs)
|
||||||
|
|
||||||
def predict(self, image_or_text: Image | str) -> list[float]:
|
def _download(self) -> None:
|
||||||
return self.model.encode(image_or_text).tolist()
|
models: tuple[tuple[str, str], tuple[str, str]] = _MODELS[self.model_name]
|
||||||
|
text_onnx_path = self.cache_dir / "textual.onnx"
|
||||||
|
vision_onnx_path = self.cache_dir / "visual.onnx"
|
||||||
|
|
||||||
|
if not text_onnx_path.is_file():
|
||||||
|
self._download_model(*models[0])
|
||||||
|
|
||||||
|
if not vision_onnx_path.is_file():
|
||||||
|
self._download_model(*models[1])
|
||||||
|
|
||||||
|
def _load(self) -> None:
|
||||||
|
if self.mode == "text" or self.mode is None:
|
||||||
|
log.debug(f"Loading clip text model '{self.model_name}'")
|
||||||
|
self.text_model = ort.InferenceSession(
|
||||||
|
self.cache_dir / "textual.onnx",
|
||||||
|
sess_options=self.sess_options,
|
||||||
|
providers=self.providers,
|
||||||
|
provider_options=self.provider_options,
|
||||||
|
)
|
||||||
|
self.text_outputs = [output.name for output in self.text_model.get_outputs()]
|
||||||
|
self.tokenizer = Tokenizer(self.model_name)
|
||||||
|
|
||||||
|
if self.mode == "vision" or self.mode is None:
|
||||||
|
log.debug(f"Loading clip vision model '{self.model_name}'")
|
||||||
|
self.vision_model = ort.InferenceSession(
|
||||||
|
self.cache_dir / "visual.onnx",
|
||||||
|
sess_options=self.sess_options,
|
||||||
|
providers=self.providers,
|
||||||
|
provider_options=self.provider_options,
|
||||||
|
)
|
||||||
|
self.vision_outputs = [output.name for output in self.vision_model.get_outputs()]
|
||||||
|
|
||||||
|
image_size = _VISUAL_MODEL_IMAGE_SIZE[CLIPOnnxModel.get_model_name(self.model_name)]
|
||||||
|
self.transform = _transform_pil_image(image_size)
|
||||||
|
|
||||||
|
def _predict(self, image_or_text: Image.Image | str) -> list[float]:
|
||||||
|
if isinstance(image_or_text, bytes):
|
||||||
|
image_or_text = Image.open(BytesIO(image_or_text))
|
||||||
|
|
||||||
|
match image_or_text:
|
||||||
|
case Image.Image():
|
||||||
|
if self.mode == "text":
|
||||||
|
raise TypeError("Cannot encode image as text-only model")
|
||||||
|
pixel_values = self.transform(image_or_text)
|
||||||
|
assert isinstance(pixel_values, torch.Tensor)
|
||||||
|
pixel_values = torch.unsqueeze(pixel_values, 0).numpy()
|
||||||
|
outputs = self.vision_model.run(self.vision_outputs, {"pixel_values": pixel_values})
|
||||||
|
case str():
|
||||||
|
if self.mode == "vision":
|
||||||
|
raise TypeError("Cannot encode text as vision-only model")
|
||||||
|
text_inputs: dict[str, torch.Tensor] = self.tokenizer(image_or_text)
|
||||||
|
inputs = {
|
||||||
|
"input_ids": text_inputs["input_ids"].int().numpy(),
|
||||||
|
"attention_mask": text_inputs["attention_mask"].int().numpy(),
|
||||||
|
}
|
||||||
|
outputs = self.text_model.run(self.text_outputs, inputs)
|
||||||
|
case _:
|
||||||
|
raise TypeError(f"Expected Image or str, but got: {type(image_or_text)}")
|
||||||
|
|
||||||
|
return outputs[0][0].tolist()
|
||||||
|
|
||||||
|
def _get_jina_model_name(self, model_name: str) -> str:
|
||||||
|
if model_name in _MODELS:
|
||||||
|
return model_name
|
||||||
|
elif model_name in _ST_TO_JINA_MODEL_NAME:
|
||||||
|
log.warn(
|
||||||
|
(
|
||||||
|
f"Sentence-Transformer models like '{model_name}' are not supported."
|
||||||
|
f"Using '{_ST_TO_JINA_MODEL_NAME[model_name]}' instead as it is the best match for '{model_name}'."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return _ST_TO_JINA_MODEL_NAME[model_name]
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unknown model name {model_name}.")
|
||||||
|
|
||||||
|
def _download_model(self, model_name: str, model_md5: str) -> bool:
|
||||||
|
# downloading logic is adapted from clip-server's CLIPOnnxModel class
|
||||||
|
download_model(
|
||||||
|
url=_S3_BUCKET_V2 + model_name,
|
||||||
|
target_folder=self.cache_dir.as_posix(),
|
||||||
|
md5sum=model_md5,
|
||||||
|
with_resume=True,
|
||||||
|
)
|
||||||
|
file = self.cache_dir / model_name.split("/")[1]
|
||||||
|
if file.suffix == ".zip":
|
||||||
|
with zipfile.ZipFile(file, "r") as zip_ref:
|
||||||
|
zip_ref.extractall(self.cache_dir)
|
||||||
|
os.remove(file)
|
||||||
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cached(self) -> bool:
|
||||||
|
return (self.cache_dir / "textual.onnx").is_file() and (self.cache_dir / "visual.onnx").is_file()
|
||||||
|
|
||||||
|
|
||||||
|
# same as `_transform_blob` without `_blob2image`
|
||||||
|
def _transform_pil_image(n_px: int) -> Compose:
|
||||||
|
return Compose(
|
||||||
|
[
|
||||||
|
Resize(n_px, interpolation=BICUBIC),
|
||||||
|
CenterCrop(n_px),
|
||||||
|
_convert_image_to_rgb,
|
||||||
|
ToTensor(),
|
||||||
|
Normalize(
|
||||||
|
(0.48145466, 0.4578275, 0.40821073),
|
||||||
|
(0.26862954, 0.26130258, 0.27577711),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
|
import zipfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
from insightface.app import FaceAnalysis
|
import numpy as np
|
||||||
|
import onnxruntime as ort
|
||||||
|
from insightface.model_zoo import ArcFaceONNX, RetinaFace
|
||||||
|
from insightface.utils.face_align import norm_crop
|
||||||
|
from insightface.utils.storage import BASE_REPO_URL, download_file
|
||||||
|
|
||||||
from ..config import settings
|
|
||||||
from ..schemas import ModelType
|
from ..schemas import ModelType
|
||||||
from .base import InferenceModel
|
from .base import InferenceModel
|
||||||
|
|
||||||
@@ -15,46 +19,90 @@ class FaceRecognizer(InferenceModel):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
model_name: str,
|
model_name: str,
|
||||||
min_score: float = settings.min_face_score,
|
min_score: float = 0.7,
|
||||||
cache_dir: Path | str | None = None,
|
cache_dir: Path | str | None = None,
|
||||||
**model_kwargs: Any,
|
**model_kwargs: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.min_score = min_score
|
self.min_score = model_kwargs.pop("minScore", min_score)
|
||||||
super().__init__(model_name, cache_dir, **model_kwargs)
|
super().__init__(model_name, cache_dir, **model_kwargs)
|
||||||
|
|
||||||
def load(self, **model_kwargs: Any) -> None:
|
def _download(self) -> None:
|
||||||
self.model = FaceAnalysis(
|
zip_file = self.cache_dir / f"{self.model_name}.zip"
|
||||||
name=self.model_name,
|
download_file(f"{BASE_REPO_URL}/{self.model_name}.zip", zip_file)
|
||||||
root=self.cache_dir.as_posix(),
|
with zipfile.ZipFile(zip_file, "r") as zip:
|
||||||
allowed_modules=["detection", "recognition"],
|
members = zip.namelist()
|
||||||
**model_kwargs,
|
det_file = next(model for model in members if model.startswith("det_"))
|
||||||
|
rec_file = next(model for model in members if model.startswith("w600k_"))
|
||||||
|
zip.extractall(self.cache_dir, members=[det_file, rec_file])
|
||||||
|
zip_file.unlink()
|
||||||
|
|
||||||
|
def _load(self) -> None:
|
||||||
|
try:
|
||||||
|
det_file = next(self.cache_dir.glob("det_*.onnx"))
|
||||||
|
rec_file = next(self.cache_dir.glob("w600k_*.onnx"))
|
||||||
|
except StopIteration:
|
||||||
|
raise FileNotFoundError("Facial recognition models not found in cache directory")
|
||||||
|
|
||||||
|
self.det_model = RetinaFace(
|
||||||
|
session=ort.InferenceSession(
|
||||||
|
det_file.as_posix(),
|
||||||
|
sess_options=self.sess_options,
|
||||||
|
providers=self.providers,
|
||||||
|
provider_options=self.provider_options,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self.model.prepare(
|
self.rec_model = ArcFaceONNX(
|
||||||
|
rec_file.as_posix(),
|
||||||
|
session=ort.InferenceSession(
|
||||||
|
rec_file.as_posix(),
|
||||||
|
sess_options=self.sess_options,
|
||||||
|
providers=self.providers,
|
||||||
|
provider_options=self.provider_options,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.det_model.prepare(
|
||||||
ctx_id=0,
|
ctx_id=0,
|
||||||
det_thresh=self.min_score,
|
det_thresh=self.min_score,
|
||||||
det_size=(640, 640),
|
input_size=(640, 640),
|
||||||
)
|
)
|
||||||
|
self.rec_model.prepare(ctx_id=0)
|
||||||
|
|
||||||
|
def _predict(self, image: np.ndarray[int, np.dtype[Any]] | bytes) -> list[dict[str, Any]]:
|
||||||
|
if isinstance(image, bytes):
|
||||||
|
image = cv2.imdecode(np.frombuffer(image, np.uint8), cv2.IMREAD_COLOR)
|
||||||
|
bboxes, kpss = self.det_model.detect(image)
|
||||||
|
if bboxes.size == 0:
|
||||||
|
return []
|
||||||
|
assert isinstance(image, np.ndarray) and isinstance(kpss, np.ndarray)
|
||||||
|
|
||||||
|
scores = bboxes[:, 4].tolist()
|
||||||
|
bboxes = bboxes[:, :4].round().tolist()
|
||||||
|
|
||||||
def predict(self, image: cv2.Mat) -> list[dict[str, Any]]:
|
|
||||||
height, width, _ = image.shape
|
|
||||||
results = []
|
results = []
|
||||||
faces = self.model.get(image)
|
height, width, _ = image.shape
|
||||||
|
for (x1, y1, x2, y2), score, kps in zip(bboxes, scores, kpss):
|
||||||
for face in faces:
|
cropped_img = norm_crop(image, kps)
|
||||||
x1, y1, x2, y2 = face.bbox
|
embedding = self.rec_model.get_feat(cropped_img)[0].tolist()
|
||||||
|
|
||||||
results.append(
|
results.append(
|
||||||
{
|
{
|
||||||
"imageWidth": width,
|
"imageWidth": width,
|
||||||
"imageHeight": height,
|
"imageHeight": height,
|
||||||
"boundingBox": {
|
"boundingBox": {
|
||||||
"x1": round(x1),
|
"x1": x1,
|
||||||
"y1": round(y1),
|
"y1": y1,
|
||||||
"x2": round(x2),
|
"x2": x2,
|
||||||
"y2": round(y2),
|
"y2": y2,
|
||||||
},
|
},
|
||||||
"score": face.det_score.item(),
|
"score": score,
|
||||||
"embedding": face.normed_embedding.tolist(),
|
"embedding": embedding,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cached(self) -> bool:
|
||||||
|
return self.cache_dir.is_dir() and any(self.cache_dir.glob("*.onnx"))
|
||||||
|
|
||||||
|
def configure(self, **model_kwargs: Any) -> None:
|
||||||
|
self.det_model.det_thresh = model_kwargs.pop("minScore", self.det_model.det_thresh)
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
|
from io import BytesIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from PIL.Image import Image
|
from huggingface_hub import snapshot_download
|
||||||
from transformers.pipelines import pipeline
|
from optimum.onnxruntime import ORTModelForImageClassification
|
||||||
|
from optimum.pipelines import pipeline
|
||||||
|
from PIL import Image
|
||||||
|
from transformers import AutoImageProcessor
|
||||||
|
|
||||||
from ..config import settings
|
from ..config import log
|
||||||
from ..schemas import ModelType
|
from ..schemas import ModelType
|
||||||
from .base import InferenceModel
|
from .base import InferenceModel
|
||||||
|
|
||||||
@@ -15,22 +19,57 @@ class ImageClassifier(InferenceModel):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
model_name: str,
|
model_name: str,
|
||||||
min_score: float = settings.min_tag_score,
|
min_score: float = 0.9,
|
||||||
cache_dir: Path | str | None = None,
|
cache_dir: Path | str | None = None,
|
||||||
**model_kwargs: Any,
|
**model_kwargs: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.min_score = min_score
|
self.min_score = model_kwargs.pop("minScore", min_score)
|
||||||
super().__init__(model_name, cache_dir, **model_kwargs)
|
super().__init__(model_name, cache_dir, **model_kwargs)
|
||||||
|
|
||||||
def load(self, **model_kwargs: Any) -> None:
|
def _download(self) -> None:
|
||||||
self.model = pipeline(
|
snapshot_download(
|
||||||
self.model_type.value,
|
cache_dir=self.cache_dir,
|
||||||
self.model_name,
|
repo_id=self.model_name,
|
||||||
model_kwargs={"cache_dir": self.cache_dir, **model_kwargs},
|
allow_patterns=["*.bin", "*.json", "*.txt"],
|
||||||
|
local_dir=self.cache_dir,
|
||||||
|
local_dir_use_symlinks=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def predict(self, image: Image) -> list[str]:
|
def _load(self) -> None:
|
||||||
|
processor = AutoImageProcessor.from_pretrained(self.cache_dir, cache_dir=self.cache_dir)
|
||||||
|
model_path = self.cache_dir / "model.onnx"
|
||||||
|
model_kwargs = {
|
||||||
|
"cache_dir": self.cache_dir,
|
||||||
|
"provider": self.providers[0],
|
||||||
|
"provider_options": self.provider_options[0],
|
||||||
|
"session_options": self.sess_options,
|
||||||
|
}
|
||||||
|
|
||||||
|
if model_path.exists():
|
||||||
|
model = ORTModelForImageClassification.from_pretrained(self.cache_dir, **model_kwargs)
|
||||||
|
self.model = pipeline(self.model_type.value, model, feature_extractor=processor)
|
||||||
|
else:
|
||||||
|
log.info(
|
||||||
|
(
|
||||||
|
f"ONNX model not found in cache directory for '{self.model_name}'."
|
||||||
|
"Exporting optimized model for future use."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
self.sess_options.optimized_model_filepath = model_path.as_posix()
|
||||||
|
self.model = pipeline(
|
||||||
|
self.model_type.value,
|
||||||
|
self.model_name,
|
||||||
|
model_kwargs=model_kwargs,
|
||||||
|
feature_extractor=processor,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _predict(self, image: Image.Image | bytes) -> list[str]:
|
||||||
|
if isinstance(image, bytes):
|
||||||
|
image = Image.open(BytesIO(image))
|
||||||
predictions: list[dict[str, Any]] = self.model(image) # type: ignore
|
predictions: list[dict[str, Any]] = self.model(image) # type: ignore
|
||||||
tags = [tag for pred in predictions for tag in pred["label"].split(", ") if pred["score"] >= self.min_score]
|
tags = [tag for pred in predictions for tag in pred["label"].split(", ") if pred["score"] >= self.min_score]
|
||||||
|
|
||||||
return tags
|
return tags
|
||||||
|
|
||||||
|
def configure(self, **model_kwargs: Any) -> None:
|
||||||
|
self.min_score = model_kwargs.pop("minScore", self.min_score)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
@@ -20,18 +20,6 @@ class MessageResponse(BaseModel):
|
|||||||
message: str
|
message: str
|
||||||
|
|
||||||
|
|
||||||
class TagResponse(BaseModel):
|
|
||||||
__root__: list[str]
|
|
||||||
|
|
||||||
|
|
||||||
class Embedding(BaseModel):
|
|
||||||
__root__: list[float]
|
|
||||||
|
|
||||||
|
|
||||||
class EmbeddingResponse(BaseModel):
|
|
||||||
__root__: Embedding
|
|
||||||
|
|
||||||
|
|
||||||
class BoundingBox(BaseModel):
|
class BoundingBox(BaseModel):
|
||||||
x1: int
|
x1: int
|
||||||
y1: int
|
y1: int
|
||||||
@@ -39,23 +27,7 @@ class BoundingBox(BaseModel):
|
|||||||
y2: int
|
y2: int
|
||||||
|
|
||||||
|
|
||||||
class Face(BaseModel):
|
class ModelType(StrEnum):
|
||||||
image_width: int
|
|
||||||
image_height: int
|
|
||||||
bounding_box: BoundingBox
|
|
||||||
score: float
|
|
||||||
embedding: Embedding
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
alias_generator = to_lower_camel
|
|
||||||
allow_population_by_field_name = True
|
|
||||||
|
|
||||||
|
|
||||||
class FaceResponse(BaseModel):
|
|
||||||
__root__: list[Face]
|
|
||||||
|
|
||||||
|
|
||||||
class ModelType(Enum):
|
|
||||||
IMAGE_CLASSIFICATION = "image-classification"
|
IMAGE_CLASSIFICATION = "image-classification"
|
||||||
CLIP = "clip"
|
CLIP = "clip"
|
||||||
FACIAL_RECOGNITION = "facial-recognition"
|
FACIAL_RECOGNITION = "facial-recognition"
|
||||||
|
|||||||
@@ -1,35 +1,44 @@
|
|||||||
|
import json
|
||||||
|
import pickle
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from pathlib import Path
|
from typing import Any, TypeAlias
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
|
import numpy as np
|
||||||
import pytest
|
import pytest
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
from pytest_mock import MockerFixture
|
||||||
|
|
||||||
from .config import settings
|
from .config import settings
|
||||||
|
from .models.base import PicklableSessionOptions
|
||||||
from .models.cache import ModelCache
|
from .models.cache import ModelCache
|
||||||
from .models.clip import CLIPSTEncoder
|
from .models.clip import CLIPEncoder
|
||||||
from .models.facial_recognition import FaceRecognizer
|
from .models.facial_recognition import FaceRecognizer
|
||||||
from .models.image_classification import ImageClassifier
|
from .models.image_classification import ImageClassifier
|
||||||
from .schemas import ModelType
|
from .schemas import ModelType
|
||||||
|
|
||||||
|
ndarray: TypeAlias = np.ndarray[int, np.dtype[np.float32]]
|
||||||
|
|
||||||
|
|
||||||
class TestImageClassifier:
|
class TestImageClassifier:
|
||||||
def test_init(self, mock_classifier_pipeline: mock.Mock) -> None:
|
classifier_preds = [
|
||||||
cache_dir = Path("test_cache")
|
{"label": "that's an image alright", "score": 0.8},
|
||||||
classifier = ImageClassifier("test_model_name", 0.5, cache_dir=cache_dir)
|
{"label": "well it ends with .jpg", "score": 0.1},
|
||||||
|
{"label": "idk, im just seeing bytes", "score": 0.05},
|
||||||
|
{"label": "not sure", "score": 0.04},
|
||||||
|
{"label": "probably a virus", "score": 0.01},
|
||||||
|
]
|
||||||
|
|
||||||
assert classifier.min_score == 0.5
|
def test_min_score(self, pil_image: Image.Image, mocker: MockerFixture) -> None:
|
||||||
mock_classifier_pipeline.assert_called_once_with(
|
mocker.patch.object(ImageClassifier, "load")
|
||||||
"image-classification",
|
|
||||||
"test_model_name",
|
|
||||||
model_kwargs={"cache_dir": cache_dir},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_min_score(self, pil_image: Image.Image, mock_classifier_pipeline: mock.Mock) -> None:
|
|
||||||
classifier = ImageClassifier("test_model_name", min_score=0.0)
|
classifier = ImageClassifier("test_model_name", min_score=0.0)
|
||||||
classifier.min_score = 0.0
|
assert classifier.min_score == 0.0
|
||||||
|
|
||||||
|
classifier.model = mock.Mock()
|
||||||
|
classifier.model.return_value = self.classifier_preds
|
||||||
|
|
||||||
all_labels = classifier.predict(pil_image)
|
all_labels = classifier.predict(pil_image)
|
||||||
classifier.min_score = 0.5
|
classifier.min_score = 0.5
|
||||||
filtered_labels = classifier.predict(pil_image)
|
filtered_labels = classifier.predict(pil_image)
|
||||||
@@ -46,45 +55,62 @@ class TestImageClassifier:
|
|||||||
|
|
||||||
|
|
||||||
class TestCLIP:
|
class TestCLIP:
|
||||||
def test_init(self, mock_st: mock.Mock) -> None:
|
embedding = np.random.rand(512).astype(np.float32)
|
||||||
CLIPSTEncoder("test_model_name", cache_dir="test_cache")
|
|
||||||
|
|
||||||
mock_st.assert_called_once_with("test_model_name", cache_folder="test_cache")
|
def test_basic_image(self, pil_image: Image.Image, mocker: MockerFixture) -> None:
|
||||||
|
mocker.patch.object(CLIPEncoder, "download")
|
||||||
def test_basic_image(self, pil_image: Image.Image, mock_st: mock.Mock) -> None:
|
mocked = mocker.patch("app.models.clip.ort.InferenceSession", autospec=True)
|
||||||
clip_encoder = CLIPSTEncoder("test_model_name", cache_dir="test_cache")
|
mocked.return_value.run.return_value = [[self.embedding]]
|
||||||
|
clip_encoder = CLIPEncoder("ViT-B-32::openai", cache_dir="test_cache", mode="vision")
|
||||||
|
assert clip_encoder.mode == "vision"
|
||||||
embedding = clip_encoder.predict(pil_image)
|
embedding = clip_encoder.predict(pil_image)
|
||||||
|
|
||||||
assert isinstance(embedding, list)
|
assert isinstance(embedding, list)
|
||||||
assert len(embedding) == 512
|
assert len(embedding) == 512
|
||||||
assert all([isinstance(num, float) for num in embedding])
|
assert all([isinstance(num, float) for num in embedding])
|
||||||
mock_st.assert_called_once()
|
clip_encoder.vision_model.run.assert_called_once()
|
||||||
|
|
||||||
def test_basic_text(self, mock_st: mock.Mock) -> None:
|
def test_basic_text(self, mocker: MockerFixture) -> None:
|
||||||
clip_encoder = CLIPSTEncoder("test_model_name", cache_dir="test_cache")
|
mocker.patch.object(CLIPEncoder, "download")
|
||||||
|
mocked = mocker.patch("app.models.clip.ort.InferenceSession", autospec=True)
|
||||||
|
mocked.return_value.run.return_value = [[self.embedding]]
|
||||||
|
clip_encoder = CLIPEncoder("ViT-B-32::openai", cache_dir="test_cache", mode="text")
|
||||||
|
assert clip_encoder.mode == "text"
|
||||||
embedding = clip_encoder.predict("test search query")
|
embedding = clip_encoder.predict("test search query")
|
||||||
|
|
||||||
assert isinstance(embedding, list)
|
assert isinstance(embedding, list)
|
||||||
assert len(embedding) == 512
|
assert len(embedding) == 512
|
||||||
assert all([isinstance(num, float) for num in embedding])
|
assert all([isinstance(num, float) for num in embedding])
|
||||||
mock_st.assert_called_once()
|
clip_encoder.text_model.run.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
class TestFaceRecognition:
|
class TestFaceRecognition:
|
||||||
def test_init(self, mock_faceanalysis: mock.Mock) -> None:
|
def test_set_min_score(self, mocker: MockerFixture) -> None:
|
||||||
FaceRecognizer("test_model_name", cache_dir="test_cache")
|
mocker.patch.object(FaceRecognizer, "load")
|
||||||
|
face_recognizer = FaceRecognizer("test_model_name", cache_dir="test_cache", min_score=0.5)
|
||||||
|
|
||||||
mock_faceanalysis.assert_called_once_with(
|
assert face_recognizer.min_score == 0.5
|
||||||
name="test_model_name",
|
|
||||||
root="test_cache",
|
|
||||||
allowed_modules=["detection", "recognition"],
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_basic(self, cv_image: cv2.Mat, mock_faceanalysis: mock.Mock) -> None:
|
def test_basic(self, cv_image: cv2.Mat, mocker: MockerFixture) -> None:
|
||||||
|
mocker.patch.object(FaceRecognizer, "load")
|
||||||
face_recognizer = FaceRecognizer("test_model_name", min_score=0.0, cache_dir="test_cache")
|
face_recognizer = FaceRecognizer("test_model_name", min_score=0.0, cache_dir="test_cache")
|
||||||
|
|
||||||
|
det_model = mock.Mock()
|
||||||
|
num_faces = 2
|
||||||
|
bbox = np.random.rand(num_faces, 4).astype(np.float32)
|
||||||
|
score = np.array([[0.67]] * num_faces).astype(np.float32)
|
||||||
|
kpss = np.random.rand(num_faces, 5, 2).astype(np.float32)
|
||||||
|
det_model.detect.return_value = (np.concatenate([bbox, score], axis=-1), kpss)
|
||||||
|
face_recognizer.det_model = det_model
|
||||||
|
|
||||||
|
rec_model = mock.Mock()
|
||||||
|
embedding = np.random.rand(num_faces, 512).astype(np.float32)
|
||||||
|
rec_model.get_feat.return_value = embedding
|
||||||
|
face_recognizer.rec_model = rec_model
|
||||||
|
|
||||||
faces = face_recognizer.predict(cv_image)
|
faces = face_recognizer.predict(cv_image)
|
||||||
|
|
||||||
assert len(faces) == 2
|
assert len(faces) == num_faces
|
||||||
for face in faces:
|
for face in faces:
|
||||||
assert face["imageHeight"] == 800
|
assert face["imageHeight"] == 800
|
||||||
assert face["imageWidth"] == 600
|
assert face["imageWidth"] == 600
|
||||||
@@ -92,7 +118,8 @@ class TestFaceRecognition:
|
|||||||
assert len(face["embedding"]) == 512
|
assert len(face["embedding"]) == 512
|
||||||
assert all([isinstance(num, float) for num in face["embedding"]])
|
assert all([isinstance(num, float) for num in face["embedding"]])
|
||||||
|
|
||||||
mock_faceanalysis.assert_called_once()
|
det_model.detect.assert_called_once()
|
||||||
|
assert rec_model.get_feat.call_count == num_faces
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -142,42 +169,71 @@ class TestCache:
|
|||||||
reason="More time-consuming since it deploys the app and loads models.",
|
reason="More time-consuming since it deploys the app and loads models.",
|
||||||
)
|
)
|
||||||
class TestEndpoints:
|
class TestEndpoints:
|
||||||
def test_tagging_endpoint(self, pil_image: Image.Image, deployed_app: TestClient) -> None:
|
def test_tagging_endpoint(
|
||||||
|
self, pil_image: Image.Image, responses: dict[str, Any], deployed_app: TestClient
|
||||||
|
) -> None:
|
||||||
byte_image = BytesIO()
|
byte_image = BytesIO()
|
||||||
pil_image.save(byte_image, format="jpeg")
|
pil_image.save(byte_image, format="jpeg")
|
||||||
headers = {"Content-Type": "image/jpg"}
|
|
||||||
response = deployed_app.post(
|
response = deployed_app.post(
|
||||||
"http://localhost:3003/image-classifier/tag-image",
|
"http://localhost:3003/predict",
|
||||||
content=byte_image.getvalue(),
|
data={
|
||||||
headers=headers,
|
"modelName": "microsoft/resnet-50",
|
||||||
|
"modelType": "image-classification",
|
||||||
|
"options": json.dumps({"minScore": 0.0}),
|
||||||
|
},
|
||||||
|
files={"image": byte_image.getvalue()},
|
||||||
)
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
assert response.json() == responses["image-classification"]
|
||||||
|
|
||||||
def test_clip_image_endpoint(self, pil_image: Image.Image, deployed_app: TestClient) -> None:
|
def test_clip_image_endpoint(
|
||||||
|
self, pil_image: Image.Image, responses: dict[str, Any], deployed_app: TestClient
|
||||||
|
) -> None:
|
||||||
byte_image = BytesIO()
|
byte_image = BytesIO()
|
||||||
pil_image.save(byte_image, format="jpeg")
|
pil_image.save(byte_image, format="jpeg")
|
||||||
headers = {"Content-Type": "image/jpg"}
|
|
||||||
response = deployed_app.post(
|
response = deployed_app.post(
|
||||||
"http://localhost:3003/sentence-transformer/encode-image",
|
"http://localhost:3003/predict",
|
||||||
content=byte_image.getvalue(),
|
data={"modelName": "ViT-B-32::openai", "modelType": "clip", "options": json.dumps({"mode": "vision"})},
|
||||||
headers=headers,
|
files={"image": byte_image.getvalue()},
|
||||||
)
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
assert response.json() == responses["clip"]["image"]
|
||||||
|
|
||||||
def test_clip_text_endpoint(self, deployed_app: TestClient) -> None:
|
def test_clip_text_endpoint(self, responses: dict[str, Any], deployed_app: TestClient) -> None:
|
||||||
response = deployed_app.post(
|
response = deployed_app.post(
|
||||||
"http://localhost:3003/sentence-transformer/encode-text",
|
"http://localhost:3003/predict",
|
||||||
json={"text": "test search query"},
|
data={
|
||||||
|
"modelName": "ViT-B-32::openai",
|
||||||
|
"modelType": "clip",
|
||||||
|
"text": "test search query",
|
||||||
|
"options": json.dumps({"mode": "text"}),
|
||||||
|
},
|
||||||
)
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
assert response.json() == responses["clip"]["text"]
|
||||||
|
|
||||||
def test_face_endpoint(self, pil_image: Image.Image, deployed_app: TestClient) -> None:
|
def test_face_endpoint(self, pil_image: Image.Image, responses: dict[str, Any], deployed_app: TestClient) -> None:
|
||||||
byte_image = BytesIO()
|
byte_image = BytesIO()
|
||||||
pil_image.save(byte_image, format="jpeg")
|
pil_image.save(byte_image, format="jpeg")
|
||||||
headers = {"Content-Type": "image/jpg"}
|
|
||||||
response = deployed_app.post(
|
response = deployed_app.post(
|
||||||
"http://localhost:3003/facial-recognition/detect-faces",
|
"http://localhost:3003/predict",
|
||||||
content=byte_image.getvalue(),
|
data={
|
||||||
headers=headers,
|
"modelName": "buffalo_l",
|
||||||
|
"modelType": "facial-recognition",
|
||||||
|
"options": json.dumps({"minScore": 0.034}),
|
||||||
|
},
|
||||||
|
files={"image": byte_image.getvalue()},
|
||||||
)
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
assert response.json() == responses["facial-recognition"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_sess_options() -> None:
|
||||||
|
sess_options = PicklableSessionOptions()
|
||||||
|
sess_options.intra_op_num_threads = 1
|
||||||
|
sess_options.inter_op_num_threads = 1
|
||||||
|
pickled = pickle.dumps(sess_options)
|
||||||
|
unpickled = pickle.loads(pickled)
|
||||||
|
assert unpickled.intra_op_num_threads == 1
|
||||||
|
assert unpickled.inter_op_num_threads == 1
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
export MACHINE_LEARNING_CACHE_FOLDER=/tmp/model_cache
|
|
||||||
export MACHINE_LEARNING_MIN_FACE_SCORE=0.034 # returns 1 face per request; setting this to 0 blows up the number of faces to the thousands
|
|
||||||
export MACHINE_LEARNING_MIN_TAG_SCORE=0.0
|
|
||||||
export PID_FILE=/tmp/locust_pid
|
|
||||||
export LOG_FILE=/tmp/gunicorn.log
|
|
||||||
export HEADLESS=false
|
|
||||||
export HOST=127.0.0.1:3003
|
|
||||||
export CONCURRENCY=4
|
|
||||||
export NUM_ENDPOINTS=3
|
|
||||||
export PYTHONPATH=app
|
|
||||||
|
|
||||||
gunicorn app.main:app --worker-class uvicorn.workers.UvicornWorker \
|
|
||||||
--bind $HOST --daemon --error-logfile $LOG_FILE --pid $PID_FILE
|
|
||||||
while true ; do
|
|
||||||
echo "Loading models..."
|
|
||||||
sleep 5
|
|
||||||
if cat $LOG_FILE | grep -q -E "startup complete"; then break; fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# "users" are assigned only one task, so multiply concurrency by the number of tasks
|
|
||||||
locust --host http://$HOST --web-host 127.0.0.1 \
|
|
||||||
--run-time 120s --users $(($CONCURRENCY * $NUM_ENDPOINTS)) $(if $HEADLESS; then echo "--headless"; fi)
|
|
||||||
|
|
||||||
if [[ -e $PID_FILE ]]; then kill $(cat $PID_FILE); fi
|
|
||||||
@@ -1,13 +1,32 @@
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
import json
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from locust import HttpUser, events, task
|
from locust import HttpUser, events, task
|
||||||
|
from locust.env import Environment
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
byte_image = BytesIO()
|
||||||
|
|
||||||
|
|
||||||
|
@events.init_command_line_parser.add_listener
|
||||||
|
def _(parser: ArgumentParser) -> None:
|
||||||
|
parser.add_argument("--tag-model", type=str, default="microsoft/resnet-50")
|
||||||
|
parser.add_argument("--clip-model", type=str, default="ViT-B-32::openai")
|
||||||
|
parser.add_argument("--face-model", type=str, default="buffalo_l")
|
||||||
|
parser.add_argument("--tag-min-score", type=int, default=0.0,
|
||||||
|
help="Returns all tags at or above this score. The default returns all tags.")
|
||||||
|
parser.add_argument("--face-min-score", type=int, default=0.034,
|
||||||
|
help=("Returns all faces at or above this score. The default returns 1 face per request; "
|
||||||
|
"setting this to 0 blows up the number of faces to the thousands."))
|
||||||
|
parser.add_argument("--image-size", type=int, default=1000)
|
||||||
|
|
||||||
|
|
||||||
@events.test_start.add_listener
|
@events.test_start.add_listener
|
||||||
def on_test_start(environment, **kwargs):
|
def on_test_start(environment: Environment, **kwargs: Any) -> None:
|
||||||
global byte_image
|
global byte_image
|
||||||
image = Image.new("RGB", (1000, 1000))
|
assert environment.parsed_options is not None
|
||||||
|
image = Image.new("RGB", (environment.parsed_options.image_size, environment.parsed_options.image_size))
|
||||||
byte_image = BytesIO()
|
byte_image = BytesIO()
|
||||||
image.save(byte_image, format="jpeg")
|
image.save(byte_image, format="jpeg")
|
||||||
|
|
||||||
@@ -19,34 +38,55 @@ class InferenceLoadTest(HttpUser):
|
|||||||
headers: dict[str, str] = {"Content-Type": "image/jpg"}
|
headers: dict[str, str] = {"Content-Type": "image/jpg"}
|
||||||
|
|
||||||
# re-use the image across all instances in a process
|
# re-use the image across all instances in a process
|
||||||
def on_start(self):
|
def on_start(self) -> None:
|
||||||
global byte_image
|
global byte_image
|
||||||
self.data = byte_image.getvalue()
|
self.data = byte_image.getvalue()
|
||||||
|
|
||||||
|
|
||||||
class ClassificationLoadTest(InferenceLoadTest):
|
class ClassificationFormDataLoadTest(InferenceLoadTest):
|
||||||
@task
|
@task
|
||||||
def classify(self):
|
def classify(self) -> None:
|
||||||
self.client.post(
|
data = [
|
||||||
"/image-classifier/tag-image", data=self.data, headers=self.headers
|
("modelName", self.environment.parsed_options.clip_model),
|
||||||
)
|
("modelType", "clip"),
|
||||||
|
("options", json.dumps({"minScore": self.environment.parsed_options.tag_min_score})),
|
||||||
|
]
|
||||||
|
files = {"image": self.data}
|
||||||
|
self.client.post("/predict", data=data, files=files)
|
||||||
|
|
||||||
|
|
||||||
class CLIPLoadTest(InferenceLoadTest):
|
class CLIPTextFormDataLoadTest(InferenceLoadTest):
|
||||||
@task
|
@task
|
||||||
def encode_image(self):
|
def encode_text(self) -> None:
|
||||||
self.client.post(
|
data = [
|
||||||
"/sentence-transformer/encode-image",
|
("modelName", self.environment.parsed_options.clip_model),
|
||||||
data=self.data,
|
("modelType", "clip"),
|
||||||
headers=self.headers,
|
("options", json.dumps({"mode": "text"})),
|
||||||
)
|
("text", "test search query")
|
||||||
|
]
|
||||||
|
self.client.post("/predict", data=data)
|
||||||
|
|
||||||
|
|
||||||
class RecognitionLoadTest(InferenceLoadTest):
|
class CLIPVisionFormDataLoadTest(InferenceLoadTest):
|
||||||
@task
|
@task
|
||||||
def recognize(self):
|
def encode_image(self) -> None:
|
||||||
self.client.post(
|
data = [
|
||||||
"/facial-recognition/detect-faces",
|
("modelName", self.environment.parsed_options.clip_model),
|
||||||
data=self.data,
|
("modelType", "clip"),
|
||||||
headers=self.headers,
|
("options", json.dumps({"mode": "vision"})),
|
||||||
)
|
]
|
||||||
|
files = {"image": self.data}
|
||||||
|
self.client.post("/predict", data=data, files=files)
|
||||||
|
|
||||||
|
|
||||||
|
class RecognitionFormDataLoadTest(InferenceLoadTest):
|
||||||
|
@task
|
||||||
|
def recognize(self) -> None:
|
||||||
|
data = [
|
||||||
|
("modelName", self.environment.parsed_options.face_model),
|
||||||
|
("modelType", "facial-recognition"),
|
||||||
|
("options", json.dumps({"minScore": self.environment.parsed_options.face_min_score})),
|
||||||
|
]
|
||||||
|
files = {"image": self.data}
|
||||||
|
|
||||||
|
self.client.post("/predict", data=data, files=files)
|
||||||
|
|||||||
17
machine-learning/log_conf.json
Normal file
17
machine-learning/log_conf.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"disable_existing_loggers": true,
|
||||||
|
"formatters": { "rich": { "show_path": false, "omit_repeated_times": false } },
|
||||||
|
"handlers": {
|
||||||
|
"console": {
|
||||||
|
"class": "app.config.CustomRichHandler",
|
||||||
|
"formatter": "rich",
|
||||||
|
"level": "INFO"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"loggers": {
|
||||||
|
"gunicorn.access": { "propagate": true },
|
||||||
|
"gunicorn.error": { "propagate": true }
|
||||||
|
},
|
||||||
|
"root": { "handlers": ["console"] }
|
||||||
|
}
|
||||||
3431
machine-learning/poetry.lock
generated
3431
machine-learning/poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "machine-learning"
|
name = "machine-learning"
|
||||||
version = "1.68.0"
|
version = "1.79.0"
|
||||||
description = ""
|
description = ""
|
||||||
authors = ["Hau Tran <alex.tran1502@gmail.com>"]
|
authors = ["Hau Tran <alex.tran1502@gmail.com>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -13,7 +13,6 @@ torch = [
|
|||||||
{markers = "platform_machine == 'amd64' or platform_machine == 'x86_64'", version = "=2.0.1", source = "pytorch-cpu"}
|
{markers = "platform_machine == 'amd64' or platform_machine == 'x86_64'", version = "=2.0.1", source = "pytorch-cpu"}
|
||||||
]
|
]
|
||||||
transformers = "^4.29.2"
|
transformers = "^4.29.2"
|
||||||
sentence-transformers = "^2.2.2"
|
|
||||||
onnxruntime = "^1.15.0"
|
onnxruntime = "^1.15.0"
|
||||||
insightface = "^0.7.3"
|
insightface = "^0.7.3"
|
||||||
opencv-python-headless = "^4.7.0.72"
|
opencv-python-headless = "^4.7.0.72"
|
||||||
@@ -22,17 +21,30 @@ fastapi = "^0.95.2"
|
|||||||
uvicorn = {extras = ["standard"], version = "^0.22.0"}
|
uvicorn = {extras = ["standard"], version = "^0.22.0"}
|
||||||
pydantic = "^1.10.8"
|
pydantic = "^1.10.8"
|
||||||
aiocache = "^0.12.1"
|
aiocache = "^0.12.1"
|
||||||
pytest-cov = "^4.1.0"
|
optimum = "^1.9.1"
|
||||||
ruff = "^0.0.272"
|
torchvision = [
|
||||||
|
{markers = "platform_machine == 'arm64' or platform_machine == 'aarch64'", version = "=0.15.2", source = "pypi"},
|
||||||
|
{markers = "platform_machine == 'amd64' or platform_machine == 'x86_64'", version = "=0.15.2", source = "pytorch-cpu"}
|
||||||
|
]
|
||||||
|
rich = "^13.4.2"
|
||||||
|
ftfy = "^6.1.1"
|
||||||
|
setuptools = "^68.0.0"
|
||||||
|
open-clip-torch = "^2.20.0"
|
||||||
|
python-multipart = "^0.0.6"
|
||||||
|
orjson = "^3.9.5"
|
||||||
|
safetensors = "0.3.2"
|
||||||
|
gunicorn = "^21.1.0"
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
mypy = "^1.3.0"
|
mypy = "^1.3.0"
|
||||||
black = "^23.3.0"
|
black = "^23.3.0"
|
||||||
pytest = "^7.3.1"
|
pytest = "^7.3.1"
|
||||||
locust = "^2.15.1"
|
locust = "^2.15.1"
|
||||||
gunicorn = "^20.1.0"
|
|
||||||
httpx = "^0.24.1"
|
httpx = "^0.24.1"
|
||||||
pytest-asyncio = "^0.21.0"
|
pytest-asyncio = "^0.21.0"
|
||||||
|
pytest-cov = "^4.1.0"
|
||||||
|
ruff = "^0.0.272"
|
||||||
|
pytest-mock = "^3.11.1"
|
||||||
|
|
||||||
[[tool.poetry.source]]
|
[[tool.poetry.source]]
|
||||||
name = "pytorch-cpu"
|
name = "pytorch-cpu"
|
||||||
@@ -60,10 +72,22 @@ warn_untyped_fields = true
|
|||||||
|
|
||||||
[[tool.mypy.overrides]]
|
[[tool.mypy.overrides]]
|
||||||
module = [
|
module = [
|
||||||
"transformers.pipelines",
|
"huggingface_hub",
|
||||||
|
"transformers",
|
||||||
|
"gunicorn",
|
||||||
"cv2",
|
"cv2",
|
||||||
"insightface.app",
|
"insightface.model_zoo",
|
||||||
"sentence_transformers",
|
"insightface.utils.face_align",
|
||||||
|
"insightface.utils.storage",
|
||||||
|
"onnxruntime",
|
||||||
|
"optimum",
|
||||||
|
"optimum.pipelines",
|
||||||
|
"optimum.onnxruntime",
|
||||||
|
"clip_server.model.clip",
|
||||||
|
"clip_server.model.clip_onnx",
|
||||||
|
"clip_server.model.pretrained_models",
|
||||||
|
"clip_server.model.tokenization",
|
||||||
|
"torchvision.transforms",
|
||||||
"aiocache.backends.memory",
|
"aiocache.backends.memory",
|
||||||
"aiocache.lock",
|
"aiocache.lock",
|
||||||
"aiocache.plugins"
|
"aiocache.plugins"
|
||||||
|
|||||||
2
machine-learning/requirements.txt
Normal file
2
machine-learning/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# requirements to be installed with `--no-deps` flag
|
||||||
|
clip-server==0.8.*
|
||||||
1570
machine-learning/responses.json
Normal file
1570
machine-learning/responses.json
Normal file
File diff suppressed because it is too large
Load Diff
15
machine-learning/start.sh
Executable file
15
machine-learning/start.sh
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
export LD_PRELOAD="/usr/lib/$(arch)-linux-gnu/libmimalloc.so.2"
|
||||||
|
|
||||||
|
: "${MACHINE_LEARNING_HOST:=0.0.0.0}"
|
||||||
|
: "${MACHINE_LEARNING_PORT:=3003}"
|
||||||
|
: "${MACHINE_LEARNING_WORKERS:=1}"
|
||||||
|
: "${MACHINE_LEARNING_WORKER_TIMEOUT:=120}"
|
||||||
|
|
||||||
|
gunicorn app.main:app \
|
||||||
|
-k uvicorn.workers.UvicornWorker \
|
||||||
|
-w $MACHINE_LEARNING_WORKERS \
|
||||||
|
-b $MACHINE_LEARNING_HOST:$MACHINE_LEARNING_PORT \
|
||||||
|
-t $MACHINE_LEARNING_WORKER_TIMEOUT \
|
||||||
|
--log-config-json log_conf.json
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"flutterSdkVersion": "3.10.5",
|
"flutterSdkVersion": "3.13.0",
|
||||||
"flavors": {}
|
"flavors": {}
|
||||||
}
|
}
|
||||||
|
|||||||
11
mobile/.vscode/settings.json
vendored
Normal file
11
mobile/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"dart.flutterSdkPath": ".fvm/flutter_sdk",
|
||||||
|
// Remove .fvm files from search
|
||||||
|
"search.exclude": {
|
||||||
|
"**/.fvm": true
|
||||||
|
},
|
||||||
|
// Remove from file watching
|
||||||
|
"files.watcherExclude": {
|
||||||
|
"**/.fvm": true
|
||||||
|
}
|
||||||
|
}
|
||||||
2
mobile/android/.gitignore
vendored
2
mobile/android/.gitignore
vendored
@@ -13,4 +13,4 @@ key.properties
|
|||||||
**/*.jks
|
**/*.jks
|
||||||
|
|
||||||
# Fastlane
|
# Fastlane
|
||||||
/fastlane/report.xml
|
fastlane/report.xml
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
applicationId "app.alextran.immich"
|
applicationId "app.alextran.immich"
|
||||||
minSdkVersion 23
|
minSdkVersion 26
|
||||||
targetSdkVersion 33
|
targetSdkVersion 33
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
@@ -96,3 +96,8 @@ dependencies {
|
|||||||
implementation "com.github.bumptech.glide:glide:$glide_version"
|
implementation "com.github.bumptech.glide:glide:$glide_version"
|
||||||
kapt "com.github.bumptech.glide:compiler:$glide_version"
|
kapt "com.github.bumptech.glide:compiler:$glide_version"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is uncommented in F-Droid build script
|
||||||
|
//f configurations.all {
|
||||||
|
//f exclude group: 'com.google.android.gms'
|
||||||
|
//f }
|
||||||
|
|||||||
@@ -7,6 +7,10 @@
|
|||||||
android:name="io.flutter.embedding.android.EnableImpeller"
|
android:name="io.flutter.embedding.android.EnableImpeller"
|
||||||
android:value="false" />
|
android:value="false" />
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||||
|
android:resource="@drawable/notification_icon" />
|
||||||
|
|
||||||
<activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTop"
|
<activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTop"
|
||||||
android:theme="@style/LaunchTheme"
|
android:theme="@style/LaunchTheme"
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
@@ -51,8 +55,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||||
@@ -60,11 +63,16 @@
|
|||||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
|
|
||||||
<queries>
|
<queries>
|
||||||
<intent>
|
<intent>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<data android:scheme="https" />
|
<data android:scheme="https" />
|
||||||
</intent>
|
</intent>
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<data android:scheme="geo" />
|
||||||
|
</intent>
|
||||||
</queries>
|
</queries>
|
||||||
</manifest>
|
</manifest>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 624 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
@@ -35,8 +35,8 @@ platform :android do
|
|||||||
task: 'bundle',
|
task: 'bundle',
|
||||||
build_type: 'Release',
|
build_type: 'Release',
|
||||||
properties: {
|
properties: {
|
||||||
"android.injected.version.code" => 91,
|
"android.injected.version.code" => 103,
|
||||||
"android.injected.version.name" => "1.68.0",
|
"android.injected.version.name" => "1.79.0",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
||||||
|
|||||||
@@ -5,17 +5,17 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000296">
|
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000269">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="1: bundleRelease" time="64.042552">
|
<testcase classname="fastlane.lanes" name="1: bundleRelease" time="81.160108">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="2: upload_to_play_store" time="29.676557">
|
<testcase classname="fastlane.lanes" name="2: upload_to_play_store" time="39.176668">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|||||||
@@ -2,60 +2,60 @@
|
|||||||
"add_to_album_bottom_sheet_added": "Added to {album}",
|
"add_to_album_bottom_sheet_added": "Added to {album}",
|
||||||
"add_to_album_bottom_sheet_already_exists": "Already in {album}",
|
"add_to_album_bottom_sheet_already_exists": "Already in {album}",
|
||||||
"advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from assets on the device. Activate this setting to load remote images instead.",
|
"advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from assets on the device. Activate this setting to load remote images instead.",
|
||||||
"advanced_settings_prefer_remote_title": "Prefer remote images",
|
"advanced_settings_prefer_remote_title": "Prefereix imatges remotes",
|
||||||
"advanced_settings_tile_subtitle": "Advanced user's settings",
|
"advanced_settings_tile_subtitle": "Advanced user's settings",
|
||||||
"advanced_settings_tile_title": "Avançat",
|
"advanced_settings_tile_title": "Avançat",
|
||||||
"advanced_settings_troubleshooting_subtitle": "Enable additional features for troubleshooting",
|
"advanced_settings_troubleshooting_subtitle": "Enable additional features for troubleshooting",
|
||||||
"advanced_settings_troubleshooting_title": "Resolució de problemes",
|
"advanced_settings_troubleshooting_title": "Resolució de problemes",
|
||||||
"album_info_card_backup_album_excluded": "EXCLUDED",
|
"album_info_card_backup_album_excluded": "Exclosos",
|
||||||
"album_info_card_backup_album_included": "INCLUDED",
|
"album_info_card_backup_album_included": "Inclosos",
|
||||||
"album_thumbnail_card_item": "1 item",
|
"album_thumbnail_card_item": "1 item",
|
||||||
"album_thumbnail_card_items": "{} items",
|
"album_thumbnail_card_items": "{} items",
|
||||||
"album_thumbnail_card_shared": " · Shared",
|
"album_thumbnail_card_shared": " · Shared",
|
||||||
"album_thumbnail_owned": "Owned",
|
"album_thumbnail_owned": "Owned",
|
||||||
"album_thumbnail_shared_by": "Compartit per {}",
|
"album_thumbnail_shared_by": "Compartit per {}",
|
||||||
"album_viewer_appbar_share_delete": "Delete album",
|
"album_viewer_appbar_share_delete": "Esborra l'àlbum",
|
||||||
"album_viewer_appbar_share_err_delete": "Failed to delete album",
|
"album_viewer_appbar_share_err_delete": "Error al esborrar l'àlbum",
|
||||||
"album_viewer_appbar_share_err_leave": "Failed to leave album",
|
"album_viewer_appbar_share_err_leave": "Error al sortir de l'àlbum",
|
||||||
"album_viewer_appbar_share_err_remove": "There are problems in removing assets from album",
|
"album_viewer_appbar_share_err_remove": "Hi ha hagut problemes al treure elements de l'àlbum",
|
||||||
"album_viewer_appbar_share_err_title": "Failed to change album title",
|
"album_viewer_appbar_share_err_title": "Error al modificar el títol de l'àlbum",
|
||||||
"album_viewer_appbar_share_leave": "Leave album",
|
"album_viewer_appbar_share_leave": "Surt de l'àlbum",
|
||||||
"album_viewer_appbar_share_remove": "Remove from album",
|
"album_viewer_appbar_share_remove": "Treu de l'àlbum",
|
||||||
"album_viewer_page_share_add_users": "Add users",
|
"album_viewer_page_share_add_users": "Afegeix usuaris",
|
||||||
"all_people_page_title": "People",
|
"all_people_page_title": "Persones",
|
||||||
"all_videos_page_title": "Vídeos",
|
"all_videos_page_title": "Vídeos",
|
||||||
"archive_page_no_archived_assets": "No s'ha trobat res arxivat",
|
"archive_page_no_archived_assets": "No s'ha trobat res arxivat",
|
||||||
"archive_page_title": "Arxiu({})",
|
"archive_page_title": "Arxiu({})",
|
||||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamic layout",
|
"asset_list_layout_settings_dynamic_layout_title": "Dynamic layout",
|
||||||
"asset_list_layout_settings_group_automatically": "Automatic",
|
"asset_list_layout_settings_group_automatically": "Automàtic",
|
||||||
"asset_list_layout_settings_group_by": "Group assets by",
|
"asset_list_layout_settings_group_by": "Group assets by",
|
||||||
"asset_list_layout_settings_group_by_month": "Month",
|
"asset_list_layout_settings_group_by_month": "Month",
|
||||||
"asset_list_layout_settings_group_by_month_day": "Month + day",
|
"asset_list_layout_settings_group_by_month_day": "Month + day",
|
||||||
"asset_list_settings_subtitle": "Photo grid layout settings",
|
"asset_list_settings_subtitle": "Photo grid layout settings",
|
||||||
"asset_list_settings_title": "Photo Grid",
|
"asset_list_settings_title": "Photo Grid",
|
||||||
"backup_album_selection_page_albums_device": "Albums on device ({})",
|
"backup_album_selection_page_albums_device": "Àlbums al dispositiu ({})",
|
||||||
"backup_album_selection_page_albums_tap": "Tap to include, double tap to exclude",
|
"backup_album_selection_page_albums_tap": "Tap to include, double tap to exclude",
|
||||||
"backup_album_selection_page_assets_scatter": "Assets can scatter across multiple albums. Thus, albums can be included or excluded during the backup process.",
|
"backup_album_selection_page_assets_scatter": "Assets can scatter across multiple albums. Thus, albums can be included or excluded during the backup process.",
|
||||||
"backup_album_selection_page_select_albums": "Select albums",
|
"backup_album_selection_page_select_albums": "Selecciona àlbums",
|
||||||
"backup_album_selection_page_selection_info": "Selection Info",
|
"backup_album_selection_page_selection_info": "Informació de la selecció",
|
||||||
"backup_album_selection_page_total_assets": "Total unique assets",
|
"backup_album_selection_page_total_assets": "Total d'elements únics",
|
||||||
"backup_all": "All",
|
"backup_all": "All",
|
||||||
"backup_background_service_backup_failed_message": "Failed to backup assets. Retrying…",
|
"backup_background_service_backup_failed_message": "Failed to backup assets. Retrying…",
|
||||||
"backup_background_service_connection_failed_message": "Failed to connect to the server. Retrying…",
|
"backup_background_service_connection_failed_message": "Failed to connect to the server. Retrying…",
|
||||||
"backup_background_service_current_upload_notification": "Uploading {}",
|
"backup_background_service_current_upload_notification": "Pujant {}",
|
||||||
"backup_background_service_default_notification": "Checking for new assets…",
|
"backup_background_service_default_notification": "Cercant nous elements...",
|
||||||
"backup_background_service_error_title": "Backup error",
|
"backup_background_service_error_title": "Error copiant",
|
||||||
"backup_background_service_in_progress_notification": "Backing up your assets…",
|
"backup_background_service_in_progress_notification": "Copiant els teus elements",
|
||||||
"backup_background_service_upload_failure_notification": "Failed to upload {}",
|
"backup_background_service_upload_failure_notification": "Error al pujar {}",
|
||||||
"backup_controller_page_albums": "Backup Albums",
|
"backup_controller_page_albums": "Copia els àlbums",
|
||||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
||||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
||||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
"backup_controller_page_background_app_refresh_enable_button_text": "Vés a configuració",
|
||||||
"backup_controller_page_background_battery_info_link": "Show me how",
|
"backup_controller_page_background_battery_info_link": "Mostra'm com",
|
||||||
"backup_controller_page_background_battery_info_message": "For the best background backup experience, please disable any battery optimizations restricting background activity for Immich.\n\nSince this is device-specific, please lookup the required information for your device manufacturer.",
|
"backup_controller_page_background_battery_info_message": "For the best background backup experience, please disable any battery optimizations restricting background activity for Immich.\n\nSince this is device-specific, please lookup the required information for your device manufacturer.",
|
||||||
"backup_controller_page_background_battery_info_ok": "OK",
|
"backup_controller_page_background_battery_info_ok": "D'acord",
|
||||||
"backup_controller_page_background_battery_info_title": "Battery optimizations",
|
"backup_controller_page_background_battery_info_title": "Optimitzacions de bateria",
|
||||||
"backup_controller_page_background_charging": "Only while charging",
|
"backup_controller_page_background_charging": "Només mentre es carrega",
|
||||||
"backup_controller_page_background_configure_error": "Failed to configure the background service",
|
"backup_controller_page_background_configure_error": "Failed to configure the background service",
|
||||||
"backup_controller_page_background_delay": "Delay new assets backup: {}",
|
"backup_controller_page_background_delay": "Delay new assets backup: {}",
|
||||||
"backup_controller_page_background_description": "Turn on the background service to automatically backup any new assets without needing to open the app",
|
"backup_controller_page_background_description": "Turn on the background service to automatically backup any new assets without needing to open the app",
|
||||||
@@ -64,85 +64,90 @@
|
|||||||
"backup_controller_page_background_turn_off": "Turn off background service",
|
"backup_controller_page_background_turn_off": "Turn off background service",
|
||||||
"backup_controller_page_background_turn_on": "Turn on background service",
|
"backup_controller_page_background_turn_on": "Turn on background service",
|
||||||
"backup_controller_page_background_wifi": "Only on WiFi",
|
"backup_controller_page_background_wifi": "Only on WiFi",
|
||||||
"backup_controller_page_backup": "Backup",
|
"backup_controller_page_backup": "Còpia",
|
||||||
"backup_controller_page_backup_selected": "Selected: ",
|
"backup_controller_page_backup_selected": "Selected: ",
|
||||||
"backup_controller_page_backup_sub": "Backed up photos and videos",
|
"backup_controller_page_backup_sub": "Fotografies i vídeos copiats",
|
||||||
"backup_controller_page_cancel": "Cancel",
|
"backup_controller_page_cancel": "Cancel·la",
|
||||||
"backup_controller_page_created": "Created on: {}",
|
"backup_controller_page_created": "Created on: {}",
|
||||||
"backup_controller_page_desc_backup": "Turn on foreground backup to automatically upload new assets to the server when opening the app.",
|
"backup_controller_page_desc_backup": "Turn on foreground backup to automatically upload new assets to the server when opening the app.",
|
||||||
"backup_controller_page_excluded": "Excluded: ",
|
"backup_controller_page_excluded": "Exclosos:",
|
||||||
"backup_controller_page_failed": "Failed ({})",
|
"backup_controller_page_failed": "Failed ({})",
|
||||||
"backup_controller_page_filename": "File name: {} [{}]",
|
"backup_controller_page_filename": "File name: {} [{}]",
|
||||||
"backup_controller_page_id": "ID: {}",
|
"backup_controller_page_id": "ID: {}",
|
||||||
"backup_controller_page_info": "Backup Information",
|
"backup_controller_page_info": "Informació de la còpia",
|
||||||
"backup_controller_page_none_selected": "None selected",
|
"backup_controller_page_none_selected": "None selected",
|
||||||
"backup_controller_page_remainder": "Remainder",
|
"backup_controller_page_remainder": "Restant",
|
||||||
"backup_controller_page_remainder_sub": "Remaining photos and videos to back up from selection",
|
"backup_controller_page_remainder_sub": "Fotografies i vídeos restants per copiar de la selecció",
|
||||||
"backup_controller_page_select": "Select",
|
"backup_controller_page_select": "Selecciona",
|
||||||
"backup_controller_page_server_storage": "Server Storage",
|
"backup_controller_page_server_storage": "Server Storage",
|
||||||
"backup_controller_page_start_backup": "Start Backup",
|
"backup_controller_page_start_backup": "Inicia la còpia",
|
||||||
"backup_controller_page_status_off": "Automatic foreground backup is off",
|
"backup_controller_page_status_off": "Automatic foreground backup is off",
|
||||||
"backup_controller_page_status_on": "Automatic foreground backup is on",
|
"backup_controller_page_status_on": "Automatic foreground backup is on",
|
||||||
"backup_controller_page_storage_format": "{} of {} used",
|
"backup_controller_page_storage_format": "{} of {} used",
|
||||||
"backup_controller_page_to_backup": "Albums to be backup",
|
"backup_controller_page_to_backup": "Àlbums a copiar",
|
||||||
"backup_controller_page_total": "Total",
|
"backup_controller_page_total": "Total",
|
||||||
"backup_controller_page_total_sub": "All unique photos and videos from selected albums",
|
"backup_controller_page_total_sub": "Totes les fotografies i vídeos dels àlbums seleccionats",
|
||||||
"backup_controller_page_turn_off": "Turn off foreground backup",
|
"backup_controller_page_turn_off": "Turn off foreground backup",
|
||||||
"backup_controller_page_turn_on": "Turn on foreground backup",
|
"backup_controller_page_turn_on": "Turn on foreground backup",
|
||||||
"backup_controller_page_uploading_file_info": "Uploading file info",
|
"backup_controller_page_uploading_file_info": "Uploading file info",
|
||||||
"backup_err_only_album": "Cannot remove the only album",
|
"backup_err_only_album": "Cannot remove the only album",
|
||||||
"backup_info_card_assets": "assets",
|
"backup_info_card_assets": "elements",
|
||||||
|
"backup_manual_cancelled": "Cancelled",
|
||||||
|
"backup_manual_failed": "Failed",
|
||||||
|
"backup_manual_in_progress": "Upload already in progress. Try after sometime",
|
||||||
|
"backup_manual_success": "Success",
|
||||||
|
"backup_manual_title": "Upload status",
|
||||||
"cache_settings_album_thumbnails": "Library page thumbnails ({} assets)",
|
"cache_settings_album_thumbnails": "Library page thumbnails ({} assets)",
|
||||||
"cache_settings_clear_cache_button": "Clear cache",
|
"cache_settings_clear_cache_button": "Clear cache",
|
||||||
"cache_settings_clear_cache_button_title": "Clears the app's cache. This will significantly impact the app's performance until the cache has rebuilt.",
|
"cache_settings_clear_cache_button_title": "Clears the app's cache. This will significantly impact the app's performance until the cache has rebuilt.",
|
||||||
"cache_settings_image_cache_size": "Image cache size ({} assets)",
|
"cache_settings_image_cache_size": "Image cache size ({} assets)",
|
||||||
"cache_settings_statistics_album": "Library thumbnails",
|
"cache_settings_statistics_album": "Library thumbnails",
|
||||||
"cache_settings_statistics_assets": "{} assets ({})",
|
"cache_settings_statistics_assets": "{} elements ({})",
|
||||||
"cache_settings_statistics_full": "Full images",
|
"cache_settings_statistics_full": "Imatges completes",
|
||||||
"cache_settings_statistics_shared": "Shared album thumbnails",
|
"cache_settings_statistics_shared": "Shared album thumbnails",
|
||||||
"cache_settings_statistics_thumbnail": "Thumbnails",
|
"cache_settings_statistics_thumbnail": "Miniatures",
|
||||||
"cache_settings_statistics_title": "Cache usage",
|
"cache_settings_statistics_title": "Ús de memòria cau",
|
||||||
"cache_settings_subtitle": "Control the caching behaviour of the Immich mobile application",
|
"cache_settings_subtitle": "Control the caching behaviour of the Immich mobile application",
|
||||||
"cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)",
|
"cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)",
|
||||||
"cache_settings_title": "Caching Settings",
|
"cache_settings_title": "Configuració de la memòria cau",
|
||||||
"change_password_form_confirm_password": "Confirm Password",
|
"change_password_form_confirm_password": "Confirma la contrasenya",
|
||||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||||
"change_password_form_new_password": "New Password",
|
"change_password_form_new_password": "New Password",
|
||||||
"change_password_form_password_mismatch": "Passwords do not match",
|
"change_password_form_password_mismatch": "Passwords do not match",
|
||||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
"change_password_form_reenter_new_password": "Re-enter New Password",
|
||||||
"common_add_to_album": "Add to album",
|
"common_add_to_album": "Add to album",
|
||||||
"common_change_password": "Change Password",
|
"common_change_password": "Change Password",
|
||||||
"common_create_new_album": "Create new album",
|
"common_create_new_album": "Crea un àlbum nou",
|
||||||
"common_server_error": "Please check your network connection, make sure the server is reachable and app/server versions are compatible.",
|
"common_server_error": "Please check your network connection, make sure the server is reachable and app/server versions are compatible.",
|
||||||
"common_shared": "Compartit",
|
"common_shared": "Compartit",
|
||||||
"control_bottom_app_bar_add_to_album": "Add to album",
|
"control_bottom_app_bar_add_to_album": "Add to album",
|
||||||
"control_bottom_app_bar_album_info": "{} items",
|
"control_bottom_app_bar_album_info": "{} elements",
|
||||||
"control_bottom_app_bar_album_info_shared": "{} items · Shared",
|
"control_bottom_app_bar_album_info_shared": "{} elements - Compartits",
|
||||||
"control_bottom_app_bar_archive": "Arxiu",
|
"control_bottom_app_bar_archive": "Arxiu",
|
||||||
"control_bottom_app_bar_create_new_album": "Create new album",
|
"control_bottom_app_bar_create_new_album": "Crea un àlbum nou",
|
||||||
"control_bottom_app_bar_delete": "Delete",
|
"control_bottom_app_bar_delete": "Esborra",
|
||||||
"control_bottom_app_bar_favorite": "Favorite",
|
"control_bottom_app_bar_favorite": "Preferit",
|
||||||
"control_bottom_app_bar_share": "Share",
|
"control_bottom_app_bar_share": "Share",
|
||||||
"control_bottom_app_bar_unarchive": "Unarchive",
|
"control_bottom_app_bar_unarchive": "Desarxiva",
|
||||||
"create_album_page_untitled": "Untitled",
|
"create_album_page_untitled": "Untitled",
|
||||||
"create_shared_album_page_create": "Create",
|
"create_shared_album_page_create": "Create",
|
||||||
"create_shared_album_page_share": "Share",
|
"create_shared_album_page_share": "Comparteix",
|
||||||
"create_shared_album_page_share_add_assets": "ADD ASSETS",
|
"create_shared_album_page_share_add_assets": "AFEGEIX ELEMENTS",
|
||||||
"create_shared_album_page_share_select_photos": "Select Photos",
|
"create_shared_album_page_share_select_photos": "Escull fotografies",
|
||||||
"curated_location_page_title": "Localitzacions",
|
"curated_location_page_title": "Localitzacions",
|
||||||
"curated_object_page_title": "Coses",
|
"curated_object_page_title": "Coses",
|
||||||
"daily_title_text_date": "E, MMM dd",
|
"daily_title_text_date": "E, MMM dd",
|
||||||
"daily_title_text_date_year": "E, MMM dd, yyyy",
|
"daily_title_text_date_year": "E, MMM dd, yyyy",
|
||||||
"date_format": "E, LLL d, y • h:mm a",
|
"date_format": "E, LLL d, y • h:mm a",
|
||||||
"delete_dialog_alert": "These items will be permanently deleted from Immich and from your device",
|
"delete_dialog_alert": "These items will be permanently deleted from Immich and from your device",
|
||||||
"delete_dialog_cancel": "Cancel",
|
"delete_dialog_cancel": "Cancel·la",
|
||||||
"delete_dialog_ok": "Delete",
|
"delete_dialog_ok": "Esborra",
|
||||||
"delete_dialog_title": "Delete Permanently",
|
"delete_dialog_title": "Esborra permanentment",
|
||||||
"description_input_hint_text": "Afegeix descripció...",
|
"description_input_hint_text": "Afegeix descripció...",
|
||||||
"description_input_submit_error": "Error updating description, check the log for more details",
|
"description_input_submit_error": "Error updating description, check the log for more details",
|
||||||
"exif_bottom_sheet_description": "Add Description...",
|
"exif_bottom_sheet_description": "Afegeix descripció",
|
||||||
"exif_bottom_sheet_details": "DETAILS",
|
"exif_bottom_sheet_details": "DETALLS",
|
||||||
"exif_bottom_sheet_location": "LOCATION",
|
"exif_bottom_sheet_location": "UBICACIÓ",
|
||||||
"experimental_settings_new_asset_list_subtitle": "Work in progress",
|
"experimental_settings_new_asset_list_subtitle": "Work in progress",
|
||||||
"experimental_settings_new_asset_list_title": "Enable experimental photo grid",
|
"experimental_settings_new_asset_list_title": "Enable experimental photo grid",
|
||||||
"experimental_settings_subtitle": "Use at your own risk!",
|
"experimental_settings_subtitle": "Use at your own risk!",
|
||||||
@@ -156,9 +161,10 @@
|
|||||||
"home_page_building_timeline": "Building the timeline",
|
"home_page_building_timeline": "Building the timeline",
|
||||||
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
|
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
|
||||||
"home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).",
|
"home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).",
|
||||||
|
"home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping",
|
||||||
"image_viewer_page_state_provider_download_error": "Download Error",
|
"image_viewer_page_state_provider_download_error": "Download Error",
|
||||||
"image_viewer_page_state_provider_download_success": "Download Success",
|
"image_viewer_page_state_provider_download_success": "Download Success",
|
||||||
"library_page_albums": "Albums",
|
"library_page_albums": "Àlbums",
|
||||||
"library_page_archive": "Arxiu",
|
"library_page_archive": "Arxiu",
|
||||||
"library_page_device_albums": "Àlbums al Dispositiu",
|
"library_page_device_albums": "Àlbums al Dispositiu",
|
||||||
"library_page_favorites": "Favorites",
|
"library_page_favorites": "Favorites",
|
||||||
@@ -166,24 +172,25 @@
|
|||||||
"library_page_sharing": "Sharing",
|
"library_page_sharing": "Sharing",
|
||||||
"library_page_sort_created": "Most recently created",
|
"library_page_sort_created": "Most recently created",
|
||||||
"library_page_sort_title": "Album title",
|
"library_page_sort_title": "Album title",
|
||||||
|
"login_disabled": "Login has been disabled",
|
||||||
"login_form_api_exception": "API exception. Please check the server URL and try again.",
|
"login_form_api_exception": "API exception. Please check the server URL and try again.",
|
||||||
"login_form_button_text": "Login",
|
"login_form_button_text": "Entra",
|
||||||
"login_form_email_hint": "youremail@email.com",
|
"login_form_email_hint": "elteu@correu.cat",
|
||||||
"login_form_endpoint_hint": "http://your-server-ip:port/api",
|
"login_form_endpoint_hint": "http://ip-del-servidor:port/api",
|
||||||
"login_form_endpoint_url": "Server Endpoint URL",
|
"login_form_endpoint_url": "URL del servidor",
|
||||||
"login_form_err_http": "Please specify http:// or https://",
|
"login_form_err_http": "Especifica http:// o https://",
|
||||||
"login_form_err_invalid_email": "Invalid Email",
|
"login_form_err_invalid_email": "Adreça de correu electrònic no vàlida",
|
||||||
"login_form_err_invalid_url": "Invalid URL",
|
"login_form_err_invalid_url": "Invalid URL",
|
||||||
"login_form_err_leading_whitespace": "Leading whitespace",
|
"login_form_err_leading_whitespace": "Espai en blanc al principi",
|
||||||
"login_form_err_trailing_whitespace": "Trailing whitespace",
|
"login_form_err_trailing_whitespace": "Espai en blanc al final",
|
||||||
"login_form_failed_get_oauth_server_config": "Error logging using OAuth, check server URL",
|
"login_form_failed_get_oauth_server_config": "Error logging using OAuth, check server URL",
|
||||||
"login_form_failed_get_oauth_server_disable": "OAuth feature is not available on this server",
|
"login_form_failed_get_oauth_server_disable": "OAuth feature is not available on this server",
|
||||||
"login_form_failed_login": "Error logging you in, check server URL, email and password",
|
"login_form_failed_login": "Error logging you in, check server URL, email and password",
|
||||||
"login_form_label_email": "Email",
|
"login_form_label_email": "Correu electrònic",
|
||||||
"login_form_label_password": "Password",
|
"login_form_label_password": "Contrasenya",
|
||||||
"login_form_next_button": "Següent",
|
"login_form_next_button": "Següent",
|
||||||
"login_form_password_hint": "password",
|
"login_form_password_hint": "contrasenya",
|
||||||
"login_form_save_login": "Stay logged in",
|
"login_form_save_login": "Mantingues identificat",
|
||||||
"login_form_server_empty": "Enter a server URL.",
|
"login_form_server_empty": "Enter a server URL.",
|
||||||
"login_form_server_error": "Could not connect to server.",
|
"login_form_server_error": "Could not connect to server.",
|
||||||
"monthly_title_text_date_format": "MMMM y",
|
"monthly_title_text_date_format": "MMMM y",
|
||||||
@@ -194,15 +201,15 @@
|
|||||||
"notification_permission_list_tile_content": "Grant permission to enable notifications.",
|
"notification_permission_list_tile_content": "Grant permission to enable notifications.",
|
||||||
"notification_permission_list_tile_enable_button": "Activa les notificacions",
|
"notification_permission_list_tile_enable_button": "Activa les notificacions",
|
||||||
"notification_permission_list_tile_title": "Notification Permission",
|
"notification_permission_list_tile_title": "Notification Permission",
|
||||||
"partner_page_add_partner": "Add partner",
|
"partner_page_add_partner": "Afegeix company",
|
||||||
"partner_page_empty_message": "Your photos are not yet shared with any partner.",
|
"partner_page_empty_message": "Your photos are not yet shared with any partner.",
|
||||||
"partner_page_no_more_users": "No more users to add",
|
"partner_page_no_more_users": "No more users to add",
|
||||||
"partner_page_partner_add_failed": "Failed to add partner",
|
"partner_page_partner_add_failed": "Failed to add partner",
|
||||||
"partner_page_select_partner": "Select partner",
|
"partner_page_select_partner": "Escull company",
|
||||||
"partner_page_shared_to_title": "Shared to",
|
"partner_page_shared_to_title": "Compartit amb",
|
||||||
"partner_page_stop_sharing_content": "{} will no longer be able to access your photos.",
|
"partner_page_stop_sharing_content": "{} will no longer be able to access your photos.",
|
||||||
"partner_page_stop_sharing_title": "Stop sharing your photos?",
|
"partner_page_stop_sharing_title": "Stop sharing your photos?",
|
||||||
"partner_page_title": "Partner",
|
"partner_page_title": "Company",
|
||||||
"permission_onboarding_continue_anyway": "Continue anyway",
|
"permission_onboarding_continue_anyway": "Continue anyway",
|
||||||
"permission_onboarding_get_started": "Get started",
|
"permission_onboarding_get_started": "Get started",
|
||||||
"permission_onboarding_go_to_settings": "Go to settings",
|
"permission_onboarding_go_to_settings": "Go to settings",
|
||||||
@@ -215,7 +222,7 @@
|
|||||||
"profile_drawer_app_logs": "Logs",
|
"profile_drawer_app_logs": "Logs",
|
||||||
"profile_drawer_client_server_up_to_date": "Client and Server are up-to-date",
|
"profile_drawer_client_server_up_to_date": "Client and Server are up-to-date",
|
||||||
"profile_drawer_settings": "Settings",
|
"profile_drawer_settings": "Settings",
|
||||||
"profile_drawer_sign_out": "Sign Out",
|
"profile_drawer_sign_out": "Tanca la sessió",
|
||||||
"recently_added_page_title": "Recently Added",
|
"recently_added_page_title": "Recently Added",
|
||||||
"search_bar_hint": "Search your photos",
|
"search_bar_hint": "Search your photos",
|
||||||
"search_page_categories": "Categories",
|
"search_page_categories": "Categories",
|
||||||
@@ -223,20 +230,20 @@
|
|||||||
"search_page_motion_photos": "Fotografies animades",
|
"search_page_motion_photos": "Fotografies animades",
|
||||||
"search_page_no_objects": "No Objects Info Available",
|
"search_page_no_objects": "No Objects Info Available",
|
||||||
"search_page_no_places": "No Places Info Available",
|
"search_page_no_places": "No Places Info Available",
|
||||||
"search_page_people": "People",
|
"search_page_people": "Persones",
|
||||||
"search_page_places": "Places",
|
"search_page_places": "Llocs",
|
||||||
"search_page_recently_added": "Afegit recentment",
|
"search_page_recently_added": "Afegit recentment",
|
||||||
"search_page_screenshots": "Captures de pantalla",
|
"search_page_screenshots": "Captures de pantalla",
|
||||||
"search_page_selfies": "Autofotos",
|
"search_page_selfies": "Autofotos",
|
||||||
"search_page_things": "Things",
|
"search_page_things": "Coses",
|
||||||
"search_page_videos": "Videos",
|
"search_page_videos": "Videos",
|
||||||
"search_page_view_all_button": "Veure tot",
|
"search_page_view_all_button": "Veure tot",
|
||||||
"search_page_your_activity": "Your activity",
|
"search_page_your_activity": "Your activity",
|
||||||
"search_result_page_new_search_hint": "New Search",
|
"search_result_page_new_search_hint": "Cerca nova",
|
||||||
"search_suggestion_list_smart_search_hint_1": "Smart search is enabled by default, to search for metadata use the syntax ",
|
"search_suggestion_list_smart_search_hint_1": "Smart search is enabled by default, to search for metadata use the syntax ",
|
||||||
"search_suggestion_list_smart_search_hint_2": "m:your-search-term",
|
"search_suggestion_list_smart_search_hint_2": "m:your-search-term",
|
||||||
"select_additional_user_for_sharing_page_suggestions": "Suggestions",
|
"select_additional_user_for_sharing_page_suggestions": "Suggeriments",
|
||||||
"select_user_for_sharing_page_err_album": "Failed to create album",
|
"select_user_for_sharing_page_err_album": "Error al crear l'àlbum",
|
||||||
"select_user_for_sharing_page_share_suggestions": "Suggestions",
|
"select_user_for_sharing_page_share_suggestions": "Suggestions",
|
||||||
"server_info_box_app_version": "Versió de l'aplicació",
|
"server_info_box_app_version": "Versió de l'aplicació",
|
||||||
"server_info_box_server_version": "Versió del servidor",
|
"server_info_box_server_version": "Versió del servidor",
|
||||||
@@ -259,21 +266,21 @@
|
|||||||
"setting_notifications_total_progress_title": "Show background backup total progress",
|
"setting_notifications_total_progress_title": "Show background backup total progress",
|
||||||
"setting_pages_app_bar_settings": "Settings",
|
"setting_pages_app_bar_settings": "Settings",
|
||||||
"settings_require_restart": "Please restart Immich to apply this setting",
|
"settings_require_restart": "Please restart Immich to apply this setting",
|
||||||
"share_add": "Add",
|
"share_add": "Afegeix",
|
||||||
"share_add_photos": "Add photos",
|
"share_add_photos": "Afegeix fotografies",
|
||||||
"share_add_title": "Add a title",
|
"share_add_title": "Afegeix un títol",
|
||||||
"share_create_album": "Create album",
|
"share_create_album": "Crea un àlbum",
|
||||||
"share_dialog_preparing": "Preparing...",
|
"share_dialog_preparing": "Preparing...",
|
||||||
"share_invite": "Invite to album",
|
"share_invite": "Convida a l'àlbum",
|
||||||
"sharing_page_album": "Shared albums",
|
"sharing_page_album": "Shared albums",
|
||||||
"sharing_page_description": "Create shared albums to share photos and videos with people in your network.",
|
"sharing_page_description": "Create shared albums to share photos and videos with people in your network.",
|
||||||
"sharing_page_empty_list": "EMPTY LIST",
|
"sharing_page_empty_list": "EMPTY LIST",
|
||||||
"sharing_silver_appbar_create_shared_album": "Create shared album",
|
"sharing_silver_appbar_create_shared_album": "Crea àlbum compartit",
|
||||||
"sharing_silver_appbar_share_partner": "Share with partner",
|
"sharing_silver_appbar_share_partner": "Comparteix amb un company",
|
||||||
"tab_controller_nav_library": "Library",
|
"tab_controller_nav_library": "Library",
|
||||||
"tab_controller_nav_photos": "Photos",
|
"tab_controller_nav_photos": "Fotografies",
|
||||||
"tab_controller_nav_search": "Search",
|
"tab_controller_nav_search": "Cerca",
|
||||||
"tab_controller_nav_sharing": "Sharing",
|
"tab_controller_nav_sharing": "Compartint",
|
||||||
"theme_setting_asset_list_storage_indicator_title": "Show storage indicator on asset tiles",
|
"theme_setting_asset_list_storage_indicator_title": "Show storage indicator on asset tiles",
|
||||||
"theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({})",
|
"theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({})",
|
||||||
"theme_setting_dark_mode_switch": "Dark mode",
|
"theme_setting_dark_mode_switch": "Dark mode",
|
||||||
@@ -284,6 +291,10 @@
|
|||||||
"theme_setting_theme_title": "Theme",
|
"theme_setting_theme_title": "Theme",
|
||||||
"theme_setting_three_stage_loading_subtitle": "Three-stage loading might increase the loading performance but causes significantly higher network load",
|
"theme_setting_three_stage_loading_subtitle": "Three-stage loading might increase the loading performance but causes significantly higher network load",
|
||||||
"theme_setting_three_stage_loading_title": "Enable three-stage loading",
|
"theme_setting_three_stage_loading_title": "Enable three-stage loading",
|
||||||
|
"upload_dialog_cancel": "Cancel",
|
||||||
|
"upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?",
|
||||||
|
"upload_dialog_ok": "Upload",
|
||||||
|
"upload_dialog_title": "Upload Asset",
|
||||||
"version_announcement_overlay_ack": "Acknowledge",
|
"version_announcement_overlay_ack": "Acknowledge",
|
||||||
"version_announcement_overlay_release_notes": "release notes",
|
"version_announcement_overlay_release_notes": "release notes",
|
||||||
"version_announcement_overlay_text_1": "Hi friend, there is a new release of",
|
"version_announcement_overlay_text_1": "Hi friend, there is a new release of",
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"add_to_album_bottom_sheet_added": "Přidáno do {album}",
|
"add_to_album_bottom_sheet_added": "Přidáno do {album}",
|
||||||
"add_to_album_bottom_sheet_already_exists": "Již v {album}",
|
"add_to_album_bottom_sheet_already_exists": "Je již v {album}",
|
||||||
"advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from assets on the device. Activate this setting to load remote images instead.",
|
"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": "Prefer remote images",
|
"advanced_settings_prefer_remote_title": "Preferovat vzdálené obrázky",
|
||||||
"advanced_settings_tile_subtitle": "Pokročilé uživatelské nastavení",
|
"advanced_settings_tile_subtitle": "Pokročilé uživatelské nastavení",
|
||||||
"advanced_settings_tile_title": "Pokročilé",
|
"advanced_settings_tile_title": "Pokročilé",
|
||||||
"advanced_settings_troubleshooting_subtitle": "Povolit dodatečné funkce pro řešení problémů",
|
"advanced_settings_troubleshooting_subtitle": "Zobrazit dodatečné vlastnosti pro řešení problémů",
|
||||||
"advanced_settings_troubleshooting_title": "Řešení problémů",
|
"advanced_settings_troubleshooting_title": "Řešení problémů",
|
||||||
"album_info_card_backup_album_excluded": "VYLOUČENO",
|
"album_info_card_backup_album_excluded": "VYLOUČENO",
|
||||||
"album_info_card_backup_album_included": "ZAHRNUTO",
|
"album_info_card_backup_album_included": "ZAHRNUTO",
|
||||||
@@ -16,18 +16,18 @@
|
|||||||
"album_thumbnail_shared_by": "Sdílené od {}",
|
"album_thumbnail_shared_by": "Sdílené od {}",
|
||||||
"album_viewer_appbar_share_delete": "Odstranit album",
|
"album_viewer_appbar_share_delete": "Odstranit album",
|
||||||
"album_viewer_appbar_share_err_delete": "Nepodařilo se odstranit album",
|
"album_viewer_appbar_share_err_delete": "Nepodařilo se odstranit album",
|
||||||
"album_viewer_appbar_share_err_leave": "Nepodařilo se ukončit album",
|
"album_viewer_appbar_share_err_leave": "Nepodařilo se opustit album",
|
||||||
"album_viewer_appbar_share_err_remove": "Při odstraňování souborů z alba se vyskytly problémy.",
|
"album_viewer_appbar_share_err_remove": "Při odstraňování položek z alba se vyskytly problémy.",
|
||||||
"album_viewer_appbar_share_err_title": "Nepodařilo se změnit název alba",
|
"album_viewer_appbar_share_err_title": "Nepodařilo se změnit název alba",
|
||||||
"album_viewer_appbar_share_leave": "Opustit album",
|
"album_viewer_appbar_share_leave": "Opustit album",
|
||||||
"album_viewer_appbar_share_remove": "Odstranit z alba",
|
"album_viewer_appbar_share_remove": "Odstranit z alba",
|
||||||
"album_viewer_page_share_add_users": "Přidat uživatele",
|
"album_viewer_page_share_add_users": "Přidat uživatele",
|
||||||
"all_people_page_title": "People",
|
"all_people_page_title": "Lidé",
|
||||||
"all_videos_page_title": "Videa",
|
"all_videos_page_title": "Videa",
|
||||||
"archive_page_no_archived_assets": "No archived assets found",
|
"archive_page_no_archived_assets": "Nebyla nalezena žádná archivovaná média",
|
||||||
"archive_page_title": "Archív ({})",
|
"archive_page_title": "Archív ({})",
|
||||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamické rozložení",
|
"asset_list_layout_settings_dynamic_layout_title": "Dynamické rozložení",
|
||||||
"asset_list_layout_settings_group_automatically": "Automatic",
|
"asset_list_layout_settings_group_automatically": "Automaticky",
|
||||||
"asset_list_layout_settings_group_by": "Seskupit položky podle",
|
"asset_list_layout_settings_group_by": "Seskupit položky podle",
|
||||||
"asset_list_layout_settings_group_by_month": "Měsíc",
|
"asset_list_layout_settings_group_by_month": "Měsíc",
|
||||||
"asset_list_layout_settings_group_by_month_day": "Měsíc + den",
|
"asset_list_layout_settings_group_by_month_day": "Měsíc + den",
|
||||||
@@ -35,18 +35,18 @@
|
|||||||
"asset_list_settings_title": "Fotografická mřížka",
|
"asset_list_settings_title": "Fotografická mřížka",
|
||||||
"backup_album_selection_page_albums_device": "Alba v zařízení ({})",
|
"backup_album_selection_page_albums_device": "Alba v zařízení ({})",
|
||||||
"backup_album_selection_page_albums_tap": "Klepnutím na položku ji zahrnete, dvojím klepnutím ji vyloučíte",
|
"backup_album_selection_page_albums_tap": "Klepnutím na položku ji zahrnete, dvojím klepnutím ji vyloučíte",
|
||||||
"backup_album_selection_page_assets_scatter": "Soubory 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",
|
||||||
"backup_album_selection_page_selection_info": "Informace o výběru",
|
"backup_album_selection_page_selection_info": "Informace o výběru",
|
||||||
"backup_album_selection_page_total_assets": "Celkový počet jedinečných souborů",
|
"backup_album_selection_page_total_assets": "Celkový počet jedinečných položek",
|
||||||
"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": "Zálohování {}",
|
||||||
"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": "Vytvářím kopii 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 zálohovat {}",
|
||||||
"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é",
|
||||||
@@ -58,12 +58,12 @@
|
|||||||
"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í: {}",
|
||||||
"backup_controller_page_background_description": "Povolte službu na pozadí pro automatické zálohování všech nových aktiv 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",
|
||||||
"backup_controller_page_background_turn_off": "Vypnout zálohování na pozadí",
|
"backup_controller_page_background_turn_off": "Vypnout zálohování na pozadí",
|
||||||
"backup_controller_page_background_turn_on": "Povolit zálohování na pozadí",
|
"backup_controller_page_background_turn_on": "Povolit zálohování na pozadí",
|
||||||
"backup_controller_page_background_wifi": "Jen na WiFi",
|
"backup_controller_page_background_wifi": "Jen na Wi-Fi",
|
||||||
"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",
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
"backup_controller_page_id": "ID: {}",
|
"backup_controller_page_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": "Zůstává",
|
"backup_controller_page_remainder": "Zbývá",
|
||||||
"backup_controller_page_remainder_sub": "Zbývající fotografie a videa, která se mají zálohovat z vybraných alb",
|
"backup_controller_page_remainder_sub": "Zbývající fotografie a videa, která se mají zálohovat z vybraných alb",
|
||||||
"backup_controller_page_select": "Vybrat",
|
"backup_controller_page_select": "Vybrat",
|
||||||
"backup_controller_page_server_storage": "Serverové úložiště",
|
"backup_controller_page_server_storage": "Serverové úložiště",
|
||||||
@@ -89,9 +89,14 @@
|
|||||||
"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í",
|
||||||
"backup_controller_page_turn_on": "Povolit zálohování na popředí",
|
"backup_controller_page_turn_on": "Povolit zálohování na popředí",
|
||||||
"backup_controller_page_uploading_file_info": "Nahrávaný soubor",
|
"backup_controller_page_uploading_file_info": "Informace o zálohovaném souboru",
|
||||||
"backup_err_only_album": "Nelze odstranit jediné vybrané album",
|
"backup_err_only_album": "Nelze odstranit jediné vybrané album",
|
||||||
"backup_info_card_assets": "položek",
|
"backup_info_card_assets": "položek",
|
||||||
|
"backup_manual_cancelled": "Zrušeno",
|
||||||
|
"backup_manual_failed": "Selhalo",
|
||||||
|
"backup_manual_in_progress": "Zálohování již probíhá. Zkuste to znovu později",
|
||||||
|
"backup_manual_success": "Úspěch",
|
||||||
|
"backup_manual_title": "Stav zálohování",
|
||||||
"cache_settings_album_thumbnails": "Náhledy stránek knihovny (položek {})",
|
"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í.",
|
||||||
@@ -106,7 +111,7 @@
|
|||||||
"cache_settings_thumbnail_size": "Velikost vyrovnávací paměti náhledů (položek {})",
|
"cache_settings_thumbnail_size": "Velikost vyrovnávací paměti náhledů (položek {})",
|
||||||
"cache_settings_title": "Nastavení vyrovnávací paměti",
|
"cache_settings_title": "Nastavení vyrovnávací paměti",
|
||||||
"change_password_form_confirm_password": "Potvrďte heslo",
|
"change_password_form_confirm_password": "Potvrďte heslo",
|
||||||
"change_password_form_description": "Dobrý den, {firstName} {lastName},\n\nJe to buď poprvé, co se přihlašujete do systému, nebo byl vytvořen požadavek na změnu hesla. Zadejte níže, prosím, nové heslo.",
|
"change_password_form_description": "Dobrý den, {firstName} {lastName},\n\nje to buď poprvé, co se přihlašujete do systému, nebo byl vytvořen požadavek na změnu hesla. Níže zadejte nové heslo.",
|
||||||
"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",
|
||||||
@@ -117,24 +122,24 @@
|
|||||||
"common_shared": "Sdílené",
|
"common_shared": "Sdílené",
|
||||||
"control_bottom_app_bar_add_to_album": "Přidat do alba",
|
"control_bottom_app_bar_add_to_album": "Přidat do alba",
|
||||||
"control_bottom_app_bar_album_info": "{} položek",
|
"control_bottom_app_bar_album_info": "{} položek",
|
||||||
"control_bottom_app_bar_album_info_shared": "{} položky - sdílené",
|
"control_bottom_app_bar_album_info_shared": "{} položky – sdílené",
|
||||||
"control_bottom_app_bar_archive": "Archív",
|
"control_bottom_app_bar_archive": "Archív",
|
||||||
"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": "Vymazat",
|
"control_bottom_app_bar_delete": "Vymazat",
|
||||||
"control_bottom_app_bar_favorite": "Oblíbené",
|
"control_bottom_app_bar_favorite": "Oblíbené",
|
||||||
"control_bottom_app_bar_share": "Sdílet",
|
"control_bottom_app_bar_share": "Sdílet",
|
||||||
"control_bottom_app_bar_unarchive": "Unarchive",
|
"control_bottom_app_bar_unarchive": "Odarchivovat",
|
||||||
"create_album_page_untitled": "Bez názvu",
|
"create_album_page_untitled": "Bez názvu",
|
||||||
"create_shared_album_page_create": "Vytvořit",
|
"create_shared_album_page_create": "Vytvořit",
|
||||||
"create_shared_album_page_share": "Sdílet",
|
"create_shared_album_page_share": "Sdílet",
|
||||||
"create_shared_album_page_share_add_assets": "PŘIDAT",
|
"create_shared_album_page_share_add_assets": "PŘIDAT POLOŽKY",
|
||||||
"create_shared_album_page_share_select_photos": "Vybrat fotografie",
|
"create_shared_album_page_share_select_photos": "Vybrat fotografie",
|
||||||
"curated_location_page_title": "Místa",
|
"curated_location_page_title": "Místa",
|
||||||
"curated_object_page_title": "Věci",
|
"curated_object_page_title": "Věci",
|
||||||
"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",
|
||||||
"date_format": "EEEE, d. MMMM y • H:mm",
|
"date_format": "EEEE, d. MMMM y • H:mm",
|
||||||
"delete_dialog_alert": "Tyto položky budou trvale odstraněny z Immich a z vašeho zařízení.",
|
"delete_dialog_alert": "Tyto položky budou trvale odstraněny z Immich i z vašeho zařízení",
|
||||||
"delete_dialog_cancel": "Zrušit",
|
"delete_dialog_cancel": "Zrušit",
|
||||||
"delete_dialog_ok": "Vymazat",
|
"delete_dialog_ok": "Vymazat",
|
||||||
"delete_dialog_title": "Vymazat trvale",
|
"delete_dialog_title": "Vymazat trvale",
|
||||||
@@ -147,15 +152,16 @@
|
|||||||
"experimental_settings_new_asset_list_title": "Povolení experimentální mřížky fotografií",
|
"experimental_settings_new_asset_list_title": "Povolení experimentální mřížky fotografií",
|
||||||
"experimental_settings_subtitle": "Používejte na vlastní riziko!",
|
"experimental_settings_subtitle": "Používejte na vlastní riziko!",
|
||||||
"experimental_settings_title": "Experimentální",
|
"experimental_settings_title": "Experimentální",
|
||||||
"favorites_page_no_favorites": "No favorite assets found",
|
"favorites_page_no_favorites": "Nebyla nalezena žádná oblíbená média",
|
||||||
"favorites_page_title": "Oblíbené",
|
"favorites_page_title": "Oblíbené",
|
||||||
"home_page_add_to_album_conflicts": "Přidáno {added} položek do alba {album}. {failed} položek již je v albu.",
|
"home_page_add_to_album_conflicts": "Přidáno {added} položek do alba {album}. {failed} položek již je v albu.",
|
||||||
"home_page_add_to_album_err_local": "Zatím není možné přidat lokální média do alb, přeskakuje se",
|
"home_page_add_to_album_err_local": "Zatím není možné přidat lokální média do alb, přeskakuji",
|
||||||
"home_page_add_to_album_success": "Přidány položky {added} do alba {album}.",
|
"home_page_add_to_album_success": "Přidány položky {added} do alba {album}.",
|
||||||
"home_page_archive_err_local": "Zatím nemohu archivovat lokální média, přeskakuji",
|
"home_page_archive_err_local": "Zatím nemohu archivovat lokální média, přeskakuji",
|
||||||
"home_page_building_timeline": "Vytváření časové osy",
|
"home_page_building_timeline": "Vytváření časové osy",
|
||||||
"home_page_favorite_err_local": "Zatím není možné zařadit lokální média mezi oblíbená, přeskakuje se",
|
"home_page_favorite_err_local": "Zatím není možné zařadit lokální média mezi 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 albech.",
|
"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_upload_err_limit": "Lze zálohovat nejvýše 30 položek najednou, přeskakuji",
|
||||||
"image_viewer_page_state_provider_download_error": "Chyba stahování",
|
"image_viewer_page_state_provider_download_error": "Chyba stahování",
|
||||||
"image_viewer_page_state_provider_download_success": "Stahování bylo úspěšné",
|
"image_viewer_page_state_provider_download_success": "Stahování bylo úspěšné",
|
||||||
"library_page_albums": "Alba",
|
"library_page_albums": "Alba",
|
||||||
@@ -166,9 +172,10 @@
|
|||||||
"library_page_sharing": "Sdílení",
|
"library_page_sharing": "Sdílení",
|
||||||
"library_page_sort_created": "Naposledy vytvořené",
|
"library_page_sort_created": "Naposledy vytvořené",
|
||||||
"library_page_sort_title": "Podle názvu alba",
|
"library_page_sort_title": "Podle názvu alba",
|
||||||
|
"login_disabled": "Přihlášení bylo zakázáno",
|
||||||
"login_form_api_exception": "Výjimka API. Zkontrolujte URL serveru a zkuste to znovu.",
|
"login_form_api_exception": "Výjimka API. Zkontrolujte URL serveru a zkuste to znovu.",
|
||||||
"login_form_button_text": "Přihlásit se",
|
"login_form_button_text": "Přihlásit se",
|
||||||
"login_form_email_hint": "tvůjmail@email.com",
|
"login_form_email_hint": "tvůje-mail@email.com",
|
||||||
"login_form_endpoint_hint": "http://ip-tvého-serveru:port/api",
|
"login_form_endpoint_hint": "http://ip-tvého-serveru:port/api",
|
||||||
"login_form_endpoint_url": "URL adresa serveru",
|
"login_form_endpoint_url": "URL adresa serveru",
|
||||||
"login_form_err_http": "Prosím, uveďte http:// nebo https://",
|
"login_form_err_http": "Prosím, uveďte http:// nebo https://",
|
||||||
@@ -178,7 +185,7 @@
|
|||||||
"login_form_err_trailing_whitespace": "Koncová mezera",
|
"login_form_err_trailing_whitespace": "Koncová mezera",
|
||||||
"login_form_failed_get_oauth_server_config": "Chyba přihlášení pomocí OAuth, zkontrolujte adresu URL serveru",
|
"login_form_failed_get_oauth_server_config": "Chyba přihlášení pomocí OAuth, zkontrolujte adresu URL serveru",
|
||||||
"login_form_failed_get_oauth_server_disable": "Funkce OAuth není na tomto serveru dostupná",
|
"login_form_failed_get_oauth_server_disable": "Funkce OAuth není na tomto serveru dostupná",
|
||||||
"login_form_failed_login": "Chyba přihlášení, zkontrolujte url adresu serveru, e-mail a heslo.",
|
"login_form_failed_login": "Chyba přihlášení, zkontrolujte URL adresu serveru, e-mail a heslo.",
|
||||||
"login_form_label_email": "E-mail",
|
"login_form_label_email": "E-mail",
|
||||||
"login_form_label_password": "Heslo",
|
"login_form_label_password": "Heslo",
|
||||||
"login_form_next_button": "Další",
|
"login_form_next_button": "Další",
|
||||||
@@ -189,45 +196,45 @@
|
|||||||
"monthly_title_text_date_format": "LLLL y",
|
"monthly_title_text_date_format": "LLLL y",
|
||||||
"motion_photos_page_title": "Pohyblivé fotky",
|
"motion_photos_page_title": "Pohyblivé fotky",
|
||||||
"notification_permission_dialog_cancel": "Zrušit",
|
"notification_permission_dialog_cancel": "Zrušit",
|
||||||
"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_dialog_settings": "Nastavení",
|
"notification_permission_dialog_settings": "Nastavení",
|
||||||
"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í",
|
||||||
"notification_permission_list_tile_title": "Povolení oznámení",
|
"notification_permission_list_tile_title": "Povolení oznámení",
|
||||||
"partner_page_add_partner": "Add partner",
|
"partner_page_add_partner": "Přidat partnera",
|
||||||
"partner_page_empty_message": "Your photos are not yet shared with any partner.",
|
"partner_page_empty_message": "Vaše fotografie zatím nejsou sdíleny s žádným partnerem.",
|
||||||
"partner_page_no_more_users": "No more users to add",
|
"partner_page_no_more_users": "Žádní další uživatelé k přidání",
|
||||||
"partner_page_partner_add_failed": "Failed to add partner",
|
"partner_page_partner_add_failed": "Nepodařilo se přidat partnera",
|
||||||
"partner_page_select_partner": "Select partner",
|
"partner_page_select_partner": "Vyberte partnera",
|
||||||
"partner_page_shared_to_title": "Shared to",
|
"partner_page_shared_to_title": "Sdíleno",
|
||||||
"partner_page_stop_sharing_content": "{} will no longer be able to access your photos.",
|
"partner_page_stop_sharing_content": "{} již nebude mít přístup k vašim fotografiím.",
|
||||||
"partner_page_stop_sharing_title": "Stop sharing your photos?",
|
"partner_page_stop_sharing_title": "Přestat sdílet vaše fotografie?",
|
||||||
"partner_page_title": "Partner",
|
"partner_page_title": "Partner",
|
||||||
"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",
|
||||||
"permission_onboarding_go_to_settings": "Přejít do nastavení",
|
"permission_onboarding_go_to_settings": "Přejít do nastavení",
|
||||||
"permission_onboarding_grant_permission": "Povolit přístup",
|
"permission_onboarding_grant_permission": "Povolit přístup",
|
||||||
"permission_onboarding_log_out": "Odhlásit se",
|
"permission_onboarding_log_out": "Odhlásit se",
|
||||||
"permission_onboarding_permission_denied": "Přístup odepřen. Pro používání Immich, je nutné povolit přístup k fotkám a videím v nastavení.",
|
"permission_onboarding_permission_denied": "Přístup odepřen. Pro používání Immich je nutné povolit přístup k fotkám a videím v nastavení.",
|
||||||
"permission_onboarding_permission_granted": "Přístup povolen! Vše je připraveno.",
|
"permission_onboarding_permission_granted": "Přístup povolen! Vše je připraveno.",
|
||||||
"permission_onboarding_permission_limited": "Přístup omezen. Chcete-li používat Immich k zálohování a správě celé vaší kolekci galerií, povolte přístup k fotkám a videím v Nastavení.",
|
"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í.",
|
||||||
"profile_drawer_app_logs": "Logy",
|
"profile_drawer_app_logs": "Logy",
|
||||||
"profile_drawer_client_server_up_to_date": "Klient a server jsou aktuální",
|
"profile_drawer_client_server_up_to_date": "Klient a server jsou aktuální",
|
||||||
"profile_drawer_settings": "Nastavení",
|
"profile_drawer_settings": "Nastavení",
|
||||||
"profile_drawer_sign_out": "Odhlásit se",
|
"profile_drawer_sign_out": "Odhlásit se",
|
||||||
"recently_added_page_title": "Nedávno přidané",
|
"recently_added_page_title": "Nedávno přidané",
|
||||||
"search_bar_hint": "Prohledejte své obrázky",
|
"search_bar_hint": "Prohledejte své fotky",
|
||||||
"search_page_categories": "Kategorie",
|
"search_page_categories": "Kategorie",
|
||||||
"search_page_favorites": "Oblíbené",
|
"search_page_favorites": "Oblíbené",
|
||||||
"search_page_motion_photos": "Pohyblivé fotky",
|
"search_page_motion_photos": "Pohyblivé fotky",
|
||||||
"search_page_no_objects": "Informace o objektech nejsou k dispozici",
|
"search_page_no_objects": "Informace o objektech nejsou k dispozici",
|
||||||
"search_page_no_places": "Informace o místě nejsou k dispozici",
|
"search_page_no_places": "Informace o místě nejsou k dispozici",
|
||||||
"search_page_people": "People",
|
"search_page_people": "Lidé",
|
||||||
"search_page_places": "Místa",
|
"search_page_places": "Místa",
|
||||||
"search_page_recently_added": "Nedávno přidané",
|
"search_page_recently_added": "Nedávno přidané",
|
||||||
"search_page_screenshots": "Snímky obrazovky",
|
"search_page_screenshots": "Snímky obrazovky",
|
||||||
"search_page_selfies": "Selfie",
|
"search_page_selfies": "Autoportréty",
|
||||||
"search_page_things": "Věci",
|
"search_page_things": "Věci",
|
||||||
"search_page_videos": "Videa",
|
"search_page_videos": "Videa",
|
||||||
"search_page_view_all_button": "Zobrazit vše",
|
"search_page_view_all_button": "Zobrazit vše",
|
||||||
@@ -251,11 +258,11 @@
|
|||||||
"setting_notifications_notify_minutes": "{} minut",
|
"setting_notifications_notify_minutes": "{} minut",
|
||||||
"setting_notifications_notify_never": "nikdy",
|
"setting_notifications_notify_never": "nikdy",
|
||||||
"setting_notifications_notify_seconds": "{} sekundy",
|
"setting_notifications_notify_seconds": "{} sekundy",
|
||||||
"setting_notifications_single_progress_subtitle": "Podrobné informace o průběhu nahrávání pro položku",
|
"setting_notifications_single_progress_subtitle": "Podrobné informace o průběhu zálohová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í",
|
||||||
"setting_notifications_title": "Oznámení",
|
"setting_notifications_title": "Oznámení",
|
||||||
"setting_notifications_total_progress_subtitle": "Celkový průběh nahrávání (nahraných/celkově)",
|
"setting_notifications_total_progress_subtitle": "Celkový průběh zálohování (hotovo/celkově)",
|
||||||
"setting_notifications_total_progress_title": "Zobrazit celkový průběh zálohování na pozadí",
|
"setting_notifications_total_progress_title": "Zobrazit celkový průběh zálohování na pozadí",
|
||||||
"setting_pages_app_bar_settings": "Nastavení",
|
"setting_pages_app_bar_settings": "Nastavení",
|
||||||
"settings_require_restart": "Pro použití tohoto nastavení restartujte Immich",
|
"settings_require_restart": "Pro použití tohoto nastavení restartujte Immich",
|
||||||
@@ -284,9 +291,13 @@
|
|||||||
"theme_setting_theme_title": "Téma",
|
"theme_setting_theme_title": "Téma",
|
||||||
"theme_setting_three_stage_loading_subtitle": "Třístupňové načítání může zvýšit výkonnost načítání, ale vede k výrazně vyššímu zatížení sítě.",
|
"theme_setting_three_stage_loading_subtitle": "Třístupňové načítání může zvýšit výkonnost načítání, ale vede k výrazně vyššímu zatížení sítě.",
|
||||||
"theme_setting_three_stage_loading_title": "Povolení třístupňového načítání",
|
"theme_setting_three_stage_loading_title": "Povolení třístupňového načítání",
|
||||||
|
"upload_dialog_cancel": "Zrušit",
|
||||||
|
"upload_dialog_info": "Chcete zálohovat vybrané položky na server?",
|
||||||
|
"upload_dialog_ok": "Zálohovat",
|
||||||
|
"upload_dialog_title": "Zálohovat položku",
|
||||||
"version_announcement_overlay_ack": "Potvrdit",
|
"version_announcement_overlay_ack": "Potvrdit",
|
||||||
"version_announcement_overlay_release_notes": "poznámky k vydání",
|
"version_announcement_overlay_release_notes": "poznámky k vydání",
|
||||||
"version_announcement_overlay_text_1": "Ahoj, je zde nová verze",
|
"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_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_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 \uD83C\uDF89"
|
"version_announcement_overlay_title": "K dispozici je nová verze serveru \uD83C\uDF89"
|
||||||
|
|||||||
@@ -40,29 +40,29 @@
|
|||||||
"backup_album_selection_page_selection_info": "Oplysninger om valgte",
|
"backup_album_selection_page_selection_info": "Oplysninger om valgte",
|
||||||
"backup_album_selection_page_total_assets": "Samlede unikke elementer",
|
"backup_album_selection_page_total_assets": "Samlede unikke elementer",
|
||||||
"backup_all": "Alt",
|
"backup_all": "Alt",
|
||||||
"backup_background_service_backup_failed_message": "Backup af elementer fejlede. Forsøger igen...",
|
"backup_background_service_backup_failed_message": "Sikkerhedskopiering af elementer fejlede. Forsøger igen...",
|
||||||
"backup_background_service_connection_failed_message": "Forbindelsen til serveren blev tabt. Forsøger igen...",
|
"backup_background_service_connection_failed_message": "Forbindelsen til serveren blev tabt. Forsøger igen...",
|
||||||
"backup_background_service_current_upload_notification": "Uploader {}",
|
"backup_background_service_current_upload_notification": "Uploader {}",
|
||||||
"backup_background_service_default_notification": "Søger efter nye elementer...",
|
"backup_background_service_default_notification": "Søger efter nye elementer...",
|
||||||
"backup_background_service_error_title": "Fejl med backup",
|
"backup_background_service_error_title": "Fejl med sikkerhedskopiering",
|
||||||
"backup_background_service_in_progress_notification": "Tager backup af dine elementer...",
|
"backup_background_service_in_progress_notification": "Tager sikkerhedskopi af dine elementer...",
|
||||||
"backup_background_service_upload_failure_notification": "Fejlede med uploade af {}",
|
"backup_background_service_upload_failure_notification": "Fejlede med uploade af {}",
|
||||||
"backup_controller_page_albums": "Sikkerhedskopiér albummer",
|
"backup_controller_page_albums": "Sikkerhedskopiér albummer",
|
||||||
"backup_controller_page_background_app_refresh_disabled_content": "Slå baggrundsopdatering af applikationen til i Indstillinger > Generelt > Baggrundsopdatering af applikationer, for at bruge baggrundsbackup.",
|
"backup_controller_page_background_app_refresh_disabled_content": "Slå baggrundsopdatering af applikationen til i Indstillinger > Generelt > Baggrundsopdatering af applikationer, for at bruge sikkerhedskopi i baggrunden.",
|
||||||
"backup_controller_page_background_app_refresh_disabled_title": "Baggrundsopdatering af app er slået fra",
|
"backup_controller_page_background_app_refresh_disabled_title": "Baggrundsopdatering af app er slået fra",
|
||||||
"backup_controller_page_background_app_refresh_enable_button_text": "Gå til indstillinger",
|
"backup_controller_page_background_app_refresh_enable_button_text": "Gå til indstillinger",
|
||||||
"backup_controller_page_background_battery_info_link": "Vis mig hvordan",
|
"backup_controller_page_background_battery_info_link": "Vis mig hvordan",
|
||||||
"backup_controller_page_background_battery_info_message": "For den bedste oplevelse med baggrundsbackup, bør du slå batterioptimering, der begrænder baggrundsaktivitet, fra.\n\nSiden dette er afhængigt af enheden, bør du undersøge denne information leveret af din enheds producent.",
|
"backup_controller_page_background_battery_info_message": "For den bedste oplevelse med sikkerhedskopiering i baggrunden, bør du slå batterioptimering, der begrænder baggrundsaktivitet, fra.\n\nSiden dette er afhængigt af enheden, bør du undersøge denne information leveret af din enheds producent.",
|
||||||
"backup_controller_page_background_battery_info_ok": "OK",
|
"backup_controller_page_background_battery_info_ok": "OK",
|
||||||
"backup_controller_page_background_battery_info_title": "Batterioptimering",
|
"backup_controller_page_background_battery_info_title": "Batterioptimering",
|
||||||
"backup_controller_page_background_charging": "Kun under opladning",
|
"backup_controller_page_background_charging": "Kun under opladning",
|
||||||
"backup_controller_page_background_configure_error": "Fejlede konfigureringen af baggrundsbackup",
|
"backup_controller_page_background_configure_error": "Fejlede konfigureringen af sikkerhedskopiering i baggrunden",
|
||||||
"backup_controller_page_background_delay": "Udskyd backup af nye elementer: {}",
|
"backup_controller_page_background_delay": "Udskyd sikkerhedskopi af nye elementer: {}",
|
||||||
"backup_controller_page_background_description": "Slå baggrundsbackup til, for automatisk at tage backup af nye elementer, uden at skulle åbne appen",
|
"backup_controller_page_background_description": "Slå sikkerhedskopiering i baggrunden til, for automatisk at tage sikkerhedskopi af nye elementer, uden at skulle åbne appen",
|
||||||
"backup_controller_page_background_is_off": "Automatisk baggrundsbackup er slået fra",
|
"backup_controller_page_background_is_off": "Automatisk sikkerhedskopiering i baggrunden er slået fra",
|
||||||
"backup_controller_page_background_is_on": "Automatisk baggrundsbackup er slået til",
|
"backup_controller_page_background_is_on": "Automatisk sikkerhedskopiering i baggrunden er slået til",
|
||||||
"backup_controller_page_background_turn_off": "Slå baggrundsbackup fra",
|
"backup_controller_page_background_turn_off": "Slå sikkerhedskopiering i baggrunden fra",
|
||||||
"backup_controller_page_background_turn_on": "Slå baggrundsbackup til",
|
"backup_controller_page_background_turn_on": "Slå sikkerhedskopiering i baggrunden til",
|
||||||
"backup_controller_page_background_wifi": "Kun med WiFi",
|
"backup_controller_page_background_wifi": "Kun med WiFi",
|
||||||
"backup_controller_page_backup": "Sikkerhedskopier",
|
"backup_controller_page_backup": "Sikkerhedskopier",
|
||||||
"backup_controller_page_backup_selected": "Valgte: ",
|
"backup_controller_page_backup_selected": "Valgte: ",
|
||||||
@@ -92,6 +92,11 @@
|
|||||||
"backup_controller_page_uploading_file_info": "Uploader filinformation",
|
"backup_controller_page_uploading_file_info": "Uploader filinformation",
|
||||||
"backup_err_only_album": "Kan ikke slette det eneste album",
|
"backup_err_only_album": "Kan ikke slette det eneste album",
|
||||||
"backup_info_card_assets": "elementer",
|
"backup_info_card_assets": "elementer",
|
||||||
|
"backup_manual_cancelled": "Annulleret",
|
||||||
|
"backup_manual_failed": "Mislykkedes",
|
||||||
|
"backup_manual_in_progress": "Upload er allerede undervejs. Prøv igen efter noget tid",
|
||||||
|
"backup_manual_success": "Succes",
|
||||||
|
"backup_manual_title": "Uploadstatus",
|
||||||
"cache_settings_album_thumbnails": "Biblioteksminiaturebilleder ({} elementer)",
|
"cache_settings_album_thumbnails": "Biblioteksminiaturebilleder ({} elementer)",
|
||||||
"cache_settings_clear_cache_button": "Fjern cache",
|
"cache_settings_clear_cache_button": "Fjern cache",
|
||||||
"cache_settings_clear_cache_button_title": "Fjern appens cache. Dette vil i stor grad påvirke appens ydeevne indtil cachen er genopbygget.",
|
"cache_settings_clear_cache_button_title": "Fjern appens cache. Dette vil i stor grad påvirke appens ydeevne indtil cachen er genopbygget.",
|
||||||
@@ -155,7 +160,8 @@
|
|||||||
"home_page_archive_err_local": "Kan ikke arkivere lokalt element endnu.. Springer over",
|
"home_page_archive_err_local": "Kan ikke arkivere lokalt element endnu.. Springer over",
|
||||||
"home_page_building_timeline": "Bygger tidslinjen",
|
"home_page_building_timeline": "Bygger tidslinjen",
|
||||||
"home_page_favorite_err_local": "Kan endnu ikke gøre lokale elementer til favoritter. Springer over..",
|
"home_page_favorite_err_local": "Kan endnu ikke gøre lokale elementer til favoritter. Springer over..",
|
||||||
"home_page_first_time_notice": "Hvis dette er din første gang i appen, bedes du vælge en backup af albummer så tidlinjen kan blive fyldt med billeder og videoer fra albummerne.",
|
"home_page_first_time_notice": "Hvis det er din første gang i appen, bedes du vælge en sikkerhedskopi af albummer så tidlinjen kan blive fyldt med billeder og videoer fra albummerne.",
|
||||||
|
"home_page_upload_err_limit": "Det er kun muligt at lave sikkerhedskopi af 30 elementer ad gangen. Springer over",
|
||||||
"image_viewer_page_state_provider_download_error": "Fejl ved download",
|
"image_viewer_page_state_provider_download_error": "Fejl ved download",
|
||||||
"image_viewer_page_state_provider_download_success": "Download succesfuld",
|
"image_viewer_page_state_provider_download_success": "Download succesfuld",
|
||||||
"library_page_albums": "Albummer",
|
"library_page_albums": "Albummer",
|
||||||
@@ -166,6 +172,7 @@
|
|||||||
"library_page_sharing": "Delte",
|
"library_page_sharing": "Delte",
|
||||||
"library_page_sort_created": "Senest oprettet",
|
"library_page_sort_created": "Senest oprettet",
|
||||||
"library_page_sort_title": "Albumtitel",
|
"library_page_sort_title": "Albumtitel",
|
||||||
|
"login_disabled": "Login er blevet deaktiveret",
|
||||||
"login_form_api_exception": "API-undtagelse. Tjek serverens URL og prøv igen. ",
|
"login_form_api_exception": "API-undtagelse. Tjek serverens URL og prøv igen. ",
|
||||||
"login_form_button_text": "Log ind",
|
"login_form_button_text": "Log ind",
|
||||||
"login_form_email_hint": "din-e-mail@e-mail.com",
|
"login_form_email_hint": "din-e-mail@e-mail.com",
|
||||||
@@ -210,7 +217,7 @@
|
|||||||
"permission_onboarding_log_out": "Log ud",
|
"permission_onboarding_log_out": "Log ud",
|
||||||
"permission_onboarding_permission_denied": "Tilladelse afvist. For at bruge Immich, skal der gives tilladelse til at se billeder og videoer i indstillinger.",
|
"permission_onboarding_permission_denied": "Tilladelse afvist. For at bruge Immich, skal der gives tilladelse til at se billeder og videoer i indstillinger.",
|
||||||
"permission_onboarding_permission_granted": "Tilladelse givet! Du er nu klar.",
|
"permission_onboarding_permission_granted": "Tilladelse givet! Du er nu klar.",
|
||||||
"permission_onboarding_permission_limited": "Tilladelse begrænset. For at lade Immich lave backup og styre hele dit galleri, skal der gives tilladelse til billeder og videoer i indstillinger.",
|
"permission_onboarding_permission_limited": "Tilladelse begrænset. For at lade Immich lave sikkerhedskopi og styre hele dit galleri, skal der gives tilladelse til billeder og videoer i indstillinger.",
|
||||||
"permission_onboarding_request": "Immich kræver tilliadelse til at se dine billeder og videoer.",
|
"permission_onboarding_request": "Immich kræver tilliadelse til at se dine billeder og videoer.",
|
||||||
"profile_drawer_app_logs": "Log",
|
"profile_drawer_app_logs": "Log",
|
||||||
"profile_drawer_client_server_up_to_date": "Klient og server er ajour",
|
"profile_drawer_client_server_up_to_date": "Klient og server er ajour",
|
||||||
@@ -245,7 +252,7 @@
|
|||||||
"setting_image_viewer_original_title": "Indlæs originalbillede",
|
"setting_image_viewer_original_title": "Indlæs originalbillede",
|
||||||
"setting_image_viewer_preview_subtitle": "Slå indlæsning af et mediumstørrelse billede til. Slå fra for enten direkte at indlæse originalen eller kun at bruge miniaturebilledet.",
|
"setting_image_viewer_preview_subtitle": "Slå indlæsning af et mediumstørrelse billede til. Slå fra for enten direkte at indlæse originalen eller kun at bruge miniaturebilledet.",
|
||||||
"setting_image_viewer_preview_title": "Indlæs forhåndsvisning af billedet",
|
"setting_image_viewer_preview_title": "Indlæs forhåndsvisning af billedet",
|
||||||
"setting_notifications_notify_failures_grace_period": "Giv besked om baggrundsbackupfejl: {}",
|
"setting_notifications_notify_failures_grace_period": "Giv besked om fejl med sikkerhedskopiering i baggrunden: {}",
|
||||||
"setting_notifications_notify_hours": "{} timer",
|
"setting_notifications_notify_hours": "{} timer",
|
||||||
"setting_notifications_notify_immediately": "med det samme",
|
"setting_notifications_notify_immediately": "med det samme",
|
||||||
"setting_notifications_notify_minutes": "{} minutter",
|
"setting_notifications_notify_minutes": "{} minutter",
|
||||||
@@ -284,6 +291,10 @@
|
|||||||
"theme_setting_theme_title": "Tema",
|
"theme_setting_theme_title": "Tema",
|
||||||
"theme_setting_three_stage_loading_subtitle": "Tre-trins indlæsning kan øge ydeevnen, men kan ligeledes føre til højere netværksbelastning",
|
"theme_setting_three_stage_loading_subtitle": "Tre-trins indlæsning kan øge ydeevnen, men kan ligeledes føre til højere netværksbelastning",
|
||||||
"theme_setting_three_stage_loading_title": "Slå tre-trins indlæsning til",
|
"theme_setting_three_stage_loading_title": "Slå tre-trins indlæsning til",
|
||||||
|
"upload_dialog_cancel": "Annuller",
|
||||||
|
"upload_dialog_info": "Vil du sikkerhedskopiere de(t) valgte element(er) til serveren?",
|
||||||
|
"upload_dialog_ok": "Upload",
|
||||||
|
"upload_dialog_title": "Upload element",
|
||||||
"version_announcement_overlay_ack": "Accepter",
|
"version_announcement_overlay_ack": "Accepter",
|
||||||
"version_announcement_overlay_release_notes": "udgivelsesnoterne",
|
"version_announcement_overlay_release_notes": "udgivelsesnoterne",
|
||||||
"version_announcement_overlay_text_1": "Hej ven, der er en ny version af",
|
"version_announcement_overlay_text_1": "Hej ven, der er en ny version af",
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"add_to_album_bottom_sheet_added": "Zu {album} hinzugefügt",
|
"add_to_album_bottom_sheet_added": "Zu {album} hinzugefügt",
|
||||||
"add_to_album_bottom_sheet_already_exists": "Bereits in {album}",
|
"add_to_album_bottom_sheet_already_exists": "Bereits in {album}",
|
||||||
"advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from assets on the device. Activate this setting to load remote images instead.",
|
"advanced_settings_prefer_remote_subtitle": "Manche Endgeräte laden Vorschaubilder lokaler Bilder sehr langsam. Durch diese Einstellung werden diese stattdessen direkt vom Server geladen.",
|
||||||
"advanced_settings_prefer_remote_title": "Prefer remote images",
|
"advanced_settings_prefer_remote_title": "Server-Bilder bevorzugen",
|
||||||
"advanced_settings_tile_subtitle": "Erweiterte Benutzereinstellungen",
|
"advanced_settings_tile_subtitle": "Erweiterte Benutzereinstellungen",
|
||||||
"advanced_settings_tile_title": "Sonstige",
|
"advanced_settings_tile_title": "Sonstige",
|
||||||
"advanced_settings_troubleshooting_subtitle": "Aktiviere erweiterte Funktionen zur Fehlersuche",
|
"advanced_settings_troubleshooting_subtitle": "Aktiviere erweiterte Funktionen zur Fehlersuche",
|
||||||
@@ -22,9 +22,9 @@
|
|||||||
"album_viewer_appbar_share_leave": "Album verlassen",
|
"album_viewer_appbar_share_leave": "Album verlassen",
|
||||||
"album_viewer_appbar_share_remove": "Entferne vom Album",
|
"album_viewer_appbar_share_remove": "Entferne vom Album",
|
||||||
"album_viewer_page_share_add_users": "Nutzer hinzufügen",
|
"album_viewer_page_share_add_users": "Nutzer hinzufügen",
|
||||||
"all_people_page_title": "People",
|
"all_people_page_title": "Personen",
|
||||||
"all_videos_page_title": "Videos",
|
"all_videos_page_title": "Videos",
|
||||||
"archive_page_no_archived_assets": "Keine archivierten Elemente gefunden",
|
"archive_page_no_archived_assets": "Keine archivierten Inhalte gefunden",
|
||||||
"archive_page_title": "Archive ({})",
|
"archive_page_title": "Archive ({})",
|
||||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamisches Layout",
|
"asset_list_layout_settings_dynamic_layout_title": "Dynamisches Layout",
|
||||||
"asset_list_layout_settings_group_automatically": "Automatisch",
|
"asset_list_layout_settings_group_automatically": "Automatisch",
|
||||||
@@ -92,6 +92,11 @@
|
|||||||
"backup_controller_page_uploading_file_info": "Informationen",
|
"backup_controller_page_uploading_file_info": "Informationen",
|
||||||
"backup_err_only_album": "Das einzige Album kann nicht entfernt werden",
|
"backup_err_only_album": "Das einzige Album kann nicht entfernt werden",
|
||||||
"backup_info_card_assets": "Elemente",
|
"backup_info_card_assets": "Elemente",
|
||||||
|
"backup_manual_cancelled": "Cancelled",
|
||||||
|
"backup_manual_failed": "Failed",
|
||||||
|
"backup_manual_in_progress": "Upload already in progress. Try after sometime",
|
||||||
|
"backup_manual_success": "Success",
|
||||||
|
"backup_manual_title": "Upload status",
|
||||||
"cache_settings_album_thumbnails": "Library page thumbnails ({} assets)",
|
"cache_settings_album_thumbnails": "Library page thumbnails ({} assets)",
|
||||||
"cache_settings_clear_cache_button": "Zwischenspeicher löschen",
|
"cache_settings_clear_cache_button": "Zwischenspeicher löschen",
|
||||||
"cache_settings_clear_cache_button_title": "Löscht den Zwischenspeicher der App. Dies wird die Leistungsfähigkeit der App deutlich einschränken, bis der Zwischenspeicher wieder aufgebaut wurde.",
|
"cache_settings_clear_cache_button_title": "Löscht den Zwischenspeicher der App. Dies wird die Leistungsfähigkeit der App deutlich einschränken, bis der Zwischenspeicher wieder aufgebaut wurde.",
|
||||||
@@ -127,7 +132,7 @@
|
|||||||
"create_album_page_untitled": "Unbenannt",
|
"create_album_page_untitled": "Unbenannt",
|
||||||
"create_shared_album_page_create": "Erstellen",
|
"create_shared_album_page_create": "Erstellen",
|
||||||
"create_shared_album_page_share": "Teilen",
|
"create_shared_album_page_share": "Teilen",
|
||||||
"create_shared_album_page_share_add_assets": "ELEMENTE HINZUFÜGEN",
|
"create_shared_album_page_share_add_assets": "INHALTE HINZUFÜGEN",
|
||||||
"create_shared_album_page_share_select_photos": "Fotos auswählen",
|
"create_shared_album_page_share_select_photos": "Fotos auswählen",
|
||||||
"curated_location_page_title": "Orte",
|
"curated_location_page_title": "Orte",
|
||||||
"curated_object_page_title": "Dinge",
|
"curated_object_page_title": "Dinge",
|
||||||
@@ -147,7 +152,7 @@
|
|||||||
"experimental_settings_new_asset_list_title": "Experimentelle Fotogitter aktivieren",
|
"experimental_settings_new_asset_list_title": "Experimentelle Fotogitter aktivieren",
|
||||||
"experimental_settings_subtitle": "Benutzung auf eigene Gefahr!",
|
"experimental_settings_subtitle": "Benutzung auf eigene Gefahr!",
|
||||||
"experimental_settings_title": "Experimentell",
|
"experimental_settings_title": "Experimentell",
|
||||||
"favorites_page_no_favorites": "Keine favorisierten Elemente gefunden",
|
"favorites_page_no_favorites": "Keine favorisierten Inhalte gefunden",
|
||||||
"favorites_page_title": "Favoriten",
|
"favorites_page_title": "Favoriten",
|
||||||
"home_page_add_to_album_conflicts": "{added} Elemente zu {album} hinzugefügt. {failed} Elemente sind bereits vorhanden.",
|
"home_page_add_to_album_conflicts": "{added} Elemente zu {album} hinzugefügt. {failed} Elemente sind bereits vorhanden.",
|
||||||
"home_page_add_to_album_err_local": "Kann lokale Elemente noch nicht zu Alben hinzufügen, überspringe",
|
"home_page_add_to_album_err_local": "Kann lokale Elemente noch nicht zu Alben hinzufügen, überspringe",
|
||||||
@@ -156,6 +161,7 @@
|
|||||||
"home_page_building_timeline": "Zeitachse wird erstellt.",
|
"home_page_building_timeline": "Zeitachse wird erstellt.",
|
||||||
"home_page_favorite_err_local": "Kann lokale Elemente noch nicht favorisieren, überspringe",
|
"home_page_favorite_err_local": "Kann lokale Elemente noch nicht favorisieren, überspringe",
|
||||||
"home_page_first_time_notice": "Wenn dies das erste Mal ist dass Du Immich nutzt, stelle bitte sicher, dass mindestens ein Album zur Sicherung ausgewählt ist, sodass die Zeitachse mit Fotos und Videos gefüllt werden kann.",
|
"home_page_first_time_notice": "Wenn dies das erste Mal ist dass Du Immich nutzt, stelle bitte sicher, dass mindestens ein Album zur Sicherung ausgewählt ist, sodass die Zeitachse mit Fotos und Videos gefüllt werden kann.",
|
||||||
|
"home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping",
|
||||||
"image_viewer_page_state_provider_download_error": "Fehler beim Herunterladen",
|
"image_viewer_page_state_provider_download_error": "Fehler beim Herunterladen",
|
||||||
"image_viewer_page_state_provider_download_success": "Erfolgreich heruntergeladen",
|
"image_viewer_page_state_provider_download_success": "Erfolgreich heruntergeladen",
|
||||||
"library_page_albums": "Alben",
|
"library_page_albums": "Alben",
|
||||||
@@ -166,6 +172,7 @@
|
|||||||
"library_page_sharing": "Teilen",
|
"library_page_sharing": "Teilen",
|
||||||
"library_page_sort_created": "Zuletzt erstellt",
|
"library_page_sort_created": "Zuletzt erstellt",
|
||||||
"library_page_sort_title": "Albumtitel",
|
"library_page_sort_title": "Albumtitel",
|
||||||
|
"login_disabled": "Login has been disabled",
|
||||||
"login_form_api_exception": "API Fehler. Bitte die Serveradresse überprüfen und erneut versuchen.",
|
"login_form_api_exception": "API Fehler. Bitte die Serveradresse überprüfen und erneut versuchen.",
|
||||||
"login_form_button_text": "Anmelden",
|
"login_form_button_text": "Anmelden",
|
||||||
"login_form_email_hint": "deine@email.de",
|
"login_form_email_hint": "deine@email.de",
|
||||||
@@ -174,8 +181,8 @@
|
|||||||
"login_form_err_http": "Bitte gebe http:// oder https:// an",
|
"login_form_err_http": "Bitte gebe http:// oder https:// an",
|
||||||
"login_form_err_invalid_email": "Ungültige E-Mail",
|
"login_form_err_invalid_email": "Ungültige E-Mail",
|
||||||
"login_form_err_invalid_url": "Ungültige URL",
|
"login_form_err_invalid_url": "Ungültige URL",
|
||||||
"login_form_err_leading_whitespace": "Führendes Leerzichen",
|
"login_form_err_leading_whitespace": "Leerzichen am Anfang",
|
||||||
"login_form_err_trailing_whitespace": "Folgendes Leerzeichen",
|
"login_form_err_trailing_whitespace": "Leerzeichen am Ende",
|
||||||
"login_form_failed_get_oauth_server_config": "Fehler beim Login per OAuth, Server-URL überprüfen",
|
"login_form_failed_get_oauth_server_config": "Fehler beim Login per OAuth, Server-URL überprüfen",
|
||||||
"login_form_failed_get_oauth_server_disable": "OAuth-Funktion nicht verfügbar auf diesem Server.",
|
"login_form_failed_get_oauth_server_disable": "OAuth-Funktion nicht verfügbar auf diesem Server.",
|
||||||
"login_form_failed_login": "Error logging you in, check server url, email and password",
|
"login_form_failed_login": "Error logging you in, check server url, email and password",
|
||||||
@@ -194,13 +201,13 @@
|
|||||||
"notification_permission_list_tile_content": "Erlaube Berechtigung für Benachrichtigungen",
|
"notification_permission_list_tile_content": "Erlaube Berechtigung für Benachrichtigungen",
|
||||||
"notification_permission_list_tile_enable_button": "Aktiviere Benachrichtigungen",
|
"notification_permission_list_tile_enable_button": "Aktiviere Benachrichtigungen",
|
||||||
"notification_permission_list_tile_title": "Benachrichtigungs-Berechtigung",
|
"notification_permission_list_tile_title": "Benachrichtigungs-Berechtigung",
|
||||||
"partner_page_add_partner": "Add partner",
|
"partner_page_add_partner": "Partner hinzufügen",
|
||||||
"partner_page_empty_message": "Your photos are not yet shared with any partner.",
|
"partner_page_empty_message": "Your photos are not yet shared with any partner.",
|
||||||
"partner_page_no_more_users": "No more users to add",
|
"partner_page_no_more_users": "No more users to add",
|
||||||
"partner_page_partner_add_failed": "Failed to add partner",
|
"partner_page_partner_add_failed": "Failed to add partner",
|
||||||
"partner_page_select_partner": "Select partner",
|
"partner_page_select_partner": "Partner auswählen",
|
||||||
"partner_page_shared_to_title": "Shared to",
|
"partner_page_shared_to_title": "Geteilt mit",
|
||||||
"partner_page_stop_sharing_content": "{} will no longer be able to access your photos.",
|
"partner_page_stop_sharing_content": "{} wird nicht mehr auf deine Fotos zugreifen können.",
|
||||||
"partner_page_stop_sharing_title": "Stop sharing your photos?",
|
"partner_page_stop_sharing_title": "Stop sharing your photos?",
|
||||||
"partner_page_title": "Partner",
|
"partner_page_title": "Partner",
|
||||||
"permission_onboarding_continue_anyway": "Trotzdem fortfahren",
|
"permission_onboarding_continue_anyway": "Trotzdem fortfahren",
|
||||||
@@ -223,7 +230,7 @@
|
|||||||
"search_page_motion_photos": "Live Photos",
|
"search_page_motion_photos": "Live Photos",
|
||||||
"search_page_no_objects": "Keine Objektinformationen verfügbar",
|
"search_page_no_objects": "Keine Objektinformationen verfügbar",
|
||||||
"search_page_no_places": "Keine Informationen über Orte verfügbar",
|
"search_page_no_places": "Keine Informationen über Orte verfügbar",
|
||||||
"search_page_people": "People",
|
"search_page_people": "Personen",
|
||||||
"search_page_places": "Orte",
|
"search_page_places": "Orte",
|
||||||
"search_page_recently_added": "Zuletzt hinzugefügt",
|
"search_page_recently_added": "Zuletzt hinzugefügt",
|
||||||
"search_page_screenshots": "Bildschirmfotos",
|
"search_page_screenshots": "Bildschirmfotos",
|
||||||
@@ -284,6 +291,10 @@
|
|||||||
"theme_setting_theme_title": "Theme",
|
"theme_setting_theme_title": "Theme",
|
||||||
"theme_setting_three_stage_loading_subtitle": "Das dreistufige Ladeverfahren kann die Performance beim Laden verbessern, erhöht allerdings den Datenverbrauch deutlich",
|
"theme_setting_three_stage_loading_subtitle": "Das dreistufige Ladeverfahren kann die Performance beim Laden verbessern, erhöht allerdings den Datenverbrauch deutlich",
|
||||||
"theme_setting_three_stage_loading_title": "Dreistufiges Laden aktivieren",
|
"theme_setting_three_stage_loading_title": "Dreistufiges Laden aktivieren",
|
||||||
|
"upload_dialog_cancel": "Cancel",
|
||||||
|
"upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?",
|
||||||
|
"upload_dialog_ok": "Upload",
|
||||||
|
"upload_dialog_title": "Upload Asset",
|
||||||
"version_announcement_overlay_ack": "Ich habe verstanden",
|
"version_announcement_overlay_ack": "Ich habe verstanden",
|
||||||
"version_announcement_overlay_release_notes": "Änderungsprotokoll",
|
"version_announcement_overlay_release_notes": "Änderungsprotokoll",
|
||||||
"version_announcement_overlay_text_1": "Hallo mein Freund! Es gibt eine neue Version von",
|
"version_announcement_overlay_text_1": "Hallo mein Freund! Es gibt eine neue Version von",
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
"advanced_settings_tile_title": "Advanced",
|
"advanced_settings_tile_title": "Advanced",
|
||||||
"advanced_settings_troubleshooting_subtitle": "Enable additional features for troubleshooting",
|
"advanced_settings_troubleshooting_subtitle": "Enable additional features for troubleshooting",
|
||||||
"advanced_settings_troubleshooting_title": "Troubleshooting",
|
"advanced_settings_troubleshooting_title": "Troubleshooting",
|
||||||
|
"advanced_settings_self_signed_ssl_title": "Allow self-signed SSL certificates",
|
||||||
|
"advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.",
|
||||||
"album_info_card_backup_album_excluded": "EXCLUDED",
|
"album_info_card_backup_album_excluded": "EXCLUDED",
|
||||||
"album_info_card_backup_album_included": "INCLUDED",
|
"album_info_card_backup_album_included": "INCLUDED",
|
||||||
"album_thumbnail_card_item": "1 item",
|
"album_thumbnail_card_item": "1 item",
|
||||||
@@ -92,6 +94,11 @@
|
|||||||
"backup_controller_page_uploading_file_info": "Uploading file info",
|
"backup_controller_page_uploading_file_info": "Uploading file info",
|
||||||
"backup_err_only_album": "Cannot remove the only album",
|
"backup_err_only_album": "Cannot remove the only album",
|
||||||
"backup_info_card_assets": "assets",
|
"backup_info_card_assets": "assets",
|
||||||
|
"backup_manual_cancelled": "Cancelled",
|
||||||
|
"backup_manual_failed": "Failed",
|
||||||
|
"backup_manual_in_progress": "Upload already in progress. Try after sometime",
|
||||||
|
"backup_manual_success": "Success",
|
||||||
|
"backup_manual_title": "Upload status",
|
||||||
"cache_settings_album_thumbnails": "Library page thumbnails ({} assets)",
|
"cache_settings_album_thumbnails": "Library page thumbnails ({} assets)",
|
||||||
"cache_settings_clear_cache_button": "Clear cache",
|
"cache_settings_clear_cache_button": "Clear cache",
|
||||||
"cache_settings_clear_cache_button_title": "Clears the app's cache. This will significantly impact the app's performance until the cache has rebuilt.",
|
"cache_settings_clear_cache_button_title": "Clears the app's cache. This will significantly impact the app's performance until the cache has rebuilt.",
|
||||||
@@ -156,6 +163,7 @@
|
|||||||
"home_page_building_timeline": "Building the timeline",
|
"home_page_building_timeline": "Building the timeline",
|
||||||
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
|
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
|
||||||
"home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).",
|
"home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).",
|
||||||
|
"home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping",
|
||||||
"image_viewer_page_state_provider_download_error": "Download Error",
|
"image_viewer_page_state_provider_download_error": "Download Error",
|
||||||
"image_viewer_page_state_provider_download_success": "Download Success",
|
"image_viewer_page_state_provider_download_success": "Download Success",
|
||||||
"library_page_albums": "Albums",
|
"library_page_albums": "Albums",
|
||||||
@@ -166,7 +174,9 @@
|
|||||||
"library_page_sharing": "Sharing",
|
"library_page_sharing": "Sharing",
|
||||||
"library_page_sort_created": "Most recently created",
|
"library_page_sort_created": "Most recently created",
|
||||||
"library_page_sort_title": "Album title",
|
"library_page_sort_title": "Album title",
|
||||||
|
"login_disabled": "Login has been disabled",
|
||||||
"login_form_api_exception": "API exception. Please check the server URL and try again.",
|
"login_form_api_exception": "API exception. Please check the server URL and try again.",
|
||||||
|
"login_form_handshake_exception": "There was an Handshake Exception with the server. Enable self-signed certificate support in the settings if you are using a self-signed certificate.",
|
||||||
"login_form_button_text": "Login",
|
"login_form_button_text": "Login",
|
||||||
"login_form_email_hint": "youremail@email.com",
|
"login_form_email_hint": "youremail@email.com",
|
||||||
"login_form_endpoint_hint": "http://your-server-ip:port/api",
|
"login_form_endpoint_hint": "http://your-server-ip:port/api",
|
||||||
@@ -186,6 +196,8 @@
|
|||||||
"login_form_save_login": "Stay logged in",
|
"login_form_save_login": "Stay logged in",
|
||||||
"login_form_server_empty": "Enter a server URL.",
|
"login_form_server_empty": "Enter a server URL.",
|
||||||
"login_form_server_error": "Could not connect to server.",
|
"login_form_server_error": "Could not connect to server.",
|
||||||
|
"login_password_changed_success": "Password updated successfully",
|
||||||
|
"login_password_changed_error": "There was an error updating your password",
|
||||||
"monthly_title_text_date_format": "MMMM y",
|
"monthly_title_text_date_format": "MMMM y",
|
||||||
"motion_photos_page_title": "Motion Photos",
|
"motion_photos_page_title": "Motion Photos",
|
||||||
"notification_permission_dialog_cancel": "Cancel",
|
"notification_permission_dialog_cancel": "Cancel",
|
||||||
@@ -284,10 +296,30 @@
|
|||||||
"theme_setting_theme_title": "Theme",
|
"theme_setting_theme_title": "Theme",
|
||||||
"theme_setting_three_stage_loading_subtitle": "Three-stage loading might increase the loading performance but causes significantly higher network load",
|
"theme_setting_three_stage_loading_subtitle": "Three-stage loading might increase the loading performance but causes significantly higher network load",
|
||||||
"theme_setting_three_stage_loading_title": "Enable three-stage loading",
|
"theme_setting_three_stage_loading_title": "Enable three-stage loading",
|
||||||
|
"upload_dialog_cancel": "Cancel",
|
||||||
|
"upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?",
|
||||||
|
"upload_dialog_ok": "Upload",
|
||||||
|
"upload_dialog_title": "Upload Asset",
|
||||||
"version_announcement_overlay_ack": "Acknowledge",
|
"version_announcement_overlay_ack": "Acknowledge",
|
||||||
"version_announcement_overlay_release_notes": "release notes",
|
"version_announcement_overlay_release_notes": "release notes",
|
||||||
"version_announcement_overlay_text_1": "Hi friend, there is a new release of",
|
"version_announcement_overlay_text_1": "Hi friend, there is a new release of",
|
||||||
"version_announcement_overlay_text_2": "please take your time to visit the ",
|
"version_announcement_overlay_text_2": "please take your time to visit the ",
|
||||||
"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_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": "New Server Version Available \uD83C\uDF89"
|
"version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89",
|
||||||
}
|
"translated_text_options": "Options",
|
||||||
|
"map_no_assets_in_bounds": "No photos in this area",
|
||||||
|
"map_zoom_to_see_photos": "Zoom out to see photos",
|
||||||
|
"map_settings_dialog_title": "Map Settings",
|
||||||
|
"map_settings_dark_mode": "Dark mode",
|
||||||
|
"map_settings_only_show_favorites": "Show Favorite Only",
|
||||||
|
"map_settings_only_relative_range": "Date range",
|
||||||
|
"map_settings_dialog_cancel": "Cancel",
|
||||||
|
"map_settings_dialog_save": "Save",
|
||||||
|
"map_cannot_get_user_location": "Cannot get user's location",
|
||||||
|
"map_location_service_disabled_title": "Location Service disabled",
|
||||||
|
"map_location_service_disabled_content": "Location service needs to be enabled to display assets from your current location. Do you want to enable it now?",
|
||||||
|
"map_no_location_permission_title": "Location Permission denied",
|
||||||
|
"map_no_location_permission_content": "Location permission is needed to display assets from your current location. Do you want to allow it now?",
|
||||||
|
"map_location_dialog_cancel": "Cancel",
|
||||||
|
"map_location_dialog_yes": "Yes"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,83 +1,83 @@
|
|||||||
{
|
{
|
||||||
"add_to_album_bottom_sheet_added": "Added to {album}",
|
"add_to_album_bottom_sheet_added": "Agregado a {album}",
|
||||||
"add_to_album_bottom_sheet_already_exists": "Already in {album}",
|
"add_to_album_bottom_sheet_already_exists": "Ya se encuentra en {album}",
|
||||||
"advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from assets on the device. Activate this setting to load remote images instead.",
|
"advanced_settings_prefer_remote_subtitle": "Algunos dispositivos tardan mucho en cargar las miniaturas de recursos encontrados el dispositivo. Activa esta opción para cargar imágenes remotas en su lugar.",
|
||||||
"advanced_settings_prefer_remote_title": "Prefer remote images",
|
"advanced_settings_prefer_remote_title": "Preferir imágenes remotas",
|
||||||
"advanced_settings_tile_subtitle": "Advanced user's settings",
|
"advanced_settings_tile_subtitle": "Configuraciones avanzadas del usuario",
|
||||||
"advanced_settings_tile_title": "Advanced",
|
"advanced_settings_tile_title": "Avanzado",
|
||||||
"advanced_settings_troubleshooting_subtitle": "Enable additional features for troubleshooting",
|
"advanced_settings_troubleshooting_subtitle": "Habilitar funciones adicionales para solución de problemas",
|
||||||
"advanced_settings_troubleshooting_title": "Troubleshooting",
|
"advanced_settings_troubleshooting_title": "Solución de problemas",
|
||||||
"album_info_card_backup_album_excluded": "EXCLUIDOS",
|
"album_info_card_backup_album_excluded": "EXCLUIDOS",
|
||||||
"album_info_card_backup_album_included": "INCLUIDOS",
|
"album_info_card_backup_album_included": "INCLUIDOS",
|
||||||
"album_thumbnail_card_item": "1 item",
|
"album_thumbnail_card_item": "1 elemento",
|
||||||
"album_thumbnail_card_items": "{} items",
|
"album_thumbnail_card_items": "{} elementos",
|
||||||
"album_thumbnail_card_shared": " · Shared",
|
"album_thumbnail_card_shared": "Compartido",
|
||||||
"album_thumbnail_owned": "Owned",
|
"album_thumbnail_owned": "Propio",
|
||||||
"album_thumbnail_shared_by": "Shared by {}",
|
"album_thumbnail_shared_by": "Compartido por {}",
|
||||||
"album_viewer_appbar_share_delete": "Eliminar álbum ",
|
"album_viewer_appbar_share_delete": "Eliminar álbum ",
|
||||||
"album_viewer_appbar_share_err_delete": "No ha podido eliminar el álbum",
|
"album_viewer_appbar_share_err_delete": "No ha podido eliminar el álbum",
|
||||||
"album_viewer_appbar_share_err_leave": "No ha podido dejar el álbum",
|
"album_viewer_appbar_share_err_leave": "No se ha podido abandonar el álbum",
|
||||||
"album_viewer_appbar_share_err_remove": "Hay problemas para eliminar los activos del álbum",
|
"album_viewer_appbar_share_err_remove": "Hay problemas para eliminar los archivos del álbum",
|
||||||
"album_viewer_appbar_share_err_title": "Error al cambiar el título del álbum ",
|
"album_viewer_appbar_share_err_title": "Error al cambiar el título del álbum ",
|
||||||
"album_viewer_appbar_share_leave": "Abandonar álbum ",
|
"album_viewer_appbar_share_leave": "Abandonar álbum ",
|
||||||
"album_viewer_appbar_share_remove": "Eliminar del álbum ",
|
"album_viewer_appbar_share_remove": "Eliminar del álbum ",
|
||||||
"album_viewer_page_share_add_users": "Añadir usuarios",
|
"album_viewer_page_share_add_users": "Agregar usuarios",
|
||||||
"all_people_page_title": "People",
|
"all_people_page_title": "Personas",
|
||||||
"all_videos_page_title": "Videos",
|
"all_videos_page_title": "Videos",
|
||||||
"archive_page_no_archived_assets": "No archived assets found",
|
"archive_page_no_archived_assets": "No se encontraron recursos archivados",
|
||||||
"archive_page_title": "Archive ({})",
|
"archive_page_title": "Archivo ({})",
|
||||||
"asset_list_layout_settings_dynamic_layout_title": "Disposición dinámica",
|
"asset_list_layout_settings_dynamic_layout_title": "Diseño dinámico",
|
||||||
"asset_list_layout_settings_group_automatically": "Automatic",
|
"asset_list_layout_settings_group_automatically": "Automatico",
|
||||||
"asset_list_layout_settings_group_by": "Agrupar recursos por",
|
"asset_list_layout_settings_group_by": "Agrupar recursos por",
|
||||||
"asset_list_layout_settings_group_by_month": "Mes",
|
"asset_list_layout_settings_group_by_month": "Mes",
|
||||||
"asset_list_layout_settings_group_by_month_day": "Mes + día",
|
"asset_list_layout_settings_group_by_month_day": "Mes + día",
|
||||||
"asset_list_settings_subtitle": "Photo grid layout settings",
|
"asset_list_settings_subtitle": "Configuraciones del diseño de la cuadrícula de fotos",
|
||||||
"asset_list_settings_title": "Photo Grid",
|
"asset_list_settings_title": "Cuadrícula de fotos",
|
||||||
"backup_album_selection_page_albums_device": "Álbumes en el dispositivo ({})",
|
"backup_album_selection_page_albums_device": "Álbumes en el dispositivo ({})",
|
||||||
"backup_album_selection_page_albums_tap": "Toque para incluir, doble toque para excluir",
|
"backup_album_selection_page_albums_tap": "Toque para incluir, doble toque para excluir",
|
||||||
"backup_album_selection_page_assets_scatter": "Los activos pueden dispersarse en varios álbumes. De este modo, los álbumes pueden ser incluidos o excluidos durante el proceso de copia de seguridad.",
|
"backup_album_selection_page_assets_scatter": "Los archivos pueden dispersarse en varios álbumes. De este modo, los álbumes pueden ser incluidos o excluidos durante el proceso de copia de seguridad.",
|
||||||
"backup_album_selection_page_select_albums": "Seleccionar Álbumes",
|
"backup_album_selection_page_select_albums": "Seleccionar Álbumes",
|
||||||
"backup_album_selection_page_selection_info": "Información sobre la Selección",
|
"backup_album_selection_page_selection_info": "Información sobre la Selección",
|
||||||
"backup_album_selection_page_total_assets": "Total de activos únicos",
|
"backup_album_selection_page_total_assets": "Total de archivos únicos",
|
||||||
"backup_all": "Todos",
|
"backup_all": "Todos",
|
||||||
"backup_background_service_backup_failed_message": "Failed to backup assets. Retrying…",
|
"backup_background_service_backup_failed_message": "Error al copiar archivos. Reintentando...",
|
||||||
"backup_background_service_connection_failed_message": "Failed to connect to the server. Retrying…",
|
"backup_background_service_connection_failed_message": "Error al conectar con el servidor. Reintentando...",
|
||||||
"backup_background_service_current_upload_notification": "Uploading {}",
|
"backup_background_service_current_upload_notification": "Cargando {}",
|
||||||
"backup_background_service_default_notification": "Checking for new assets…",
|
"backup_background_service_default_notification": "Verificando si hay nuevos archivos",
|
||||||
"backup_background_service_error_title": "Backup error",
|
"backup_background_service_error_title": "Error de copia de seguridad",
|
||||||
"backup_background_service_in_progress_notification": "Backing up your assets…",
|
"backup_background_service_in_progress_notification": "Creando copia de seguridad de tus archivos...",
|
||||||
"backup_background_service_upload_failure_notification": "Failed to upload {}",
|
"backup_background_service_upload_failure_notification": "Error al cargar {}",
|
||||||
"backup_controller_page_albums": "Álbumes de copia de seguridad",
|
"backup_controller_page_albums": "Álbumes de copia de seguridad",
|
||||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
"backup_controller_page_background_app_refresh_disabled_content": "Activa la actualización en segundo plano de la aplicación en Configuración > General > Actualización en segundo plano para usar la copia de seguridad en segundo plano.",
|
||||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
"backup_controller_page_background_app_refresh_disabled_title": "Actualización en segundo plano desactivada",
|
||||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
"backup_controller_page_background_app_refresh_enable_button_text": "Ir a configuración",
|
||||||
"backup_controller_page_background_battery_info_link": "Show me how",
|
"backup_controller_page_background_battery_info_link": "Muestrame cómo",
|
||||||
"backup_controller_page_background_battery_info_message": "For the best background backup experience, please disable any battery optimizations restricting background activity for Immich.\n\nSince this is device-specific, please lookup the required information for your device manufacturer.",
|
"backup_controller_page_background_battery_info_message": "Para obtener la mejor experiencia de copia de seguridad en segundo plano, desactiva cualquier optimización de batería que restrinja la actividad en segundo plano para Immich.\n\nDado que esto es específico en cada dispositivo, busca la información necesaria de el fabricante de tu dispositivo.",
|
||||||
"backup_controller_page_background_battery_info_ok": "OK",
|
"backup_controller_page_background_battery_info_ok": "Ok",
|
||||||
"backup_controller_page_background_battery_info_title": "Battery optimizations",
|
"backup_controller_page_background_battery_info_title": "Optimizaciones de batería",
|
||||||
"backup_controller_page_background_charging": "Only while charging",
|
"backup_controller_page_background_charging": "Solo mientras se carga",
|
||||||
"backup_controller_page_background_configure_error": "Failed to configure the background service",
|
"backup_controller_page_background_configure_error": "Error al configurar el servicio en segundo plano",
|
||||||
"backup_controller_page_background_delay": "Delay new assets backup: {}",
|
"backup_controller_page_background_delay": "Retraso en la copia de seguridad de nuevos activos: {}",
|
||||||
"backup_controller_page_background_description": "Turn on the background service to automatically backup any new assets without needing to open the app",
|
"backup_controller_page_background_description": "Activa el servicio en segundo plano para copiar automáticamente cualquier nuevos archivos sin necesidad de abrir la aplicación.",
|
||||||
"backup_controller_page_background_is_off": "Automatic background backup is off",
|
"backup_controller_page_background_is_off": "La copia de seguridad en segundo plano automática está desactivada",
|
||||||
"backup_controller_page_background_is_on": "Automatic background backup is on",
|
"backup_controller_page_background_is_on": "La copia de seguridad en segundo plano automática está activada",
|
||||||
"backup_controller_page_background_turn_off": "Turn off background service",
|
"backup_controller_page_background_turn_off": "Desactivar el servicio en segundo plano",
|
||||||
"backup_controller_page_background_turn_on": "Turn on background service",
|
"backup_controller_page_background_turn_on": "Activar el servicio en segundo plano",
|
||||||
"backup_controller_page_background_wifi": "Only on WiFi",
|
"backup_controller_page_background_wifi": "Solo en WiFi",
|
||||||
"backup_controller_page_backup": "Copia de Seguridad",
|
"backup_controller_page_backup": "Copia de Seguridad",
|
||||||
"backup_controller_page_backup_selected": "Seleccionado:",
|
"backup_controller_page_backup_selected": "Seleccionado:",
|
||||||
"backup_controller_page_backup_sub": "Copia de seguridad de fotos y vídeos",
|
"backup_controller_page_backup_sub": "Fotos y videos respaldados",
|
||||||
"backup_controller_page_cancel": "Cancelar",
|
"backup_controller_page_cancel": "Cancelar",
|
||||||
"backup_controller_page_created": "Created on: {}",
|
"backup_controller_page_created": "Creado el: {}",
|
||||||
"backup_controller_page_desc_backup": "Active la copia de seguridad para cargar automáticamente los nuevos activos al servidor.",
|
"backup_controller_page_desc_backup": "Active la copia de seguridad para cargar automáticamente los nuevos activos al servidor.",
|
||||||
"backup_controller_page_excluded": "Excluido:",
|
"backup_controller_page_excluded": "Excluido:",
|
||||||
"backup_controller_page_failed": "Failed ({})",
|
"backup_controller_page_failed": "Fallidos ({})",
|
||||||
"backup_controller_page_filename": "File name: {} [{}]",
|
"backup_controller_page_filename": "Nombre del archivo: {} [{}]",
|
||||||
"backup_controller_page_id": "ID: {}",
|
"backup_controller_page_id": "ID: {}",
|
||||||
"backup_controller_page_info": "Información de la Copia de Seguridad",
|
"backup_controller_page_info": "Información de la Copia de Seguridad",
|
||||||
"backup_controller_page_none_selected": "Ninguno seleccionado",
|
"backup_controller_page_none_selected": "Ninguno seleccionado",
|
||||||
"backup_controller_page_remainder": "Remanente",
|
"backup_controller_page_remainder": "Restante",
|
||||||
"backup_controller_page_remainder_sub": "Fotos y álbumes restantes para hacer una copia de seguridad de la selección",
|
"backup_controller_page_remainder_sub": "Fotos y videos restantes para hacer una copia de seguridad de la selección",
|
||||||
"backup_controller_page_select": "Seleccionar",
|
"backup_controller_page_select": "Seleccionar",
|
||||||
"backup_controller_page_server_storage": "Almacenamiento en el servidor",
|
"backup_controller_page_server_storage": "Almacenamiento en el servidor",
|
||||||
"backup_controller_page_start_backup": "Iniciar copia de seguridad",
|
"backup_controller_page_start_backup": "Iniciar copia de seguridad",
|
||||||
@@ -89,48 +89,53 @@
|
|||||||
"backup_controller_page_total_sub": "Todas las fotos y vídeos únicos de los álbumes seleccionados",
|
"backup_controller_page_total_sub": "Todas las fotos y vídeos únicos de los álbumes seleccionados",
|
||||||
"backup_controller_page_turn_off": "Apagar la copia de seguridad",
|
"backup_controller_page_turn_off": "Apagar la copia de seguridad",
|
||||||
"backup_controller_page_turn_on": "Activar la copia de seguridad",
|
"backup_controller_page_turn_on": "Activar la copia de seguridad",
|
||||||
"backup_controller_page_uploading_file_info": "Uploading file info",
|
"backup_controller_page_uploading_file_info": "Cargando información del archivo",
|
||||||
"backup_err_only_album": "No se puede eliminar el único álbum",
|
"backup_err_only_album": "No se puede eliminar el único álbum",
|
||||||
"backup_info_card_assets": "activos",
|
"backup_info_card_assets": "archivos",
|
||||||
"cache_settings_album_thumbnails": "Library page thumbnails ({} assets)",
|
"backup_manual_cancelled": "Cancelled",
|
||||||
"cache_settings_clear_cache_button": "Clear cache",
|
"backup_manual_failed": "Failed",
|
||||||
"cache_settings_clear_cache_button_title": "Clears the app's cache. This will significantly impact the app's performance until the cache has rebuilt.",
|
"backup_manual_in_progress": "Upload already in progress. Try after sometime",
|
||||||
"cache_settings_image_cache_size": "Image cache size ({} assets)",
|
"backup_manual_success": "Success",
|
||||||
"cache_settings_statistics_album": "Library thumbnails",
|
"backup_manual_title": "Upload status",
|
||||||
"cache_settings_statistics_assets": "{} assets ({})",
|
"cache_settings_album_thumbnails": "Miniaturas de la página de la biblioteca ({} archivos)",
|
||||||
"cache_settings_statistics_full": "Full images",
|
"cache_settings_clear_cache_button": "Borrar caché",
|
||||||
"cache_settings_statistics_shared": "Shared album thumbnails",
|
"cache_settings_clear_cache_button_title": "Borra la caché de la aplicación. Esto afectará significativamente el rendimiento de la aplicación hasta que se reconstruya la caché.",
|
||||||
"cache_settings_statistics_thumbnail": "Thumbnails",
|
"cache_settings_image_cache_size": "Tamaño de la caché de imágenes ({} archivos)",
|
||||||
"cache_settings_statistics_title": "Cache usage",
|
"cache_settings_statistics_album": "Miniaturas de la biblioteca",
|
||||||
"cache_settings_subtitle": "Control the caching behaviour of the Immich mobile application",
|
"cache_settings_statistics_assets": "{} archivos ({})",
|
||||||
"cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)",
|
"cache_settings_statistics_full": "Imágenes completas",
|
||||||
"cache_settings_title": "Caching Settings",
|
"cache_settings_statistics_shared": "Miniaturas de álbumes compartidos",
|
||||||
"change_password_form_confirm_password": "Confirm Password",
|
"cache_settings_statistics_thumbnail": "Miniaturas",
|
||||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
"cache_settings_statistics_title": "Uso de caché",
|
||||||
"change_password_form_new_password": "New Password",
|
"cache_settings_subtitle": "Controla el comportamiento del almacenamiento en caché de la aplicación móvil Immich",
|
||||||
"change_password_form_password_mismatch": "Passwords do not match",
|
"cache_settings_thumbnail_size": "Tamaño de la caché de miniaturas ({} archivos)",
|
||||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
"cache_settings_title": "Configuración de la caché",
|
||||||
"common_add_to_album": "Add to album",
|
"change_password_form_confirm_password": "Confirmar Contraseña",
|
||||||
"common_change_password": "Change Password",
|
"change_password_form_description": "Hola {firstName} {lastName},\n\nEsta es la primera vez que inicias sesión en el sistema o se ha solicitado cambiar tu contraseña. Por favor, introduce la nueva contraseña a continuación.",
|
||||||
"common_create_new_album": "Create new album",
|
"change_password_form_new_password": "Nueva Contraseña",
|
||||||
"common_server_error": "Please check your network connection, make sure the server is reachable and app/server versions are compatible.",
|
"change_password_form_password_mismatch": "Las contraseñas no coinciden",
|
||||||
"common_shared": "Shared",
|
"change_password_form_reenter_new_password": "Vuelve a ingresar la nueva contraseña",
|
||||||
"control_bottom_app_bar_add_to_album": "Añadir al álbum",
|
"common_add_to_album": "Agregar al álbum",
|
||||||
|
"common_change_password": "Cambiar Contraseña",
|
||||||
|
"common_create_new_album": "Crear nuevo álbum",
|
||||||
|
"common_server_error": "Por favor, verifica tu conexión de red, asegúrate de que el servidor esté accesible y las versiones de la aplicación y del servidor sean compatibles.",
|
||||||
|
"common_shared": "Compartido",
|
||||||
|
"control_bottom_app_bar_add_to_album": "Agregar al álbum",
|
||||||
"control_bottom_app_bar_album_info": "{} elementos",
|
"control_bottom_app_bar_album_info": "{} elementos",
|
||||||
"control_bottom_app_bar_album_info_shared": "{} elementos · Compartido",
|
"control_bottom_app_bar_album_info_shared": "{} elementos · Compartidos",
|
||||||
"control_bottom_app_bar_archive": "Archive",
|
"control_bottom_app_bar_archive": "Archivar",
|
||||||
"control_bottom_app_bar_create_new_album": "Crear nuevo álbum",
|
"control_bottom_app_bar_create_new_album": "Crear nuevo álbum",
|
||||||
"control_bottom_app_bar_delete": "Eliminar",
|
"control_bottom_app_bar_delete": "Eliminar",
|
||||||
"control_bottom_app_bar_favorite": "Favorite",
|
"control_bottom_app_bar_favorite": "Favorito",
|
||||||
"control_bottom_app_bar_share": "Share",
|
"control_bottom_app_bar_share": "Compartir",
|
||||||
"control_bottom_app_bar_unarchive": "Unarchive",
|
"control_bottom_app_bar_unarchive": "Desarchivar",
|
||||||
"create_album_page_untitled": "Untitled",
|
"create_album_page_untitled": "Sin título",
|
||||||
"create_shared_album_page_create": "Create",
|
"create_shared_album_page_create": "Crear",
|
||||||
"create_shared_album_page_share": "Compartir",
|
"create_shared_album_page_share": "Compartir",
|
||||||
"create_shared_album_page_share_add_assets": "AÑADIR ACTIVOS",
|
"create_shared_album_page_share_add_assets": "AGREGAR ARCHIVOS",
|
||||||
"create_shared_album_page_share_select_photos": "Seleccionar Fotos",
|
"create_shared_album_page_share_select_photos": "Seleccionar Fotos",
|
||||||
"curated_location_page_title": "Places",
|
"curated_location_page_title": "Lugares",
|
||||||
"curated_object_page_title": "Things",
|
"curated_object_page_title": "Objetos",
|
||||||
"daily_title_text_date": "E dd, MMM",
|
"daily_title_text_date": "E dd, MMM",
|
||||||
"daily_title_text_date_year": "E dd de MMM, yyyy",
|
"daily_title_text_date_year": "E dd de MMM, yyyy",
|
||||||
"date_format": "E d, LLL y • h:mm a",
|
"date_format": "E d, LLL y • h:mm a",
|
||||||
@@ -138,35 +143,37 @@
|
|||||||
"delete_dialog_cancel": "Cancelar",
|
"delete_dialog_cancel": "Cancelar",
|
||||||
"delete_dialog_ok": "Eliminar",
|
"delete_dialog_ok": "Eliminar",
|
||||||
"delete_dialog_title": "Eliminar Permanentemente",
|
"delete_dialog_title": "Eliminar Permanentemente",
|
||||||
"description_input_hint_text": "Add description...",
|
"description_input_hint_text": "Agregar descripción...",
|
||||||
"description_input_submit_error": "Error updating description, check the log for more details",
|
"description_input_submit_error": "Error al actualizar la descripción, verifica el registro para obtener más detalles",
|
||||||
"exif_bottom_sheet_description": "Añadir Descripción...",
|
"exif_bottom_sheet_description": "Agregar Descripción...",
|
||||||
"exif_bottom_sheet_details": "DETALLES",
|
"exif_bottom_sheet_details": "DETALLES",
|
||||||
"exif_bottom_sheet_location": "LOCALZACIÓN",
|
"exif_bottom_sheet_location": "UBICACIÓN",
|
||||||
"experimental_settings_new_asset_list_subtitle": "Work in progress",
|
"experimental_settings_new_asset_list_subtitle": "Trabajo en progreso",
|
||||||
"experimental_settings_new_asset_list_title": "Enable experimental photo grid",
|
"experimental_settings_new_asset_list_title": "Habilitar cuadrícula fotográfica experimental",
|
||||||
"experimental_settings_subtitle": "Use at your own risk!",
|
"experimental_settings_subtitle": "Úsalo bajo tu responsabilidad",
|
||||||
"experimental_settings_title": "Experimental",
|
"experimental_settings_title": "Experimental",
|
||||||
"favorites_page_no_favorites": "No favorite assets found",
|
"favorites_page_no_favorites": "No se encontraron recursos marcados como favoritos",
|
||||||
"favorites_page_title": "Favoritos",
|
"favorites_page_title": "Favoritos",
|
||||||
"home_page_add_to_album_conflicts": "Añadidos {added} elementos al álbum {album}. {failed} elementos ya están añadidos.",
|
"home_page_add_to_album_conflicts": "{added} elementos agregados al álbum {album}.{failed} elementos ya existen en el álbum.",
|
||||||
"home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping",
|
"home_page_add_to_album_err_local": "Aún no se pueden agregar recursos locales a álbumes, omitiendo",
|
||||||
"home_page_add_to_album_success": "Añadidos {added} elementos al álbum {album}.",
|
"home_page_add_to_album_success": "{added} elementos agregados al álbum {album}. ",
|
||||||
"home_page_archive_err_local": "Can not archive local assets yet, skipping",
|
"home_page_archive_err_local": "Los recursos locales no pueden ser archivados, omitiendo",
|
||||||
"home_page_building_timeline": "Construyendo la línea de tiempo",
|
"home_page_building_timeline": "Construyendo la línea de tiempo",
|
||||||
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
|
"home_page_favorite_err_local": "Aún no se pueden archivar recursos locales, omitiendo",
|
||||||
"home_page_first_time_notice": "Si esta es la primera vez que usas la app, por favor, asegúrate de elegir un álbum de respaldo para que la línea de tiempo pueda cargar fotos y videos en los álbumes.",
|
"home_page_first_time_notice": "Si esta es la primera vez que usas la app, por favor, asegúrate de elegir un álbum de respaldo para que la línea de tiempo pueda cargar fotos y videos en los álbumes.",
|
||||||
"image_viewer_page_state_provider_download_error": "Download Error",
|
"home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping",
|
||||||
"image_viewer_page_state_provider_download_success": "Download Success",
|
"image_viewer_page_state_provider_download_error": "Error de descarga",
|
||||||
"library_page_albums": "Albums",
|
"image_viewer_page_state_provider_download_success": "Descarga exitosa",
|
||||||
"library_page_archive": "Archive",
|
"library_page_albums": "Álbumes",
|
||||||
"library_page_device_albums": "Albums on Device",
|
"library_page_archive": "Archivo",
|
||||||
|
"library_page_device_albums": "Álbumes en el dispositivo",
|
||||||
"library_page_favorites": "Favoritos",
|
"library_page_favorites": "Favoritos",
|
||||||
"library_page_new_album": "New album",
|
"library_page_new_album": "Nuevo álbum",
|
||||||
"library_page_sharing": "Compartiendo",
|
"library_page_sharing": "Compartiendo",
|
||||||
"library_page_sort_created": "Creado más recientemente",
|
"library_page_sort_created": "Creado más recientemente",
|
||||||
"library_page_sort_title": "Título del álbum",
|
"library_page_sort_title": "Título del álbum",
|
||||||
"login_form_api_exception": "API exception. Please check the server URL and try again.",
|
"login_disabled": "Login has been disabled",
|
||||||
|
"login_form_api_exception": "Excepción producida por API. Por favor, verifica el URL del servidor e inténtalo de nuevo.",
|
||||||
"login_form_button_text": "Iniciar Sesión",
|
"login_form_button_text": "Iniciar Sesión",
|
||||||
"login_form_email_hint": "tucorreo@correo.com",
|
"login_form_email_hint": "tucorreo@correo.com",
|
||||||
"login_form_endpoint_hint": "http://tu-ip-de-servidor:puerto/api",
|
"login_form_endpoint_hint": "http://tu-ip-de-servidor:puerto/api",
|
||||||
@@ -176,118 +183,122 @@
|
|||||||
"login_form_err_invalid_url": "URL no válida",
|
"login_form_err_invalid_url": "URL no válida",
|
||||||
"login_form_err_leading_whitespace": "Espacio en blanco inicial",
|
"login_form_err_leading_whitespace": "Espacio en blanco inicial",
|
||||||
"login_form_err_trailing_whitespace": "Espacio en blanco al final",
|
"login_form_err_trailing_whitespace": "Espacio en blanco al final",
|
||||||
"login_form_failed_get_oauth_server_config": "Error logging using OAuth, check server URL",
|
"login_form_failed_get_oauth_server_config": "Error al iniciar sesión con OAuth, verifica la URL del servidor",
|
||||||
"login_form_failed_get_oauth_server_disable": "OAuth feature is not available on this server",
|
"login_form_failed_get_oauth_server_disable": "La función de OAuth no está disponible en este servidor",
|
||||||
"login_form_failed_login": "Error logging you in, check server URL, email and password",
|
"login_form_failed_login": "Error al iniciar sesión, comprueba la URL del servidor, el correo electrónico y la contraseña",
|
||||||
"login_form_label_email": "Correo",
|
"login_form_label_email": "Correo",
|
||||||
"login_form_label_password": "Contraseña",
|
"login_form_label_password": "Contraseña",
|
||||||
"login_form_next_button": "Next",
|
"login_form_next_button": "Siguiente",
|
||||||
"login_form_password_hint": "contraseña",
|
"login_form_password_hint": "contraseña",
|
||||||
"login_form_save_login": "Mantener la sesión iniciada",
|
"login_form_save_login": "Mantener la sesión iniciada",
|
||||||
"login_form_server_empty": "Enter a server URL.",
|
"login_form_server_empty": "Agrega la URL del servidor.",
|
||||||
"login_form_server_error": "Could not connect to server.",
|
"login_form_server_error": "No se pudo conectar al servidor.",
|
||||||
"monthly_title_text_date_format": "MMMM y",
|
"monthly_title_text_date_format": "MMMM y",
|
||||||
"motion_photos_page_title": "Motion Photos",
|
"motion_photos_page_title": "Foto en Movimiento",
|
||||||
"notification_permission_dialog_cancel": "Cancel",
|
"notification_permission_dialog_cancel": "Cancelar",
|
||||||
"notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.",
|
"notification_permission_dialog_content": "Para activar las notificaciones, ve a Configuración y selecciona permitir.",
|
||||||
"notification_permission_dialog_settings": "Settings",
|
"notification_permission_dialog_settings": "Ajustes",
|
||||||
"notification_permission_list_tile_content": "Grant permission to enable notifications.",
|
"notification_permission_list_tile_content": "Concede permiso para habilitar las notificaciones.",
|
||||||
"notification_permission_list_tile_enable_button": "Enable Notifications",
|
"notification_permission_list_tile_enable_button": "Permitir notificaciones",
|
||||||
"notification_permission_list_tile_title": "Notification Permission",
|
"notification_permission_list_tile_title": "Permisos de Notificacion",
|
||||||
"partner_page_add_partner": "Add partner",
|
"partner_page_add_partner": "Agregar compañero",
|
||||||
"partner_page_empty_message": "Your photos are not yet shared with any partner.",
|
"partner_page_empty_message": "Tus fotos aún no se han compartido con ningún compañero.",
|
||||||
"partner_page_no_more_users": "No more users to add",
|
"partner_page_no_more_users": "No hay más usuarios para agregar",
|
||||||
"partner_page_partner_add_failed": "Failed to add partner",
|
"partner_page_partner_add_failed": "Compañero no pudo ser agregado ",
|
||||||
"partner_page_select_partner": "Select partner",
|
"partner_page_select_partner": "Seleccionar compañero",
|
||||||
"partner_page_shared_to_title": "Shared to",
|
"partner_page_shared_to_title": "Compartido con",
|
||||||
"partner_page_stop_sharing_content": "{} will no longer be able to access your photos.",
|
"partner_page_stop_sharing_content": "{} ya no podrá acceder a tus fotos",
|
||||||
"partner_page_stop_sharing_title": "Stop sharing your photos?",
|
"partner_page_stop_sharing_title": "¿Dejar de compartir tus fotos?",
|
||||||
"partner_page_title": "Partner",
|
"partner_page_title": "Compañero",
|
||||||
"permission_onboarding_continue_anyway": "Continue anyway",
|
"permission_onboarding_continue_anyway": "Continuar de todos modos",
|
||||||
"permission_onboarding_get_started": "Get started",
|
"permission_onboarding_get_started": "Empezar",
|
||||||
"permission_onboarding_go_to_settings": "Go to settings",
|
"permission_onboarding_go_to_settings": "Ir a configuración",
|
||||||
"permission_onboarding_grant_permission": "Grant permission",
|
"permission_onboarding_grant_permission": "Conceder permiso",
|
||||||
"permission_onboarding_log_out": "Log out",
|
"permission_onboarding_log_out": "Cerrar sesión",
|
||||||
"permission_onboarding_permission_denied": "Permission denied. To use Immich, grant photo and video permissions in Settings.",
|
"permission_onboarding_permission_denied": "Permiso denegado. Para usar Immich, concede permisos de fotos y videos en Configuración.",
|
||||||
"permission_onboarding_permission_granted": "Permission granted! You are all set.",
|
"permission_onboarding_permission_granted": "¡Permiso concedido! Todo listo.",
|
||||||
"permission_onboarding_permission_limited": "Permission limited. To let Immich backup and manage your entire gallery collection, grant photo and video permissions in Settings.",
|
"permission_onboarding_permission_limited": "Permiso limitado. Para permitir que Immich haga copia de seguridad y gestione toda tu colección de galería, concede permisos de fotos y videos en Configuración.",
|
||||||
"permission_onboarding_request": "Immich requires permission to view your photos and videos.",
|
"permission_onboarding_request": "Immich requiere permiso para ver tus fotos y videos.",
|
||||||
"profile_drawer_app_logs": "Logs",
|
"profile_drawer_app_logs": "Registros",
|
||||||
"profile_drawer_client_server_up_to_date": "El Cliente y el Servidor están actualizados",
|
"profile_drawer_client_server_up_to_date": "El Cliente y el Servidor están actualizados",
|
||||||
"profile_drawer_settings": "Settings",
|
"profile_drawer_settings": "Configuración",
|
||||||
"profile_drawer_sign_out": "Cerrar Sesión",
|
"profile_drawer_sign_out": "Cerrar Sesión",
|
||||||
"recently_added_page_title": "Recently Added",
|
"recently_added_page_title": "Recién Agregadas",
|
||||||
"search_bar_hint": "Busca tus fotos",
|
"search_bar_hint": "Busca tus fotos",
|
||||||
"search_page_categories": "Categories",
|
"search_page_categories": "Categorías",
|
||||||
"search_page_favorites": "Favorites",
|
"search_page_favorites": "Favoritos",
|
||||||
"search_page_motion_photos": "Motion Photos",
|
"search_page_motion_photos": "Foto en Movimiento",
|
||||||
"search_page_no_objects": "No Objects Info Available",
|
"search_page_no_objects": "No hay información de objetos disponibles",
|
||||||
"search_page_no_places": "No hay información de lugares disponibles",
|
"search_page_no_places": "No hay información de lugares disponibles",
|
||||||
"search_page_people": "People",
|
"search_page_people": "Personas",
|
||||||
"search_page_places": "Lugares",
|
"search_page_places": "Lugares",
|
||||||
"search_page_recently_added": "Recently added",
|
"search_page_recently_added": "Recién agregadas",
|
||||||
"search_page_screenshots": "Screenshots",
|
"search_page_screenshots": "Capturas de pantalla",
|
||||||
"search_page_selfies": "Selfies",
|
"search_page_selfies": "Selfies",
|
||||||
"search_page_things": "Cosas",
|
"search_page_things": "Cosas",
|
||||||
"search_page_videos": "Videos",
|
"search_page_videos": "Videos",
|
||||||
"search_page_view_all_button": "View all",
|
"search_page_view_all_button": "Ver todo",
|
||||||
"search_page_your_activity": "Your activity",
|
"search_page_your_activity": "Tu actividad",
|
||||||
"search_result_page_new_search_hint": "Nueva Busqueda",
|
"search_result_page_new_search_hint": "Nueva Busqueda",
|
||||||
"search_suggestion_list_smart_search_hint_1": "Smart search is enabled by default, to search for metadata use the syntax ",
|
"search_suggestion_list_smart_search_hint_1": "La búsqueda inteligente está habilitada por defecto, para buscar metadatos utiliza esta sintaxis ",
|
||||||
"search_suggestion_list_smart_search_hint_2": "m:your-search-term",
|
"search_suggestion_list_smart_search_hint_2": "m:tu-término-de-búsqueda",
|
||||||
"select_additional_user_for_sharing_page_suggestions": "Sugerencias",
|
"select_additional_user_for_sharing_page_suggestions": "Sugerencias",
|
||||||
"select_user_for_sharing_page_err_album": "Fallo al crear el álbum",
|
"select_user_for_sharing_page_err_album": "Fallo al crear el álbum",
|
||||||
"select_user_for_sharing_page_share_suggestions": "Suggestions",
|
"select_user_for_sharing_page_share_suggestions": "Sugerencias",
|
||||||
"server_info_box_app_version": "App Version",
|
"server_info_box_app_version": "Versión de la Aplicación",
|
||||||
"server_info_box_server_version": "Server Version",
|
"server_info_box_server_version": "Versión del Servidor",
|
||||||
"setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).",
|
"setting_image_viewer_help": "El visor de detalles carga primero la miniatura pequeña, luego carga la vista previa de tamaño mediano (si está habilitada), finalmente carga la original (si está habilitada).",
|
||||||
"setting_image_viewer_original_subtitle": "Habilitar para cargar la imagen en resolución original (¡muy grande!). Deshabilitar para reducir el consumo de datos (de red y caché).",
|
"setting_image_viewer_original_subtitle": "Activar para cargar la imagen en resolución original (¡muy grande!). Deshabilitar para reducir el consumo de datos (de red y caché).",
|
||||||
"setting_image_viewer_original_title": "Cargar imagen original",
|
"setting_image_viewer_original_title": "Cargar imagen original",
|
||||||
"setting_image_viewer_preview_subtitle": "Habilitar para cargar una imagen de resolución media. Deshabilitar para cargar directamente la imagen original o usar una miniatura.",
|
"setting_image_viewer_preview_subtitle": "Activar para cargar una imagen de resolución media. Deshabilitar para cargar directamente la imagen original o usar una miniatura.",
|
||||||
"setting_image_viewer_preview_title": "Cargar imagen de previsualización",
|
"setting_image_viewer_preview_title": "Cargar imagen de previsualización",
|
||||||
"setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}",
|
"setting_notifications_notify_failures_grace_period": "Notificar fallos de copia de seguridad en segundo plano: {}",
|
||||||
"setting_notifications_notify_hours": "{} hours",
|
"setting_notifications_notify_hours": "{} horas",
|
||||||
"setting_notifications_notify_immediately": "immediately",
|
"setting_notifications_notify_immediately": "inmediatamente",
|
||||||
"setting_notifications_notify_minutes": "{} minutes",
|
"setting_notifications_notify_minutes": "{} minutos",
|
||||||
"setting_notifications_notify_never": "never",
|
"setting_notifications_notify_never": "nunca",
|
||||||
"setting_notifications_notify_seconds": "{} seconds",
|
"setting_notifications_notify_seconds": "{} segundos",
|
||||||
"setting_notifications_single_progress_subtitle": "Detailed upload progress information per asset",
|
"setting_notifications_single_progress_subtitle": "Información detallada del progreso de subida de cada archivo",
|
||||||
"setting_notifications_single_progress_title": "Show background backup detail progress",
|
"setting_notifications_single_progress_title": "Mostrar progreso detallado de copia de seguridad en segundo plano",
|
||||||
"setting_notifications_subtitle": "Adjust your notification preferences",
|
"setting_notifications_subtitle": "Ajusta tus preferencias de notificación",
|
||||||
"setting_notifications_title": "Notifications",
|
"setting_notifications_title": "Notificaciones",
|
||||||
"setting_notifications_total_progress_subtitle": "Overall upload progress (done/total assets)",
|
"setting_notifications_total_progress_subtitle": "Progreso general de subida (archivos completados/total)",
|
||||||
"setting_notifications_total_progress_title": "Show background backup total progress",
|
"setting_notifications_total_progress_title": "Mostrar progreso total de copia de seguridad en segundo plano",
|
||||||
"setting_pages_app_bar_settings": "Settings",
|
"setting_pages_app_bar_settings": "Ajustes",
|
||||||
"settings_require_restart": "Please restart Immich to apply this setting",
|
"settings_require_restart": "Por favor, reinicia Immich para aplicar este ajuste",
|
||||||
"share_add": "Añadir",
|
"share_add": "Agregar",
|
||||||
"share_add_photos": "Añadir fotos",
|
"share_add_photos": "Agregar fotos",
|
||||||
"share_add_title": "Añadir un título",
|
"share_add_title": "Agregar un título",
|
||||||
"share_create_album": "Crear álbum",
|
"share_create_album": "Crear álbum",
|
||||||
"share_dialog_preparing": "Preparing...",
|
"share_dialog_preparing": "Preparando...",
|
||||||
"share_invite": "Invitar al álbum",
|
"share_invite": "Invitar al álbum",
|
||||||
"sharing_page_album": "Álbumes compartidos",
|
"sharing_page_album": "Álbumes compartidos",
|
||||||
"sharing_page_description": "Crea álbumes compartidos para compartir fotos y vídeos con las personas de tu red.",
|
"sharing_page_description": "Crea álbumes compartidos para compartir fotos y vídeos con las personas de tu red.",
|
||||||
"sharing_page_empty_list": "LISTA VACIA",
|
"sharing_page_empty_list": "LISTA VACIA",
|
||||||
"sharing_silver_appbar_create_shared_album": "Crear un álbum compartido",
|
"sharing_silver_appbar_create_shared_album": "Crear un álbum compartido",
|
||||||
"sharing_silver_appbar_share_partner": "Compartir con el compañero",
|
"sharing_silver_appbar_share_partner": "Compartir con el compañero",
|
||||||
"tab_controller_nav_library": "Library",
|
"tab_controller_nav_library": "Biblioteca",
|
||||||
"tab_controller_nav_photos": "Fotos",
|
"tab_controller_nav_photos": "Fotos",
|
||||||
"tab_controller_nav_search": "Buscar",
|
"tab_controller_nav_search": "Buscar",
|
||||||
"tab_controller_nav_sharing": "Compartiendo",
|
"tab_controller_nav_sharing": "Compartiendo",
|
||||||
"theme_setting_asset_list_storage_indicator_title": "Show storage indicator on asset tiles",
|
"theme_setting_asset_list_storage_indicator_title": "Mostrar indicador de almacenamiento en las miniaturas de los archivos",
|
||||||
"theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({})",
|
"theme_setting_asset_list_tiles_per_row_title": "Número de activos por fila ({})",
|
||||||
"theme_setting_dark_mode_switch": "Dark mode",
|
"theme_setting_dark_mode_switch": "Modo oscuro",
|
||||||
"theme_setting_image_viewer_quality_subtitle": "Adjust the quality of the detail image viewer",
|
"theme_setting_image_viewer_quality_subtitle": "Ajustar la calidad del visor de detalles de imágenes",
|
||||||
"theme_setting_image_viewer_quality_title": "Image viewer quality",
|
"theme_setting_image_viewer_quality_title": "Calidad del visor de imágenes",
|
||||||
"theme_setting_system_theme_switch": "Automatic (Follow system setting)",
|
"theme_setting_system_theme_switch": "Automático (seguir ajuste del sistema)",
|
||||||
"theme_setting_theme_subtitle": "Choose the app's theme setting",
|
"theme_setting_theme_subtitle": "Elige la configuración del tema de la aplicación",
|
||||||
"theme_setting_theme_title": "Theme",
|
"theme_setting_theme_title": "Tema",
|
||||||
"theme_setting_three_stage_loading_subtitle": "Three-stage loading might increase the loading performance but causes significantly higher network load",
|
"theme_setting_three_stage_loading_subtitle": "La carga en tres etapas puede aumentar el rendimiento de carga pero provoca un consumo de red significativamente mayor",
|
||||||
"theme_setting_three_stage_loading_title": "Enable three-stage loading",
|
"theme_setting_three_stage_loading_title": "Activar carga en tres etapas",
|
||||||
"version_announcement_overlay_ack": "Reconocer",
|
"upload_dialog_cancel": "Cancel",
|
||||||
|
"upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?",
|
||||||
|
"upload_dialog_ok": "Upload",
|
||||||
|
"upload_dialog_title": "Upload Asset",
|
||||||
|
"version_announcement_overlay_ack": "Aceptar",
|
||||||
"version_announcement_overlay_release_notes": "notas de versión",
|
"version_announcement_overlay_release_notes": "notas de versión",
|
||||||
"version_announcement_overlay_text_1": "Hola amigo, hay una nueva versión de",
|
"version_announcement_overlay_text_1": "Hola amigo, hay una nueva versión de",
|
||||||
"version_announcement_overlay_text_2": "tómese su tiempo para visitar la ",
|
"version_announcement_overlay_text_2": "por favor, tómate tu tiempo para visitar las ",
|
||||||
"version_announcement_overlay_text_3": "y asegurate de que tu configuración de docker-compose y .env está actualizada para evitar cualquier desconfiguración, especialmente si utiliza WatchTower o cualquier mecanismo que se encargue de actualizar su aplicación de servidor automáticamente.",
|
"version_announcement_overlay_text_3": " y asegúrate de que la configuración de docker-compose y .env estén actualizadas para evitar cualquier error de configuración, especialmente si utilizas WatchTower o cualquier mecanismo que actualice automáticamente la aplicación del servidor.",
|
||||||
"version_announcement_overlay_title": "Nueva versión del servidor disponible \uD83C\uDF89"
|
"version_announcement_overlay_title": "Nueva versión del servidor disponible \uD83C\uDF89"
|
||||||
}
|
}
|
||||||
@@ -1,69 +1,69 @@
|
|||||||
{
|
{
|
||||||
"add_to_album_bottom_sheet_added": "Added to {album}",
|
"add_to_album_bottom_sheet_added": "Agregado a {album}",
|
||||||
"add_to_album_bottom_sheet_already_exists": "Already in {album}",
|
"add_to_album_bottom_sheet_already_exists": "Ya se encuentra en {album}",
|
||||||
"advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from assets on the device. Activate this setting to load remote images instead.",
|
"advanced_settings_prefer_remote_subtitle": "Algunos dispositivos tardan mucho en cargar las miniaturas de recursos encontrados el dispositivo. Activa esta opción para cargar imágenes remotas en su lugar.",
|
||||||
"advanced_settings_prefer_remote_title": "Prefer remote images",
|
"advanced_settings_prefer_remote_title": "Preferir imágenes remotas",
|
||||||
"advanced_settings_tile_subtitle": "Advanced user's settings",
|
"advanced_settings_tile_subtitle": "Configuraciones avanzadas del usuario",
|
||||||
"advanced_settings_tile_title": "Advanced",
|
"advanced_settings_tile_title": "Avanzado",
|
||||||
"advanced_settings_troubleshooting_subtitle": "Enable additional features for troubleshooting",
|
"advanced_settings_troubleshooting_subtitle": "Habilitar funciones adicionales para solución de problemas",
|
||||||
"advanced_settings_troubleshooting_title": "Troubleshooting",
|
"advanced_settings_troubleshooting_title": "Solución de problemas",
|
||||||
"album_info_card_backup_album_excluded": "EXCLUIDOS",
|
"album_info_card_backup_album_excluded": "EXCLUIDOS",
|
||||||
"album_info_card_backup_album_included": "INCLUIDOS",
|
"album_info_card_backup_album_included": "INCLUIDOS",
|
||||||
"album_thumbnail_card_item": "1 elemento",
|
"album_thumbnail_card_item": "1 elemento",
|
||||||
"album_thumbnail_card_items": "{} elementos",
|
"album_thumbnail_card_items": "{} elementos",
|
||||||
"album_thumbnail_card_shared": " · Compartido",
|
"album_thumbnail_card_shared": " · Compartido",
|
||||||
"album_thumbnail_owned": "Owned",
|
"album_thumbnail_owned": "Propio",
|
||||||
"album_thumbnail_shared_by": "Shared by {}",
|
"album_thumbnail_shared_by": "Compartido por {}",
|
||||||
"album_viewer_appbar_share_delete": "Eliminar álbum",
|
"album_viewer_appbar_share_delete": "Eliminar álbum",
|
||||||
"album_viewer_appbar_share_err_delete": "No se ha podido eliminar el álbum",
|
"album_viewer_appbar_share_err_delete": "No se ha podido eliminar el álbum",
|
||||||
"album_viewer_appbar_share_err_leave": "No se ha podido abandonar el álbum",
|
"album_viewer_appbar_share_err_leave": "No se ha podido abandonar el álbum",
|
||||||
"album_viewer_appbar_share_err_remove": "Hay problemas para eliminar recursos del álbum",
|
"album_viewer_appbar_share_err_remove": "Hay problemas para eliminar los archivos del álbum",
|
||||||
"album_viewer_appbar_share_err_title": "Error al cambiar el título del álbum",
|
"album_viewer_appbar_share_err_title": "Error al cambiar el título del álbum",
|
||||||
"album_viewer_appbar_share_leave": "Abandonar álbum ",
|
"album_viewer_appbar_share_leave": "Abandonar álbum ",
|
||||||
"album_viewer_appbar_share_remove": "Eliminar del álbum",
|
"album_viewer_appbar_share_remove": "Eliminar del álbum",
|
||||||
"album_viewer_page_share_add_users": "Añadir usuarios",
|
"album_viewer_page_share_add_users": "Agregar usuarios",
|
||||||
"all_people_page_title": "People",
|
"all_people_page_title": "Personas",
|
||||||
"all_videos_page_title": "Videos",
|
"all_videos_page_title": "Videos",
|
||||||
"archive_page_no_archived_assets": "No archived assets found",
|
"archive_page_no_archived_assets": "No se encontraron recursos archivados",
|
||||||
"archive_page_title": "Archive ({})",
|
"archive_page_title": "Archivo ({})",
|
||||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamic layout",
|
"asset_list_layout_settings_dynamic_layout_title": "Diseño dinámico",
|
||||||
"asset_list_layout_settings_group_automatically": "Automatic",
|
"asset_list_layout_settings_group_automatically": "Automatico",
|
||||||
"asset_list_layout_settings_group_by": "Group assets by",
|
"asset_list_layout_settings_group_by": "Agrupar recursos por",
|
||||||
"asset_list_layout_settings_group_by_month": "Month",
|
"asset_list_layout_settings_group_by_month": "Mes",
|
||||||
"asset_list_layout_settings_group_by_month_day": "Month + day",
|
"asset_list_layout_settings_group_by_month_day": "Mes + día",
|
||||||
"asset_list_settings_subtitle": "Photo grid layout settings",
|
"asset_list_settings_subtitle": "Configuraciones del diseño de la cuadrícula de fotos",
|
||||||
"asset_list_settings_title": "Photo Grid",
|
"asset_list_settings_title": "Cuadrícula de fotos",
|
||||||
"backup_album_selection_page_albums_device": "Álbumes en el dispositivo ({})",
|
"backup_album_selection_page_albums_device": "Álbumes en el dispositivo ({})",
|
||||||
"backup_album_selection_page_albums_tap": "Pulsar para incluir, pulsar dos veces para excluir",
|
"backup_album_selection_page_albums_tap": "Pulsar para incluir, pulsar dos veces para excluir",
|
||||||
"backup_album_selection_page_assets_scatter": "Los recursos pueden dispersarse entre varios álbumes. Por lo tanto, los álbumes pueden incluirse o excluirse durante el proceso de respaldo.",
|
"backup_album_selection_page_assets_scatter": "Los archivos pueden dispersarse en varios álbumes. De este modo, los álbumes pueden ser incluidos o excluidos durante el proceso de copia de seguridad.",
|
||||||
"backup_album_selection_page_select_albums": "Seleccionar álbumes",
|
"backup_album_selection_page_select_albums": "Seleccionar álbumes",
|
||||||
"backup_album_selection_page_selection_info": "Información de la selección",
|
"backup_album_selection_page_selection_info": "Información de la selección",
|
||||||
"backup_album_selection_page_total_assets": "Total de recursos únicos",
|
"backup_album_selection_page_total_assets": "Total de archivos únicos",
|
||||||
"backup_all": "Todos",
|
"backup_all": "Todos",
|
||||||
"backup_background_service_backup_failed_message": "Failed to backup assets. Retrying…",
|
"backup_background_service_backup_failed_message": "Error al copiar archivos. Reintentando...",
|
||||||
"backup_background_service_connection_failed_message": "Failed to connect to the server. Retrying…",
|
"backup_background_service_connection_failed_message": "Error al conectar con el servidor. Reintentando...",
|
||||||
"backup_background_service_current_upload_notification": "Cargando {}",
|
"backup_background_service_current_upload_notification": "Cargando {}",
|
||||||
"backup_background_service_default_notification": "Comprobando por nuevos recursos...",
|
"backup_background_service_default_notification": "Verificando si hay nuevos archivos",
|
||||||
"backup_background_service_error_title": "Error al respaldar",
|
"backup_background_service_error_title": "Error de copia de seguridad",
|
||||||
"backup_background_service_in_progress_notification": "Respaldando tus recursos...",
|
"backup_background_service_in_progress_notification": "Creando copia de seguridad de tus archivos...",
|
||||||
"backup_background_service_upload_failure_notification": "Error al cargar {}",
|
"backup_background_service_upload_failure_notification": "Error al cargar {}",
|
||||||
"backup_controller_page_albums": "Álbumes de respaldo",
|
"backup_controller_page_albums": "Álbumes de respaldo",
|
||||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
"backup_controller_page_background_app_refresh_disabled_content": "Activa la actualización en segundo plano de la aplicación en Configuración > General > Actualización en segundo plano para usar la copia de seguridad en segundo plano.",
|
||||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
"backup_controller_page_background_app_refresh_disabled_title": "Actualización en segundo plano desactivada",
|
||||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
"backup_controller_page_background_app_refresh_enable_button_text": "Ir a configuración",
|
||||||
"backup_controller_page_background_battery_info_link": "Show me how",
|
"backup_controller_page_background_battery_info_link": "Muestrame cómo",
|
||||||
"backup_controller_page_background_battery_info_message": "For the best background backup experience, please disable any battery optimizations restricting background activity for Immich.\n\nSince this is device-specific, please lookup the required information for your device manufacturer.",
|
"backup_controller_page_background_battery_info_message": "Para obtener la mejor experiencia de copia de seguridad en segundo plano, desactiva cualquier optimización de batería que restrinja la actividad en segundo plano para Immich.\n\nDado que esto es específico en cada dispositivo, busca la información necesaria de el fabricante de tu dispositivo.",
|
||||||
"backup_controller_page_background_battery_info_ok": "OK",
|
"backup_controller_page_background_battery_info_ok": "Ok",
|
||||||
"backup_controller_page_background_battery_info_title": "Battery optimizations",
|
"backup_controller_page_background_battery_info_title": "Optimizaciones de batería",
|
||||||
"backup_controller_page_background_charging": "Only while charging",
|
"backup_controller_page_background_charging": "Solo mientras se carga",
|
||||||
"backup_controller_page_background_configure_error": "Failed to configure the background service",
|
"backup_controller_page_background_configure_error": "Error al configurar el servicio en segundo plano",
|
||||||
"backup_controller_page_background_delay": "Delay new assets backup: {}",
|
"backup_controller_page_background_delay": "Retraso en la copia de seguridad de nuevos activos: {}",
|
||||||
"backup_controller_page_background_description": "Turn on the background service to automatically backup any new assets without needing to open the app",
|
"backup_controller_page_background_description": "Activa el servicio en segundo plano para copiar automáticamente cualquier nuevos archivos sin necesidad de abrir la aplicación.",
|
||||||
"backup_controller_page_background_is_off": "Automatic background backup is off",
|
"backup_controller_page_background_is_off": "La copia de seguridad en segundo plano automática está desactivada",
|
||||||
"backup_controller_page_background_is_on": "Automatic background backup is on",
|
"backup_controller_page_background_is_on": "La copia de seguridad en segundo plano automática está desactivada",
|
||||||
"backup_controller_page_background_turn_off": "Turn off background service",
|
"backup_controller_page_background_turn_off": "Desactivar el servicio en segundo plano",
|
||||||
"backup_controller_page_background_turn_on": "Turn on background service",
|
"backup_controller_page_background_turn_on": "Activar el servicio en segundo plano",
|
||||||
"backup_controller_page_background_wifi": "Only on WiFi",
|
"backup_controller_page_background_wifi": "Solo en WiFi",
|
||||||
"backup_controller_page_backup": "Respaldo",
|
"backup_controller_page_backup": "Respaldo",
|
||||||
"backup_controller_page_backup_selected": "Seleccionado:",
|
"backup_controller_page_backup_selected": "Seleccionado:",
|
||||||
"backup_controller_page_backup_sub": "Fotos y videos respaldados",
|
"backup_controller_page_backup_sub": "Fotos y videos respaldados",
|
||||||
@@ -72,12 +72,12 @@
|
|||||||
"backup_controller_page_desc_backup": "Activa la copia de seguridad en primer plano para cargar automáticamente nuevos recursos al servidor al abrir la aplicación.",
|
"backup_controller_page_desc_backup": "Activa la copia de seguridad en primer plano para cargar automáticamente nuevos recursos al servidor al abrir la aplicación.",
|
||||||
"backup_controller_page_excluded": "Excluido:",
|
"backup_controller_page_excluded": "Excluido:",
|
||||||
"backup_controller_page_failed": "Fallidos ({})",
|
"backup_controller_page_failed": "Fallidos ({})",
|
||||||
"backup_controller_page_filename": "Nombre: {} [{}]",
|
"backup_controller_page_filename": "Nombre del archivo: {} [{}]",
|
||||||
"backup_controller_page_id": "ID: {}",
|
"backup_controller_page_id": "ID: {}",
|
||||||
"backup_controller_page_info": "Información del respaldo",
|
"backup_controller_page_info": "Información del respaldo",
|
||||||
"backup_controller_page_none_selected": "Ninguno seleccionado",
|
"backup_controller_page_none_selected": "Ninguno seleccionado",
|
||||||
"backup_controller_page_remainder": "Restante",
|
"backup_controller_page_remainder": "Restante",
|
||||||
"backup_controller_page_remainder_sub": "Fotos y videos restantes de la selección a los que realizar un respaldo",
|
"backup_controller_page_remainder_sub": "Fotos y videos restantes para hacer una copia de seguridad de la selección",
|
||||||
"backup_controller_page_select": "Seleccionar",
|
"backup_controller_page_select": "Seleccionar",
|
||||||
"backup_controller_page_server_storage": "Almacenamiento del servidor",
|
"backup_controller_page_server_storage": "Almacenamiento del servidor",
|
||||||
"backup_controller_page_start_backup": "Iniciar respaldo",
|
"backup_controller_page_start_backup": "Iniciar respaldo",
|
||||||
@@ -89,48 +89,53 @@
|
|||||||
"backup_controller_page_total_sub": "Todas las fotos y videos únicos de los álbumes seleccionados",
|
"backup_controller_page_total_sub": "Todas las fotos y videos únicos de los álbumes seleccionados",
|
||||||
"backup_controller_page_turn_off": "Desactivar la copia de seguridad en primer plano",
|
"backup_controller_page_turn_off": "Desactivar la copia de seguridad en primer plano",
|
||||||
"backup_controller_page_turn_on": "Activar la copia de seguridad en primer plano",
|
"backup_controller_page_turn_on": "Activar la copia de seguridad en primer plano",
|
||||||
"backup_controller_page_uploading_file_info": "Info de carga de archivo",
|
"backup_controller_page_uploading_file_info": "Cargando información del archivo",
|
||||||
"backup_err_only_album": "No se puede eliminar el único álbum",
|
"backup_err_only_album": "No se puede eliminar el único álbum",
|
||||||
"backup_info_card_assets": "recursos",
|
"backup_info_card_assets": "archivos",
|
||||||
"cache_settings_album_thumbnails": "Library page thumbnails ({} assets)",
|
"backup_manual_cancelled": "Cancelled",
|
||||||
"cache_settings_clear_cache_button": "Clear cache",
|
"backup_manual_failed": "Failed",
|
||||||
"cache_settings_clear_cache_button_title": "Clears the app's cache. This will significantly impact the app's performance until the cache has rebuilt.",
|
"backup_manual_in_progress": "Upload already in progress. Try after sometime",
|
||||||
"cache_settings_image_cache_size": "Image cache size ({} assets)",
|
"backup_manual_success": "Success",
|
||||||
"cache_settings_statistics_album": "Library thumbnails",
|
"backup_manual_title": "Upload status",
|
||||||
"cache_settings_statistics_assets": "{} assets ({})",
|
"cache_settings_album_thumbnails": "Miniaturas de la página de la biblioteca ({} archivos)",
|
||||||
"cache_settings_statistics_full": "Full images",
|
"cache_settings_clear_cache_button": "Borrar caché",
|
||||||
"cache_settings_statistics_shared": "Shared album thumbnails",
|
"cache_settings_clear_cache_button_title": "Borra la caché de la aplicación. Esto afectará significativamente el rendimiento de la aplicación hasta que se reconstruya la caché.",
|
||||||
"cache_settings_statistics_thumbnail": "Thumbnails",
|
"cache_settings_image_cache_size": "Tamaño de la caché de imágenes ({} archivos)",
|
||||||
"cache_settings_statistics_title": "Cache usage",
|
"cache_settings_statistics_album": "Miniaturas de la biblioteca",
|
||||||
"cache_settings_subtitle": "Control the caching behaviour of the Immich mobile application",
|
"cache_settings_statistics_assets": "{} archivos ({})",
|
||||||
"cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)",
|
"cache_settings_statistics_full": "Imágenes completas",
|
||||||
"cache_settings_title": "Caching Settings",
|
"cache_settings_statistics_shared": "Miniaturas de álbumes compartidos",
|
||||||
"change_password_form_confirm_password": "Confirm Password",
|
"cache_settings_statistics_thumbnail": "Miniaturas",
|
||||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
"cache_settings_statistics_title": "Uso de caché",
|
||||||
"change_password_form_new_password": "New Password",
|
"cache_settings_subtitle": "Controla el comportamiento del almacenamiento en caché de la aplicación móvil Immich",
|
||||||
"change_password_form_password_mismatch": "Passwords do not match",
|
"cache_settings_thumbnail_size": "Tamaño de la caché de miniaturas ({} archivos)",
|
||||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
"cache_settings_title": "Configuración de la caché",
|
||||||
"common_add_to_album": "Add to album",
|
"change_password_form_confirm_password": "Confirmar Contraseña",
|
||||||
"common_change_password": "Change Password",
|
"change_password_form_description": "Hola {firstName} {lastName},\n\nEsta es la primera vez que inicias sesión en el sistema o se ha solicitado cambiar tu contraseña. Por favor, introduce la nueva contraseña a continuación.",
|
||||||
"common_create_new_album": "Create new album",
|
"change_password_form_new_password": "Nueva Contraseña",
|
||||||
"common_server_error": "Please check your network connection, make sure the server is reachable and app/server versions are compatible.",
|
"change_password_form_password_mismatch": "Las contraseñas no coinciden",
|
||||||
"common_shared": "Shared",
|
"change_password_form_reenter_new_password": "Vuelve a ingresar la nueva contraseña",
|
||||||
"control_bottom_app_bar_add_to_album": "Add to album",
|
"common_add_to_album": "Agregar al álbum",
|
||||||
"control_bottom_app_bar_album_info": "{} items",
|
"common_change_password": "Cambiar Contraseña",
|
||||||
"control_bottom_app_bar_album_info_shared": "{} items · Shared",
|
"common_create_new_album": "Crear nuevo álbum",
|
||||||
"control_bottom_app_bar_archive": "Archive",
|
"common_server_error": "Por favor, verifica tu conexión de red, asegúrate de que el servidor esté accesible y las versiones de la aplicación y del servidor sean compatibles.",
|
||||||
"control_bottom_app_bar_create_new_album": "Create new album",
|
"common_shared": "Compartido",
|
||||||
|
"control_bottom_app_bar_add_to_album": "Agregar al álbum",
|
||||||
|
"control_bottom_app_bar_album_info": "{} elementos",
|
||||||
|
"control_bottom_app_bar_album_info_shared": "{} elementos · Compartidos",
|
||||||
|
"control_bottom_app_bar_archive": "Archivar",
|
||||||
|
"control_bottom_app_bar_create_new_album": "Crear nuevo álbum",
|
||||||
"control_bottom_app_bar_delete": "Eliminar",
|
"control_bottom_app_bar_delete": "Eliminar",
|
||||||
"control_bottom_app_bar_favorite": "Favorite",
|
"control_bottom_app_bar_favorite": "Favorito",
|
||||||
"control_bottom_app_bar_share": "Compartir",
|
"control_bottom_app_bar_share": "Compartir",
|
||||||
"control_bottom_app_bar_unarchive": "Unarchive",
|
"control_bottom_app_bar_unarchive": "Desarchivar",
|
||||||
"create_album_page_untitled": "Sin título",
|
"create_album_page_untitled": "Sin título",
|
||||||
"create_shared_album_page_create": "Crear",
|
"create_shared_album_page_create": "Crear",
|
||||||
"create_shared_album_page_share": "Compartir",
|
"create_shared_album_page_share": "Compartir",
|
||||||
"create_shared_album_page_share_add_assets": "AÑADIR RECURSOS",
|
"create_shared_album_page_share_add_assets": "AGREGAR ARCHIVOS",
|
||||||
"create_shared_album_page_share_select_photos": "Seleccionar fotos",
|
"create_shared_album_page_share_select_photos": "Seleccionar fotos",
|
||||||
"curated_location_page_title": "Places",
|
"curated_location_page_title": "Lugares",
|
||||||
"curated_object_page_title": "Things",
|
"curated_object_page_title": "Objetos",
|
||||||
"daily_title_text_date": "E, dd MMM",
|
"daily_title_text_date": "E, dd MMM",
|
||||||
"daily_title_text_date_year": "E, dd de MMM de yyyy",
|
"daily_title_text_date_year": "E, dd de MMM de yyyy",
|
||||||
"date_format": "E d, LLL y • h:mm a",
|
"date_format": "E d, LLL y • h:mm a",
|
||||||
@@ -138,130 +143,132 @@
|
|||||||
"delete_dialog_cancel": "Cancelar",
|
"delete_dialog_cancel": "Cancelar",
|
||||||
"delete_dialog_ok": "Eliminar",
|
"delete_dialog_ok": "Eliminar",
|
||||||
"delete_dialog_title": "Eliminar permanentemente",
|
"delete_dialog_title": "Eliminar permanentemente",
|
||||||
"description_input_hint_text": "Add description...",
|
"description_input_hint_text": "Agregar descripción...",
|
||||||
"description_input_submit_error": "Error updating description, check the log for more details",
|
"description_input_submit_error": "Error al actualizar la descripción, verifica el registro para obtener más detalles",
|
||||||
"exif_bottom_sheet_description": "Añadir descripción...",
|
"exif_bottom_sheet_description": "Agregar Descripción...",
|
||||||
"exif_bottom_sheet_details": "DETALLES",
|
"exif_bottom_sheet_details": "DETALLES",
|
||||||
"exif_bottom_sheet_location": "UBICACIÓN",
|
"exif_bottom_sheet_location": "UBICACIÓN",
|
||||||
"experimental_settings_new_asset_list_subtitle": "Work in progress",
|
"experimental_settings_new_asset_list_subtitle": "Trabajo en progreso",
|
||||||
"experimental_settings_new_asset_list_title": "Enable experimental photo grid",
|
"experimental_settings_new_asset_list_title": "Habilitar cuadrícula fotográfica experimental",
|
||||||
"experimental_settings_subtitle": "Use at your own risk!",
|
"experimental_settings_subtitle": "Úsalo bajo tu responsabilidad",
|
||||||
"experimental_settings_title": "Experimental",
|
"experimental_settings_title": "Experimental",
|
||||||
"favorites_page_no_favorites": "No favorite assets found",
|
"favorites_page_no_favorites": "No se encontraron recursos marcados como favoritos",
|
||||||
"favorites_page_title": "Favorites",
|
"favorites_page_title": "Favoritos",
|
||||||
"home_page_add_to_album_conflicts": "Added {added} assets to album {album}. {failed} assets are already in the album.",
|
"home_page_add_to_album_conflicts": "{added} elementos agregados al álbum {album}.\n{failed} elementos ya existen en el álbum.",
|
||||||
"home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping",
|
"home_page_add_to_album_err_local": "Aún no se pueden agregar recursos locales a álbumes, omitiendo",
|
||||||
"home_page_add_to_album_success": "Added {added} assets to album {album}.",
|
"home_page_add_to_album_success": "{added} elementos agregados al álbum {album}. ",
|
||||||
"home_page_archive_err_local": "Can not archive local assets yet, skipping",
|
"home_page_archive_err_local": "Los recursos locales no pueden ser archivados, omitiendo",
|
||||||
"home_page_building_timeline": "Building the timeline",
|
"home_page_building_timeline": "Construyendo la línea de tiempo",
|
||||||
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
|
"home_page_favorite_err_local": "Aún no se pueden archivar recursos locales, omitiendo",
|
||||||
"home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).",
|
"home_page_first_time_notice": "Si esta es la primera vez que usas la app, por favor, asegúrate de elegir un álbum de respaldo para que la línea de tiempo pueda cargar fotos y videos en los álbumes.",
|
||||||
"image_viewer_page_state_provider_download_error": "Download Error",
|
"home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping",
|
||||||
"image_viewer_page_state_provider_download_success": "Download Success",
|
"image_viewer_page_state_provider_download_error": "Error de descarga",
|
||||||
|
"image_viewer_page_state_provider_download_success": "Descarga exitosa",
|
||||||
"library_page_albums": "Álbumes",
|
"library_page_albums": "Álbumes",
|
||||||
"library_page_archive": "Archive",
|
"library_page_archive": "Archivo",
|
||||||
"library_page_device_albums": "Albums on Device",
|
"library_page_device_albums": "Álbumes en el dispositivo",
|
||||||
"library_page_favorites": "Favorites",
|
"library_page_favorites": "Favoritos",
|
||||||
"library_page_new_album": "Nuevo álbum",
|
"library_page_new_album": "Nuevo álbum",
|
||||||
"library_page_sharing": "Sharing",
|
"library_page_sharing": "Compartiendo",
|
||||||
"library_page_sort_created": "Most recently created",
|
"library_page_sort_created": "Creado más recientemente",
|
||||||
"library_page_sort_title": "Album title",
|
"library_page_sort_title": "Título del álbum",
|
||||||
"login_form_api_exception": "API exception. Please check the server URL and try again.",
|
"login_disabled": "Login has been disabled",
|
||||||
|
"login_form_api_exception": "Excepción producida por API. Por favor, verifica el URL del servidor e inténtalo de nuevo.",
|
||||||
"login_form_button_text": "Iniciar sesión",
|
"login_form_button_text": "Iniciar sesión",
|
||||||
"login_form_email_hint": "tucorreo@correo.com",
|
"login_form_email_hint": "tucorreo@correo.com",
|
||||||
"login_form_endpoint_hint": "http://la-ip-de-tu-servidor:puerto/api",
|
"login_form_endpoint_hint": "http://la-ip-de-tu-servidor:puerto/api",
|
||||||
"login_form_endpoint_url": "URL del servidor",
|
"login_form_endpoint_url": "URL del servidor",
|
||||||
"login_form_err_http": "Por favor, especifique http:// o https://",
|
"login_form_err_http": "Por favor, especifique http:// o https://",
|
||||||
"login_form_err_invalid_email": "Correo electrónico inválido",
|
"login_form_err_invalid_email": "Correo electrónico inválido",
|
||||||
"login_form_err_invalid_url": "Invalid URL",
|
"login_form_err_invalid_url": "URL no válida",
|
||||||
"login_form_err_leading_whitespace": "Espacio en blanco inicial",
|
"login_form_err_leading_whitespace": "Espacio en blanco inicial",
|
||||||
"login_form_err_trailing_whitespace": "Espacio en blanco al final",
|
"login_form_err_trailing_whitespace": "Espacio en blanco al final",
|
||||||
"login_form_failed_get_oauth_server_config": "Error logging using OAuth, check server URL",
|
"login_form_failed_get_oauth_server_config": "Error al iniciar sesión con OAuth, verifica la URL del servidor",
|
||||||
"login_form_failed_get_oauth_server_disable": "OAuth feature is not available on this server",
|
"login_form_failed_get_oauth_server_disable": "La función de OAuth no está disponible en este servidor",
|
||||||
"login_form_failed_login": "Error al iniciar sesión, comprueba la URL del servidor, el correo electrónico y la contraseña",
|
"login_form_failed_login": "Error al iniciar sesión, comprueba la URL del servidor, el correo electrónico y la contraseña",
|
||||||
"login_form_label_email": "Correo electrónico",
|
"login_form_label_email": "Correo electrónico",
|
||||||
"login_form_label_password": "Contraseña",
|
"login_form_label_password": "Contraseña",
|
||||||
"login_form_next_button": "Next",
|
"login_form_next_button": "Siguiente",
|
||||||
"login_form_password_hint": "contraseña",
|
"login_form_password_hint": "contraseña",
|
||||||
"login_form_save_login": "Permanecer conectado",
|
"login_form_save_login": "Permanecer conectado",
|
||||||
"login_form_server_empty": "Enter a server URL.",
|
"login_form_server_empty": "Agrega la URL del servidor.",
|
||||||
"login_form_server_error": "Could not connect to server.",
|
"login_form_server_error": "No se pudo conectar al servidor.",
|
||||||
"monthly_title_text_date_format": "MMMM y",
|
"monthly_title_text_date_format": "MMMM y",
|
||||||
"motion_photos_page_title": "Motion Photos",
|
"motion_photos_page_title": "Foto en Movimiento",
|
||||||
"notification_permission_dialog_cancel": "Cancel",
|
"notification_permission_dialog_cancel": "Cancelar",
|
||||||
"notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.",
|
"notification_permission_dialog_content": "Para activar las notificaciones, ve a Configuración y selecciona permitir.",
|
||||||
"notification_permission_dialog_settings": "Settings",
|
"notification_permission_dialog_settings": "Ajustes",
|
||||||
"notification_permission_list_tile_content": "Grant permission to enable notifications.",
|
"notification_permission_list_tile_content": "Concede permiso para habilitar las notificaciones.",
|
||||||
"notification_permission_list_tile_enable_button": "Enable Notifications",
|
"notification_permission_list_tile_enable_button": "Permitir notificaciones",
|
||||||
"notification_permission_list_tile_title": "Notification Permission",
|
"notification_permission_list_tile_title": "Permisos de Notificacion",
|
||||||
"partner_page_add_partner": "Add partner",
|
"partner_page_add_partner": "Agregar compañero",
|
||||||
"partner_page_empty_message": "Your photos are not yet shared with any partner.",
|
"partner_page_empty_message": "Tus fotos aún no se han compartido con ningún compañero.",
|
||||||
"partner_page_no_more_users": "No more users to add",
|
"partner_page_no_more_users": "No hay más usuarios para agregar",
|
||||||
"partner_page_partner_add_failed": "Failed to add partner",
|
"partner_page_partner_add_failed": "Compañero no pudo ser agregado ",
|
||||||
"partner_page_select_partner": "Select partner",
|
"partner_page_select_partner": "Seleccionar compañero",
|
||||||
"partner_page_shared_to_title": "Shared to",
|
"partner_page_shared_to_title": "Compartido con",
|
||||||
"partner_page_stop_sharing_content": "{} will no longer be able to access your photos.",
|
"partner_page_stop_sharing_content": "{} ya no podrá acceder a tus fotos",
|
||||||
"partner_page_stop_sharing_title": "Stop sharing your photos?",
|
"partner_page_stop_sharing_title": "¿Dejar de compartir tus fotos?",
|
||||||
"partner_page_title": "Partner",
|
"partner_page_title": "Compañero",
|
||||||
"permission_onboarding_continue_anyway": "Continue anyway",
|
"permission_onboarding_continue_anyway": "Continuar de todos modos",
|
||||||
"permission_onboarding_get_started": "Get started",
|
"permission_onboarding_get_started": "Empezar",
|
||||||
"permission_onboarding_go_to_settings": "Go to settings",
|
"permission_onboarding_go_to_settings": "Ir a configuración",
|
||||||
"permission_onboarding_grant_permission": "Grant permission",
|
"permission_onboarding_grant_permission": "Conceder permiso",
|
||||||
"permission_onboarding_log_out": "Log out",
|
"permission_onboarding_log_out": "Cerrar sesión",
|
||||||
"permission_onboarding_permission_denied": "Permission denied. To use Immich, grant photo and video permissions in Settings.",
|
"permission_onboarding_permission_denied": "Permiso denegado. Para usar Immich, concede permisos de fotos y videos en Configuración.",
|
||||||
"permission_onboarding_permission_granted": "Permission granted! You are all set.",
|
"permission_onboarding_permission_granted": "¡Permiso concedido! Todo listo.",
|
||||||
"permission_onboarding_permission_limited": "Permission limited. To let Immich backup and manage your entire gallery collection, grant photo and video permissions in Settings.",
|
"permission_onboarding_permission_limited": "Permiso limitado. Para permitir que Immich haga copia de seguridad y gestione toda tu colección de galería, concede permisos de fotos y videos en Configuración.",
|
||||||
"permission_onboarding_request": "Immich requires permission to view your photos and videos.",
|
"permission_onboarding_request": "Immich requiere permiso para ver tus fotos y videos.",
|
||||||
"profile_drawer_app_logs": "Logs",
|
"profile_drawer_app_logs": "Registros",
|
||||||
"profile_drawer_client_server_up_to_date": "El cliente y el servidor están actualizados",
|
"profile_drawer_client_server_up_to_date": "El cliente y el servidor están actualizados",
|
||||||
"profile_drawer_settings": "Configuración",
|
"profile_drawer_settings": "Configuración",
|
||||||
"profile_drawer_sign_out": "Cerrar sesión",
|
"profile_drawer_sign_out": "Cerrar sesión",
|
||||||
"recently_added_page_title": "Recently Added",
|
"recently_added_page_title": "Recién Agregadas",
|
||||||
"search_bar_hint": "Busca tus fotos",
|
"search_bar_hint": "Busca tus fotos",
|
||||||
"search_page_categories": "Categories",
|
"search_page_categories": "Categorías",
|
||||||
"search_page_favorites": "Favorites",
|
"search_page_favorites": "Favoritos",
|
||||||
"search_page_motion_photos": "Motion Photos",
|
"search_page_motion_photos": "Foto en Movimiento",
|
||||||
"search_page_no_objects": "No hay información de objetos disponible",
|
"search_page_no_objects": "No hay información de objetos disponibles",
|
||||||
"search_page_no_places": "No hay información de lugares disponible",
|
"search_page_no_places": "No hay información de lugares disponible",
|
||||||
"search_page_people": "People",
|
"search_page_people": "Personas",
|
||||||
"search_page_places": "Lugares",
|
"search_page_places": "Lugares",
|
||||||
"search_page_recently_added": "Recently added",
|
"search_page_recently_added": "Recién agregadas",
|
||||||
"search_page_screenshots": "Screenshots",
|
"search_page_screenshots": "Capturas de pantalla",
|
||||||
"search_page_selfies": "Selfies",
|
"search_page_selfies": "Selfies",
|
||||||
"search_page_things": "Cosas",
|
"search_page_things": "Cosas",
|
||||||
"search_page_videos": "Videos",
|
"search_page_videos": "Videos",
|
||||||
"search_page_view_all_button": "View all",
|
"search_page_view_all_button": "Ver todo",
|
||||||
"search_page_your_activity": "Your activity",
|
"search_page_your_activity": "Tu actividad",
|
||||||
"search_result_page_new_search_hint": "Nueva búsqueda",
|
"search_result_page_new_search_hint": "Nueva búsqueda",
|
||||||
"search_suggestion_list_smart_search_hint_1": "Smart search is enabled by default, to search for metadata use the syntax ",
|
"search_suggestion_list_smart_search_hint_1": "La búsqueda inteligente está habilitada por defecto, para buscar metadatos utiliza esta sintaxis ",
|
||||||
"search_suggestion_list_smart_search_hint_2": "m:your-search-term",
|
"search_suggestion_list_smart_search_hint_2": "m:tu-término-de-búsqueda",
|
||||||
"select_additional_user_for_sharing_page_suggestions": "Sugerencias",
|
"select_additional_user_for_sharing_page_suggestions": "Sugerencias",
|
||||||
"select_user_for_sharing_page_err_album": "Error al crear álbum",
|
"select_user_for_sharing_page_err_album": "Error al crear álbum",
|
||||||
"select_user_for_sharing_page_share_suggestions": "Sugerencias",
|
"select_user_for_sharing_page_share_suggestions": "Sugerencias",
|
||||||
"server_info_box_app_version": "App Version",
|
"server_info_box_app_version": "Versión de la Aplicación",
|
||||||
"server_info_box_server_version": "Server Version",
|
"server_info_box_server_version": "Versión del Servidor",
|
||||||
"setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).",
|
"setting_image_viewer_help": "El visor de detalles carga primero la miniatura pequeña, luego carga la vista previa de tamaño mediano (si está habilitada), finalmente carga la original (si está habilitada).",
|
||||||
"setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).",
|
"setting_image_viewer_original_subtitle": "Activar para cargar la imagen en resolución original (¡muy grande!). Deshabilitar para reducir el consumo de datos (de red y caché).",
|
||||||
"setting_image_viewer_original_title": "Load original image",
|
"setting_image_viewer_original_title": "Cargar imagen original",
|
||||||
"setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.",
|
"setting_image_viewer_preview_subtitle": "Activar para cargar una imagen de resolución media. Deshabilitar para cargar directamente la imagen original o usar una miniatura.",
|
||||||
"setting_image_viewer_preview_title": "Load preview image",
|
"setting_image_viewer_preview_title": "Cargar imagen de previsualización",
|
||||||
"setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}",
|
"setting_notifications_notify_failures_grace_period": "Notificar fallos de copia de seguridad en segundo plano: {}",
|
||||||
"setting_notifications_notify_hours": "{} hours",
|
"setting_notifications_notify_hours": "{} horas",
|
||||||
"setting_notifications_notify_immediately": "immediately",
|
"setting_notifications_notify_immediately": "inmediatamente",
|
||||||
"setting_notifications_notify_minutes": "{} minutes",
|
"setting_notifications_notify_minutes": "{} minutos",
|
||||||
"setting_notifications_notify_never": "never",
|
"setting_notifications_notify_never": "nunca",
|
||||||
"setting_notifications_notify_seconds": "{} seconds",
|
"setting_notifications_notify_seconds": "{} segundos",
|
||||||
"setting_notifications_single_progress_subtitle": "Detailed upload progress information per asset",
|
"setting_notifications_single_progress_subtitle": "Información detallada del progreso de subida de cada archivo",
|
||||||
"setting_notifications_single_progress_title": "Show background backup detail progress",
|
"setting_notifications_single_progress_title": "Mostrar progreso detallado de copia de seguridad en segundo plano",
|
||||||
"setting_notifications_subtitle": "Adjust your notification preferences",
|
"setting_notifications_subtitle": "Ajusta tus preferencias de notificación",
|
||||||
"setting_notifications_title": "Notifications",
|
"setting_notifications_title": "Notificaciones",
|
||||||
"setting_notifications_total_progress_subtitle": "Overall upload progress (done/total assets)",
|
"setting_notifications_total_progress_subtitle": "Progreso general de subida (archivos completados/total)",
|
||||||
"setting_notifications_total_progress_title": "Show background backup total progress",
|
"setting_notifications_total_progress_title": "Mostrar progreso total de copia de seguridad en segundo plano",
|
||||||
"setting_pages_app_bar_settings": "Settings",
|
"setting_pages_app_bar_settings": "Ajustes",
|
||||||
"settings_require_restart": "Please restart Immich to apply this setting",
|
"settings_require_restart": "Por favor, reinicia Immich para aplicar este ajuste",
|
||||||
"share_add": "Añadir",
|
"share_add": "Agregar",
|
||||||
"share_add_photos": "Añadir fotos",
|
"share_add_photos": "Agregar fotos",
|
||||||
"share_add_title": "Añadir un título",
|
"share_add_title": "Agregar un título",
|
||||||
"share_create_album": "Crear álbum",
|
"share_create_album": "Crear álbum",
|
||||||
"share_dialog_preparing": "Preparando...",
|
"share_dialog_preparing": "Preparando...",
|
||||||
"share_invite": "Invitar al álbum",
|
"share_invite": "Invitar al álbum",
|
||||||
@@ -274,20 +281,24 @@
|
|||||||
"tab_controller_nav_photos": "Fotos",
|
"tab_controller_nav_photos": "Fotos",
|
||||||
"tab_controller_nav_search": "Buscar",
|
"tab_controller_nav_search": "Buscar",
|
||||||
"tab_controller_nav_sharing": "Compartiendo",
|
"tab_controller_nav_sharing": "Compartiendo",
|
||||||
"theme_setting_asset_list_storage_indicator_title": "Show storage indicator on asset tiles",
|
"theme_setting_asset_list_storage_indicator_title": "Mostrar indicador de almacenamiento en las miniaturas de los archivos",
|
||||||
"theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({})",
|
"theme_setting_asset_list_tiles_per_row_title": "Número de activos por fila ({})",
|
||||||
"theme_setting_dark_mode_switch": "Dark mode",
|
"theme_setting_dark_mode_switch": "Modo oscuro",
|
||||||
"theme_setting_image_viewer_quality_subtitle": "Adjust the quality of the detail image viewer",
|
"theme_setting_image_viewer_quality_subtitle": "Ajustar la calidad del visor de detalles de imágenes",
|
||||||
"theme_setting_image_viewer_quality_title": "Image viewer quality",
|
"theme_setting_image_viewer_quality_title": "Calidad del visor de imágenes",
|
||||||
"theme_setting_system_theme_switch": "Automatic (Follow system setting)",
|
"theme_setting_system_theme_switch": "Automático (seguir ajuste del sistema)",
|
||||||
"theme_setting_theme_subtitle": "Choose the app's theme setting",
|
"theme_setting_theme_subtitle": "Elige la configuración del tema de la aplicación",
|
||||||
"theme_setting_theme_title": "Theme",
|
"theme_setting_theme_title": "Tema",
|
||||||
"theme_setting_three_stage_loading_subtitle": "Three-stage loading might increase the loading performance but causes significantly higher network load",
|
"theme_setting_three_stage_loading_subtitle": "La carga en tres etapas puede aumentar el rendimiento de carga pero provoca un consumo de red significativamente mayor",
|
||||||
"theme_setting_three_stage_loading_title": "Enable three-stage loading",
|
"theme_setting_three_stage_loading_title": "Activar carga en tres etapas",
|
||||||
|
"upload_dialog_cancel": "Cancel",
|
||||||
|
"upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?",
|
||||||
|
"upload_dialog_ok": "Upload",
|
||||||
|
"upload_dialog_title": "Upload Asset",
|
||||||
"version_announcement_overlay_ack": "Aceptar",
|
"version_announcement_overlay_ack": "Aceptar",
|
||||||
"version_announcement_overlay_release_notes": "notas de la versión",
|
"version_announcement_overlay_release_notes": "notas de la versión",
|
||||||
"version_announcement_overlay_text_1": "Hola, amigo, hay una nueva versión de",
|
"version_announcement_overlay_text_1": "Hola, amigo, hay una nueva versión de",
|
||||||
"version_announcement_overlay_text_2": "por favor, tómese su tiempo para visitar las",
|
"version_announcement_overlay_text_2": "por favor, tómate tu tiempo para visitar las ",
|
||||||
"version_announcement_overlay_text_3": "y asegúrate de que tu configuración de docker-compose y .env está actualizada para evitar cualquier error de configuración, especialmente si utilizas WatchTower o cualquier mecanismo que se encargue de actualizar tu aplicación de servidor automáticamente.",
|
"version_announcement_overlay_text_3": " y asegúrate de que la configuración de docker-compose y .env estén actualizadas para evitar cualquier error de configuración, especialmente si utilizas WatchTower o cualquier mecanismo que actualice automáticamente la aplicación del servidor.",
|
||||||
"version_announcement_overlay_title": "Nueva versión del servidor disponible \uD83C\uDF89"
|
"version_announcement_overlay_title": "Nueva versión del servidor disponible \uD83C\uDF89"
|
||||||
}
|
}
|
||||||
@@ -1,69 +1,69 @@
|
|||||||
{
|
{
|
||||||
"add_to_album_bottom_sheet_added": "Added to {album}",
|
"add_to_album_bottom_sheet_added": "Agregado a {album}",
|
||||||
"add_to_album_bottom_sheet_already_exists": "Already in {album}",
|
"add_to_album_bottom_sheet_already_exists": "Ya se encuentra en {album}",
|
||||||
"advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from assets on the device. Activate this setting to load remote images instead.",
|
"advanced_settings_prefer_remote_subtitle": "Algunos dispositivos tardan mucho en cargar las miniaturas de recursos encontrados el dispositivo. Activa esta opción para cargar imágenes remotas en su lugar.",
|
||||||
"advanced_settings_prefer_remote_title": "Prefer remote images",
|
"advanced_settings_prefer_remote_title": "Preferir imágenes remotas",
|
||||||
"advanced_settings_tile_subtitle": "Advanced user's settings",
|
"advanced_settings_tile_subtitle": "Configuraciones avanzadas del usuario",
|
||||||
"advanced_settings_tile_title": "Advanced",
|
"advanced_settings_tile_title": "Avanzado",
|
||||||
"advanced_settings_troubleshooting_subtitle": "Enable additional features for troubleshooting",
|
"advanced_settings_troubleshooting_subtitle": "Habilitar funciones adicionales para solución de problemas",
|
||||||
"advanced_settings_troubleshooting_title": "Troubleshooting",
|
"advanced_settings_troubleshooting_title": "Solución de problemas",
|
||||||
"album_info_card_backup_album_excluded": "EXCLUIDOS",
|
"album_info_card_backup_album_excluded": "EXCLUIDOS",
|
||||||
"album_info_card_backup_album_included": "INCLUIDOS",
|
"album_info_card_backup_album_included": "INCLUIDOS",
|
||||||
"album_thumbnail_card_item": "1 elemento",
|
"album_thumbnail_card_item": "1 elemento",
|
||||||
"album_thumbnail_card_items": "{} elementos",
|
"album_thumbnail_card_items": "{} elementos",
|
||||||
"album_thumbnail_card_shared": " · Compartido",
|
"album_thumbnail_card_shared": " · Compartido",
|
||||||
"album_thumbnail_owned": "Owned",
|
"album_thumbnail_owned": "Propio",
|
||||||
"album_thumbnail_shared_by": "Shared by {}",
|
"album_thumbnail_shared_by": "Compartido por {}",
|
||||||
"album_viewer_appbar_share_delete": "Eliminar álbum",
|
"album_viewer_appbar_share_delete": "Eliminar álbum",
|
||||||
"album_viewer_appbar_share_err_delete": "No se ha podido eliminar el álbum",
|
"album_viewer_appbar_share_err_delete": "No se ha podido eliminar el álbum",
|
||||||
"album_viewer_appbar_share_err_leave": "No se ha podido abandonar el álbum",
|
"album_viewer_appbar_share_err_leave": "No se ha podido abandonar el álbum",
|
||||||
"album_viewer_appbar_share_err_remove": "Hay problemas para eliminar recursos del álbum",
|
"album_viewer_appbar_share_err_remove": "Hay problemas para eliminar los archivos del álbum",
|
||||||
"album_viewer_appbar_share_err_title": "Error al cambiar el título del álbum",
|
"album_viewer_appbar_share_err_title": "Error al cambiar el título del álbum",
|
||||||
"album_viewer_appbar_share_leave": "Abandonar álbum ",
|
"album_viewer_appbar_share_leave": "Abandonar álbum ",
|
||||||
"album_viewer_appbar_share_remove": "Eliminar del álbum",
|
"album_viewer_appbar_share_remove": "Eliminar del álbum",
|
||||||
"album_viewer_page_share_add_users": "Añadir usuarios",
|
"album_viewer_page_share_add_users": "Agregar usuarios",
|
||||||
"all_people_page_title": "People",
|
"all_people_page_title": "Personas",
|
||||||
"all_videos_page_title": "Videos",
|
"all_videos_page_title": "Videos",
|
||||||
"archive_page_no_archived_assets": "No archived assets found",
|
"archive_page_no_archived_assets": "No se encontraron recursos archivados",
|
||||||
"archive_page_title": "Archive ({})",
|
"archive_page_title": "Archivo ({})",
|
||||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamic layout",
|
"asset_list_layout_settings_dynamic_layout_title": "Diseño dinámico",
|
||||||
"asset_list_layout_settings_group_automatically": "Automatic",
|
"asset_list_layout_settings_group_automatically": "Automatico",
|
||||||
"asset_list_layout_settings_group_by": "Group assets by",
|
"asset_list_layout_settings_group_by": "Agrupar recursos por",
|
||||||
"asset_list_layout_settings_group_by_month": "Month",
|
"asset_list_layout_settings_group_by_month": "Mes",
|
||||||
"asset_list_layout_settings_group_by_month_day": "Month + day",
|
"asset_list_layout_settings_group_by_month_day": "Mes + día",
|
||||||
"asset_list_settings_subtitle": "Photo grid layout settings",
|
"asset_list_settings_subtitle": "Configuraciones del diseño de la cuadrícula de fotos",
|
||||||
"asset_list_settings_title": "Photo Grid",
|
"asset_list_settings_title": "Cuadrícula de fotos",
|
||||||
"backup_album_selection_page_albums_device": "Álbumes en el dispositivo ({})",
|
"backup_album_selection_page_albums_device": "Álbumes en el dispositivo ({})",
|
||||||
"backup_album_selection_page_albums_tap": "Pulsar para incluir, pulsar dos veces para excluir",
|
"backup_album_selection_page_albums_tap": "Pulsar para incluir, pulsar dos veces para excluir",
|
||||||
"backup_album_selection_page_assets_scatter": "Los recursos pueden dispersarse entre varios álbumes. Por lo tanto, los álbumes pueden incluirse o excluirse durante el proceso de respaldo.",
|
"backup_album_selection_page_assets_scatter": "Los archivos pueden dispersarse en varios álbumes. De este modo, los álbumes pueden ser incluidos o excluidos durante el proceso de copia de seguridad.",
|
||||||
"backup_album_selection_page_select_albums": "Seleccionar álbumes",
|
"backup_album_selection_page_select_albums": "Seleccionar álbumes",
|
||||||
"backup_album_selection_page_selection_info": "Información de la selección",
|
"backup_album_selection_page_selection_info": "Información de la selección",
|
||||||
"backup_album_selection_page_total_assets": "Total de recursos únicos",
|
"backup_album_selection_page_total_assets": "Total de archivos únicos",
|
||||||
"backup_all": "Todos",
|
"backup_all": "Todos",
|
||||||
"backup_background_service_backup_failed_message": "Failed to backup assets. Retrying…",
|
"backup_background_service_backup_failed_message": "Error al copiar archivos. Reintentando...",
|
||||||
"backup_background_service_connection_failed_message": "Failed to connect to the server. Retrying…",
|
"backup_background_service_connection_failed_message": "Error al conectar con el servidor. Reintentando...",
|
||||||
"backup_background_service_current_upload_notification": "Cargando {}",
|
"backup_background_service_current_upload_notification": "Cargando {}",
|
||||||
"backup_background_service_default_notification": "Comprobando por nuevos recursos...",
|
"backup_background_service_default_notification": "Verificando si hay nuevos archivos",
|
||||||
"backup_background_service_error_title": "Error al respaldar",
|
"backup_background_service_error_title": "Error de copia de seguridad",
|
||||||
"backup_background_service_in_progress_notification": "Respaldando tus recursos...",
|
"backup_background_service_in_progress_notification": "Creando copia de seguridad de tus archivos...",
|
||||||
"backup_background_service_upload_failure_notification": "Error al cargar {}",
|
"backup_background_service_upload_failure_notification": "Error al cargar {}",
|
||||||
"backup_controller_page_albums": "Álbumes de respaldo",
|
"backup_controller_page_albums": "Álbumes de respaldo",
|
||||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
"backup_controller_page_background_app_refresh_disabled_content": "Activa la actualización en segundo plano de la aplicación en Configuración > General > Actualización en segundo plano para usar la copia de seguridad en segundo plano.",
|
||||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
"backup_controller_page_background_app_refresh_disabled_title": "Actualización en segundo plano desactivada",
|
||||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
"backup_controller_page_background_app_refresh_enable_button_text": "Ir a configuración",
|
||||||
"backup_controller_page_background_battery_info_link": "Show me how",
|
"backup_controller_page_background_battery_info_link": "Muestrame cómo",
|
||||||
"backup_controller_page_background_battery_info_message": "For the best background backup experience, please disable any battery optimizations restricting background activity for Immich.\n\nSince this is device-specific, please lookup the required information for your device manufacturer.",
|
"backup_controller_page_background_battery_info_message": "Para obtener la mejor experiencia de copia de seguridad en segundo plano, desactiva cualquier optimización de batería que restrinja la actividad en segundo plano para Immich.\n\nDado que esto es específico en cada dispositivo, busca la información necesaria de el fabricante de tu dispositivo.",
|
||||||
"backup_controller_page_background_battery_info_ok": "OK",
|
"backup_controller_page_background_battery_info_ok": "Ok",
|
||||||
"backup_controller_page_background_battery_info_title": "Battery optimizations",
|
"backup_controller_page_background_battery_info_title": "Optimizaciones de batería",
|
||||||
"backup_controller_page_background_charging": "Only while charging",
|
"backup_controller_page_background_charging": "Solo mientras se carga",
|
||||||
"backup_controller_page_background_configure_error": "Failed to configure the background service",
|
"backup_controller_page_background_configure_error": "Error al configurar el servicio en segundo plano",
|
||||||
"backup_controller_page_background_delay": "Delay new assets backup: {}",
|
"backup_controller_page_background_delay": "Retraso en la copia de seguridad de nuevos activos: {}",
|
||||||
"backup_controller_page_background_description": "Turn on the background service to automatically backup any new assets without needing to open the app",
|
"backup_controller_page_background_description": "Activa el servicio en segundo plano para copiar automáticamente cualquier nuevos archivos sin necesidad de abrir la aplicación.",
|
||||||
"backup_controller_page_background_is_off": "Automatic background backup is off",
|
"backup_controller_page_background_is_off": "La copia de seguridad en segundo plano automática está desactivada",
|
||||||
"backup_controller_page_background_is_on": "Automatic background backup is on",
|
"backup_controller_page_background_is_on": "La copia de seguridad en segundo plano automática está desactivada",
|
||||||
"backup_controller_page_background_turn_off": "Turn off background service",
|
"backup_controller_page_background_turn_off": "Desactivar el servicio en segundo plano",
|
||||||
"backup_controller_page_background_turn_on": "Turn on background service",
|
"backup_controller_page_background_turn_on": "Activar el servicio en segundo plano",
|
||||||
"backup_controller_page_background_wifi": "Only on WiFi",
|
"backup_controller_page_background_wifi": "Solo en WiFi",
|
||||||
"backup_controller_page_backup": "Respaldo",
|
"backup_controller_page_backup": "Respaldo",
|
||||||
"backup_controller_page_backup_selected": "Seleccionado:",
|
"backup_controller_page_backup_selected": "Seleccionado:",
|
||||||
"backup_controller_page_backup_sub": "Fotos y videos respaldados",
|
"backup_controller_page_backup_sub": "Fotos y videos respaldados",
|
||||||
@@ -72,12 +72,12 @@
|
|||||||
"backup_controller_page_desc_backup": "Activa la copia de seguridad en primer plano para cargar automáticamente nuevos recursos al servidor al abrir la aplicación.",
|
"backup_controller_page_desc_backup": "Activa la copia de seguridad en primer plano para cargar automáticamente nuevos recursos al servidor al abrir la aplicación.",
|
||||||
"backup_controller_page_excluded": "Excluido:",
|
"backup_controller_page_excluded": "Excluido:",
|
||||||
"backup_controller_page_failed": "Fallidos ({})",
|
"backup_controller_page_failed": "Fallidos ({})",
|
||||||
"backup_controller_page_filename": "Nombre: {} [{}]",
|
"backup_controller_page_filename": "Nombre del archivo: {} [{}]",
|
||||||
"backup_controller_page_id": "ID: {}",
|
"backup_controller_page_id": "ID: {}",
|
||||||
"backup_controller_page_info": "Información del respaldo",
|
"backup_controller_page_info": "Información del respaldo",
|
||||||
"backup_controller_page_none_selected": "Ninguno seleccionado",
|
"backup_controller_page_none_selected": "Ninguno seleccionado",
|
||||||
"backup_controller_page_remainder": "Restante",
|
"backup_controller_page_remainder": "Restante",
|
||||||
"backup_controller_page_remainder_sub": "Fotos y videos restantes de la selección a los que realizar un respaldo",
|
"backup_controller_page_remainder_sub": "Fotos y videos restantes para hacer una copia de seguridad de la selección",
|
||||||
"backup_controller_page_select": "Seleccionar",
|
"backup_controller_page_select": "Seleccionar",
|
||||||
"backup_controller_page_server_storage": "Almacenamiento del servidor",
|
"backup_controller_page_server_storage": "Almacenamiento del servidor",
|
||||||
"backup_controller_page_start_backup": "Iniciar respaldo",
|
"backup_controller_page_start_backup": "Iniciar respaldo",
|
||||||
@@ -89,48 +89,53 @@
|
|||||||
"backup_controller_page_total_sub": "Todas las fotos y videos únicos de los álbumes seleccionados",
|
"backup_controller_page_total_sub": "Todas las fotos y videos únicos de los álbumes seleccionados",
|
||||||
"backup_controller_page_turn_off": "Desactivar la copia de seguridad en primer plano",
|
"backup_controller_page_turn_off": "Desactivar la copia de seguridad en primer plano",
|
||||||
"backup_controller_page_turn_on": "Activar la copia de seguridad en primer plano",
|
"backup_controller_page_turn_on": "Activar la copia de seguridad en primer plano",
|
||||||
"backup_controller_page_uploading_file_info": "Info de carga de archivo",
|
"backup_controller_page_uploading_file_info": "Cargando información del archivo",
|
||||||
"backup_err_only_album": "No se puede eliminar el único álbum",
|
"backup_err_only_album": "No se puede eliminar el único álbum",
|
||||||
"backup_info_card_assets": "recursos",
|
"backup_info_card_assets": "archivos",
|
||||||
"cache_settings_album_thumbnails": "Library page thumbnails ({} assets)",
|
"backup_manual_cancelled": "Cancelled",
|
||||||
"cache_settings_clear_cache_button": "Clear cache",
|
"backup_manual_failed": "Failed",
|
||||||
"cache_settings_clear_cache_button_title": "Clears the app's cache. This will significantly impact the app's performance until the cache has rebuilt.",
|
"backup_manual_in_progress": "Upload already in progress. Try after sometime",
|
||||||
"cache_settings_image_cache_size": "Image cache size ({} assets)",
|
"backup_manual_success": "Success",
|
||||||
"cache_settings_statistics_album": "Library thumbnails",
|
"backup_manual_title": "Upload status",
|
||||||
"cache_settings_statistics_assets": "{} assets ({})",
|
"cache_settings_album_thumbnails": "Miniaturas de la página de la biblioteca ({} archivos)",
|
||||||
"cache_settings_statistics_full": "Full images",
|
"cache_settings_clear_cache_button": "Borrar caché",
|
||||||
"cache_settings_statistics_shared": "Shared album thumbnails",
|
"cache_settings_clear_cache_button_title": "Borra la caché de la aplicación. Esto afectará significativamente el rendimiento de la aplicación hasta que se reconstruya la caché.",
|
||||||
"cache_settings_statistics_thumbnail": "Thumbnails",
|
"cache_settings_image_cache_size": "Tamaño de la caché de imágenes ({} archivos)",
|
||||||
"cache_settings_statistics_title": "Cache usage",
|
"cache_settings_statistics_album": "Miniaturas de la biblioteca",
|
||||||
"cache_settings_subtitle": "Control the caching behaviour of the Immich mobile application",
|
"cache_settings_statistics_assets": "{} archivos ({})",
|
||||||
"cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)",
|
"cache_settings_statistics_full": "Imágenes completas",
|
||||||
"cache_settings_title": "Caching Settings",
|
"cache_settings_statistics_shared": "Miniaturas de álbumes compartidos",
|
||||||
"change_password_form_confirm_password": "Confirm Password",
|
"cache_settings_statistics_thumbnail": "Miniaturas",
|
||||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
"cache_settings_statistics_title": "Uso de caché",
|
||||||
"change_password_form_new_password": "New Password",
|
"cache_settings_subtitle": "Controla el comportamiento del almacenamiento en caché de la aplicación móvil Immich",
|
||||||
"change_password_form_password_mismatch": "Passwords do not match",
|
"cache_settings_thumbnail_size": "Tamaño de la caché de miniaturas ({} archivos)",
|
||||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
"cache_settings_title": "Configuración de la caché",
|
||||||
"common_add_to_album": "Add to album",
|
"change_password_form_confirm_password": "Confirmar Contraseña",
|
||||||
"common_change_password": "Change Password",
|
"change_password_form_description": "Hola {firstName} {lastName},\n\nEsta es la primera vez que inicias sesión en el sistema o se ha solicitado cambiar tu contraseña. Por favor, introduce la nueva contraseña a continuación.",
|
||||||
"common_create_new_album": "Create new album",
|
"change_password_form_new_password": "Nueva Contraseña",
|
||||||
"common_server_error": "Please check your network connection, make sure the server is reachable and app/server versions are compatible.",
|
"change_password_form_password_mismatch": "Las contraseñas no coinciden",
|
||||||
"common_shared": "Shared",
|
"change_password_form_reenter_new_password": "Vuelve a ingresar la nueva contraseña",
|
||||||
"control_bottom_app_bar_add_to_album": "Add to album",
|
"common_add_to_album": "Agregar al álbum",
|
||||||
"control_bottom_app_bar_album_info": "{} items",
|
"common_change_password": "Cambiar Contraseña",
|
||||||
"control_bottom_app_bar_album_info_shared": "{} items · Shared",
|
"common_create_new_album": "Crear nuevo álbum",
|
||||||
"control_bottom_app_bar_archive": "Archive",
|
"common_server_error": "Por favor, verifica tu conexión de red, asegúrate de que el servidor esté accesible y las versiones de la aplicación y del servidor sean compatibles.",
|
||||||
"control_bottom_app_bar_create_new_album": "Create new album",
|
"common_shared": "Compartido",
|
||||||
|
"control_bottom_app_bar_add_to_album": "Agregar al álbum",
|
||||||
|
"control_bottom_app_bar_album_info": "{} elementos",
|
||||||
|
"control_bottom_app_bar_album_info_shared": "{} elementos · Compartidos",
|
||||||
|
"control_bottom_app_bar_archive": "Archivar",
|
||||||
|
"control_bottom_app_bar_create_new_album": "Crear nuevo álbum",
|
||||||
"control_bottom_app_bar_delete": "Eliminar",
|
"control_bottom_app_bar_delete": "Eliminar",
|
||||||
"control_bottom_app_bar_favorite": "Favorite",
|
"control_bottom_app_bar_favorite": "Favorito",
|
||||||
"control_bottom_app_bar_share": "Compartir",
|
"control_bottom_app_bar_share": "Compartir",
|
||||||
"control_bottom_app_bar_unarchive": "Unarchive",
|
"control_bottom_app_bar_unarchive": "Desarchivar",
|
||||||
"create_album_page_untitled": "Sin título",
|
"create_album_page_untitled": "Sin título",
|
||||||
"create_shared_album_page_create": "Crear",
|
"create_shared_album_page_create": "Crear",
|
||||||
"create_shared_album_page_share": "Compartir",
|
"create_shared_album_page_share": "Compartir",
|
||||||
"create_shared_album_page_share_add_assets": "AÑADIR RECURSOS",
|
"create_shared_album_page_share_add_assets": "AGREGAR ARCHIVOS",
|
||||||
"create_shared_album_page_share_select_photos": "Seleccionar fotos",
|
"create_shared_album_page_share_select_photos": "Seleccionar fotos",
|
||||||
"curated_location_page_title": "Places",
|
"curated_location_page_title": "Lugares",
|
||||||
"curated_object_page_title": "Things",
|
"curated_object_page_title": "Objetos",
|
||||||
"daily_title_text_date": "E, dd MMM",
|
"daily_title_text_date": "E, dd MMM",
|
||||||
"daily_title_text_date_year": "E, dd de MMM de yyyy",
|
"daily_title_text_date_year": "E, dd de MMM de yyyy",
|
||||||
"date_format": "E d, LLL y • h:mm a",
|
"date_format": "E d, LLL y • h:mm a",
|
||||||
@@ -138,130 +143,132 @@
|
|||||||
"delete_dialog_cancel": "Cancelar",
|
"delete_dialog_cancel": "Cancelar",
|
||||||
"delete_dialog_ok": "Eliminar",
|
"delete_dialog_ok": "Eliminar",
|
||||||
"delete_dialog_title": "Eliminar permanentemente",
|
"delete_dialog_title": "Eliminar permanentemente",
|
||||||
"description_input_hint_text": "Add description...",
|
"description_input_hint_text": "Agregar descripción...",
|
||||||
"description_input_submit_error": "Error updating description, check the log for more details",
|
"description_input_submit_error": "Error al actualizar la descripción, verifica el registro para obtener más detalles",
|
||||||
"exif_bottom_sheet_description": "Añadir descripción...",
|
"exif_bottom_sheet_description": "Agregar Descripción...",
|
||||||
"exif_bottom_sheet_details": "DETALLES",
|
"exif_bottom_sheet_details": "DETALLES",
|
||||||
"exif_bottom_sheet_location": "UBICACIÓN",
|
"exif_bottom_sheet_location": "UBICACIÓN",
|
||||||
"experimental_settings_new_asset_list_subtitle": "Work in progress",
|
"experimental_settings_new_asset_list_subtitle": "Trabajo en progreso",
|
||||||
"experimental_settings_new_asset_list_title": "Enable experimental photo grid",
|
"experimental_settings_new_asset_list_title": "Habilitar cuadrícula fotográfica experimental",
|
||||||
"experimental_settings_subtitle": "Use at your own risk!",
|
"experimental_settings_subtitle": "Úsalo bajo tu responsabilidad",
|
||||||
"experimental_settings_title": "Experimental",
|
"experimental_settings_title": "Experimental",
|
||||||
"favorites_page_no_favorites": "No favorite assets found",
|
"favorites_page_no_favorites": "No se encontraron recursos marcados como favoritos",
|
||||||
"favorites_page_title": "Favorites",
|
"favorites_page_title": "Favoritos",
|
||||||
"home_page_add_to_album_conflicts": "Added {added} assets to album {album}. {failed} assets are already in the album.",
|
"home_page_add_to_album_conflicts": "{added} elementos agregados al álbum {album}.\n{failed} elementos ya existen en el álbum.",
|
||||||
"home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping",
|
"home_page_add_to_album_err_local": "Aún no se pueden agregar recursos locales a álbumes, omitiendo",
|
||||||
"home_page_add_to_album_success": "Added {added} assets to album {album}.",
|
"home_page_add_to_album_success": "{added} elementos agregados al álbum {album}. ",
|
||||||
"home_page_archive_err_local": "Can not archive local assets yet, skipping",
|
"home_page_archive_err_local": "Los recursos locales no pueden ser archivados, omitiendo",
|
||||||
"home_page_building_timeline": "Building the timeline",
|
"home_page_building_timeline": "Construyendo la línea de tiempo",
|
||||||
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
|
"home_page_favorite_err_local": "Aún no se pueden archivar recursos locales, omitiendo",
|
||||||
"home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).",
|
"home_page_first_time_notice": "Si esta es la primera vez que usas la app, por favor, asegúrate de elegir un álbum de respaldo para que la línea de tiempo pueda cargar fotos y videos en los álbumes.",
|
||||||
"image_viewer_page_state_provider_download_error": "Download Error",
|
"home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping",
|
||||||
"image_viewer_page_state_provider_download_success": "Download Success",
|
"image_viewer_page_state_provider_download_error": "Error de descarga",
|
||||||
|
"image_viewer_page_state_provider_download_success": "Descarga exitosa",
|
||||||
"library_page_albums": "Álbumes",
|
"library_page_albums": "Álbumes",
|
||||||
"library_page_archive": "Archive",
|
"library_page_archive": "Archivo",
|
||||||
"library_page_device_albums": "Albums on Device",
|
"library_page_device_albums": "Álbumes en el dispositivo",
|
||||||
"library_page_favorites": "Favorites",
|
"library_page_favorites": "Favoritos",
|
||||||
"library_page_new_album": "Nuevo álbum",
|
"library_page_new_album": "Nuevo álbum",
|
||||||
"library_page_sharing": "Sharing",
|
"library_page_sharing": "Compartiendo",
|
||||||
"library_page_sort_created": "Most recently created",
|
"library_page_sort_created": "Creado más recientemente",
|
||||||
"library_page_sort_title": "Album title",
|
"library_page_sort_title": "Título del álbum",
|
||||||
"login_form_api_exception": "API exception. Please check the server URL and try again.",
|
"login_disabled": "Login has been disabled",
|
||||||
|
"login_form_api_exception": "Excepción producida por API. Por favor, verifica el URL del servidor e inténtalo de nuevo.",
|
||||||
"login_form_button_text": "Iniciar sesión",
|
"login_form_button_text": "Iniciar sesión",
|
||||||
"login_form_email_hint": "tucorreo@correo.com",
|
"login_form_email_hint": "tucorreo@correo.com",
|
||||||
"login_form_endpoint_hint": "http://la-ip-de-tu-servidor:puerto/api",
|
"login_form_endpoint_hint": "http://la-ip-de-tu-servidor:puerto/api",
|
||||||
"login_form_endpoint_url": "URL del servidor",
|
"login_form_endpoint_url": "URL del servidor",
|
||||||
"login_form_err_http": "Por favor, especifique http:// o https://",
|
"login_form_err_http": "Por favor, especifique http:// o https://",
|
||||||
"login_form_err_invalid_email": "Correo electrónico inválido",
|
"login_form_err_invalid_email": "Correo electrónico inválido",
|
||||||
"login_form_err_invalid_url": "Invalid URL",
|
"login_form_err_invalid_url": "URL no válida",
|
||||||
"login_form_err_leading_whitespace": "Espacio en blanco inicial",
|
"login_form_err_leading_whitespace": "Espacio en blanco inicial",
|
||||||
"login_form_err_trailing_whitespace": "Espacio en blanco al final",
|
"login_form_err_trailing_whitespace": "Espacio en blanco al final",
|
||||||
"login_form_failed_get_oauth_server_config": "Error logging using OAuth, check server URL",
|
"login_form_failed_get_oauth_server_config": "Error al iniciar sesión con OAuth, verifica la URL del servidor",
|
||||||
"login_form_failed_get_oauth_server_disable": "OAuth feature is not available on this server",
|
"login_form_failed_get_oauth_server_disable": "La función de OAuth no está disponible en este servidor",
|
||||||
"login_form_failed_login": "Error al iniciar sesión, comprueba la URL del servidor, el correo electrónico y la contraseña",
|
"login_form_failed_login": "Error al iniciar sesión, comprueba la URL del servidor, el correo electrónico y la contraseña",
|
||||||
"login_form_label_email": "Correo electrónico",
|
"login_form_label_email": "Correo electrónico",
|
||||||
"login_form_label_password": "Contraseña",
|
"login_form_label_password": "Contraseña",
|
||||||
"login_form_next_button": "Next",
|
"login_form_next_button": "Siguiente",
|
||||||
"login_form_password_hint": "contraseña",
|
"login_form_password_hint": "contraseña",
|
||||||
"login_form_save_login": "Permanecer conectado",
|
"login_form_save_login": "Permanecer conectado",
|
||||||
"login_form_server_empty": "Enter a server URL.",
|
"login_form_server_empty": "Agrega la URL del servidor.",
|
||||||
"login_form_server_error": "Could not connect to server.",
|
"login_form_server_error": "No se pudo conectar al servidor.",
|
||||||
"monthly_title_text_date_format": "MMMM y",
|
"monthly_title_text_date_format": "MMMM y",
|
||||||
"motion_photos_page_title": "Motion Photos",
|
"motion_photos_page_title": "Foto en Movimiento",
|
||||||
"notification_permission_dialog_cancel": "Cancel",
|
"notification_permission_dialog_cancel": "Cancelar",
|
||||||
"notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.",
|
"notification_permission_dialog_content": "Para activar las notificaciones, ve a Configuración y selecciona permitir.",
|
||||||
"notification_permission_dialog_settings": "Settings",
|
"notification_permission_dialog_settings": "Ajustes",
|
||||||
"notification_permission_list_tile_content": "Grant permission to enable notifications.",
|
"notification_permission_list_tile_content": "Concede permiso para habilitar las notificaciones.",
|
||||||
"notification_permission_list_tile_enable_button": "Enable Notifications",
|
"notification_permission_list_tile_enable_button": "Permitir notificaciones",
|
||||||
"notification_permission_list_tile_title": "Notification Permission",
|
"notification_permission_list_tile_title": "Permisos de Notificacion",
|
||||||
"partner_page_add_partner": "Add partner",
|
"partner_page_add_partner": "Agregar compañero",
|
||||||
"partner_page_empty_message": "Your photos are not yet shared with any partner.",
|
"partner_page_empty_message": "Tus fotos aún no se han compartido con ningún compañero.",
|
||||||
"partner_page_no_more_users": "No more users to add",
|
"partner_page_no_more_users": "No hay más usuarios para agregar",
|
||||||
"partner_page_partner_add_failed": "Failed to add partner",
|
"partner_page_partner_add_failed": "Compañero no pudo ser agregado ",
|
||||||
"partner_page_select_partner": "Select partner",
|
"partner_page_select_partner": "Seleccionar compañero",
|
||||||
"partner_page_shared_to_title": "Shared to",
|
"partner_page_shared_to_title": "Compartido con",
|
||||||
"partner_page_stop_sharing_content": "{} will no longer be able to access your photos.",
|
"partner_page_stop_sharing_content": "{} ya no podrá acceder a tus fotos",
|
||||||
"partner_page_stop_sharing_title": "Stop sharing your photos?",
|
"partner_page_stop_sharing_title": "¿Dejar de compartir tus fotos?",
|
||||||
"partner_page_title": "Partner",
|
"partner_page_title": "Compañero",
|
||||||
"permission_onboarding_continue_anyway": "Continue anyway",
|
"permission_onboarding_continue_anyway": "Continuar de todos modos",
|
||||||
"permission_onboarding_get_started": "Get started",
|
"permission_onboarding_get_started": "Empezar",
|
||||||
"permission_onboarding_go_to_settings": "Go to settings",
|
"permission_onboarding_go_to_settings": "Ir a configuración",
|
||||||
"permission_onboarding_grant_permission": "Grant permission",
|
"permission_onboarding_grant_permission": "Conceder permiso",
|
||||||
"permission_onboarding_log_out": "Log out",
|
"permission_onboarding_log_out": "Cerrar sesión",
|
||||||
"permission_onboarding_permission_denied": "Permission denied. To use Immich, grant photo and video permissions in Settings.",
|
"permission_onboarding_permission_denied": "Permiso denegado. Para usar Immich, concede permisos de fotos y videos en Configuración.",
|
||||||
"permission_onboarding_permission_granted": "Permission granted! You are all set.",
|
"permission_onboarding_permission_granted": "¡Permiso concedido! Todo listo.",
|
||||||
"permission_onboarding_permission_limited": "Permission limited. To let Immich backup and manage your entire gallery collection, grant photo and video permissions in Settings.",
|
"permission_onboarding_permission_limited": "Permiso limitado. Para permitir que Immich haga copia de seguridad y gestione toda tu colección de galería, concede permisos de fotos y videos en Configuración.",
|
||||||
"permission_onboarding_request": "Immich requires permission to view your photos and videos.",
|
"permission_onboarding_request": "Immich requiere permiso para ver tus fotos y videos.",
|
||||||
"profile_drawer_app_logs": "Logs",
|
"profile_drawer_app_logs": "Registros",
|
||||||
"profile_drawer_client_server_up_to_date": "El cliente y el servidor están actualizados",
|
"profile_drawer_client_server_up_to_date": "El cliente y el servidor están actualizados",
|
||||||
"profile_drawer_settings": "Configuración",
|
"profile_drawer_settings": "Configuración",
|
||||||
"profile_drawer_sign_out": "Cerrar sesión",
|
"profile_drawer_sign_out": "Cerrar sesión",
|
||||||
"recently_added_page_title": "Recently Added",
|
"recently_added_page_title": "Recién Agregadas",
|
||||||
"search_bar_hint": "Busca tus fotos",
|
"search_bar_hint": "Busca tus fotos",
|
||||||
"search_page_categories": "Categories",
|
"search_page_categories": "Categorías",
|
||||||
"search_page_favorites": "Favorites",
|
"search_page_favorites": "Favoritos",
|
||||||
"search_page_motion_photos": "Motion Photos",
|
"search_page_motion_photos": "Foto en Movimiento",
|
||||||
"search_page_no_objects": "No hay información de objetos disponible",
|
"search_page_no_objects": "No hay información de objetos disponibles",
|
||||||
"search_page_no_places": "No hay información de lugares disponible",
|
"search_page_no_places": "No hay información de lugares disponible",
|
||||||
"search_page_people": "People",
|
"search_page_people": "Personas",
|
||||||
"search_page_places": "Lugares",
|
"search_page_places": "Lugares",
|
||||||
"search_page_recently_added": "Recently added",
|
"search_page_recently_added": "Recién agregadas",
|
||||||
"search_page_screenshots": "Screenshots",
|
"search_page_screenshots": "Capturas de pantalla",
|
||||||
"search_page_selfies": "Selfies",
|
"search_page_selfies": "Selfies",
|
||||||
"search_page_things": "Cosas",
|
"search_page_things": "Cosas",
|
||||||
"search_page_videos": "Videos",
|
"search_page_videos": "Videos",
|
||||||
"search_page_view_all_button": "View all",
|
"search_page_view_all_button": "Ver todo",
|
||||||
"search_page_your_activity": "Your activity",
|
"search_page_your_activity": "Tu actividad",
|
||||||
"search_result_page_new_search_hint": "Nueva búsqueda",
|
"search_result_page_new_search_hint": "Nueva búsqueda",
|
||||||
"search_suggestion_list_smart_search_hint_1": "Smart search is enabled by default, to search for metadata use the syntax ",
|
"search_suggestion_list_smart_search_hint_1": "La búsqueda inteligente está habilitada por defecto, para buscar metadatos utiliza esta sintaxis ",
|
||||||
"search_suggestion_list_smart_search_hint_2": "m:your-search-term",
|
"search_suggestion_list_smart_search_hint_2": "m:tu-término-de-búsqueda",
|
||||||
"select_additional_user_for_sharing_page_suggestions": "Sugerencias",
|
"select_additional_user_for_sharing_page_suggestions": "Sugerencias",
|
||||||
"select_user_for_sharing_page_err_album": "Error al crear álbum",
|
"select_user_for_sharing_page_err_album": "Error al crear álbum",
|
||||||
"select_user_for_sharing_page_share_suggestions": "Sugerencias",
|
"select_user_for_sharing_page_share_suggestions": "Sugerencias",
|
||||||
"server_info_box_app_version": "App Version",
|
"server_info_box_app_version": "Versión de la Aplicación",
|
||||||
"server_info_box_server_version": "Server Version",
|
"server_info_box_server_version": "Versión del Servidor",
|
||||||
"setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).",
|
"setting_image_viewer_help": "El visor de detalles carga primero la miniatura pequeña, luego carga la vista previa de tamaño mediano (si está habilitada), finalmente carga la original (si está habilitada).",
|
||||||
"setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).",
|
"setting_image_viewer_original_subtitle": "Activar para cargar la imagen en resolución original (¡muy grande!). Deshabilitar para reducir el consumo de datos (de red y caché).",
|
||||||
"setting_image_viewer_original_title": "Load original image",
|
"setting_image_viewer_original_title": "Cargar imagen original",
|
||||||
"setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.",
|
"setting_image_viewer_preview_subtitle": "Activar para cargar una imagen de resolución media. Deshabilitar para cargar directamente la imagen original o usar una miniatura.",
|
||||||
"setting_image_viewer_preview_title": "Load preview image",
|
"setting_image_viewer_preview_title": "Cargar imagen de previsualización",
|
||||||
"setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}",
|
"setting_notifications_notify_failures_grace_period": "Notificar fallos de copia de seguridad en segundo plano: {}",
|
||||||
"setting_notifications_notify_hours": "{} hours",
|
"setting_notifications_notify_hours": "{} horas",
|
||||||
"setting_notifications_notify_immediately": "immediately",
|
"setting_notifications_notify_immediately": "inmediatamente",
|
||||||
"setting_notifications_notify_minutes": "{} minutes",
|
"setting_notifications_notify_minutes": "{} minutos",
|
||||||
"setting_notifications_notify_never": "never",
|
"setting_notifications_notify_never": "nunca",
|
||||||
"setting_notifications_notify_seconds": "{} seconds",
|
"setting_notifications_notify_seconds": "{} segundos",
|
||||||
"setting_notifications_single_progress_subtitle": "Detailed upload progress information per asset",
|
"setting_notifications_single_progress_subtitle": "Información detallada del progreso de subida de cada archivo",
|
||||||
"setting_notifications_single_progress_title": "Show background backup detail progress",
|
"setting_notifications_single_progress_title": "Mostrar progreso detallado de copia de seguridad en segundo plano",
|
||||||
"setting_notifications_subtitle": "Adjust your notification preferences",
|
"setting_notifications_subtitle": "Ajusta tus preferencias de notificación",
|
||||||
"setting_notifications_title": "Notifications",
|
"setting_notifications_title": "Notificaciones",
|
||||||
"setting_notifications_total_progress_subtitle": "Overall upload progress (done/total assets)",
|
"setting_notifications_total_progress_subtitle": "Progreso general de subida (archivos completados/total)",
|
||||||
"setting_notifications_total_progress_title": "Show background backup total progress",
|
"setting_notifications_total_progress_title": "Mostrar progreso total de copia de seguridad en segundo plano",
|
||||||
"setting_pages_app_bar_settings": "Settings",
|
"setting_pages_app_bar_settings": "Ajustes",
|
||||||
"settings_require_restart": "Please restart Immich to apply this setting",
|
"settings_require_restart": "Por favor, reinicia Immich para aplicar este ajuste",
|
||||||
"share_add": "Añadir",
|
"share_add": "Agregar",
|
||||||
"share_add_photos": "Añadir fotos",
|
"share_add_photos": "Agregar fotos",
|
||||||
"share_add_title": "Añadir un título",
|
"share_add_title": "Agregar un título",
|
||||||
"share_create_album": "Crear álbum",
|
"share_create_album": "Crear álbum",
|
||||||
"share_dialog_preparing": "Preparando...",
|
"share_dialog_preparing": "Preparando...",
|
||||||
"share_invite": "Invitar al álbum",
|
"share_invite": "Invitar al álbum",
|
||||||
@@ -274,20 +281,24 @@
|
|||||||
"tab_controller_nav_photos": "Fotos",
|
"tab_controller_nav_photos": "Fotos",
|
||||||
"tab_controller_nav_search": "Buscar",
|
"tab_controller_nav_search": "Buscar",
|
||||||
"tab_controller_nav_sharing": "Compartiendo",
|
"tab_controller_nav_sharing": "Compartiendo",
|
||||||
"theme_setting_asset_list_storage_indicator_title": "Show storage indicator on asset tiles",
|
"theme_setting_asset_list_storage_indicator_title": "Mostrar indicador de almacenamiento en las miniaturas de los archivos",
|
||||||
"theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({})",
|
"theme_setting_asset_list_tiles_per_row_title": "Número de activos por fila ({})",
|
||||||
"theme_setting_dark_mode_switch": "Dark mode",
|
"theme_setting_dark_mode_switch": "Modo oscuro",
|
||||||
"theme_setting_image_viewer_quality_subtitle": "Adjust the quality of the detail image viewer",
|
"theme_setting_image_viewer_quality_subtitle": "Ajustar la calidad del visor de detalles de imágenes",
|
||||||
"theme_setting_image_viewer_quality_title": "Image viewer quality",
|
"theme_setting_image_viewer_quality_title": "Calidad del visor de imágenes",
|
||||||
"theme_setting_system_theme_switch": "Automatic (Follow system setting)",
|
"theme_setting_system_theme_switch": "Automático (seguir ajuste del sistema)",
|
||||||
"theme_setting_theme_subtitle": "Choose the app's theme setting",
|
"theme_setting_theme_subtitle": "Elige la configuración del tema de la aplicación",
|
||||||
"theme_setting_theme_title": "Theme",
|
"theme_setting_theme_title": "Tema",
|
||||||
"theme_setting_three_stage_loading_subtitle": "Three-stage loading might increase the loading performance but causes significantly higher network load",
|
"theme_setting_three_stage_loading_subtitle": "La carga en tres etapas puede aumentar el rendimiento de carga pero provoca un consumo de red significativamente mayor",
|
||||||
"theme_setting_three_stage_loading_title": "Enable three-stage loading",
|
"theme_setting_three_stage_loading_title": "Activar carga en tres etapas",
|
||||||
|
"upload_dialog_cancel": "Cancel",
|
||||||
|
"upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?",
|
||||||
|
"upload_dialog_ok": "Upload",
|
||||||
|
"upload_dialog_title": "Upload Asset",
|
||||||
"version_announcement_overlay_ack": "Aceptar",
|
"version_announcement_overlay_ack": "Aceptar",
|
||||||
"version_announcement_overlay_release_notes": "notas de la versión",
|
"version_announcement_overlay_release_notes": "notas de la versión",
|
||||||
"version_announcement_overlay_text_1": "Hola, amigo, hay una nueva versión de",
|
"version_announcement_overlay_text_1": "Hola, amigo, hay una nueva versión de",
|
||||||
"version_announcement_overlay_text_2": "por favor, tómese su tiempo para visitar las",
|
"version_announcement_overlay_text_2": "por favor, tómate tu tiempo para visitar las ",
|
||||||
"version_announcement_overlay_text_3": "y asegúrate de que tu configuración de docker-compose y .env está actualizada para evitar cualquier error de configuración, especialmente si utilizas WatchTower o cualquier mecanismo que se encargue de actualizar tu aplicación de servidor automáticamente.",
|
"version_announcement_overlay_text_3": " y asegúrate de que la configuración de docker-compose y .env estén actualizadas para evitar cualquier error de configuración, especialmente si utilizas WatchTower o cualquier mecanismo que actualice automáticamente la aplicación del servidor.",
|
||||||
"version_announcement_overlay_title": "Nueva versión del servidor disponible \uD83C\uDF89"
|
"version_announcement_overlay_title": "Nueva versión del servidor disponible \uD83C\uDF89"
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user