mirror of
https://github.com/immich-app/immich.git
synced 2026-07-03 03:15:22 -07:00
Compare commits
392 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 698b96d597 | |||
| 4099fa6b4a | |||
| 9751530af8 | |||
| 0931a19c5c | |||
| 08b2e2c0b5 | |||
| e5b50a55a4 | |||
| 9d6c219276 | |||
| f29f86542c | |||
| 5165cf1d2f | |||
| f4c8459484 | |||
| 22ec449e43 | |||
| 0b1019c344 | |||
| 06f3b4f259 | |||
| 99f94a363d | |||
| c3092b1c2c | |||
| 0656e7e231 | |||
| 1692b81b7c | |||
| ff2028c4c8 | |||
| f22836e1bf | |||
| 7dd02ffbad | |||
| e51c4cb355 | |||
| d4102c0489 | |||
| 30a73c1105 | |||
| ec7c0f9ec8 | |||
| a5198e23a8 | |||
| 51f2905fcc | |||
| 3b7d75c18a | |||
| c484bd99b6 | |||
| c0bf5a4c56 | |||
| d9d50d2848 | |||
| c7453a67fd | |||
| e918e3a313 | |||
| dc7d57ff9a | |||
| b24a617142 | |||
| 62b00a1f26 | |||
| 95fc5e9682 | |||
| 38920fc4ca | |||
| 3abeb4df92 | |||
| 805bb84877 | |||
| a719552243 | |||
| 9a5e7a8e47 | |||
| 62c6bb27e3 | |||
| f3cb3cf98d | |||
| c35abb2f66 | |||
| 793487e52c | |||
| 769c4015d3 | |||
| c07cbe7ca8 | |||
| 6d73bf4e36 | |||
| 735f52a321 | |||
| 53fe26593c | |||
| 40cffcd414 | |||
| 48861b085e | |||
| 09d0380804 | |||
| 83091d2834 | |||
| 9a3071ae5c | |||
| 7ef2de6b53 | |||
| cbe34d7931 | |||
| 06c8d5a183 | |||
| ad9817c582 | |||
| 14f6f2c04f | |||
| 327521fa27 | |||
| 3be803d0c0 | |||
| a364b56b1c | |||
| f9db76433e | |||
| 3f2e51c5d4 | |||
| 430a2bbfd3 | |||
| fbb0bc6e39 | |||
| 8d30cfa2ce | |||
| c9aa9ba711 | |||
| 7cf904acbe | |||
| 983a005709 | |||
| cda499f2b5 | |||
| 12b7cd066b | |||
| 83b4dc1733 | |||
| a9d64b30ad | |||
| 3927eb6755 | |||
| 27cfa0e788 | |||
| 76c042abc7 | |||
| 0f49bcbd27 | |||
| df4a708aed | |||
| 34f78e3fd4 | |||
| 3c2296b818 | |||
| 805ca1a2bf | |||
| 9ee412110f | |||
| 010220d588 | |||
| 10fddf2d51 | |||
| d307ab60ca | |||
| ce59cc9241 | |||
| 1fa034125c | |||
| 8036dc4b8c | |||
| 54895fb10e | |||
| e70a1163f3 | |||
| a23a7c69ae | |||
| f21a753aff | |||
| cc8d3b4107 | |||
| 622a330d82 | |||
| 5e8744a568 | |||
| b633cc4f04 | |||
| a9ee6a7ce9 | |||
| c273ccf2e2 | |||
| 5f1a180d1a | |||
| cc54de87aa | |||
| a97e5999e4 | |||
| 46631b3786 | |||
| 5a3be158b9 | |||
| b21af78454 | |||
| abd62d9295 | |||
| e31d4aa909 | |||
| 43b2d04e2c | |||
| e4dbe777a0 | |||
| d36aed4c5b | |||
| f1da9d2429 | |||
| 232ca3cf3f | |||
| 50f1121459 | |||
| 892397807c | |||
| 714c647937 | |||
| c56f477a0f | |||
| 296cd40da9 | |||
| a17276fd1e | |||
| c3e23a6b3a | |||
| 13a7b4a276 | |||
| 563cff26bf | |||
| e81b6778ca | |||
| aa6af7ce36 | |||
| 59d036a2ed | |||
| 7a5c014558 | |||
| e2954b6411 | |||
| 0fb18ed241 | |||
| c0b3b08ce6 | |||
| e8a1084e5b | |||
| d227ba2d51 | |||
| 9cb94343d1 | |||
| aa126e377c | |||
| 74878628c8 | |||
| 4ead3e697d | |||
| fb798a8f29 | |||
| 07813135b5 | |||
| 92a75b0cd3 | |||
| 8132e8a38c | |||
| 43f2f56530 | |||
| e580bb5d0a | |||
| d3680871ef | |||
| b9b1cc2f65 | |||
| 7d198956a6 | |||
| a7b5f81701 | |||
| 5c38373808 | |||
| 1ce961fbb3 | |||
| 4bc411b7c7 | |||
| 11c1025271 | |||
| 8b5385f94b | |||
| d3438cf4a7 | |||
| 6c5c6a1035 | |||
| c928787b3e | |||
| fe9ca4f40a | |||
| a665cec920 | |||
| 568283a8eb | |||
| f382624e68 | |||
| 24dad15636 | |||
| 7ab533b57b | |||
| d10153bbc7 | |||
| b846afeb08 | |||
| e222b19576 | |||
| 1fee99cd2a | |||
| 70bb7e4b7e | |||
| f973927c68 | |||
| e29267359e | |||
| 164cda87a3 | |||
| 12d344efe0 | |||
| 474efd39f8 | |||
| 9e453440e6 | |||
| 8860817c76 | |||
| 3c108a8d22 | |||
| 8d553d6e9c | |||
| 346b98ed4f | |||
| 60683bd91e | |||
| b6938614b2 | |||
| 98961a1d36 | |||
| 5ae95102b4 | |||
| 216d0ba365 | |||
| 28e42f7e29 | |||
| 733373c0ca | |||
| 5617d6ca7c | |||
| 875dd2dead | |||
| 9043bc8435 | |||
| b3d49045de | |||
| 58528cad08 | |||
| 99281de6ab | |||
| 6268d23d12 | |||
| d7999ce1d1 | |||
| 6b0fd89cd2 | |||
| 4b0adb7a1e | |||
| de70d19d20 | |||
| 7155bb1e80 | |||
| fa08e72d30 | |||
| e2de8c7c53 | |||
| 429e181c8f | |||
| 7f611d9031 | |||
| e94e22f3f8 | |||
| 4a8c3b60be | |||
| 2190aa72a8 | |||
| d21cb28526 | |||
| 5c33eb3204 | |||
| 137687bc0f | |||
| 9d4a6614b1 | |||
| e4352a7817 | |||
| 911dde39c9 | |||
| afa836181c | |||
| 963862b1b9 | |||
| 96d521e149 | |||
| 1bb7517da0 | |||
| 814c2e32e4 | |||
| 92841f311f | |||
| 9d2e576630 | |||
| 936418a464 | |||
| 84c75d95c7 | |||
| 9287fa08c6 | |||
| 408e1180ca | |||
| 07f19d2caa | |||
| 368cb7a4ad | |||
| 109e0a7ad0 | |||
| 59750dad7d | |||
| 13ecfc8876 | |||
| 65d8b35f8b | |||
| 942d3c648c | |||
| 82db8be5ff | |||
| 03554b24ad | |||
| c5fb67c004 | |||
| 40983b46c8 | |||
| 5dcdbf04ea | |||
| da8ed3eceb | |||
| 2afde23a5d | |||
| d57a152040 | |||
| 728e92ea33 | |||
| 138e2d9158 | |||
| 7eabac6702 | |||
| cf4789e008 | |||
| 412884fce3 | |||
| 16aee2b869 | |||
| 3f7af51531 | |||
| 4eb100327e | |||
| 69b1946484 | |||
| 61cd69a286 | |||
| c8a1d0e400 | |||
| d120444a87 | |||
| 2382894fa2 | |||
| a52e7dc11a | |||
| 206992605e | |||
| 65611bb860 | |||
| 14aff51da9 | |||
| c42cea5ca9 | |||
| da8505f61d | |||
| 58586483dc | |||
| a838167f11 | |||
| b189fc571c | |||
| 96923f6115 | |||
| 0d6cce4a5b | |||
| 55947cb227 | |||
| 8783180cf3 | |||
| 134c0d4dfb | |||
| aecf8ec88b | |||
| bcff1d42b0 | |||
| 1bd367bd51 | |||
| 725f266b81 | |||
| d08e3de207 | |||
| 26714f6bfe | |||
| a5ce3fc927 | |||
| 3b23f71a3f | |||
| dec33cadd9 | |||
| 80c15a5e27 | |||
| 936c28a40b | |||
| 1a837a28ac | |||
| 8d5d12b108 | |||
| dd7a94135f | |||
| 1acc511b5c | |||
| 452e88267a | |||
| b941108cbd | |||
| e46f2843f7 | |||
| cf991e7b1b | |||
| 748a13104a | |||
| 2dd6b47714 | |||
| 8682be4774 | |||
| dc66892ca1 | |||
| 53a24783f5 | |||
| 0546bc900c | |||
| 7c25bcc0a7 | |||
| 7905853639 | |||
| 073dcc1fbe | |||
| ccdaa4223c | |||
| 5386b62dc4 | |||
| 9733fa4872 | |||
| 3b34c53092 | |||
| fd7ddfef54 | |||
| 0975b1599c | |||
| 78ac0ade01 | |||
| 7b9dab872b | |||
| 6413495fb8 | |||
| b414b3d32b | |||
| 20da7c4267 | |||
| 92b6778d2d | |||
| 5a61e589e8 | |||
| 85192bb110 | |||
| c7ae97fa2b | |||
| 8d02f3625d | |||
| a5a7380a26 | |||
| d9ce3d2046 | |||
| 815ff677fc | |||
| 915d865ce2 | |||
| c28e5f90b6 | |||
| 4383473ed6 | |||
| 77701dd5a3 | |||
| d4808fdc4d | |||
| 7fa967a98e | |||
| 9cffcc9f4e | |||
| 40925f0a06 | |||
| 0544d22902 | |||
| 3d075f2bf8 | |||
| 7384799f19 | |||
| 4a7f06e8fd | |||
| 8f662fc459 | |||
| 24b1dae9f2 | |||
| 3a3469a5f9 | |||
| 7993619ed2 | |||
| 4d1f6f869b | |||
| 3eb03f7934 | |||
| 03ed3daa31 | |||
| 02581e81a7 | |||
| 3ab3d5cf43 | |||
| 0ef04d9baa | |||
| df016f9228 | |||
| 17779c1e74 | |||
| 01d6a244d8 | |||
| 21d6755f39 | |||
| e91c017dd0 | |||
| 43687cd8b4 | |||
| 06729ee5a5 | |||
| b0c9743d9a | |||
| 37cc028868 | |||
| 84a2b7a3c8 | |||
| 89b3433346 | |||
| 3ff0d47ee3 | |||
| aeaf846482 | |||
| b031548791 | |||
| fcea617313 | |||
| 024f20ea26 | |||
| 0a4ed6fd71 | |||
| b6e2ce1f35 | |||
| e323e778cd | |||
| 6a87797649 | |||
| f4a4649bbc | |||
| 6ca54ee722 | |||
| 8e3035f783 | |||
| 79801595db | |||
| 3e1c8aacb1 | |||
| 91ac56cef2 | |||
| 58beac8fe0 | |||
| f632d320f5 | |||
| 2ddaf6a611 | |||
| 1932c60e1c | |||
| dc6f8e746e | |||
| ad7aedb843 | |||
| 571e6a8560 | |||
| 4791313def | |||
| f88fdae048 | |||
| bcef7aa6b6 | |||
| ce292bdce9 | |||
| 4eee023648 | |||
| 8f4b0fce49 | |||
| c6b3127b35 | |||
| 4d6a50c2cb | |||
| 15f3947ae6 | |||
| e142e3aca7 | |||
| 38438c8d9a | |||
| a278c10c75 | |||
| 2276443c56 | |||
| bb44773e57 | |||
| 14d9e90a03 | |||
| 03e042213c | |||
| db589455f4 | |||
| fb0a54d548 | |||
| 7013cc0904 | |||
| dcaf7b4a65 | |||
| 12f7b2a005 | |||
| 7837d40f57 | |||
| b4f719653f | |||
| f370b4bac6 | |||
| d788169bf3 | |||
| eea820fa2f | |||
| 271f1cb868 | |||
| 8c8dc9d32f | |||
| fd18e55f7c | |||
| faab9e620d | |||
| 5ba3efafd8 |
@@ -75,7 +75,7 @@
|
|||||||
{
|
{
|
||||||
"label": "Build Immich CLI",
|
"label": "Build Immich CLI",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "pnpm --filter cli build:dev"
|
"command": "pnpm --filter @immich/cli build:dev"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ${UPLOAD_LOCATION:-upload-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data
|
- ${UPLOAD_LOCATION:-upload-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data
|
||||||
- /etc/localtime:/etc/localtime:ro
|
- /etc/localtime:/etc/localtime:ro
|
||||||
- pnpm_store_server:/buildcache/pnpm-store
|
- build_cache:/buildcache
|
||||||
- ../plugins:/build/corePlugin
|
- ../packages/plugin-core:/build/plugins/immich-plugin-core
|
||||||
immich-web:
|
immich-web:
|
||||||
env_file: !reset []
|
env_file: !reset []
|
||||||
immich-machine-learning:
|
immich-machine-learning:
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ log "Preparing Immich Web Frontend"
|
|||||||
log ""
|
log ""
|
||||||
run_cmd pnpm --filter @immich/sdk install
|
run_cmd pnpm --filter @immich/sdk install
|
||||||
run_cmd pnpm --filter @immich/sdk build
|
run_cmd pnpm --filter @immich/sdk build
|
||||||
|
run_cmd pnpm --filter @immich/plugin-sdk install
|
||||||
|
run_cmd pnpm --filter @immich/plugin-sdk build
|
||||||
run_cmd pnpm --filter immich-web install
|
run_cmd pnpm --filter immich-web install
|
||||||
|
|
||||||
log "Starting Immich Web Frontend"
|
log "Starting Immich Web Frontend"
|
||||||
|
|||||||
+1
-3
@@ -30,9 +30,7 @@ machine-learning/
|
|||||||
misc/
|
misc/
|
||||||
mobile/
|
mobile/
|
||||||
|
|
||||||
open-api/typescript-sdk/build/
|
packages/sdk/build/
|
||||||
!open-api/typescript-sdk/package.json
|
|
||||||
!open-api/typescript-sdk/package-lock.json
|
|
||||||
|
|
||||||
server/upload/
|
server/upload/
|
||||||
server/src/queries
|
server/src/queries
|
||||||
|
|||||||
+2
-2
@@ -24,7 +24,7 @@ mobile/lib/infrastructure/repositories/db.repository.steps.dart linguist-generat
|
|||||||
mobile/test/drift/main/generated/** -diff -merge
|
mobile/test/drift/main/generated/** -diff -merge
|
||||||
mobile/test/drift/main/generated/** linguist-generated=true
|
mobile/test/drift/main/generated/** linguist-generated=true
|
||||||
|
|
||||||
open-api/typescript-sdk/fetch-client.ts -diff -merge
|
packages/sdk/fetch-client.ts -diff -merge
|
||||||
open-api/typescript-sdk/fetch-client.ts linguist-generated=true
|
packages/sdk/fetch-client.ts linguist-generated=true
|
||||||
|
|
||||||
*.sh text eol=lf
|
*.sh text eol=lf
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
custom: ['https://buy.immich.app', 'https://immich.store']
|
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
cli:
|
cli:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file:
|
- any-glob-to-any-file:
|
||||||
- cli/src/**
|
- packages/cli/src/**
|
||||||
|
|
||||||
documentation:
|
documentation:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
uses_template: ${{ steps.check.outputs.uses_template }}
|
uses_template: ${{ steps.check.outputs.uses_template }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
sparse-checkout: .github/pull_request_template.md
|
sparse-checkout: .github/pull_request_template.md
|
||||||
sparse-checkout-cone-mode: false
|
sparse-checkout-cone-mode: false
|
||||||
|
|||||||
@@ -51,8 +51,7 @@ jobs:
|
|||||||
should_run: ${{ steps.check.outputs.should_run }}
|
should_run: ${{ steps.check.outputs.should_run }}
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -61,7 +60,7 @@ jobs:
|
|||||||
id: check
|
id: check
|
||||||
uses: immich-app/devtools/actions/pre-job@91f342bb4477c4bc10c576ae739da875d85aa164 # pre-job-action-v2.0.4
|
uses: immich-app/devtools/actions/pre-job@91f342bb4477c4bc10c576ae739da875d85aa164 # pre-job-action-v2.0.4
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token || github.token }}
|
github-token: ${{ steps.token.outputs.token }}
|
||||||
filters: |
|
filters: |
|
||||||
mobile:
|
mobile:
|
||||||
- 'mobile/**'
|
- 'mobile/**'
|
||||||
@@ -80,17 +79,22 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
ref: ${{ inputs.ref || github.sha }}
|
ref: ${{ inputs.ref }}
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token || github.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
|
- name: Setup Mise
|
||||||
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
|
with:
|
||||||
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
working_directory: ./mobile
|
||||||
|
|
||||||
- name: Create the Keystore
|
- name: Create the Keystore
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
@@ -99,7 +103,7 @@ jobs:
|
|||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
run: printf "%s" $KEY_JKS | base64 -d > android/key.jks
|
run: printf "%s" $KEY_JKS | base64 -d > android/key.jks
|
||||||
|
|
||||||
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
- uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: 'zulu'
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
@@ -113,16 +117,8 @@ jobs:
|
|||||||
~/.gradle/wrapper
|
~/.gradle/wrapper
|
||||||
~/.android/sdk
|
~/.android/sdk
|
||||||
mobile/android/.gradle
|
mobile/android/.gradle
|
||||||
mobile/.dart_tool
|
|
||||||
key: build-mobile-gradle-${{ runner.os }}-main
|
key: build-mobile-gradle-${{ runner.os }}-main
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
|
||||||
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
|
|
||||||
with:
|
|
||||||
channel: 'stable'
|
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Setup Android SDK
|
- name: Setup Android SDK
|
||||||
uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4.0.1
|
uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4.0.1
|
||||||
with:
|
with:
|
||||||
@@ -133,11 +129,10 @@ jobs:
|
|||||||
run: flutter pub get
|
run: flutter pub get
|
||||||
|
|
||||||
- name: Generate translation file
|
- name: Generate translation file
|
||||||
run: dart run easy_localization:generate -S ../i18n && dart run bin/generate_keys.dart
|
run: mise //mobile:codegen:translation
|
||||||
working-directory: ./mobile
|
|
||||||
|
|
||||||
- name: Generate platform APIs
|
- name: Generate platform APIs
|
||||||
run: make pigeon
|
run: mise //mobile:codegen:pigeon
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
|
|
||||||
- name: Build Android App Bundle
|
- name: Build Android App Bundle
|
||||||
@@ -165,21 +160,24 @@ jobs:
|
|||||||
|
|
||||||
- name: Comment APK download link on PR
|
- name: Comment APK download link on PR
|
||||||
if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }}
|
if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }}
|
||||||
uses: mshick/add-pr-comment@8e4927817251f1ff60c001f04568532b38e0b4a0 # v3.11.0
|
uses: immich-app/devtools/actions/sticky-comment@0135acd12ad9f3369b94a2aa3c0ae8c835a4e926 # sticky-comment-action-v1.0.0
|
||||||
env:
|
env:
|
||||||
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
APK_URL: ${{ steps.upload-apk.outputs.artifact-url }}
|
APK_URL: ${{ steps.upload-apk.outputs.artifact-url }}
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
id: mobile-android-apk
|
||||||
message-id: 'mobile-android-apk'
|
token: ${{ steps.token.outputs.token }}
|
||||||
message: |
|
body: |
|
||||||
📱 **Android release APK (universal)** — `${{ env.HEAD_SHA }}`
|
📱 **Android release APK (universal)** — `${{ env.HEAD_SHA }}`
|
||||||
|
|
||||||
Download: ${{ env.APK_URL }}
|
Download: ${{ env.APK_URL }}
|
||||||
|
|
||||||
<img src="https://api.qrserver.com/v1/create-qr-code/?size=240x240&data=${{ env.APK_URL }}" alt="QR code" />
|
<details>
|
||||||
|
<summary>QR code</summary>
|
||||||
|
<img src="https://api.qrserver.com/v1/create-qr-code/?size=240x240&data=${{ env.APK_URL }}" alt="QR code" />
|
||||||
|
</details>
|
||||||
|
|
||||||
GitHub login required. Downloads as a zip containing a single `app-release.apk` — extract and install. Installs as a separate app (applicationId `app.alextran.immich.pr${{ github.event.pull_request.number }}`), so it coexists with the Play Store version and any other PR builds.
|
Installs as a separate app (applicationId `app.alextran.immich.pr${{ github.event.pull_request.number }}`), so it coexists with the Play Store version and any other PR builds.
|
||||||
|
|
||||||
- name: Save Gradle Cache
|
- name: Save Gradle Cache
|
||||||
id: cache-gradle-save
|
id: cache-gradle-save
|
||||||
@@ -191,7 +189,6 @@ jobs:
|
|||||||
~/.gradle/wrapper
|
~/.gradle/wrapper
|
||||||
~/.android/sdk
|
~/.android/sdk
|
||||||
mobile/android/.gradle
|
mobile/android/.gradle
|
||||||
mobile/.dart_tool
|
|
||||||
key: ${{ steps.cache-gradle-restore.outputs.cache-primary-key }}
|
key: ${{ steps.cache-gradle-restore.outputs.cache-primary-key }}
|
||||||
|
|
||||||
build-sign-ios:
|
build-sign-ios:
|
||||||
@@ -204,36 +201,43 @@ jobs:
|
|||||||
runs-on: macos-15
|
runs-on: macos-15
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- id: token
|
||||||
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
|
with:
|
||||||
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Select Xcode 26
|
- name: Select Xcode 26
|
||||||
run: sudo xcode-select -s /Applications/Xcode_26.2.app/Contents/Developer
|
run: sudo xcode-select -s /Applications/Xcode_26.2.app/Contents/Developer
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
ref: ${{ inputs.ref || github.sha }}
|
ref: ${{ inputs.ref || github.sha }}
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Mise
|
||||||
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
working_directory: ./mobile
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Install Flutter dependencies
|
- name: Install Flutter dependencies
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
run: flutter pub get
|
run: flutter pub get
|
||||||
|
|
||||||
- name: Generate translation files
|
- name: Generate translation files
|
||||||
run: dart run easy_localization:generate -S ../i18n && dart run bin/generate_keys.dart
|
run: mise //mobile:codegen:translation
|
||||||
working-directory: ./mobile
|
|
||||||
|
|
||||||
- name: Generate platform APIs
|
- name: Generate platform APIs
|
||||||
run: make pigeon
|
run: mise //mobile:codegen:pigeon
|
||||||
|
|
||||||
|
- name: Resolve iOS Swift Packages
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
|
run: flutter build ios --config-only --no-codesign
|
||||||
|
|
||||||
- name: Setup Ruby
|
- name: Setup Ruby
|
||||||
uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1.306.0
|
uses: ruby/setup-ruby@89f90524b88a01fe6e0b732220432cc6142926af # v1.313.0
|
||||||
with:
|
with:
|
||||||
ruby-version: '3.3'
|
ruby-version: '3.3'
|
||||||
bundler-cache: true
|
bundler-cache: true
|
||||||
@@ -290,7 +294,6 @@ jobs:
|
|||||||
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
|
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
|
||||||
APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }}
|
APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }}
|
||||||
ENVIRONMENT: ${{ inputs.environment || 'development' }}
|
ENVIRONMENT: ${{ inputs.environment || 'development' }}
|
||||||
BUNDLE_ID_SUFFIX: ${{ inputs.environment == 'production' && '' || 'development' }}
|
|
||||||
GITHUB_REF: ${{ github.ref }}
|
GITHUB_REF: ${{ github.ref }}
|
||||||
FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT: 120
|
FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT: 120
|
||||||
FASTLANE_XCODEBUILD_SETTINGS_RETRIES: 6
|
FASTLANE_XCODEBUILD_SETTINGS_RETRIES: 6
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ jobs:
|
|||||||
actions: write
|
actions: write
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- 'open-api/**'
|
- 'open-api/**'
|
||||||
|
- 'mobile/lib/utils/openapi_patching.dart'
|
||||||
- '.github/workflows/check-openapi.yml'
|
- '.github/workflows/check-openapi.yml'
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
@@ -19,13 +20,47 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Check for breaking API changes
|
- name: Check for breaking API changes
|
||||||
uses: oasdiff/oasdiff-action/breaking@37bf9ff785c7315df88216660826e71be4cc03da # v0.0.44
|
uses: oasdiff/oasdiff-action/breaking@e24529087d93f837b28b50bb66ba9016380a7fcc # v0.1.2
|
||||||
with:
|
with:
|
||||||
base: https://raw.githubusercontent.com/${{ github.repository }}/main/open-api/immich-openapi-specs.json
|
base: https://raw.githubusercontent.com/${{ github.repository }}/main/open-api/immich-openapi-specs.json
|
||||||
revision: open-api/immich-openapi-specs.json
|
revision: open-api/immich-openapi-specs.json
|
||||||
fail-on: ERR
|
fail-on: ERR
|
||||||
|
review: false
|
||||||
|
|
||||||
|
check-mobile-patches:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Setup Mise
|
||||||
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
|
with:
|
||||||
|
github_token: ${{ github.token }}
|
||||||
|
working_directory: ./mobile
|
||||||
|
|
||||||
|
- name: Get packages
|
||||||
|
working-directory: ./mobile
|
||||||
|
run: flutter pub get
|
||||||
|
|
||||||
|
- name: Fetch base spec from main
|
||||||
|
run: |
|
||||||
|
curl -fsSL \
|
||||||
|
"https://raw.githubusercontent.com/${{ github.repository }}/main/open-api/immich-openapi-specs.json" \
|
||||||
|
-o /tmp/base-spec.json
|
||||||
|
|
||||||
|
- name: Check newly-required fields have a backward-compat patch
|
||||||
|
working-directory: ./mobile
|
||||||
|
env:
|
||||||
|
OPENAPI_BASE_SPEC: /tmp/base-spec.json
|
||||||
|
OPENAPI_REVISION_SPEC: ../open-api/immich-openapi-specs.json
|
||||||
|
run: flutter test test/openapi_patches_coverage.dart
|
||||||
|
|||||||
+19
-17
@@ -3,11 +3,11 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
paths:
|
||||||
- 'cli/**'
|
- 'packages/cli/**'
|
||||||
- '.github/workflows/cli.yml'
|
- '.github/workflows/cli.yml'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- 'cli/**'
|
- 'packages/cli/**'
|
||||||
- '.github/workflows/cli.yml'
|
- '.github/workflows/cli.yml'
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
@@ -28,28 +28,30 @@ jobs:
|
|||||||
packages: write
|
packages: write
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./cli
|
working-directory: ./packages/cli
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@01a4d354b70f99a6baf4a1b72827f6d4922e4978 # use-mise-action-v2.0.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Publish
|
- name: Publish
|
||||||
if: ${{ github.event_name == 'release' }}
|
if: ${{ github.event_name == 'release' }}
|
||||||
run: mise run ci-publish
|
env:
|
||||||
|
NPM_TAG: ${{ github.event.release.prerelease && 'rc' || 'latest' }}
|
||||||
|
run: mise run ci-publish -- --tag "$NPM_TAG"
|
||||||
|
|
||||||
docker:
|
docker:
|
||||||
name: Docker
|
name: Docker
|
||||||
@@ -61,25 +63,25 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
@@ -89,12 +91,12 @@ jobs:
|
|||||||
- name: Get package version
|
- name: Get package version
|
||||||
id: package-version
|
id: package-version
|
||||||
run: |
|
run: |
|
||||||
version=$(jq -r '.version' cli/package.json)
|
version=$(jq -r '.version' packages/cli/package.json)
|
||||||
echo "version=$version" >> "$GITHUB_OUTPUT"
|
echo "version=$version" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Generate docker image tags
|
- name: Generate docker image tags
|
||||||
id: metadata
|
id: metadata
|
||||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
|
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
|
||||||
with:
|
with:
|
||||||
flavor: |
|
flavor: |
|
||||||
latest=false
|
latest=false
|
||||||
@@ -102,12 +104,12 @@ jobs:
|
|||||||
name=ghcr.io/${{ github.repository_owner }}/immich-cli
|
name=ghcr.io/${{ github.repository_owner }}/immich-cli
|
||||||
tags: |
|
tags: |
|
||||||
type=raw,value=${{ steps.package-version.outputs.version }},enable=${{ github.event_name == 'release' }}
|
type=raw,value=${{ steps.package-version.outputs.version }},enable=${{ github.event_name == 'release' }}
|
||||||
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
|
type=raw,value=latest,enable=${{ github.event_name == 'release' && !github.event.release.prerelease }}
|
||||||
|
|
||||||
- name: Build and push image
|
- name: Build and push image
|
||||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
|
||||||
with:
|
with:
|
||||||
file: cli/Dockerfile
|
file: packages/cli/Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: ${{ github.event_name == 'release' }}
|
push: ${{ github.event_name == 'release' }}
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
|
|||||||
@@ -14,7 +14,11 @@ jobs:
|
|||||||
should_run: ${{ steps.should_run.outputs.run }}
|
should_run: ${{ steps.should_run.outputs.run }}
|
||||||
steps:
|
steps:
|
||||||
- id: should_run
|
- id: should_run
|
||||||
run: echo "run=${{ github.event_name == 'issues' || github.event.discussion.category.name == 'Feature Request' }}" >> $GITHUB_OUTPUT
|
run: |
|
||||||
|
echo "run=${{
|
||||||
|
(github.event_name == 'issues' || github.event.discussion.category.name == 'Feature Request')
|
||||||
|
&& !contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.issue.author_association || github.event.discussion.author_association)
|
||||||
|
}}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
get_body:
|
get_body:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -35,7 +39,7 @@ jobs:
|
|||||||
needs: [get_body, should_run]
|
needs: [get_body, should_run]
|
||||||
if: ${{ needs.should_run.outputs.should_run == 'true' }}
|
if: ${{ needs.should_run.outputs.should_run == 'true' }}
|
||||||
container:
|
container:
|
||||||
image: ghcr.io/immich-app/mdq:main@sha256:32abe582452b12dff55055e1d6bc24508a8f17164f9d1831db7bb70953c014c6
|
image: ghcr.io/immich-app/mdq:main@sha256:2a64c7f045cb7b580fbdf3614d7d1805f5775fec453e3d1023764180efa8c70b
|
||||||
outputs:
|
outputs:
|
||||||
checked: ${{ steps.get_checkbox.outputs.checked }}
|
checked: ${{ steps.get_checkbox.outputs.checked }}
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -44,20 +44,20 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
|
uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@@ -70,7 +70,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
|
uses: github/codeql-action/autobuild@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
@@ -83,6 +83,6 @@ jobs:
|
|||||||
# ./location_of_script_within_repo/buildscript.sh
|
# ./location_of_script_within_repo/buildscript.sh
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
|
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
|
||||||
with:
|
with:
|
||||||
category: '/language:${{matrix.language}}'
|
category: '/language:${{matrix.language}}'
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ jobs:
|
|||||||
should_run: ${{ steps.check.outputs.should_run }}
|
should_run: ${{ steps.check.outputs.should_run }}
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -60,7 +60,7 @@ jobs:
|
|||||||
suffix: ['', '-cuda', '-rocm', '-openvino', '-armnn', '-rknn']
|
suffix: ['', '-cuda', '-rocm', '-openvino', '-armnn', '-rknn']
|
||||||
steps:
|
steps:
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
@@ -90,7 +90,7 @@ jobs:
|
|||||||
suffix: ['']
|
suffix: ['']
|
||||||
steps:
|
steps:
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
@@ -132,7 +132,7 @@ jobs:
|
|||||||
suffixes: '-rocm'
|
suffixes: '-rocm'
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
runner-mapping: '{"linux/amd64": "pokedex-large"}'
|
runner-mapping: '{"linux/amd64": "pokedex-large"}'
|
||||||
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@5813c7c4f7016c748ae7ac5d5f684846649d4d20 # multi-runner-build-workflow-v2.4.0
|
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@50dc3a14f0606ecd8fc28d78d3a3c655115ab695 # multi-runner-build-workflow-v3.1.0
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
actions: read
|
actions: read
|
||||||
@@ -147,7 +147,7 @@ jobs:
|
|||||||
platforms: ${{ matrix.platforms }}
|
platforms: ${{ matrix.platforms }}
|
||||||
runner-mapping: ${{ matrix.runner-mapping }}
|
runner-mapping: ${{ matrix.runner-mapping }}
|
||||||
suffixes: ${{ matrix.suffixes }}
|
suffixes: ${{ matrix.suffixes }}
|
||||||
dockerhub-push: ${{ github.event_name == 'release' }}
|
dockerhub-push: ${{ github.event_name == 'release' && !github.event.release.prerelease }}
|
||||||
build-args: |
|
build-args: |
|
||||||
DEVICE=${{ matrix.device }}
|
DEVICE=${{ matrix.device }}
|
||||||
|
|
||||||
@@ -155,7 +155,7 @@ jobs:
|
|||||||
name: Build and Push Server
|
name: Build and Push Server
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ fromJSON(needs.pre-job.outputs.should_run).server == true }}
|
if: ${{ fromJSON(needs.pre-job.outputs.should_run).server == true }}
|
||||||
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@5813c7c4f7016c748ae7ac5d5f684846649d4d20 # multi-runner-build-workflow-v2.4.0
|
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@50dc3a14f0606ecd8fc28d78d3a3c655115ab695 # multi-runner-build-workflow-v3.1.0
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
actions: read
|
actions: read
|
||||||
@@ -167,7 +167,7 @@ jobs:
|
|||||||
image: immich-server
|
image: immich-server
|
||||||
context: .
|
context: .
|
||||||
dockerfile: server/Dockerfile
|
dockerfile: server/Dockerfile
|
||||||
dockerhub-push: ${{ github.event_name == 'release' }}
|
dockerhub-push: ${{ github.event_name == 'release' && !github.event.release.prerelease }}
|
||||||
build-args: |
|
build-args: |
|
||||||
DEVICE=cpu
|
DEVICE=cpu
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
should_run: ${{ steps.check.outputs.should_run }}
|
should_run: ${{ steps.check.outputs.should_run }}
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -54,19 +54,19 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@01a4d354b70f99a6baf4a1b72827f6d4922e4978 # use-mise-action-v2.0.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ jobs:
|
|||||||
artifact: ${{ steps.get-artifact.outputs.result }}
|
artifact: ${{ steps.get-artifact.outputs.result }}
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -98,9 +98,16 @@ jobs:
|
|||||||
shouldDeploy: true
|
shouldDeploy: true
|
||||||
};
|
};
|
||||||
} else if (eventType == "release") {
|
} else if (eventType == "release") {
|
||||||
|
const tag = context.payload.workflow_run.head_branch;
|
||||||
|
const { data: release } = await github.rest.repos.getReleaseByTag({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
tag,
|
||||||
|
});
|
||||||
parameters = {
|
parameters = {
|
||||||
event: "release",
|
event: "release",
|
||||||
name: context.payload.workflow_run.head_branch,
|
name: tag,
|
||||||
|
prerelease: release.prerelease,
|
||||||
shouldDeploy: !isFork
|
shouldDeploy: !isFork
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -119,19 +126,19 @@ jobs:
|
|||||||
if: ${{ fromJson(needs.checks.outputs.artifact).found && fromJson(needs.checks.outputs.parameters).shouldDeploy }}
|
if: ${{ fromJson(needs.checks.outputs.artifact).found && fromJson(needs.checks.outputs.parameters).shouldDeploy }}
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@01a4d354b70f99a6baf4a1b72827f6d4922e4978 # use-mise-action-v2.0.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -146,6 +153,7 @@ jobs:
|
|||||||
const parameters = JSON.parse(process.env.PARAM_JSON);
|
const parameters = JSON.parse(process.env.PARAM_JSON);
|
||||||
core.setOutput("event", parameters.event);
|
core.setOutput("event", parameters.event);
|
||||||
core.setOutput("name", parameters.name);
|
core.setOutput("name", parameters.name);
|
||||||
|
core.setOutput("prerelease", parameters.prerelease);
|
||||||
core.setOutput("shouldDeploy", parameters.shouldDeploy);
|
core.setOutput("shouldDeploy", parameters.shouldDeploy);
|
||||||
|
|
||||||
- name: Download artifact
|
- name: Download artifact
|
||||||
@@ -203,7 +211,7 @@ jobs:
|
|||||||
run: mise run //docs:deploy
|
run: mise run //docs:deploy
|
||||||
|
|
||||||
- name: Deploy Docs Release Domain
|
- name: Deploy Docs Release Domain
|
||||||
if: ${{ steps.parameters.outputs.event == 'release' }}
|
if: ${{ steps.parameters.outputs.event == 'release' && steps.parameters.outputs.prerelease != 'true' }}
|
||||||
env:
|
env:
|
||||||
TF_VAR_prefix_name: ${{ steps.parameters.outputs.name}}
|
TF_VAR_prefix_name: ${{ steps.parameters.outputs.name}}
|
||||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
@@ -213,12 +221,11 @@ jobs:
|
|||||||
run: 'mise run //deployment:tf apply'
|
run: 'mise run //deployment:tf apply'
|
||||||
|
|
||||||
- name: Comment
|
- name: Comment
|
||||||
uses: actions-cool/maintain-one-comment@909842216bc8e8658364c572ec52100f4c2cc50a # v3.3.0
|
uses: immich-app/devtools/actions/sticky-comment@0135acd12ad9f3369b94a2aa3c0ae8c835a4e926 # sticky-comment-action-v1.0.0
|
||||||
if: ${{ steps.parameters.outputs.event == 'pr' }}
|
if: ${{ steps.parameters.outputs.event == 'pr' }}
|
||||||
with:
|
with:
|
||||||
|
id: docs-pr-url
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
number: ${{ fromJson(needs.checks.outputs.parameters).pr_number }}
|
number: ${{ fromJson(needs.checks.outputs.parameters).pr_number }}
|
||||||
body: |
|
body: |
|
||||||
📖 Documentation deployed to [${{ steps.docs-output.outputs.subdomain }}](https://${{ steps.docs-output.outputs.subdomain }})
|
📖 Documentation deployed to [${{ steps.docs-output.outputs.subdomain }}](https://${{ steps.docs-output.outputs.subdomain }})
|
||||||
emojis: 'rocket'
|
|
||||||
body-include: '<!-- Docs PR URL -->'
|
|
||||||
|
|||||||
@@ -17,19 +17,19 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@01a4d354b70f99a6baf4a1b72827f6d4922e4978 # use-mise-action-v2.0.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -44,9 +44,8 @@ jobs:
|
|||||||
run: 'mise run //deployment:tf destroy -- -refresh=false'
|
run: 'mise run //deployment:tf destroy -- -refresh=false'
|
||||||
|
|
||||||
- name: Comment
|
- name: Comment
|
||||||
uses: actions-cool/maintain-one-comment@909842216bc8e8658364c572ec52100f4c2cc50a # v3.3.0
|
uses: immich-app/devtools/actions/sticky-comment@0135acd12ad9f3369b94a2aa3c0ae8c835a4e926 # sticky-comment-action-v1.0.0
|
||||||
with:
|
with:
|
||||||
|
id: docs-pr-url
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
number: ${{ github.event.number }}
|
|
||||||
delete: true
|
delete: true
|
||||||
body-include: '<!-- Docs PR URL -->'
|
|
||||||
|
|||||||
@@ -15,20 +15,20 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.ref }}
|
ref: ${{ github.event.pull_request.head.ref }}
|
||||||
persist-credentials: true
|
persist-credentials: true
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@01a4d354b70f99a6baf4a1b72827f6d4922e4978 # use-mise-action-v2.0.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ jobs:
|
|||||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.generate-token.outputs.token }}
|
github-token: ${{ steps.token.outputs.token }}
|
||||||
script: |
|
script: |
|
||||||
github.rest.issues.removeLabel({
|
github.rest.issues.removeLabel({
|
||||||
issue_number: context.payload.pull_request.number,
|
issue_number: context.payload.pull_request.number,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate_token
|
id: generate_token
|
||||||
if: ${{ inputs.skip != true }}
|
if: ${{ inputs.skip != true }}
|
||||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
|
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|||||||
@@ -13,3 +13,4 @@ jobs:
|
|||||||
actions: read
|
actions: read
|
||||||
contents: read
|
contents: read
|
||||||
security-events: write
|
security-events: write
|
||||||
|
secrets: inherit
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1
|
- uses: actions/labeler@f27b608878404679385c85cfa523b85ccb86e213 # v6.1.0
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ steps.token.outputs.token }}
|
repo-token: ${{ steps.token.outputs.token }}
|
||||||
|
|||||||
@@ -10,9 +10,13 @@ on:
|
|||||||
type: choice
|
type: choice
|
||||||
options:
|
options:
|
||||||
- 'false'
|
- 'false'
|
||||||
- major
|
|
||||||
- minor
|
- minor
|
||||||
- patch
|
- patch
|
||||||
|
- premajor
|
||||||
|
- preminor
|
||||||
|
- prepatch
|
||||||
|
- prerelease
|
||||||
|
- release
|
||||||
mobileBump:
|
mobileBump:
|
||||||
description: 'Bump mobile build number'
|
description: 'Bump mobile build number'
|
||||||
required: false
|
required: false
|
||||||
@@ -46,38 +50,45 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
ref: ${{ steps.push-tag.outputs.commit_long_sha }}
|
ref: ${{ steps.push-tag.outputs.commit_long_sha }}
|
||||||
version: ${{ steps.output.outputs.version }}
|
version: ${{ steps.output.outputs.version }}
|
||||||
|
rc: ${{ steps.output.outputs.rc }}
|
||||||
permissions: {} # No job-level permissions are needed because it uses the app-token
|
permissions: {} # No job-level permissions are needed because it uses the app-token
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
persist-credentials: true
|
persist-credentials: true
|
||||||
ref: main
|
ref: main
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@01a4d354b70f99a6baf4a1b72827f6d4922e4978 # use-mise-action-v2.0.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
# TODO move to mise
|
# TODO move to mise
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
|
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
|
||||||
|
|
||||||
- name: Bump version
|
- name: Bump version
|
||||||
env:
|
env:
|
||||||
SERVER_BUMP: ${{ inputs.serverBump }}
|
SERVER_BUMP: ${{ inputs.serverBump }}
|
||||||
MOBILE_BUMP: ${{ inputs.mobileBump }}
|
MOBILE_BUMP: ${{ inputs.mobileBump }}
|
||||||
run: misc/release/pump-version.sh -s "${SERVER_BUMP}" -m "${MOBILE_BUMP}"
|
run: pnpm --silent release -s "${SERVER_BUMP}" -m "${MOBILE_BUMP}"
|
||||||
|
|
||||||
- id: output
|
- id: output
|
||||||
run: echo "version=$IMMICH_VERSION" >> $GITHUB_OUTPUT
|
run: |
|
||||||
|
echo "version=$IMMICH_VERSION" >> $GITHUB_OUTPUT
|
||||||
|
if [[ "$IMMICH_VERSION" =~ -rc\.[0-9]+$ ]]; then
|
||||||
|
echo "rc=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "rc=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Commit and tag
|
- name: Commit and tag
|
||||||
id: push-tag
|
id: push-tag
|
||||||
@@ -93,6 +104,7 @@ jobs:
|
|||||||
needs: bump_version
|
needs: bump_version
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
secrets:
|
secrets:
|
||||||
KEY_JKS: ${{ secrets.KEY_JKS }}
|
KEY_JKS: ${{ secrets.KEY_JKS }}
|
||||||
ALIAS: ${{ secrets.ALIAS }}
|
ALIAS: ${{ secrets.ALIAS }}
|
||||||
@@ -119,13 +131,13 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
|
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
@@ -137,9 +149,10 @@ jobs:
|
|||||||
github-token: ${{ steps.generate-token.outputs.token }}
|
github-token: ${{ steps.generate-token.outputs.token }}
|
||||||
|
|
||||||
- name: Create draft release
|
- name: Create draft release
|
||||||
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
|
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
|
prerelease: ${{ needs.bump_version.outputs.rc }}
|
||||||
tag_name: ${{ needs.bump_version.outputs.version }}
|
tag_name: ${{ needs.bump_version.outputs.version }}
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
generate_release_notes: true
|
generate_release_notes: true
|
||||||
|
|||||||
@@ -14,16 +14,16 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- uses: mshick/add-pr-comment@8e4927817251f1ff60c001f04568532b38e0b4a0 # v3.11.0
|
- uses: immich-app/devtools/actions/sticky-comment@0135acd12ad9f3369b94a2aa3c0ae8c835a4e926 # sticky-comment-action-v1.0.0
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
id: preview-status
|
||||||
message-id: 'preview-status'
|
token: ${{ steps.token.outputs.token }}
|
||||||
message: 'Deploying preview environment to https://pr-${{ github.event.pull_request.number }}.preview.internal.immich.build/'
|
body: 'Deploying preview environment to https://pr-${{ github.event.pull_request.number }}.preview.internal.immich.build/'
|
||||||
|
|
||||||
remove-label:
|
remove-label:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -32,7 +32,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -48,16 +48,16 @@ jobs:
|
|||||||
name: 'preview'
|
name: 'preview'
|
||||||
})
|
})
|
||||||
|
|
||||||
- uses: mshick/add-pr-comment@8e4927817251f1ff60c001f04568532b38e0b4a0 # v3.11.0
|
- uses: immich-app/devtools/actions/sticky-comment@0135acd12ad9f3369b94a2aa3c0ae8c835a4e926 # sticky-comment-action-v1.0.0
|
||||||
if: ${{ github.event.pull_request.head.repo.fork }}
|
if: ${{ github.event.pull_request.head.repo.fork }}
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
id: preview-status
|
||||||
message-id: 'preview-status'
|
token: ${{ steps.token.outputs.token }}
|
||||||
message: 'PRs from forks cannot have preview environments.'
|
body: 'PRs from forks cannot have preview environments.'
|
||||||
|
|
||||||
- uses: mshick/add-pr-comment@8e4927817251f1ff60c001f04568532b38e0b4a0 # v3.11.0
|
- uses: immich-app/devtools/actions/sticky-comment@0135acd12ad9f3369b94a2aa3c0ae8c835a4e926 # sticky-comment-action-v1.0.0
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
id: preview-status
|
||||||
message-id: 'preview-status'
|
token: ${{ steps.token.outputs.token }}
|
||||||
message: 'Preview environment has been removed.'
|
body: 'Preview environment has been removed.'
|
||||||
|
|||||||
@@ -14,32 +14,31 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
id-token: write
|
id-token: write
|
||||||
packages: write
|
packages: write
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./open-api/typescript-sdk
|
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@01a4d354b70f99a6baf4a1b72827f6d4922e4978 # use-mise-action-v2.0.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: pnpm install --frozen-lockfile
|
run: pnpm --filter @immich/sdk install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm build
|
run: pnpm --filter @immich/sdk build
|
||||||
|
|
||||||
- name: Publish
|
- name: Publish
|
||||||
run: pnpm publish --provenance --no-git-checks
|
env:
|
||||||
|
NPM_TAG: ${{ github.event.release.prerelease && 'rc' || 'latest' }}
|
||||||
|
run: pnpm --filter @immich/sdk publish --provenance --no-git-checks --tag "$NPM_TAG"
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ jobs:
|
|||||||
should_run: ${{ steps.check.outputs.should_run }}
|
should_run: ${{ steps.check.outputs.should_run }}
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -49,49 +49,38 @@ jobs:
|
|||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Mise
|
||||||
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
working_directory: ./mobile
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: dart pub get
|
run: flutter pub get
|
||||||
|
|
||||||
- name: Install dependencies for UI package
|
- name: Install dependencies for UI package
|
||||||
run: dart pub get
|
run: flutter pub get
|
||||||
working-directory: ./mobile/packages/ui
|
working-directory: ./mobile/packages/ui
|
||||||
|
|
||||||
- name: Install dependencies for UI Showcase
|
- name: Generate translation files
|
||||||
run: dart pub get
|
run: mise //mobile:codegen:translation
|
||||||
working-directory: ./mobile/packages/ui/showcase
|
|
||||||
|
|
||||||
- name: Install DCM
|
|
||||||
uses: CQLabs/setup-dcm@8697ae0790c0852e964a6ef1d768d62a6675481a # v2.0.1
|
|
||||||
with:
|
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
|
||||||
version: auto
|
|
||||||
working-directory: ./mobile
|
|
||||||
|
|
||||||
- name: Generate translation file
|
|
||||||
run: dart run easy_localization:generate -S ../i18n && dart run bin/generate_keys.dart
|
|
||||||
|
|
||||||
- name: Run Build Runner
|
- name: Run Build Runner
|
||||||
run: make build
|
run: mise //mobile:codegen:dart
|
||||||
|
|
||||||
- name: Generate platform API
|
- name: Generate platform API
|
||||||
run: make pigeon
|
run: mise //mobile:codegen:pigeon
|
||||||
|
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||||
@@ -101,26 +90,24 @@ jobs:
|
|||||||
mobile/**/*.g.dart
|
mobile/**/*.g.dart
|
||||||
mobile/**/*.gr.dart
|
mobile/**/*.gr.dart
|
||||||
mobile/**/*.drift.dart
|
mobile/**/*.drift.dart
|
||||||
|
mobile/**/*.g.swift
|
||||||
|
mobile/**/*.g.kt
|
||||||
|
|
||||||
- name: Verify files have not changed
|
- name: Verify files have not changed
|
||||||
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
||||||
env:
|
env:
|
||||||
CHANGED_FILES: ${{ steps.verify-changed-files.outputs.changed_files }}
|
CHANGED_FILES: ${{ steps.verify-changed-files.outputs.changed_files }}
|
||||||
run: |
|
run: |
|
||||||
echo "ERROR: Generated files not up to date! Run 'make build' and 'make pigeon' inside the mobile directory"
|
echo "ERROR: Generated files not up to date! Run 'mise //mobile:codegen:dart' and 'mise //mobile:codegen:pigeon'"
|
||||||
echo "Changed files: ${CHANGED_FILES}"
|
echo "Changed files: ${CHANGED_FILES}"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
- name: Run dart analyze
|
- name: Run analyze
|
||||||
run: dart analyze --fatal-infos
|
run: mise //mobile:analyze
|
||||||
|
|
||||||
- name: Run dart format
|
- name: Run format
|
||||||
run: make format
|
run: mise //mobile:format
|
||||||
|
|
||||||
# TODO: Re-enable after upgrading custom_lint
|
# TODO: Re-enable after upgrading custom_lint
|
||||||
# - name: Run dart custom_lint
|
# - name: Run dart custom_lint
|
||||||
# run: dart run custom_lint
|
# run: dart run custom_lint
|
||||||
|
|
||||||
# TODO: Use https://github.com/CQLabs/dcm-action
|
|
||||||
- name: Run DCM
|
|
||||||
run: dcm analyze lib --fatal-style --fatal-warnings
|
|
||||||
|
|||||||
+132
-104
@@ -17,7 +17,7 @@ jobs:
|
|||||||
should_run: ${{ steps.check.outputs.should_run }}
|
should_run: ${{ steps.check.outputs.should_run }}
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -28,33 +28,74 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
github-token: ${{ steps.token.outputs.token }}
|
||||||
filters: |
|
filters: |
|
||||||
|
root:
|
||||||
|
- 'misc/**'
|
||||||
|
- 'pnpm-lock.yaml'
|
||||||
|
- 'mise.toml'
|
||||||
i18n:
|
i18n:
|
||||||
- 'i18n/**'
|
- 'i18n/**'
|
||||||
|
- 'mise.toml'
|
||||||
web:
|
web:
|
||||||
- 'web/**'
|
- 'web/**'
|
||||||
- 'i18n/**'
|
- 'i18n/**'
|
||||||
- 'open-api/typescript-sdk/**'
|
- 'packages/sdk/**'
|
||||||
- 'pnpm-lock.yaml'
|
- 'pnpm-lock.yaml'
|
||||||
|
- 'mise.toml'
|
||||||
server:
|
server:
|
||||||
- 'server/**'
|
- 'server/**'
|
||||||
- 'pnpm-lock.yaml'
|
- 'pnpm-lock.yaml'
|
||||||
|
- 'mise.toml'
|
||||||
|
- 'packages/plugin-core/**'
|
||||||
|
- 'packages/plugin-sdk/**'
|
||||||
cli:
|
cli:
|
||||||
- 'cli/**'
|
- 'packages/cli/**'
|
||||||
- 'open-api/typescript-sdk/**'
|
- 'packages/sdk/**'
|
||||||
- 'pnpm-lock.yaml'
|
- 'pnpm-lock.yaml'
|
||||||
|
- 'mise.toml'
|
||||||
e2e:
|
e2e:
|
||||||
- 'e2e/**'
|
- 'e2e/**'
|
||||||
- 'pnpm-lock.yaml'
|
- 'pnpm-lock.yaml'
|
||||||
|
- 'mise.toml'
|
||||||
mobile:
|
mobile:
|
||||||
- 'mobile/**'
|
- 'mobile/**'
|
||||||
|
- 'mise.toml'
|
||||||
machine-learning:
|
machine-learning:
|
||||||
- 'machine-learning/**'
|
- 'machine-learning/**'
|
||||||
|
- 'mise.toml'
|
||||||
.github:
|
.github:
|
||||||
- '.github/**'
|
- '.github/**'
|
||||||
force-filters: |
|
force-filters: |
|
||||||
- '.github/workflows/test.yml'
|
- '.github/workflows/test.yml'
|
||||||
force-events: 'workflow_dispatch'
|
force-events: 'workflow_dispatch'
|
||||||
|
|
||||||
|
root-unit-tests:
|
||||||
|
name: Test the root workspace
|
||||||
|
needs: pre-job
|
||||||
|
if: ${{ fromJSON(needs.pre-job.outputs.should_run).root == true }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- id: token
|
||||||
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
|
with:
|
||||||
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
|
- name: Setup Mise
|
||||||
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
|
with:
|
||||||
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
|
- name: Run unit tests
|
||||||
|
run: pnpm test
|
||||||
|
|
||||||
server-unit-tests:
|
server-unit-tests:
|
||||||
name: Test & Lint Server
|
name: Test & Lint Server
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
@@ -62,29 +103,26 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./server
|
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@01a4d354b70f99a6baf4a1b72827f6d4922e4978 # use-mise-action-v2.0.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Run ci-unit
|
- name: Run ci-unit
|
||||||
run: mise run ci-unit
|
run: mise run //server:ci-unit
|
||||||
|
|
||||||
cli-unit-tests:
|
cli-unit-tests:
|
||||||
name: Unit Test CLI
|
name: Unit Test CLI
|
||||||
@@ -95,22 +133,22 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./cli
|
working-directory: ./packages/cli
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@01a4d354b70f99a6baf4a1b72827f6d4922e4978 # use-mise-action-v2.0.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -126,34 +164,29 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./cli
|
working-directory: ./packages/cli
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup Mise
|
||||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
|
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
- name: Setup typescript-sdk
|
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
|
||||||
working-directory: ./open-api/typescript-sdk
|
|
||||||
|
|
||||||
- name: Install deps
|
- name: Run setup @immich/sdk
|
||||||
|
run: mise run //:sdk:install && mise run //:sdk:build
|
||||||
|
|
||||||
|
- name: Run pnpm install
|
||||||
run: pnpm install --frozen-lockfile
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
# Skip linter & formatter in Windows test.
|
# Skip linter & formatter in Windows test.
|
||||||
@@ -178,23 +211,23 @@ jobs:
|
|||||||
working-directory: ./web
|
working-directory: ./web
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@01a4d354b70f99a6baf4a1b72827f6d4922e4978 # use-mise-action-v2.0.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup @immich/sdk
|
||||||
run: mise run //:sdk:install && mise run //:sdk:build
|
run: mise run //:sdk:install && mise run //:sdk:build
|
||||||
|
|
||||||
- name: Run pnpm install
|
- name: Run pnpm install
|
||||||
@@ -216,19 +249,19 @@ jobs:
|
|||||||
working-directory: ./web
|
working-directory: ./web
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@01a4d354b70f99a6baf4a1b72827f6d4922e4978 # use-mise-action-v2.0.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -244,27 +277,27 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@01a4d354b70f99a6baf4a1b72827f6d4922e4978 # use-mise-action-v2.0.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm --filter=immich-i18n install --frozen-lockfile
|
run: pnpm -w install --frozen-lockfile
|
||||||
|
|
||||||
- name: Format
|
- name: Format
|
||||||
run: pnpm --filter=immich-i18n format:fix
|
run: pnpm format:fix
|
||||||
|
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||||
@@ -294,19 +327,19 @@ jobs:
|
|||||||
working-directory: ./e2e
|
working-directory: ./e2e
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@01a4d354b70f99a6baf4a1b72827f6d4922e4978 # use-mise-action-v2.0.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -326,20 +359,20 @@ jobs:
|
|||||||
working-directory: ./server
|
working-directory: ./server
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@01a4d354b70f99a6baf4a1b72827f6d4922e4978 # use-mise-action-v2.0.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -362,20 +395,20 @@ jobs:
|
|||||||
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||||
@@ -384,20 +417,14 @@ jobs:
|
|||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
cache-dependency-path: '**/pnpm-lock.yaml'
|
||||||
|
|
||||||
- name: Setup typescript-sdk
|
- name: Setup packages
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
run: pnpm --filter @immich/sdk --filter @immich/cli install --frozen-lockfile && pnpm --filter @immich/sdk --filter @immich/cli build
|
||||||
working-directory: ./open-api/typescript-sdk
|
|
||||||
|
|
||||||
- name: Run setup web
|
- name: Run setup web
|
||||||
run: pnpm install --frozen-lockfile && pnpm exec svelte-kit sync
|
run: pnpm install --frozen-lockfile && pnpm exec svelte-kit sync
|
||||||
working-directory: ./web
|
working-directory: ./web
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run setup cli
|
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
|
||||||
working-directory: ./cli
|
|
||||||
if: ${{ !cancelled() }}
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --frozen-lockfile
|
run: pnpm install --frozen-lockfile
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
@@ -445,20 +472,20 @@ jobs:
|
|||||||
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||||
@@ -467,9 +494,8 @@ jobs:
|
|||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
cache-dependency-path: '**/pnpm-lock.yaml'
|
||||||
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup @immich/sdk
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
run: pnpm --filter @immich/sdk install --frozen-lockfile && pnpm --filter @immich/sdk build
|
||||||
working-directory: ./open-api/typescript-sdk
|
|
||||||
|
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
@@ -554,26 +580,32 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
- name: Setup Flutter SDK
|
|
||||||
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
|
- name: Setup Mise
|
||||||
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
working_directory: ./mobile
|
||||||
- name: Generate translation file
|
|
||||||
run: dart run easy_localization:generate -S ../i18n && dart run bin/generate_keys.dart
|
- name: Install dependencies
|
||||||
|
run: flutter pub get
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
|
|
||||||
|
- name: Generate translation files
|
||||||
|
run: mise //mobile:codegen:translation
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
working-directory: ./mobile
|
run: mise //mobile:test
|
||||||
run: flutter test -j 1
|
|
||||||
ml-unit-tests:
|
ml-unit-tests:
|
||||||
name: Unit Test ML
|
name: Unit Test ML
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
@@ -586,18 +618,18 @@ jobs:
|
|||||||
working-directory: ./machine-learning
|
working-directory: ./machine-learning
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@01a4d354b70f99a6baf4a1b72827f6d4922e4978 # use-mise-action-v2.0.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -616,19 +648,19 @@ jobs:
|
|||||||
working-directory: ./.github
|
working-directory: ./.github
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@01a4d354b70f99a6baf4a1b72827f6d4922e4978 # use-mise-action-v2.0.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -646,12 +678,12 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
@@ -667,30 +699,26 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@01a4d354b70f99a6baf4a1b72827f6d4922e4978 # use-mise-action-v2.0.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Install server dependencies
|
- name: Install server dependencies
|
||||||
run: SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter immich install --frozen-lockfile
|
run: SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter immich install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build the app
|
|
||||||
run: pnpm --filter immich build
|
|
||||||
|
|
||||||
- name: Run API generation
|
- name: Run API generation
|
||||||
run: ./bin/generate-open-api.sh
|
run: mise //:open-api
|
||||||
working-directory: open-api
|
working-directory: open-api
|
||||||
|
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
@@ -699,7 +727,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
mobile/openapi
|
mobile/openapi
|
||||||
open-api/typescript-sdk
|
packages/sdk
|
||||||
open-api/immich-openapi-specs.json
|
open-api/immich-openapi-specs.json
|
||||||
|
|
||||||
- name: Verify files have not changed
|
- name: Verify files have not changed
|
||||||
@@ -727,42 +755,42 @@ jobs:
|
|||||||
--health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
--health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./server
|
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@01a4d354b70f99a6baf4a1b72827f6d4922e4978 # use-mise-action-v2.0.0
|
uses: immich-app/devtools/actions/use-mise@3bca63ca3c15020293b36b51737a3ee2c773340b # use-mise-action-v3.1.0
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Install server dependencies
|
- name: Install server dependencies
|
||||||
run: SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm install --frozen-lockfile
|
run: SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Build plugins
|
||||||
|
run: mise //:plugins
|
||||||
|
|
||||||
- name: Build the app
|
- name: Build the app
|
||||||
run: pnpm build
|
run: mise //server:build
|
||||||
|
|
||||||
- name: Run existing migrations
|
- name: Run existing migrations
|
||||||
run: pnpm migrations:run
|
run: pnpm --filter immich migrations:run
|
||||||
|
|
||||||
- name: Test npm run schema:reset command works
|
- name: Test npm run schema:reset command works
|
||||||
run: pnpm schema:reset
|
run: pnpm --filter immich schema:reset
|
||||||
|
|
||||||
- name: Generate new migrations
|
- name: Generate new migrations
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: pnpm migrations:generate src/TestMigration
|
run: pnpm --filter migrations:generate src/TestMigration
|
||||||
|
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||||
@@ -778,11 +806,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "ERROR: Generated migration files not up to date!"
|
echo "ERROR: Generated migration files not up to date!"
|
||||||
echo "Changed files: ${CHANGED_FILES}"
|
echo "Changed files: ${CHANGED_FILES}"
|
||||||
cat ./src/*-TestMigration.ts
|
cat ./server/src/*-TestMigration.ts
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
- name: Run SQL generation
|
- name: Run SQL generation
|
||||||
run: pnpm sync:sql
|
run: mise //:sql
|
||||||
env:
|
env:
|
||||||
DB_URL: postgres://postgres:postgres@localhost:5432/immich
|
DB_URL: postgres://postgres:postgres@localhost:5432/immich
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
should_run: ${{ steps.check.outputs.should_run }}
|
should_run: ${{ steps.check.outputs.should_run }}
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
github-token: ${{ steps.token.outputs.token }}
|
github-token: ${{ steps.token.outputs.token }}
|
||||||
filters: |
|
filters: |
|
||||||
i18n:
|
i18n:
|
||||||
- modified: 'i18n/!(en|package)**\.json'
|
- modified: 'i18n/!(en)**\.json'
|
||||||
skip-force-logic: 'true'
|
skip-force-logic: 'true'
|
||||||
|
|
||||||
enforce-lock:
|
enforce-lock:
|
||||||
@@ -47,7 +47,7 @@ jobs:
|
|||||||
if: ${{ fromJSON(needs.pre-job.outputs.should_run).i18n == true }}
|
if: ${{ fromJSON(needs.pre-job.outputs.should_run).i18n == true }}
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ mobile/openapi/doc
|
|||||||
mobile/openapi/.openapi-generator/FILES
|
mobile/openapi/.openapi-generator/FILES
|
||||||
mobile/ios/build
|
mobile/ios/build
|
||||||
|
|
||||||
open-api/typescript-sdk/build
|
packages/**/build
|
||||||
mobile/android/fastlane/report.xml
|
mobile/android/fastlane/report.xml
|
||||||
mobile/ios/fastlane/report.xml
|
mobile/ios/fastlane/report.xml
|
||||||
|
|
||||||
|
|||||||
Vendored
+6
-4
@@ -23,15 +23,17 @@
|
|||||||
"type": "node",
|
"type": "node",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "Immich CLI",
|
"name": "Immich CLI",
|
||||||
"program": "${workspaceFolder}/cli/dist/index.js",
|
"program": "${workspaceFolder}/packages/cli/dist/index.js",
|
||||||
"args": ["upload", "--help"],
|
"args": ["upload", "--help"],
|
||||||
"runtimeArgs": ["--enable-source-maps"],
|
"runtimeArgs": ["--enable-source-maps"],
|
||||||
"console": "integratedTerminal",
|
"console": "integratedTerminal",
|
||||||
"resolveSourceMapLocations": ["${workspaceFolder}/cli/dist/**/*.js.map"],
|
"resolveSourceMapLocations": [
|
||||||
|
"${workspaceFolder}/packages/cli/dist/**/*.js.map"
|
||||||
|
],
|
||||||
"sourceMaps": true,
|
"sourceMaps": true,
|
||||||
"outFiles": ["${workspaceFolder}/cli/dist/**/*.js"],
|
"outFiles": ["${workspaceFolder}/packages/cli/dist/**/*.js"],
|
||||||
"skipFiles": ["<node_internals>/**"],
|
"skipFiles": ["<node_internals>/**"],
|
||||||
"preLaunchTask": "Build Immich CLI"
|
"preLaunchTask": "Build @immich/cli"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
@@ -60,6 +60,7 @@
|
|||||||
"explorer.fileNesting.patterns": {
|
"explorer.fileNesting.patterns": {
|
||||||
"*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart",
|
"*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart",
|
||||||
"*.ts": "${capture}.spec.ts,${capture}.mock.ts",
|
"*.ts": "${capture}.spec.ts,${capture}.mock.ts",
|
||||||
|
"*.js": "${capture}.spec.js,${capture}.mock.js",
|
||||||
"package.json": "package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb, bun.lock, pnpm-workspace.yaml, .pnpmfile.cjs"
|
"package.json": "package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb, bun.lock, pnpm-workspace.yaml, .pnpmfile.cjs"
|
||||||
},
|
},
|
||||||
"search.exclude": {
|
"search.exclude": {
|
||||||
|
|||||||
+1
-1
@@ -4,4 +4,4 @@
|
|||||||
/web/ @danieldietzler
|
/web/ @danieldietzler
|
||||||
/machine-learning/ @mertalev
|
/machine-learning/ @mertalev
|
||||||
/e2e/ @danieldietzler
|
/e2e/ @danieldietzler
|
||||||
/mobile/ @shenlong-tanwen
|
/mobile/ @shenlong-tanwen @santoshakil
|
||||||
|
|||||||
@@ -1,134 +0,0 @@
|
|||||||
# Contributor Covenant Code of Conduct
|
|
||||||
|
|
||||||
## Our Pledge
|
|
||||||
|
|
||||||
We as members, contributors, and leaders pledge to make participation
|
|
||||||
in our community a harassment-free experience for everyone, regardless
|
|
||||||
of age, body size, visible or invisible disability, ethnicity, sex
|
|
||||||
characteristics, gender identity and expression, level of experience,
|
|
||||||
education, socio-economic status, nationality, personal appearance,
|
|
||||||
race, religion, or sexual identity and orientation.
|
|
||||||
|
|
||||||
We pledge to act and interact in ways that contribute to an open,
|
|
||||||
welcoming, diverse, inclusive, and healthy community.
|
|
||||||
|
|
||||||
## Our Standards
|
|
||||||
|
|
||||||
Examples of behavior that contributes to a positive environment for
|
|
||||||
our community include:
|
|
||||||
|
|
||||||
- Demonstrating empathy and kindness toward other people
|
|
||||||
- Being respectful of differing opinions, viewpoints, and experiences
|
|
||||||
- Giving and gracefully accepting constructive feedback
|
|
||||||
- Accepting responsibility and apologizing to those affected by our
|
|
||||||
mistakes, and learning from the experience
|
|
||||||
- Focusing on what is best not just for us as individuals, but for the
|
|
||||||
overall community
|
|
||||||
|
|
||||||
Examples of unacceptable behavior include:
|
|
||||||
|
|
||||||
- The use of sexualized language or imagery, and sexual attention or
|
|
||||||
advances of any kind
|
|
||||||
- Trolling, insulting or derogatory comments, and personal or
|
|
||||||
political attacks
|
|
||||||
- Public or private harassment
|
|
||||||
- Publishing others' private information, such as a physical or email
|
|
||||||
address, without their explicit permission
|
|
||||||
- Other conduct which could reasonably be considered inappropriate in
|
|
||||||
a professional setting
|
|
||||||
|
|
||||||
## Enforcement Responsibilities
|
|
||||||
|
|
||||||
Community leaders are responsible for clarifying and enforcing our
|
|
||||||
standards of acceptable behavior and will take appropriate and fair
|
|
||||||
corrective action in response to any behavior that they deem
|
|
||||||
inappropriate, threatening, offensive, or harmful.
|
|
||||||
|
|
||||||
Community leaders have the right and responsibility to remove, edit,
|
|
||||||
or reject comments, commits, code, wiki edits, issues, and other
|
|
||||||
contributions that are not aligned to this Code of Conduct, and will
|
|
||||||
communicate reasons for moderation decisions when appropriate.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This Code of Conduct applies within all community spaces, and also
|
|
||||||
applies when an individual is officially representing the community in
|
|
||||||
public spaces. Examples of representing our community include using an
|
|
||||||
official e-mail address, posting via an official social media account,
|
|
||||||
or acting as an appointed representative at an online or offline
|
|
||||||
event.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior
|
|
||||||
may be reported to the community leaders responsible for enforcement
|
|
||||||
at our Discord channel. All complaints
|
|
||||||
will be reviewed and investigated promptly and fairly.
|
|
||||||
|
|
||||||
All community leaders are obligated to respect the privacy and
|
|
||||||
security of the reporter of any incident.
|
|
||||||
|
|
||||||
## Enforcement Guidelines
|
|
||||||
|
|
||||||
Community leaders will follow these Community Impact Guidelines in
|
|
||||||
determining the consequences for any action they deem in violation of
|
|
||||||
this Code of Conduct:
|
|
||||||
|
|
||||||
### 1. Correction
|
|
||||||
|
|
||||||
**Community Impact**: Use of inappropriate language or other behavior
|
|
||||||
deemed unprofessional or unwelcome in the community.
|
|
||||||
|
|
||||||
**Consequence**: A private, written warning from community leaders,
|
|
||||||
providing clarity around the nature of the violation and an
|
|
||||||
explanation of why the behavior was inappropriate. A public apology
|
|
||||||
may be requested.
|
|
||||||
|
|
||||||
### 2. Warning
|
|
||||||
|
|
||||||
**Community Impact**: A violation through a single incident or series
|
|
||||||
of actions.
|
|
||||||
|
|
||||||
**Consequence**: A warning with consequences for continued
|
|
||||||
behavior. No interaction with the people involved, including
|
|
||||||
unsolicited interaction with those enforcing the Code of Conduct, for
|
|
||||||
a specified period of time. This includes avoiding interactions in
|
|
||||||
community spaces as well as external channels like social
|
|
||||||
media. Violating these terms may lead to a temporary or permanent ban.
|
|
||||||
|
|
||||||
### 3. Temporary Ban
|
|
||||||
|
|
||||||
**Community Impact**: A serious violation of community standards,
|
|
||||||
including sustained inappropriate behavior.
|
|
||||||
|
|
||||||
**Consequence**: A temporary ban from any sort of interaction or
|
|
||||||
public communication with the community for a specified period of
|
|
||||||
time. No public or private interaction with the people involved,
|
|
||||||
including unsolicited interaction with those enforcing the Code of
|
|
||||||
Conduct, is allowed during this period. Violating these terms may lead
|
|
||||||
to a permanent ban.
|
|
||||||
|
|
||||||
### 4. Permanent Ban
|
|
||||||
|
|
||||||
**Community Impact**: Demonstrating a pattern of violation of
|
|
||||||
community standards, including sustained inappropriate behavior,
|
|
||||||
harassment of an individual, or aggression toward or disparagement of
|
|
||||||
classes of individuals.
|
|
||||||
|
|
||||||
**Consequence**: A permanent ban from any sort of public interaction
|
|
||||||
within the community.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor
|
|
||||||
Covenant][homepage], version 2.0, available at
|
|
||||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
|
||||||
|
|
||||||
Community Impact Guidelines were inspired by [Mozilla's code of
|
|
||||||
conduct enforcement ladder](https://github.com/mozilla/diversity).
|
|
||||||
|
|
||||||
[homepage]: https://www.contributor-covenant.org
|
|
||||||
|
|
||||||
For answers to common questions about this code of conduct, see the
|
|
||||||
FAQ at https://www.contributor-covenant.org/faq. Translations are
|
|
||||||
available at https://www.contributor-covenant.org/translations.
|
|
||||||
@@ -1,152 +1,58 @@
|
|||||||
dev:
|
dev:
|
||||||
@trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --remove-orphans
|
@printf "This command has been removed. Please use:\n\n mise dev # or mise //:dev from another directory\n\n" >&2 && exit 1
|
||||||
|
|
||||||
dev-down:
|
dev-down:
|
||||||
docker compose -f ./docker/docker-compose.dev.yml down --remove-orphans
|
@printf "This command has been removed. Please use:\n\n mise dev-down # or mise //:dev-down from another directory\n\n" >&2 && exit 1
|
||||||
|
|
||||||
dev-update:
|
dev-update:
|
||||||
@trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --build -V --remove-orphans
|
@printf "This command has been removed. Please use:\n\n mise dev-update # or mise //:dev-update from another directory\n\n" >&2 && exit 1
|
||||||
|
|
||||||
dev-scale:
|
dev-scale:
|
||||||
@trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans
|
@printf "This command has been removed. Please use:\n\n mise dev-scale # or mise //:dev-scale from another directory\n\n" >&2 && exit 1
|
||||||
|
|
||||||
dev-docs:
|
dev-docs:
|
||||||
npm --prefix docs run start
|
npm --prefix docs run start
|
||||||
|
|
||||||
.PHONY: e2e
|
.PHONY: e2e
|
||||||
e2e:
|
e2e:
|
||||||
@trap 'make e2e-down' EXIT; COMPOSE_BAKE=true docker compose -f ./e2e/docker-compose.yml up --remove-orphans
|
@printf "This command has been removed. Please use:\n\n mise e2e # or mise //:e2e from another directory\n\n" >&2 && exit 1
|
||||||
|
|
||||||
e2e-dev:
|
e2e-dev:
|
||||||
@trap 'make e2e-down' EXIT; COMPOSE_BAKE=true docker compose -f ./e2e/docker-compose.dev.yml up --remove-orphans
|
@printf "This command has been removed. Please use:\n\n mise e2e-dev # or mise //:e2e-dev from another directory\n\n" >&2 && exit 1
|
||||||
|
|
||||||
e2e-update:
|
e2e-update:
|
||||||
@trap 'make e2e-down' EXIT; COMPOSE_BAKE=true docker compose -f ./e2e/docker-compose.yml up --build -V --remove-orphans
|
@printf "This command has been removed. Please use:\n\n mise e2e-update # or mise //:e2e-update from another directory\n\n" >&2 && exit 1
|
||||||
|
|
||||||
e2e-down:
|
e2e-down:
|
||||||
docker compose -f ./e2e/docker-compose.yml down --remove-orphans
|
@printf "This command has been removed. Please use:\n\n mise e2e-down # or mise //:e2e-down from another directory\n\n" >&2 && exit 1
|
||||||
|
|
||||||
prod:
|
prod:
|
||||||
@trap 'make prod-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.prod.yml up --build -V --remove-orphans
|
@printf "This command has been removed. Please use:\n\n mise prod # or mise //:prod from another directory\n\n" >&2 && exit 1
|
||||||
|
|
||||||
prod-down:
|
prod-down:
|
||||||
docker compose -f ./docker/docker-compose.prod.yml down --remove-orphans
|
@printf "This command has been removed. Please use:\n\n mise prod-down # or mise //:prod-down from another directory\n\n" >&2 && exit 1
|
||||||
|
|
||||||
prod-scale:
|
prod-scale:
|
||||||
@trap 'make prod-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.prod.yml up --build -V --scale immich-server=3 --scale immich-microservices=3 --remove-orphans
|
@printf "This command has been removed. Please use:\n\n mise prod-scale # or mise //:prod-scale from another directory\n\n" >&2 && exit 1
|
||||||
|
|
||||||
.PHONY: open-api
|
.PHONY: open-api
|
||||||
open-api:
|
open-api:
|
||||||
cd ./open-api && bash ./bin/generate-open-api.sh
|
@printf "This command has been removed. Please use:\n\n mise open-api # or mise //:open-api from another directory\n\n" >&2 && exit 1
|
||||||
|
|
||||||
open-api-dart:
|
|
||||||
cd ./open-api && bash ./bin/generate-open-api.sh dart
|
|
||||||
|
|
||||||
open-api-typescript:
|
|
||||||
cd ./open-api && bash ./bin/generate-open-api.sh typescript
|
|
||||||
|
|
||||||
sql:
|
sql:
|
||||||
pnpm --filter immich run sync:sql
|
@printf "This command has been removed. Please use:\n\n mise sql # or mise //:sql from another directory\n\n" >&2 && exit 1
|
||||||
|
|
||||||
attach-server:
|
|
||||||
docker exec -it docker_immich-server_1 sh
|
|
||||||
|
|
||||||
renovate:
|
renovate:
|
||||||
LOG_LEVEL=debug pnpm exec renovate --platform=local --repository-cache=reset
|
LOG_LEVEL=debug pnpm exec renovate --platform=local --repository-cache=reset
|
||||||
|
|
||||||
# Directories that need to be created for volumes or build output
|
|
||||||
VOLUME_DIRS = \
|
|
||||||
./.pnpm-store \
|
|
||||||
./web/.svelte-kit \
|
|
||||||
./web/node_modules \
|
|
||||||
./web/coverage \
|
|
||||||
./e2e/node_modules \
|
|
||||||
./docs/node_modules \
|
|
||||||
./server/node_modules \
|
|
||||||
./open-api/typescript-sdk/node_modules \
|
|
||||||
./.github/node_modules \
|
|
||||||
./node_modules \
|
|
||||||
./cli/node_modules
|
|
||||||
|
|
||||||
# Include .env file if it exists
|
# Include .env file if it exists
|
||||||
-include docker/.env
|
-include docker/.env
|
||||||
|
|
||||||
MODULES = e2e server web cli sdk docs .github
|
MODULES = e2e server web cli sdk docs .github
|
||||||
|
|
||||||
# directory to package name mapping function
|
|
||||||
# cli = @immich/cli
|
|
||||||
# docs = documentation
|
|
||||||
# e2e = immich-e2e
|
|
||||||
# open-api/typescript-sdk = @immich/sdk
|
|
||||||
# server = immich
|
|
||||||
# web = immich-web
|
|
||||||
map-package = $(subst sdk,@immich/sdk,$(subst cli,@immich/cli,$(subst docs,documentation,$(subst e2e,immich-e2e,$(subst server,immich,$(subst web,immich-web,$1))))))
|
|
||||||
|
|
||||||
audit-%:
|
|
||||||
pnpm --filter $(call map-package,$*) audit fix
|
|
||||||
install-%:
|
|
||||||
pnpm --filter $(call map-package,$*) install $(if $(FROZEN),--frozen-lockfile) $(if $(OFFLINE),--offline)
|
|
||||||
build-cli: build-sdk
|
|
||||||
build-web: build-sdk
|
|
||||||
build-%: install-%
|
|
||||||
pnpm --filter $(call map-package,$*) run build
|
|
||||||
format-%:
|
|
||||||
pnpm --filter $(call map-package,$*) run format:fix
|
|
||||||
lint-%:
|
|
||||||
pnpm --filter $(call map-package,$*) run lint:fix
|
|
||||||
check-%:
|
|
||||||
pnpm --filter $(call map-package,$*) run check
|
|
||||||
check-web:
|
|
||||||
pnpm --filter immich-web run check:typescript
|
|
||||||
pnpm --filter immich-web run check:svelte
|
|
||||||
test-%:
|
|
||||||
pnpm --filter $(call map-package,$*) run test
|
|
||||||
test-e2e:
|
test-e2e:
|
||||||
docker compose -f ./e2e/docker-compose.yml build
|
@printf "This command has been removed. Please use:\n\n mise //e2e:test # or mise //e2e:test-web for web tests, respectively\n\n" >&2 && exit 1
|
||||||
pnpm --filter immich-e2e run test
|
|
||||||
pnpm --filter immich-e2e run test:web
|
|
||||||
test-medium:
|
|
||||||
docker run \
|
|
||||||
--rm \
|
|
||||||
-v ./server/src:/usr/src/app/src \
|
|
||||||
-v ./server/test:/usr/src/app/test \
|
|
||||||
-v ./server/vitest.config.medium.mjs:/usr/src/app/vitest.config.medium.mjs \
|
|
||||||
-v ./server/tsconfig.json:/usr/src/app/tsconfig.json \
|
|
||||||
-e NODE_ENV=development \
|
|
||||||
immich-server:latest \
|
|
||||||
-c "pnpm test:medium -- --run"
|
|
||||||
test-medium-dev:
|
|
||||||
docker exec -it immich_server /bin/sh -c "pnpm run test:medium"
|
|
||||||
|
|
||||||
install-all:
|
|
||||||
pnpm -r --filter '!documentation' install
|
|
||||||
|
|
||||||
build-all: $(foreach M,$(filter-out e2e docs .github,$(MODULES)),build-$M) ;
|
|
||||||
|
|
||||||
check-all:
|
|
||||||
pnpm -r --filter '!documentation' run "/^(check|check\:svelte|check\:typescript)$/"
|
|
||||||
lint-all:
|
|
||||||
pnpm -r --filter '!documentation' run lint:fix
|
|
||||||
format-all:
|
|
||||||
pnpm -r --filter '!documentation' run format:fix
|
|
||||||
audit-all:
|
|
||||||
pnpm -r --filter '!documentation' audit fix
|
|
||||||
hygiene-all: audit-all
|
|
||||||
pnpm -r --filter '!documentation' run "/(format:fix|check|check:svelte|check:typescript|sql)/"
|
|
||||||
|
|
||||||
test-all:
|
|
||||||
pnpm -r --filter '!documentation' run "/^test/"
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
find . -name "node_modules" -type d -prune -exec rm -rf {} +
|
@printf "This command has been removed. Please use:\n\n mise clean # or mise //:clean from another directory\n\n" >&2 && exit 1
|
||||||
find . -name "dist" -type d -prune -exec rm -rf '{}' +
|
|
||||||
find . -name "build" -type d -prune -exec rm -rf '{}' +
|
|
||||||
find . -name ".svelte-kit" -type d -prune -exec rm -rf '{}' +
|
|
||||||
find . -name "coverage" -type d -prune -exec rm -rf '{}' +
|
|
||||||
find . -name ".pnpm-store" -type d -prune -exec rm -rf '{}' +
|
|
||||||
command -v docker >/dev/null 2>&1 && docker compose -f ./docker/docker-compose.dev.yml down -v --remove-orphans || true
|
|
||||||
command -v docker >/dev/null 2>&1 && docker compose -f ./e2e/docker-compose.yml down -v --remove-orphans || true
|
|
||||||
|
|
||||||
|
|
||||||
setup-server-dev: install-server
|
|
||||||
setup-web-dev: install-sdk build-sdk install-web
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
# Security Policy
|
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
|
||||||
|
|
||||||
Please report security issues to `security@immich.app`
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
FROM node:24.1.0-alpine3.20@sha256:8fe019e0d57dbdce5f5c27c0b63d2775cf34b00e3755a7dea969802d7e0c2b25 AS core
|
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
|
||||||
COPY package* pnpm* .pnpmfile.cjs ./
|
|
||||||
COPY ./cli ./cli/
|
|
||||||
COPY ./open-api/typescript-sdk ./open-api/typescript-sdk/
|
|
||||||
RUN corepack enable pnpm && \
|
|
||||||
pnpm install --filter @immich/sdk --filter @immich/cli --frozen-lockfile && \
|
|
||||||
pnpm --filter @immich/sdk build && \
|
|
||||||
pnpm --filter @immich/cli build
|
|
||||||
|
|
||||||
WORKDIR /import
|
|
||||||
|
|
||||||
ENTRYPOINT ["node", "/usr/src/app/cli/dist"]
|
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# @generated - this file is auto-generated by `mise lock` https://mise.en.dev/dev-tools/mise-lock.html
|
||||||
|
|
||||||
|
[[tools.opentofu]]
|
||||||
|
version = "1.11.6"
|
||||||
|
backend = "aqua:opentofu/opentofu"
|
||||||
|
|
||||||
|
[tools.opentofu."platforms.linux-arm64"]
|
||||||
|
checksum = "sha256:d4f2ab15776925864b049bb329d69682851de6f5204f256e9fa86d07a0308850"
|
||||||
|
url = "https://github.com/opentofu/opentofu/releases/download/v1.11.6/tofu_1.11.6_linux_arm64.tar.gz"
|
||||||
|
|
||||||
|
[tools.opentofu."platforms.linux-arm64-musl"]
|
||||||
|
checksum = "sha256:d4f2ab15776925864b049bb329d69682851de6f5204f256e9fa86d07a0308850"
|
||||||
|
url = "https://github.com/opentofu/opentofu/releases/download/v1.11.6/tofu_1.11.6_linux_arm64.tar.gz"
|
||||||
|
|
||||||
|
[tools.opentofu."platforms.linux-x64"]
|
||||||
|
checksum = "sha256:02800fafa2753a9f50c38483e2fdf5bc353fd62895eb9e25eec9a5145df3a69e"
|
||||||
|
url = "https://github.com/opentofu/opentofu/releases/download/v1.11.6/tofu_1.11.6_linux_amd64.tar.gz"
|
||||||
|
|
||||||
|
[tools.opentofu."platforms.linux-x64-musl"]
|
||||||
|
checksum = "sha256:02800fafa2753a9f50c38483e2fdf5bc353fd62895eb9e25eec9a5145df3a69e"
|
||||||
|
url = "https://github.com/opentofu/opentofu/releases/download/v1.11.6/tofu_1.11.6_linux_amd64.tar.gz"
|
||||||
|
|
||||||
|
[tools.opentofu."platforms.macos-arm64"]
|
||||||
|
checksum = "sha256:62d7fa8539e13b444827aa0a3b90c5972da5c47e8f8882d9dcf2e430e78840c1"
|
||||||
|
url = "https://github.com/opentofu/opentofu/releases/download/v1.11.6/tofu_1.11.6_darwin_arm64.tar.gz"
|
||||||
|
|
||||||
|
[tools.opentofu."platforms.macos-x64"]
|
||||||
|
checksum = "sha256:1408cdef1c380f914565e6b4bb70794c6b163f195fcb233357f3d6c5745906b6"
|
||||||
|
url = "https://github.com/opentofu/opentofu/releases/download/v1.11.6/tofu_1.11.6_darwin_amd64.tar.gz"
|
||||||
|
|
||||||
|
[tools.opentofu."platforms.windows-x64"]
|
||||||
|
checksum = "sha256:27323f70c875b8251bfd7e61a4cffc3ebff4e56ed1e611b955016f0c7077367e"
|
||||||
|
url = "https://github.com/opentofu/opentofu/releases/download/v1.11.6/tofu_1.11.6_windows_amd64.tar.gz"
|
||||||
|
|
||||||
|
[[tools.terragrunt]]
|
||||||
|
version = "1.0.3"
|
||||||
|
backend = "aqua:gruntwork-io/terragrunt"
|
||||||
|
|
||||||
|
[tools.terragrunt."platforms.linux-arm64"]
|
||||||
|
checksum = "sha256:e5b60ab05b5214db694e6bc215d8124fb626e277cdb56b86f6147ae110d510fe"
|
||||||
|
url = "https://github.com/gruntwork-io/terragrunt/releases/download/v1.0.3/terragrunt_linux_arm64.tar.gz"
|
||||||
|
|
||||||
|
[tools.terragrunt."platforms.linux-arm64-musl"]
|
||||||
|
checksum = "sha256:e5b60ab05b5214db694e6bc215d8124fb626e277cdb56b86f6147ae110d510fe"
|
||||||
|
url = "https://github.com/gruntwork-io/terragrunt/releases/download/v1.0.3/terragrunt_linux_arm64.tar.gz"
|
||||||
|
|
||||||
|
[tools.terragrunt."platforms.linux-x64"]
|
||||||
|
checksum = "sha256:6d48049baf82e0bf9c804368dc85cbfeadc10955e33777e9e8de3e020b94b073"
|
||||||
|
url = "https://github.com/gruntwork-io/terragrunt/releases/download/v1.0.3/terragrunt_linux_amd64.tar.gz"
|
||||||
|
|
||||||
|
[tools.terragrunt."platforms.linux-x64-musl"]
|
||||||
|
checksum = "sha256:6d48049baf82e0bf9c804368dc85cbfeadc10955e33777e9e8de3e020b94b073"
|
||||||
|
url = "https://github.com/gruntwork-io/terragrunt/releases/download/v1.0.3/terragrunt_linux_amd64.tar.gz"
|
||||||
|
|
||||||
|
[tools.terragrunt."platforms.macos-arm64"]
|
||||||
|
checksum = "sha256:aacb5be2ca5475300cbce246dfbd8a45eb47510fbaa70fab8561c49ef5db03aa"
|
||||||
|
url = "https://github.com/gruntwork-io/terragrunt/releases/download/v1.0.3/terragrunt_darwin_arm64.tar.gz"
|
||||||
|
|
||||||
|
[tools.terragrunt."platforms.macos-x64"]
|
||||||
|
checksum = "sha256:3133c2251e191aede8e3dd2a5b3aee2e91c5f08f88f117aee40eed9a24c8ef6b"
|
||||||
|
url = "https://github.com/gruntwork-io/terragrunt/releases/download/v1.0.3/terragrunt_darwin_amd64.tar.gz"
|
||||||
|
|
||||||
|
[tools.terragrunt."platforms.windows-x64"]
|
||||||
|
checksum = "sha256:183b2745b4e04980a6bfa4450ff81956a12596ca22d70f7aaa793980f5b036db"
|
||||||
|
url = "https://github.com/gruntwork-io/terragrunt/releases/download/v1.0.3/terragrunt_windows_amd64.exe.tar.gz"
|
||||||
+30
-30
@@ -2,37 +2,37 @@
|
|||||||
# Manual edits may be lost in future updates.
|
# Manual edits may be lost in future updates.
|
||||||
|
|
||||||
provider "registry.opentofu.org/cloudflare/cloudflare" {
|
provider "registry.opentofu.org/cloudflare/cloudflare" {
|
||||||
version = "4.52.5"
|
version = "4.52.7"
|
||||||
constraints = "4.52.5"
|
constraints = "4.52.7"
|
||||||
hashes = [
|
hashes = [
|
||||||
"h1:+rfzF+16ZcWZWnTyW/p1HHTzYbPKX8Zt2nIFtR/+f+E=",
|
"h1:+O72J3QYiZtYmYYZM/Eh0f4NNfl1BvjX1eju43qTQsQ=",
|
||||||
"h1:18bXaaOSq8MWKuMxo/4y7EB7/i7G90y5QsKHZRmkoDo=",
|
"h1:0oqjYIPXcXh7XiDiKI085cHDYQQ5mh8kDl9dmBtvtog=",
|
||||||
"h1:4vZVOpKeEQZsF2VrARRZFeL37Ed/gD4rRMtfnvWQres=",
|
"h1:4b4ESb87MGv5bnadgYe7sK5rEkKMZhbkQcwPubQTsR4=",
|
||||||
"h1:BZOsTF83QPKXTAaYqxPKzdl1KRjk/L2qbPpFjM0w28A=",
|
"h1:6mTr3eA1Ddb348lLmJuyvn98z4KF+ejqaUEJ76D1rzQ=",
|
||||||
"h1:CDuC+HXLvc1z6wkCRsSDcc/+QENIHEtssYshiWg3opA=",
|
"h1:9/3YH+9k9HqsvFtbmBf7SO2+xqZeZrXNKzLkjNuhUEA=",
|
||||||
"h1:DE+YFzLnqSe79pI2R4idRGx5QzLdrA7RXvngTkGfZ30=",
|
"h1:Jcq4tBWgyH4/2JsojNBSRaN0mcItVMchO+lynonrlqc=",
|
||||||
"h1:DfaJwH3Ml4yrRbdAY4AcDVy0QTQk5T3A622TXzS/u2E=",
|
"h1:Y4Vv/2RdP0Q+uxqhOxzOdKxuuEMjXPDcU0vPc5bCQzI=",
|
||||||
"h1:EIDXP0W3kgIv2pecrFmqtK/DnlqkyckzBzhxKaXU+4A=",
|
"h1:a0gW8FBKsbP9Fi0HEDoy49WIbEWVHk9+BR4/iwuBdDQ=",
|
||||||
"h1:EV4kYyaOnwGA0bh/3hU6Ezqnt1PFDxopH7i85e48IzY=",
|
"h1:gElv6iqJtg8OKN77gbw+MjrkrQmJHPkkMEi1J+0xkpU=",
|
||||||
"h1:M0iXabfzamU+MPDi0G9XACpbacFKMakmM+Z9HZ8HrsM=",
|
"h1:oslXUugD/NQ+duJgT4BhKQyfGbuFOANknMvR73fiOeM=",
|
||||||
"h1:YWmCbGF/KbsrUzcYVBLscwLizidbp95TDQa0N2qpmVo=",
|
"h1:pPItIWii5oymR+geZB219ROSPuSODPLTlM4S/u8xLvM=",
|
||||||
"h1:cxPcCB5gbrpUO1+IXkQYs1YTY50/0IlApCzGea0cwuQ=",
|
"h1:u67GWw8GwD9NDlDzp9Y5VRnSQGcCrE8rSpkGPaBpDl0=",
|
||||||
"h1:g6DldikTV2HXUu9uoeNY5FuLufgaYWF4ufgZg7wq62s=",
|
"h1:uUUa9dY0XQOycI8pxg16PFFtL0WCTi9uEJz8trTQ7pU=",
|
||||||
"h1:oi/Hrx9pwoQ+Z52CBC+rrowVH387EIj0qvnxQgDeI+0=",
|
"h1:y3rV8KF2q6GEMANNlf5EkKJurlfbKlIKpjGcdxoy7pQ=",
|
||||||
"zh:1a3400cb38863b2585968d1876706bcfc67a148e1318a1d325c6c7704adc999b",
|
"zh:0c904ce31a4c6c4a5b3bf7ff1560e77c0cc7e2450c8553ded8e8c90398e1418b",
|
||||||
"zh:4c5062cb9e9da1676f06ae92b8370186d98976cc4c7030d3cd76df12af54282a",
|
"zh:36183d310c36373fe4cb936b83c595c6fd3b0a94bc7827f28e5789ccbf59752e",
|
||||||
"zh:52110f493b5f0587ef77a1cfd1a67001fd4c617b14c6502d732ab47352bdc2f7",
|
"zh:556a568a6f0235e8f41647de9e4d3a1e7b1d6502df8b19b54ec441f1c653ea10",
|
||||||
"zh:5aa536f9eaeb43823aaf2aa80e7d39b25ef2b383405ed034aa16a28b446a9238",
|
"zh:633ebbd5b0245e75e500ef9be4d9e62288f97e8da3baaa51323892a786d90285",
|
||||||
"zh:5cc39459a1c6be8a918f17054e4fbba573825ed5597dcada588fe99614d98a5b",
|
"zh:6acfe60cf52a65ba8f044f748548d2119e7f4fd7f8ebcb14698960d87c68f529",
|
||||||
"zh:629ae6a7ba298815131da826474d199312d21cec53a4d5ded4fa56a692e6f072",
|
|
||||||
"zh:719cc7c75dc1d3eb30c22ff5102a017996d9788b948078c7e1c5b3446aeca661",
|
|
||||||
"zh:8698635a3ca04383c1e93b21d6963346bdae54d27177a48e4b1435b7f731731c",
|
|
||||||
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
|
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
|
||||||
"zh:8a9993f1dcadf1dd6ca43b23348abe374605d29945a2fafc07fb3457644e6a54",
|
"zh:904acc31ebb9d6ef68c792074b30532ee61bf515f19e0a3c75b46f126cca1f13",
|
||||||
"zh:b1b9a1e6bcc24d5863a664a411d2dc906373ae7a2399d2d65548ce7377057852",
|
"zh:a1d0a81246afc8750286d3f6fe7a8fbe6460dd2662407b28dbfbabb612e5fa9d",
|
||||||
"zh:b270184cdeec277218e84b94cb136fead753da717f9b9dc378e51907f3f00bb0",
|
"zh:a41a36fe253fc365fe2b7ffc749624688b2693b4634862fda161179ab100029f",
|
||||||
"zh:dff2bc10071210181726ce270f954995fe42c696e61e2e8f874021fed02521e5",
|
"zh:a7ef269e77ffa8715c8945a2c14322c7ff159ea44c15f62505f3cbb2cae3b32d",
|
||||||
"zh:e8e87b40b6a87dc097b0fdc20d3f725cec0d82abc9cc3755c1f89f8f6e8b0036",
|
"zh:b01aa3bed30610633b762df64332b26f8844a68c3960cebcb30f04918efc67fe",
|
||||||
"zh:ee964a6573d399a5dd22ce328fb38ca1207797a02248f14b2e4913ee390e7803",
|
"zh:b069cc2cd18cae10757df3ae030508eac8d55de7e49eda7a5e3e11f2f7fe6455",
|
||||||
|
"zh:b2d2c6313729ebb7465dceece374049e2d08bda34473901be9ff46a8836d42b2",
|
||||||
|
"zh:db0e114edaf4bc2f3d4769958807c83022bfbc619a00bdf4c4bd17faa4ab2d8b",
|
||||||
|
"zh:ecc0aa8b9044f664fd2aaf8fa992d976578f78478980555b4b8f6148e8d1a5fe",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ terraform {
|
|||||||
required_providers {
|
required_providers {
|
||||||
cloudflare = {
|
cloudflare = {
|
||||||
source = "cloudflare/cloudflare"
|
source = "cloudflare/cloudflare"
|
||||||
version = "4.52.5"
|
version = "4.52.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-30
@@ -2,37 +2,37 @@
|
|||||||
# Manual edits may be lost in future updates.
|
# Manual edits may be lost in future updates.
|
||||||
|
|
||||||
provider "registry.opentofu.org/cloudflare/cloudflare" {
|
provider "registry.opentofu.org/cloudflare/cloudflare" {
|
||||||
version = "4.52.5"
|
version = "4.52.7"
|
||||||
constraints = "4.52.5"
|
constraints = "4.52.7"
|
||||||
hashes = [
|
hashes = [
|
||||||
"h1:+rfzF+16ZcWZWnTyW/p1HHTzYbPKX8Zt2nIFtR/+f+E=",
|
"h1:+O72J3QYiZtYmYYZM/Eh0f4NNfl1BvjX1eju43qTQsQ=",
|
||||||
"h1:18bXaaOSq8MWKuMxo/4y7EB7/i7G90y5QsKHZRmkoDo=",
|
"h1:0oqjYIPXcXh7XiDiKI085cHDYQQ5mh8kDl9dmBtvtog=",
|
||||||
"h1:4vZVOpKeEQZsF2VrARRZFeL37Ed/gD4rRMtfnvWQres=",
|
"h1:4b4ESb87MGv5bnadgYe7sK5rEkKMZhbkQcwPubQTsR4=",
|
||||||
"h1:BZOsTF83QPKXTAaYqxPKzdl1KRjk/L2qbPpFjM0w28A=",
|
"h1:6mTr3eA1Ddb348lLmJuyvn98z4KF+ejqaUEJ76D1rzQ=",
|
||||||
"h1:CDuC+HXLvc1z6wkCRsSDcc/+QENIHEtssYshiWg3opA=",
|
"h1:9/3YH+9k9HqsvFtbmBf7SO2+xqZeZrXNKzLkjNuhUEA=",
|
||||||
"h1:DE+YFzLnqSe79pI2R4idRGx5QzLdrA7RXvngTkGfZ30=",
|
"h1:Jcq4tBWgyH4/2JsojNBSRaN0mcItVMchO+lynonrlqc=",
|
||||||
"h1:DfaJwH3Ml4yrRbdAY4AcDVy0QTQk5T3A622TXzS/u2E=",
|
"h1:Y4Vv/2RdP0Q+uxqhOxzOdKxuuEMjXPDcU0vPc5bCQzI=",
|
||||||
"h1:EIDXP0W3kgIv2pecrFmqtK/DnlqkyckzBzhxKaXU+4A=",
|
"h1:a0gW8FBKsbP9Fi0HEDoy49WIbEWVHk9+BR4/iwuBdDQ=",
|
||||||
"h1:EV4kYyaOnwGA0bh/3hU6Ezqnt1PFDxopH7i85e48IzY=",
|
"h1:gElv6iqJtg8OKN77gbw+MjrkrQmJHPkkMEi1J+0xkpU=",
|
||||||
"h1:M0iXabfzamU+MPDi0G9XACpbacFKMakmM+Z9HZ8HrsM=",
|
"h1:oslXUugD/NQ+duJgT4BhKQyfGbuFOANknMvR73fiOeM=",
|
||||||
"h1:YWmCbGF/KbsrUzcYVBLscwLizidbp95TDQa0N2qpmVo=",
|
"h1:pPItIWii5oymR+geZB219ROSPuSODPLTlM4S/u8xLvM=",
|
||||||
"h1:cxPcCB5gbrpUO1+IXkQYs1YTY50/0IlApCzGea0cwuQ=",
|
"h1:u67GWw8GwD9NDlDzp9Y5VRnSQGcCrE8rSpkGPaBpDl0=",
|
||||||
"h1:g6DldikTV2HXUu9uoeNY5FuLufgaYWF4ufgZg7wq62s=",
|
"h1:uUUa9dY0XQOycI8pxg16PFFtL0WCTi9uEJz8trTQ7pU=",
|
||||||
"h1:oi/Hrx9pwoQ+Z52CBC+rrowVH387EIj0qvnxQgDeI+0=",
|
"h1:y3rV8KF2q6GEMANNlf5EkKJurlfbKlIKpjGcdxoy7pQ=",
|
||||||
"zh:1a3400cb38863b2585968d1876706bcfc67a148e1318a1d325c6c7704adc999b",
|
"zh:0c904ce31a4c6c4a5b3bf7ff1560e77c0cc7e2450c8553ded8e8c90398e1418b",
|
||||||
"zh:4c5062cb9e9da1676f06ae92b8370186d98976cc4c7030d3cd76df12af54282a",
|
"zh:36183d310c36373fe4cb936b83c595c6fd3b0a94bc7827f28e5789ccbf59752e",
|
||||||
"zh:52110f493b5f0587ef77a1cfd1a67001fd4c617b14c6502d732ab47352bdc2f7",
|
"zh:556a568a6f0235e8f41647de9e4d3a1e7b1d6502df8b19b54ec441f1c653ea10",
|
||||||
"zh:5aa536f9eaeb43823aaf2aa80e7d39b25ef2b383405ed034aa16a28b446a9238",
|
"zh:633ebbd5b0245e75e500ef9be4d9e62288f97e8da3baaa51323892a786d90285",
|
||||||
"zh:5cc39459a1c6be8a918f17054e4fbba573825ed5597dcada588fe99614d98a5b",
|
"zh:6acfe60cf52a65ba8f044f748548d2119e7f4fd7f8ebcb14698960d87c68f529",
|
||||||
"zh:629ae6a7ba298815131da826474d199312d21cec53a4d5ded4fa56a692e6f072",
|
|
||||||
"zh:719cc7c75dc1d3eb30c22ff5102a017996d9788b948078c7e1c5b3446aeca661",
|
|
||||||
"zh:8698635a3ca04383c1e93b21d6963346bdae54d27177a48e4b1435b7f731731c",
|
|
||||||
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
|
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
|
||||||
"zh:8a9993f1dcadf1dd6ca43b23348abe374605d29945a2fafc07fb3457644e6a54",
|
"zh:904acc31ebb9d6ef68c792074b30532ee61bf515f19e0a3c75b46f126cca1f13",
|
||||||
"zh:b1b9a1e6bcc24d5863a664a411d2dc906373ae7a2399d2d65548ce7377057852",
|
"zh:a1d0a81246afc8750286d3f6fe7a8fbe6460dd2662407b28dbfbabb612e5fa9d",
|
||||||
"zh:b270184cdeec277218e84b94cb136fead753da717f9b9dc378e51907f3f00bb0",
|
"zh:a41a36fe253fc365fe2b7ffc749624688b2693b4634862fda161179ab100029f",
|
||||||
"zh:dff2bc10071210181726ce270f954995fe42c696e61e2e8f874021fed02521e5",
|
"zh:a7ef269e77ffa8715c8945a2c14322c7ff159ea44c15f62505f3cbb2cae3b32d",
|
||||||
"zh:e8e87b40b6a87dc097b0fdc20d3f725cec0d82abc9cc3755c1f89f8f6e8b0036",
|
"zh:b01aa3bed30610633b762df64332b26f8844a68c3960cebcb30f04918efc67fe",
|
||||||
"zh:ee964a6573d399a5dd22ce328fb38ca1207797a02248f14b2e4913ee390e7803",
|
"zh:b069cc2cd18cae10757df3ae030508eac8d55de7e49eda7a5e3e11f2f7fe6455",
|
||||||
|
"zh:b2d2c6313729ebb7465dceece374049e2d08bda34473901be9ff46a8836d42b2",
|
||||||
|
"zh:db0e114edaf4bc2f3d4769958807c83022bfbc619a00bdf4c4bd17faa4ab2d8b",
|
||||||
|
"zh:ecc0aa8b9044f664fd2aaf8fa992d976578f78478980555b4b8f6148e8d1a5fe",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ terraform {
|
|||||||
required_providers {
|
required_providers {
|
||||||
cloudflare = {
|
cloudflare = {
|
||||||
source = "cloudflare/cloudflare"
|
source = "cloudflare/cloudflare"
|
||||||
version = "4.52.5"
|
version = "4.52.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ..:/usr/src/app
|
- ..:/usr/src/app
|
||||||
# - ../../ui:/usr/src/ui
|
# - ../../ui:/usr/src/ui
|
||||||
- pnpm_cache:/buildcache/pnpm_cache
|
- build_cache:/buildcache
|
||||||
- server_node_modules:/usr/src/app/server/node_modules
|
- server_node_modules:/usr/src/app/server/node_modules
|
||||||
- web_node_modules:/usr/src/app/web/node_modules
|
- web_node_modules:/usr/src/app/web/node_modules
|
||||||
- github_node_modules:/usr/src/app/.github/node_modules
|
- github_node_modules:/usr/src/app/.github/node_modules
|
||||||
- cli_node_modules:/usr/src/app/cli/node_modules
|
- cli_node_modules:/usr/src/app/packages/cli/node_modules
|
||||||
- docs_node_modules:/usr/src/app/docs/node_modules
|
- docs_node_modules:/usr/src/app/docs/node_modules
|
||||||
- e2e_node_modules:/usr/src/app/e2e/node_modules
|
- e2e_node_modules:/usr/src/app/e2e/node_modules
|
||||||
- sdk_node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
|
- sdk_node_modules:/usr/src/app/packages/sdk/node_modules
|
||||||
- app_node_modules:/usr/src/app/node_modules
|
- app_node_modules:/usr/src/app/node_modules
|
||||||
- sveltekit:/usr/src/app/web/.svelte-kit
|
- sveltekit:/usr/src/app/web/.svelte-kit
|
||||||
- coverage:/usr/src/app/web/coverage
|
- coverage:/usr/src/app/web/coverage
|
||||||
@@ -45,11 +45,11 @@ services:
|
|||||||
target: dev
|
target: dev
|
||||||
command:
|
command:
|
||||||
- |
|
- |
|
||||||
pnpm install
|
mise install
|
||||||
touch /tmp/init-complete
|
touch /tmp/init-complete
|
||||||
exec tail -f /dev/null
|
exec tail -f /dev/null
|
||||||
volumes:
|
volumes:
|
||||||
- pnpm_store_server:/buildcache/pnpm-store
|
- build_cache:/buildcache
|
||||||
restart: 'no'
|
restart: 'no'
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ['CMD', 'test', '-f', '/tmp/init-complete']
|
test: ['CMD', 'test', '-f', '/tmp/init-complete']
|
||||||
@@ -73,8 +73,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ${UPLOAD_LOCATION}/photos:/data
|
- ${UPLOAD_LOCATION}/photos:/data
|
||||||
- /etc/localtime:/etc/localtime:ro
|
- /etc/localtime:/etc/localtime:ro
|
||||||
- pnpm_store_server:/buildcache/pnpm-store
|
- ../packages/plugin-core:/build/plugins/immich-plugin-core
|
||||||
- ../plugins:/build/corePlugin
|
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
environment:
|
environment:
|
||||||
@@ -122,8 +121,6 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
- 24678:24678
|
- 24678:24678
|
||||||
volumes:
|
|
||||||
- pnpm_store_web:/buildcache/pnpm-store
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
immich-init:
|
immich-init:
|
||||||
@@ -157,7 +154,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:9@sha256:3b55fbaa0cd93cf0d9d961f405e4dfcc70efe325e2d84da207a0a8e6d8fde4f9
|
image: docker.io/valkey/valkey:9@sha256:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
|
|
||||||
@@ -203,9 +200,7 @@ volumes:
|
|||||||
model_cache:
|
model_cache:
|
||||||
prometheus_data:
|
prometheus_data:
|
||||||
grafana_data:
|
grafana_data:
|
||||||
pnpm_cache:
|
build_cache:
|
||||||
pnpm_store_server:
|
|
||||||
pnpm_store_web:
|
|
||||||
server_node_modules:
|
server_node_modules:
|
||||||
web_node_modules:
|
web_node_modules:
|
||||||
github_node_modules:
|
github_node_modules:
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:9@sha256:3b55fbaa0cd93cf0d9d961f405e4dfcc70efe325e2d84da207a0a8e6d8fde4f9
|
image: docker.io/valkey/valkey:9@sha256:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
@@ -85,7 +85,7 @@ services:
|
|||||||
container_name: immich_prometheus
|
container_name: immich_prometheus
|
||||||
ports:
|
ports:
|
||||||
- 9090:9090
|
- 9090:9090
|
||||||
image: prom/prometheus@sha256:e4254400b85610324913f0dc4acf92603d9984e7519414c5a12811aa6146acc3
|
image: prom/prometheus@sha256:a75c5a35bc21d7afe69551eefa3cb1e1fb1775fe759408007a66b54ec3de1f29
|
||||||
volumes:
|
volumes:
|
||||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||||
- prometheus-data:/prometheus
|
- prometheus-data:/prometheus
|
||||||
@@ -97,7 +97,7 @@ services:
|
|||||||
command: ['./run.sh', '-disable-reporting']
|
command: ['./run.sh', '-disable-reporting']
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
image: grafana/grafana:12.4.3-ubuntu@sha256:ca3f764fdc48cebdf22dd206f33ecb0795a9a7210eacd1b5c02204aebd78b223
|
image: grafana/grafana:12.4.4-ubuntu@sha256:df2e7ef5f32f771794cf76bad5f2bceac227036460a2cc269a9045e5662abc58
|
||||||
volumes:
|
volumes:
|
||||||
- grafana-data:/var/lib/grafana
|
- grafana-data:/var/lib/grafana
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:9@sha256:3b55fbaa0cd93cf0d9d961f405e4dfcc70efe325e2d84da207a0a8e6d8fde4f9
|
image: docker.io/valkey/valkey:9@sha256:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9
|
||||||
user: '1000:1000'
|
user: '1000:1000'
|
||||||
security_opt:
|
security_opt:
|
||||||
- no-new-privileges:true
|
- no-new-privileges:true
|
||||||
@@ -95,6 +95,3 @@ services:
|
|||||||
restart: always
|
restart: always
|
||||||
healthcheck:
|
healthcheck:
|
||||||
disable: false
|
disable: false
|
||||||
|
|
||||||
volumes:
|
|
||||||
model-cache:
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:9@sha256:3b55fbaa0cd93cf0d9d961f405e4dfcc70efe325e2d84da207a0a8e6d8fde4f9
|
image: docker.io/valkey/valkey:9@sha256:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ For organizations seeking to resell Immich, we have established the following gu
|
|||||||
|
|
||||||
When in doubt or if you have an edge case scenario, we encourage you to contact us directly via email to discuss the use of our trademark. We can provide clear guidance on what is acceptable and what is not. You can reach out at: questions@immich.app
|
When in doubt or if you have an edge case scenario, we encourage you to contact us directly via email to discuss the use of our trademark. We can provide clear guidance on what is acceptable and what is not. You can reach out at: questions@immich.app
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## User
|
## User
|
||||||
|
|
||||||
### How can I reset the admin password?
|
### How can I reset the admin password?
|
||||||
@@ -36,6 +38,10 @@ The admin password can be reset by running the [reset-admin-password](/administr
|
|||||||
|
|
||||||
You can see the list of all users by running [list-users](/administration/server-commands.md) Command on the Immich-server.
|
You can see the list of all users by running [list-users](/administration/server-commands.md) Command on the Immich-server.
|
||||||
|
|
||||||
|
### How can I change my profile picture?
|
||||||
|
|
||||||
|
View a single photo, press the three dots in the top-right to show context menu, and select "Set as profile picture". In the pop-up, use your mouse scroll wheel to zoom in the picture until it completely fills the circle. Click and drag the picture to align it to your liking. Press "Save" to save your changes.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Mobile App
|
## Mobile App
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ running `apt install postgresql-NN-pgvector`, where `NN` is your Postgres versio
|
|||||||
You must install VectorChord into your instance of Postgres using their [instructions][vchord-install]. After installation, add `shared_preload_libraries = 'vchord.so'` to your `postgresql.conf`. If you already have some `shared_preload_libraries` set, you can separate each extension with a comma. For example, `shared_preload_libraries = 'pg_stat_statements, vchord.so'`.
|
You must install VectorChord into your instance of Postgres using their [instructions][vchord-install]. After installation, add `shared_preload_libraries = 'vchord.so'` to your `postgresql.conf`. If you already have some `shared_preload_libraries` set, you can separate each extension with a comma. For example, `shared_preload_libraries = 'pg_stat_statements, vchord.so'`.
|
||||||
|
|
||||||
:::note Supported versions
|
:::note Supported versions
|
||||||
Immich is known to work with Postgres versions `>= 14, < 19`.
|
Immich is known to work with Postgres versions `>= 14, < 20`.
|
||||||
|
|
||||||
VectorChord is known to work with pgvector versions `>= 0.7, < 0.9`.
|
VectorChord is known to work with pgvector versions `>= 0.7, < 0.9`.
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE vector(512);
|
|||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Migration steps</summary>
|
<summary>Migration steps</summary>
|
||||||
1. Ensure you have at least 0.7.0 of pgvector installed. If it is below that, please upgrade it and run the SQL command `ALTER EXTENSION vector UPDATE;` using psql or your choice of database client
|
1. Ensure you have at least `0.7.0` of pgvector installed. If it is below that, please upgrade it and run the SQL command `ALTER EXTENSION vector UPDATE;` using psql or your choice of database client
|
||||||
2. Follow the Prerequisites to install VectorChord
|
2. Follow the Prerequisites to install VectorChord
|
||||||
3. If Immich does not have superuser permissions, run the SQL command `CREATE EXTENSION vchord CASCADE;`
|
3. If Immich does not have superuser permissions, run the SQL command `CREATE EXTENSION vchord CASCADE;`
|
||||||
4. Remove the `DB_VECTOR_EXTENSION=pgvector` environmental variable as it will make Immich still use pgvector if set
|
4. Remove the `DB_VECTOR_EXTENSION=pgvector` environmental variable as it will make Immich still use pgvector if set
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ services:
|
|||||||
traefik.enable: true
|
traefik.enable: true
|
||||||
# increase readingTimeouts for the entrypoint used here
|
# increase readingTimeouts for the entrypoint used here
|
||||||
traefik.http.routers.immich.entrypoints: websecure
|
traefik.http.routers.immich.entrypoints: websecure
|
||||||
traefik.http.routers.immich.rule: Host(`immich.your-domain.com`)
|
traefik.http.routers.immich.rule: Host(`immich.example.com`)
|
||||||
traefik.http.services.immich.loadbalancer.server.port: 2283
|
traefik.http.services.immich.loadbalancer.server.port: 2283
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,11 @@ The `immich-server` docker image comes preinstalled with an administrative CLI (
|
|||||||
| `enable-oauth-login` | Enable OAuth login |
|
| `enable-oauth-login` | Enable OAuth login |
|
||||||
| `disable-oauth-login` | Disable OAuth login |
|
| `disable-oauth-login` | Disable OAuth login |
|
||||||
| `list-users` | List Immich users |
|
| `list-users` | List Immich users |
|
||||||
|
| `grant-admin` | Grant admin privileges to a user (by email) |
|
||||||
|
| `revoke-admin` | Revoke admin privileges from a user (by email) |
|
||||||
| `version` | Print Immich version |
|
| `version` | Print Immich version |
|
||||||
| `change-media-location` | Change database file paths to align with a new media location |
|
| `change-media-location` | Change database file paths to align with a new media location |
|
||||||
|
| `schema-check` | Verify database migrations and check for schema drift |
|
||||||
|
|
||||||
## How to run a command
|
## How to run a command
|
||||||
|
|
||||||
@@ -87,7 +90,7 @@ immich-admin list-users
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
id: 'e65e6f88-2a30-4dbe-8dd9-1885f4889b53',
|
id: 'e65e6f88-2a30-4dbe-8dd9-1885f4889b53',
|
||||||
email: 'immich@example.com.com',
|
email: 'immich@example.com',
|
||||||
name: 'Immich Admin',
|
name: 'Immich Admin',
|
||||||
storageLabel: 'admin',
|
storageLabel: 'admin',
|
||||||
externalPath: null,
|
externalPath: null,
|
||||||
@@ -102,6 +105,22 @@ immich-admin list-users
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Grant Admin
|
||||||
|
|
||||||
|
```
|
||||||
|
immich-admin grant-admin
|
||||||
|
? Please enter the user email: user@example.com
|
||||||
|
Admin access has been granted to user@example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
Revoke Admin
|
||||||
|
|
||||||
|
```
|
||||||
|
immich-admin revoke-admin
|
||||||
|
? Please enter the user email: user@example.com
|
||||||
|
Admin access has been revoked from user@example.com
|
||||||
|
```
|
||||||
|
|
||||||
Print Immich Version
|
Print Immich Version
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -126,3 +145,12 @@ immich-admin change-media-location
|
|||||||
Database file paths updated successfully! 🎉
|
Database file paths updated successfully! 🎉
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Schema Check
|
||||||
|
|
||||||
|
```
|
||||||
|
immich-admin schema-check
|
||||||
|
Migrations are up to date
|
||||||
|
|
||||||
|
No schema drift detected
|
||||||
|
```
|
||||||
|
|||||||
+2
-2
@@ -7,7 +7,7 @@ Immich uses the [OpenAPI](https://swagger.io/specification/) standard to generat
|
|||||||
OpenAPI is used to generate the client (Typescript, Dart) SDK. `openapi-generator-cli` can be installed [here](https://openapi-generator.tech/docs/installation/). The generated SDK is based on the `immich-openapi-specs.json` file, which is autogenerated by the server **when running in development mode**. The `immich-openapi-specs.json` file can be modified with `@nestjs/swagger` decorators used or referenced by controller endpoints. See the [NestJS OpenAPI docs](https://docs.nestjs.com/openapi/types-and-parameters) for more info. When you add a new endpoint or modify an existing one, you must run the server in development mode and run the command below to update the client SDK.
|
OpenAPI is used to generate the client (Typescript, Dart) SDK. `openapi-generator-cli` can be installed [here](https://openapi-generator.tech/docs/installation/). The generated SDK is based on the `immich-openapi-specs.json` file, which is autogenerated by the server **when running in development mode**. The `immich-openapi-specs.json` file can be modified with `@nestjs/swagger` decorators used or referenced by controller endpoints. See the [NestJS OpenAPI docs](https://docs.nestjs.com/openapi/types-and-parameters) for more info. When you add a new endpoint or modify an existing one, you must run the server in development mode and run the command below to update the client SDK.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make open-api
|
mise open-api
|
||||||
```
|
```
|
||||||
|
|
||||||
You can find the generated client SDK in the `open-api/typescript-sdk/client` for Typescript SDK and `mobile/openapi` for Dart SDK.
|
You can find the generated client SDK in the `packages/sdk/client` for Typescript SDK and `mobile/openapi` for Dart SDK.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ After making any changes in the `server/src/schema`, a database migration need t
|
|||||||
1. Run the command
|
1. Run the command
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm run migrations:generate <migration-name>
|
mise //server:migrations generate <migration-name>
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Check if the migration file makes sense.
|
2. Check if the migration file makes sense.
|
||||||
@@ -18,7 +18,7 @@ The server will automatically detect `*.ts` file changes and restart. Part of th
|
|||||||
If you need to undo the most recently applied migration—for example, when developing or testing on schema changes—run:
|
If you need to undo the most recently applied migration—for example, when developing or testing on schema changes—run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm run migrations:revert
|
mise //server:migrations revert
|
||||||
```
|
```
|
||||||
|
|
||||||
This command rolls back the latest migration and brings the database schema back to its previous state.
|
This command rolls back the latest migration and brings the database schema back to its previous state.
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ When the Dev Container starts, it automatically:
|
|||||||
1. **Runs post-create script** (`container-server-post-create.sh`):
|
1. **Runs post-create script** (`container-server-post-create.sh`):
|
||||||
- Adjusts file permissions for the `node` user
|
- Adjusts file permissions for the `node` user
|
||||||
- Installs dependencies: `pnpm install` in all packages
|
- Installs dependencies: `pnpm install` in all packages
|
||||||
- Builds TypeScript SDK: `pnpm run build` in `open-api/typescript-sdk`
|
- Builds TypeScript SDK: `pnpm --filter @immich/sdk build`
|
||||||
|
|
||||||
2. **Starts development servers** via VS Code tasks:
|
2. **Starts development servers** via VS Code tasks:
|
||||||
- `Immich API Server (Nest)` - API server with hot-reloading on port 2283
|
- `Immich API Server (Nest)` - API server with hot-reloading on port 2283
|
||||||
@@ -218,7 +218,7 @@ When the Dev Container starts, it automatically:
|
|||||||
- Debug ports: 9230 (workers), 9231 (API)
|
- Debug ports: 9230 (workers), 9231 (API)
|
||||||
|
|
||||||
:::info
|
:::info
|
||||||
The Dev Container setup replaces the `make dev` command from the traditional setup. All services start automatically when you open the container.
|
The Dev Container setup replaces the `mise dev` command from the traditional setup. All services start automatically when you open the container.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### Accessing Services
|
### Accessing Services
|
||||||
@@ -243,8 +243,8 @@ To connect the mobile app to your Dev Container:
|
|||||||
|
|
||||||
- **Server code** (`/server`): Changes trigger automatic restart
|
- **Server code** (`/server`): Changes trigger automatic restart
|
||||||
- **Web code** (`/web`): Changes trigger hot module replacement
|
- **Web code** (`/web`): Changes trigger hot module replacement
|
||||||
- **Database migrations**: Run `pnpm run sync:sql` in the server directory
|
- **Database migrations**: Run `mise //:sql`
|
||||||
- **API changes**: Regenerate TypeScript SDK with `make open-api`
|
- **API changes**: Regenerate TypeScript SDK with `mise //:open-api`
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
@@ -252,85 +252,33 @@ To connect the mobile app to your Dev Container:
|
|||||||
|
|
||||||
The Dev Container supports multiple ways to run tests:
|
The Dev Container supports multiple ways to run tests:
|
||||||
|
|
||||||
#### Using Make Commands (Recommended)
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Run tests for specific components
|
# Server
|
||||||
make test-server # Server unit tests
|
mise //server:test # unit tests
|
||||||
make test-web # Web unit tests
|
mise //server:test-medium # medium / integration tests
|
||||||
make test-e2e # End-to-end tests
|
|
||||||
make test-cli # CLI tests
|
|
||||||
|
|
||||||
# Run all tests
|
# Web
|
||||||
make test-all # Runs tests for all components
|
mise //web:test # unit tests
|
||||||
|
|
||||||
# Medium tests (integration tests)
|
# E2E
|
||||||
make test-medium-dev # End-to-end tests
|
mise //e2e:test # API tests
|
||||||
|
mise //e2e:test-web # web UI tests (Playwright)
|
||||||
|
|
||||||
|
# Run all checks for a component
|
||||||
|
mise //server:checklist
|
||||||
|
mise //web:checklist
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Using PNPM Directly
|
### Additional Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Server tests
|
|
||||||
cd /workspaces/immich/server
|
|
||||||
pnpm test # Run all tests
|
|
||||||
pnpm run test:medium # Medium tests (integration tests)
|
|
||||||
pnpm run test:watch # Watch mode
|
|
||||||
pnpm run test:cov # Coverage report
|
|
||||||
|
|
||||||
# Web tests
|
|
||||||
cd /workspaces/immich/web
|
|
||||||
pnpm test # Run all tests
|
|
||||||
pnpm run test:watch # Watch mode
|
|
||||||
|
|
||||||
# E2E tests
|
|
||||||
cd /workspaces/immich/e2e
|
|
||||||
pnpm run test # Run API tests
|
|
||||||
pnpm run test:web # Run web UI tests
|
|
||||||
```
|
|
||||||
|
|
||||||
### Code Quality Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Linting
|
|
||||||
make lint-server # Lint server code
|
|
||||||
make lint-web # Lint web code
|
|
||||||
make lint-all # Lint all components
|
|
||||||
|
|
||||||
# Formatting
|
|
||||||
make format-server # Format server code
|
|
||||||
make format-web # Format web code
|
|
||||||
make format-all # Format all code
|
|
||||||
|
|
||||||
# Type checking
|
|
||||||
make check-server # Type check server
|
|
||||||
make check-web # Type check web
|
|
||||||
make check-all # Check all components
|
|
||||||
|
|
||||||
# Complete hygiene check
|
|
||||||
make hygiene-all # Run lint, format, check, SQL sync, and audit
|
|
||||||
```
|
|
||||||
|
|
||||||
### Additional Make Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Build commands
|
|
||||||
make build-server # Build server
|
|
||||||
make build-web # Build web app
|
|
||||||
make build-all # Build everything
|
|
||||||
|
|
||||||
# API generation
|
# API generation
|
||||||
make open-api # Generate OpenAPI specs
|
mise //:open-api # Generate OpenAPI specs
|
||||||
make open-api-typescript # Generate TypeScript SDK
|
mise //:open-api-typescript # Generate TypeScript SDK
|
||||||
make open-api-dart # Generate Dart SDK
|
mise //:open-api-dart # Generate Dart SDK
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
make sql # Sync database schema
|
mise //server:sql # Sync database schema
|
||||||
|
|
||||||
# Dependencies
|
|
||||||
make install-server # Install server dependencies
|
|
||||||
make install-web # Install web dependencies
|
|
||||||
make install-all # Install all dependencies
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Debugging
|
### Debugging
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ Our [GitHub Repository](https://github.com/immich-app/immich) is a [monorepo](ht
|
|||||||
| :------------------ | :------------------------------------------------------------------- |
|
| :------------------ | :------------------------------------------------------------------- |
|
||||||
| `.github/` | Github templates and action workflows |
|
| `.github/` | Github templates and action workflows |
|
||||||
| `.vscode/` | VSCode debug launch profiles |
|
| `.vscode/` | VSCode debug launch profiles |
|
||||||
| `cli/` | Source code for the work-in-progress CLI rewrite |
|
| `packages/cli` | Source code for the CLI |
|
||||||
|
| `packages/sdk` | Source code for the generated OpenAPI SDK |
|
||||||
| `docker/` | Docker compose resources for dev, test, production |
|
| `docker/` | Docker compose resources for dev, test, production |
|
||||||
| `design/` | Screenshots and logos for the README |
|
| `design/` | Screenshots and logos for the README |
|
||||||
| `docs/` | Source code for the [https://immich.app](https://immich.app) website |
|
| `docs/` | Source code for the [https://immich.app](https://immich.app) website |
|
||||||
|
|||||||
@@ -2,53 +2,74 @@
|
|||||||
|
|
||||||
A minimal devcontainer is supplied with this repository. All commands can be executed directly inside this container to avoid tedious installation of the environment.
|
A minimal devcontainer is supplied with this repository. All commands can be executed directly inside this container to avoid tedious installation of the environment.
|
||||||
:::warning
|
:::warning
|
||||||
The provided devcontainer isn't complete at the moment. At least all dockerized steps in the Makefile won't work (`make dev`, ....). Feel free to contribute!
|
The provided devcontainer isn't complete at the moment. At least all dockerized steps in the Makefile won't work (`mise dev`, ....). Feel free to contribute!
|
||||||
:::
|
:::
|
||||||
When contributing code through a pull request, please check the following:
|
When contributing code through a pull request, please check the following:
|
||||||
|
|
||||||
## Web Checks
|
## Web Checks
|
||||||
|
|
||||||
- [ ] `pnpm run lint` (linting via ESLint)
|
- [ ] `mise //web:lint` (linting via ESLint)
|
||||||
- [ ] `pnpm run format` (formatting via Prettier)
|
- [ ] `mise //web:format` (formatting via Prettier)
|
||||||
- [ ] `pnpm run check:svelte` (Type checking via SvelteKit)
|
- [ ] `mise //web:check-svelte` (type checking via SvelteKit)
|
||||||
- [ ] `pnpm run check:typescript` (check typescript)
|
- [ ] `mise //web:check-typescript` (type checking via `tsc`)
|
||||||
- [ ] `pnpm test` (unit tests)
|
- [ ] `mise //web:test` (unit tests)
|
||||||
|
|
||||||
:::tip AIO
|
:::tip AIO
|
||||||
Run all web checks with `pnpm run check:all`
|
Run all web checks with `mise //web:checklist`
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::tip Auto Fix
|
||||||
|
Use `mise //web:lint-fix` and `mise //web:format-fix` to automatically correct some issues.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
- [ ] `pnpm run format` (formatting via Prettier)
|
- [ ] `mise //docs:format` (formatting via Prettier)
|
||||||
- [ ] Update the `_redirects` file if you have renamed a page or removed it from the documentation.
|
- [ ] Update the `_redirects` file if you have renamed a page or removed it from the documentation.
|
||||||
|
|
||||||
|
:::tip Auto Fix
|
||||||
|
Use `mise //docs:format-fix` to automatically fix formatting.
|
||||||
|
:::
|
||||||
|
|
||||||
## Server Checks
|
## Server Checks
|
||||||
|
|
||||||
- [ ] `pnpm run lint` (linting via ESLint)
|
- [ ] `mise //server:lint` (linting via ESLint)
|
||||||
- [ ] `pnpm run format` (formatting via Prettier)
|
- [ ] `mise //server:format` (formatting via Prettier)
|
||||||
- [ ] `pnpm run check` (Type checking via `tsc`)
|
- [ ] `mise //server:check` (type checking via `tsc`)
|
||||||
- [ ] `pnpm test` (unit tests)
|
- [ ] `mise //server:test` (unit tests)
|
||||||
|
|
||||||
:::tip AIO
|
:::tip AIO
|
||||||
Run all server checks with `pnpm run check:all`
|
Run all server checks with `mise //server:checklist`
|
||||||
:::
|
:::
|
||||||
|
|
||||||
:::info Auto Fix
|
:::tip Auto Fix
|
||||||
You can use `pnpm run __:fix` to potentially correct some issues automatically for `pnpm run format` and `lint`.
|
Use `mise //server:lint-fix` and `mise //server:format-fix` to automatically correct some issues.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## Mobile Checks
|
## Mobile Checklist
|
||||||
|
|
||||||
The following commands must be executed from within the mobile app directory of the codebase.
|
- [ ] `mise //mobile:codegen` (auto-generate files using build_runner)
|
||||||
|
- [ ] `mise //mobile:lint` (static analysis via Dart Analyzer and DCM)
|
||||||
|
- [ ] `mise //mobile:format` (formatting via Dart Formatter)
|
||||||
|
- [ ] `mise //mobile:test` (unit tests)
|
||||||
|
|
||||||
- [ ] `make build` (auto-generate files using build_runner)
|
:::tip
|
||||||
- [ ] `make analyze` (static analysis via Dart Analyzer and DCM)
|
Run all these commands at once with `mise //mobile:checklist`
|
||||||
- [ ] `make format` (formatting via Dart Formatter)
|
:::
|
||||||
- [ ] `make test` (unit tests)
|
|
||||||
|
|
||||||
:::info Auto Fix
|
:::tip Auto Fix
|
||||||
You can use `dart fix --apply` and `dcm fix lib` to potentially correct some issues automatically for `make analyze`.
|
You can use `mise //mobile:lint-fix` to potentially correct some issues automatically for `mise //mobile:lint`.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Machine Learning Checklist
|
||||||
|
|
||||||
|
- [ ] `mise //machine-learning:lint` (linting via ruff)
|
||||||
|
- [ ] `mise //machine-learning:format` (formatting via ruff)
|
||||||
|
- [ ] `mise //machine-learning:check` (type checking via mypy)
|
||||||
|
- [ ] `mise //machine-learning:test` (unit tests via pytest)
|
||||||
|
|
||||||
|
:::tip AIO
|
||||||
|
Run all machine learning checks with `mise //machine-learning:checklist`
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## OpenAPI
|
## OpenAPI
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ This environment includes the services below. Additional details are available i
|
|||||||
|
|
||||||
All the services are packaged to run as with single Docker Compose command.
|
All the services are packaged to run as with single Docker Compose command.
|
||||||
|
|
||||||
|
:::tip mise
|
||||||
|
[mise](https://mise.jdx.dev) is used throughout the project to manage tool versions and run tasks. [Install mise](https://mise.jdx.dev/installing-mise.html), then from the repo root run `mise trust` and `mise install` to get all required tools. Tasks for each service can be run from the repo root using `mise //namespace:task` (e.g. `mise //server:lint`). To list all available tasks, run `mise tasks ls --all`.
|
||||||
|
:::
|
||||||
|
|
||||||
### Server and web apps
|
### Server and web apps
|
||||||
|
|
||||||
1. Clone the project repo.
|
1. Clone the project repo.
|
||||||
@@ -41,7 +45,7 @@ All the services are packaged to run as with single Docker Compose command.
|
|||||||
5. From the root directory, run:
|
5. From the root directory, run:
|
||||||
|
|
||||||
```bash title="Start development server"
|
```bash title="Start development server"
|
||||||
make dev # required Makefile installed on the system.
|
mise dev
|
||||||
```
|
```
|
||||||
|
|
||||||
5. Access the dev instance in your browser at http://localhost:3000, or connect via the mobile app.
|
5. Access the dev instance in your browser at http://localhost:3000, or connect via the mobile app.
|
||||||
@@ -56,22 +60,23 @@ You can access the web from `http://your-machine-ip:3000` or `http://localhost:3
|
|||||||
|
|
||||||
#### Connect web to a remote backend
|
#### Connect web to a remote backend
|
||||||
|
|
||||||
If you only want to do web development connected to an existing, remote backend, follow these steps:
|
If you only want to do web development connected to an existing, remote backend, run from the repo root:
|
||||||
|
|
||||||
1. Build the Immich SDK - `cd open-api/typescript-sdk && pnpm i && pnpm run build && cd -`
|
|
||||||
2. Enter the web directory - `cd web/`
|
|
||||||
3. Install web dependencies - `pnpm i`
|
|
||||||
4. Start the web development server
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
IMMICH_SERVER_URL=https://demo.immich.app/ pnpm run dev
|
IMMICH_SERVER_URL=https://demo.immich.app/ mise //web:start
|
||||||
|
```
|
||||||
|
|
||||||
|
This will install all dependencies (including the SDK) and start the dev server in one step. To connect to the hosted demo server specifically, use the shorthand:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mise //web:start-demo
|
||||||
```
|
```
|
||||||
|
|
||||||
If you're using PowerShell on Windows you may need to set the env var separately like so:
|
If you're using PowerShell on Windows you may need to set the env var separately like so:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
$env:IMMICH_SERVER_URL = "https://demo.immich.app/"
|
$env:IMMICH_SERVER_URL = "https://demo.immich.app/"
|
||||||
pnpm run dev
|
mise //web:start
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `@immich/ui`
|
#### `@immich/ui`
|
||||||
@@ -83,31 +88,45 @@ To see local changes to `@immich/ui` in Immich, do the following:
|
|||||||
3. Uncomment the corresponding volume in web service of the `docker/docker-compose.dev.yml` file (`../../ui:/usr/src/ui`)
|
3. Uncomment the corresponding volume in web service of the `docker/docker-compose.dev.yml` file (`../../ui:/usr/src/ui`)
|
||||||
4. Uncomment the corresponding alias in the `web/vite.config.ts` file (`'@immich/ui': path.resolve(\_\_dirname, '../../ui/packages/ui')`)
|
4. Uncomment the corresponding alias in the `web/vite.config.ts` file (`'@immich/ui': path.resolve(\_\_dirname, '../../ui/packages/ui')`)
|
||||||
5. Uncomment the import statement in `web/src/app.css` file `@import '../../../ui/packages/ui/dist/theme/default.css';` and comment out `@import '@immich/ui/theme/default.css';`
|
5. Uncomment the import statement in `web/src/app.css` file `@import '../../../ui/packages/ui/dist/theme/default.css';` and comment out `@import '@immich/ui/theme/default.css';`
|
||||||
6. Start up the stack via `make dev`
|
6. Start up the stack via `mise dev`
|
||||||
7. After making changes in `@immich/ui`, rebuild it (`pnpm run build`)
|
7. After making changes in `@immich/ui`, rebuild it (`pnpm run build`)
|
||||||
|
|
||||||
### Mobile app
|
### Mobile app
|
||||||
|
|
||||||
#### Setup
|
#### Setup
|
||||||
|
|
||||||
1. [Install mise](https://mise.jdx.dev/installing-mise.html).
|
1. Run `mise //mobile:install` to install Flutter dependencies.
|
||||||
2. Change to the immich (root) directory and trust the mise config with `mise trust`.
|
2. Run `mise //mobile:translation` to generate the translation file.
|
||||||
3. Install tools with mise: `mise install`.
|
3. Change to the `mobile/` directory and run `flutter run` to start the app.
|
||||||
4. Change to the `mobile/` directory.
|
|
||||||
5. Run `flutter pub get` to install the dependencies.
|
|
||||||
6. Run `make translation` to generate the translation file.
|
|
||||||
7. Run `flutter run` to start the app.
|
|
||||||
|
|
||||||
#### Translation
|
#### Translation
|
||||||
|
|
||||||
To add a new translation text, enter the key-value pair in the `i18n/en.json` in the root of the immich project. Then, from the `mobile/` directory, run
|
To add a new translation text, enter the key-value pair in the `i18n/en.json` in the root of the immich project. Then run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make translation
|
mise //mobile:translation
|
||||||
```
|
```
|
||||||
|
|
||||||
The mobile app asks you what backend to connect to. You can utilize the demo backend (https://demo.immich.app/) if you don't need to change server code or upload photos. Alternatively, you can run the server yourself per the instructions above.
|
The mobile app asks you what backend to connect to. You can utilize the demo backend (https://demo.immich.app/) if you don't need to change server code or upload photos. Alternatively, you can run the server yourself per the instructions above.
|
||||||
|
|
||||||
|
#### UI components and widget previews
|
||||||
|
|
||||||
|
Shared design-system widgets (buttons, inputs, forms) live in the
|
||||||
|
[`immich_ui` package](https://github.com/immich-app/immich/tree/main/mobile/packages/ui/)
|
||||||
|
under `mobile/packages/ui/`. Components are defined in `lib/src/components/`
|
||||||
|
and have matching previews in `lib/src/previews/`.
|
||||||
|
|
||||||
|
To inspect a component in isolation with a light/dark toggle and hot reload,
|
||||||
|
launch [Flutter's Widget Previewer](https://docs.flutter.dev/tools/widget-previewer):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd mobile/packages/ui
|
||||||
|
flutter widget-preview start
|
||||||
|
```
|
||||||
|
|
||||||
|
In VS Code or Android Studio with the Flutter plugin, the previewer
|
||||||
|
auto-starts when you open the **Flutter Widget Preview** tab in the sidebar.
|
||||||
|
|
||||||
## IDE setup
|
## IDE setup
|
||||||
|
|
||||||
### Lint / format extensions
|
### Lint / format extensions
|
||||||
|
|||||||
@@ -4,28 +4,26 @@
|
|||||||
|
|
||||||
### Unit tests
|
### Unit tests
|
||||||
|
|
||||||
Unit are run by calling `pnpm run test` from the `server/` directory.
|
Unit tests are run with `mise //server:test`.
|
||||||
You need to run `pnpm install` (in `server/`) before _once_.
|
You need to run `mise //server:install` before _once_.
|
||||||
|
|
||||||
### End to end tests
|
### End to end tests
|
||||||
|
|
||||||
The e2e tests can be run by first starting up a test production environment via:
|
The e2e tests can be run by first starting up a test production environment via:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make e2e
|
mise e2e
|
||||||
```
|
```
|
||||||
|
|
||||||
Before you can run the tests, you need to run the following commands _once_:
|
Before you can run the tests, you need to run the following commands _once_:
|
||||||
|
|
||||||
- `pnpm install` (in `e2e/`)
|
- `mise //e2e:ci-setup` (installs e2e, SDK, and CLI dependencies)
|
||||||
- `pnpm run build` (in `cli/`)
|
- `mise //:open-api`
|
||||||
- `make open-api` (in the project root `/`)
|
|
||||||
|
|
||||||
Once the test environment is running, the e2e tests can be run via:
|
Once the test environment is running, the e2e tests can be run via:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd e2e/
|
mise //e2e:test
|
||||||
pnpm test
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The tests check various things including:
|
The tests check various things including:
|
||||||
|
|||||||
@@ -15,14 +15,14 @@ When using "Deduplicate All" or viewing suggestions, Immich automatically presel
|
|||||||
|
|
||||||
### Synchronizing metadata
|
### Synchronizing metadata
|
||||||
|
|
||||||
When resolving duplicates, metadata from trashed assets is automatically synchronized to the kept assets. The following metadata is synchronized:
|
When resolving duplicates, metadata from trashed assets is automatically synchronized to the kept asset. This synchronization only happens when **exactly one** asset is kept and at least one asset is trashed. When more than one asset is kept, metadata is not merged — the assets keep their own metadata and are simply removed from the duplicate group. The following metadata is synchronized:
|
||||||
|
|
||||||
| Name | Description |
|
| Name | Description |
|
||||||
| ----------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
| ----------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| Album | The kept assets will be added to _every_ album that the other assets in the group belong to. |
|
| Album | The kept asset will be added to _every_ album that the other assets in the group belong to. |
|
||||||
| Favorite | If any of the assets in the group have been added to favorites, every kept asset will also be added to favorites. |
|
| Favorite | If any of the assets in the group have been added to favorites, the kept asset will also be added to favorites. |
|
||||||
| Rating | If one or more assets in the duplicate group have a rating, the highest rating is selected and synchronized to the kept assets. |
|
| Rating | If one or more assets in the duplicate group have a rating, the highest rating is selected and synchronized to the kept asset. |
|
||||||
| Description | Descriptions from each asset are combined together and synchronized to all the kept assets. |
|
| Description | Descriptions from each asset are combined together and synchronized to the kept asset. |
|
||||||
| Visibility | The most restrictive visibility is applied to the kept assets. |
|
| Visibility | The most restrictive visibility is applied to the kept asset. |
|
||||||
| Location | Latitude and longitude are copied if all assets with geolocation data in the group share the same coordinates. |
|
| Location | Latitude and longitude are copied if all assets with geolocation data in the group share the same coordinates. |
|
||||||
| Tag | Tags from all assets in the group are merged and applied to every kept asset. |
|
| Tag | Tags from all assets in the group are merged and applied to the kept asset. |
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ import MobileAppBackup from '/docs/partials/_mobile-app-backup.md';
|
|||||||
:::info Android verification
|
:::info Android verification
|
||||||
Below are the SHA-256 fingerprints for the certificates signing the android applications.
|
Below are the SHA-256 fingerprints for the certificates signing the android applications.
|
||||||
|
|
||||||
- Playstore / Github releases:
|
- Google Play releases:
|
||||||
|
`5A:22:C1:83:47:54:05:F5:49:C4:EB:9F:B2:6C:2E:93:A3:EF:9C:57:66:15:0A:7A:F3:8C:8D:3F:E5:15:CC:D6`
|
||||||
|
- GitHub releases:
|
||||||
`86:C5:C4:55:DF:AF:49:85:92:3A:8F:35:AD:B3:1D:0C:9E:0B:95:7D:7F:94:C2:D2:AF:6A:24:38:AA:96:00:20`
|
`86:C5:C4:55:DF:AF:49:85:92:3A:8F:35:AD:B3:1D:0C:9E:0B:95:7D:7F:94:C2:D2:AF:6A:24:38:AA:96:00:20`
|
||||||
- F-Droid releases:
|
- F-Droid releases:
|
||||||
`FA:8B:43:95:F4:A6:47:71:A0:53:D1:C7:57:73:5F:A2:30:13:74:F5:3D:58:0D:D1:75:AA:F7:A1:35:72:9C:BF`
|
`FA:8B:43:95:F4:A6:47:71:A0:53:D1:C7:57:73:5F:A2:30:13:74:F5:3D:58:0D:D1:75:AA:F7:A1:35:72:9C:BF`
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "8888:80"
|
- "8888:80"
|
||||||
environment:
|
environment:
|
||||||
PGADMIN_DEFAULT_EMAIL: user-name@domain-name.com
|
PGADMIN_DEFAULT_EMAIL: admin@example.com
|
||||||
PGADMIN_DEFAULT_PASSWORD: strong-password
|
PGADMIN_DEFAULT_PASSWORD: strong-password
|
||||||
volumes:
|
volumes:
|
||||||
- pgadmin-data:/var/lib/pgadmin
|
- pgadmin-data:/var/lib/pgadmin
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ The default configuration looks like this:
|
|||||||
},
|
},
|
||||||
"ffmpeg": {
|
"ffmpeg": {
|
||||||
"accel": "disabled",
|
"accel": "disabled",
|
||||||
"accelDecode": false,
|
"accelDecode": true,
|
||||||
"acceptedAudioCodecs": ["aac", "mp3", "opus"],
|
"acceptedAudioCodecs": ["aac", "mp3", "opus"],
|
||||||
"acceptedContainers": ["mov", "ogg", "webm"],
|
"acceptedContainers": ["mov", "ogg", "webm"],
|
||||||
"acceptedVideoCodecs": ["h264"],
|
"acceptedVideoCodecs": ["h264"],
|
||||||
|
|||||||
@@ -154,33 +154,33 @@ Redis (Sentinel) URL example JSON before encoding:
|
|||||||
|
|
||||||
## Machine Learning
|
## Machine Learning
|
||||||
|
|
||||||
| Variable | Description | Default | Containers |
|
| Variable | Description | Default | Containers |
|
||||||
| :---------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----------------------------: | :--------------- |
|
| :---------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------: | :--------------- |
|
||||||
| `MACHINE_LEARNING_MODEL_TTL` | Inactivity time (s) before a model is unloaded (disabled if \<= 0) | `300` | machine learning |
|
| `MACHINE_LEARNING_MODEL_TTL` | Inactivity time (s) before a model is unloaded (disabled if \<= 0) | `300` | machine learning |
|
||||||
| `MACHINE_LEARNING_MODEL_TTL_POLL_S` | Interval (s) between checks for the model TTL (disabled if \<= 0) | `10` | machine learning |
|
| `MACHINE_LEARNING_MODEL_TTL_POLL_S` | Interval (s) between checks for the model TTL (disabled if \<= 0) | `10` | machine learning |
|
||||||
| `MACHINE_LEARNING_CACHE_FOLDER` | Directory where models are downloaded | `/cache` | machine learning |
|
| `MACHINE_LEARNING_CACHE_FOLDER` | Directory where models are downloaded | `/cache` | machine learning |
|
||||||
| `MACHINE_LEARNING_REQUEST_THREADS`<sup>\*1</sup> | Thread count of the request thread pool (disabled if \<= 0) | number of CPU cores | machine learning |
|
| `MACHINE_LEARNING_REQUEST_THREADS`<sup>\*1</sup> | Thread count of the request thread pool (disabled if \<= 0) | number of CPU cores | machine learning |
|
||||||
| `MACHINE_LEARNING_MODEL_INTER_OP_THREADS` | Number of parallel model operations | `1` | machine learning |
|
| `MACHINE_LEARNING_MODEL_INTER_OP_THREADS` | Number of parallel model operations | `1` | machine learning |
|
||||||
| `MACHINE_LEARNING_MODEL_INTRA_OP_THREADS` | Number of threads for each model operation | `2` | machine learning |
|
| `MACHINE_LEARNING_MODEL_INTRA_OP_THREADS` | Number of threads for each model operation | `2` | machine learning |
|
||||||
| `MACHINE_LEARNING_WORKERS`<sup>\*2</sup> | Number of worker processes to spawn | `1` | machine learning |
|
| `MACHINE_LEARNING_WORKERS`<sup>\*2</sup> | Number of worker processes to spawn | `1` | machine learning |
|
||||||
| `MACHINE_LEARNING_HTTP_KEEPALIVE_TIMEOUT_S`<sup>\*3</sup> | HTTP Keep-alive time in seconds | `2` | machine learning |
|
| `MACHINE_LEARNING_HTTP_KEEPALIVE_TIMEOUT_S`<sup>\*3</sup> | HTTP Keep-alive time in seconds | `2` | machine learning |
|
||||||
| `MACHINE_LEARNING_WORKER_TIMEOUT` | Maximum time (s) of unresponsiveness before a worker is killed | `120` (`300` if using OpenVINO) | machine learning |
|
| `MACHINE_LEARNING_WORKER_TIMEOUT` | Maximum time (s) of unresponsiveness before a worker is killed | `300` (`900` if using ROCm) | machine learning |
|
||||||
| `MACHINE_LEARNING_PRELOAD__CLIP__TEXTUAL` | Comma-separated list of (textual) CLIP model(s) to preload and cache | | machine learning |
|
| `MACHINE_LEARNING_PRELOAD__CLIP__TEXTUAL` | Comma-separated list of (textual) CLIP model(s) to preload and cache | | machine learning |
|
||||||
| `MACHINE_LEARNING_PRELOAD__CLIP__VISUAL` | Comma-separated list of (visual) CLIP model(s) to preload and cache | | machine learning |
|
| `MACHINE_LEARNING_PRELOAD__CLIP__VISUAL` | Comma-separated list of (visual) CLIP model(s) to preload and cache | | machine learning |
|
||||||
| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__RECOGNITION` | Comma-separated list of (recognition) facial recognition model(s) to preload and cache | | machine learning |
|
| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__RECOGNITION` | Comma-separated list of (recognition) facial recognition model(s) to preload and cache | | machine learning |
|
||||||
| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__DETECTION` | Comma-separated list of (detection) facial recognition model(s) to preload and cache | | machine learning |
|
| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__DETECTION` | Comma-separated list of (detection) facial recognition model(s) to preload and cache | | machine learning |
|
||||||
| `MACHINE_LEARNING_PRELOAD__OCR__RECOGNITION` | Comma-separated list of (recognition) OCR model(s) to preload and cache | | machine learning |
|
| `MACHINE_LEARNING_PRELOAD__OCR__RECOGNITION` | Comma-separated list of (recognition) OCR model(s) to preload and cache | | machine learning |
|
||||||
| `MACHINE_LEARNING_PRELOAD__OCR__DETECTION` | Comma-separated list of (detection) OCR model(s) to preload and cache | | machine learning |
|
| `MACHINE_LEARNING_PRELOAD__OCR__DETECTION` | Comma-separated list of (detection) OCR model(s) to preload and cache | | machine learning |
|
||||||
| `MACHINE_LEARNING_ANN` | Enable ARM-NN hardware acceleration if supported | `True` | machine learning |
|
| `MACHINE_LEARNING_ANN` | Enable ARM-NN hardware acceleration if supported | `True` | machine learning |
|
||||||
| `MACHINE_LEARNING_ANN_FP16_TURBO` | Execute operations in FP16 precision: increasing speed, reducing precision (applies only to ARM-NN) | `False` | machine learning |
|
| `MACHINE_LEARNING_ANN_FP16_TURBO` | Execute operations in FP16 precision: increasing speed, reducing precision (applies only to ARM-NN) | `False` | machine learning |
|
||||||
| `MACHINE_LEARNING_ANN_TUNING_LEVEL` | ARM-NN GPU tuning level (1: rapid, 2: normal, 3: exhaustive) | `2` | machine learning |
|
| `MACHINE_LEARNING_ANN_TUNING_LEVEL` | ARM-NN GPU tuning level (1: rapid, 2: normal, 3: exhaustive) | `2` | machine learning |
|
||||||
| `MACHINE_LEARNING_DEVICE_IDS`<sup>\*4</sup> | Device IDs to use in multi-GPU environments | `0` | machine learning |
|
| `MACHINE_LEARNING_DEVICE_IDS`<sup>\*4</sup> | Device IDs to use in multi-GPU environments | `0` | machine learning |
|
||||||
| `MACHINE_LEARNING_MAX_BATCH_SIZE__FACIAL_RECOGNITION` | Set the maximum number of faces that will be processed at once by the facial recognition model | None (`1` if using OpenVINO) | machine learning |
|
| `MACHINE_LEARNING_MAX_BATCH_SIZE__FACIAL_RECOGNITION` | Set the maximum number of faces that will be processed at once by the facial recognition model | None (`1` if using OpenVINO) | machine learning |
|
||||||
| `MACHINE_LEARNING_MAX_BATCH_SIZE__OCR` | Set the maximum number of boxes that will be processed at once by the OCR model | `6` | machine learning |
|
| `MACHINE_LEARNING_MAX_BATCH_SIZE__OCR` | Set the maximum number of boxes that will be processed at once by the OCR model | `6` | machine learning |
|
||||||
| `MACHINE_LEARNING_RKNN` | Enable RKNN hardware acceleration if supported | `True` | machine learning |
|
| `MACHINE_LEARNING_RKNN` | Enable RKNN hardware acceleration if supported | `True` | machine learning |
|
||||||
| `MACHINE_LEARNING_RKNN_THREADS` | How many threads of RKNN runtime should be spun up while inferencing. | `1` | machine learning |
|
| `MACHINE_LEARNING_RKNN_THREADS` | How many threads of RKNN runtime should be spun up while inferencing. | `1` | machine learning |
|
||||||
| `MACHINE_LEARNING_MODEL_ARENA` | Pre-allocates CPU memory to avoid memory fragmentation | true | machine learning |
|
| `MACHINE_LEARNING_MODEL_ARENA` | Pre-allocates CPU memory to avoid memory fragmentation | true | machine learning |
|
||||||
| `MACHINE_LEARNING_OPENVINO_PRECISION` | If set to FP16, uses half-precision floating-point operations for faster inference with reduced accuracy (one of [`FP16`, `FP32`], applies only to OpenVINO) | `FP32` | machine learning |
|
| `MACHINE_LEARNING_OPENVINO_PRECISION` | If set to FP16, uses half-precision floating-point operations for faster inference with reduced accuracy (one of [`FP16`, `FP32`], applies only to OpenVINO) | `FP32` | machine learning |
|
||||||
|
|
||||||
\*1: It is recommended to begin with this parameter when changing the concurrency levels of the machine learning service and then tune the other ones.
|
\*1: It is recommended to begin with this parameter when changing the concurrency levels of the machine learning service and then tune the other ones.
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,11 @@ Hardware and software requirements for Immich:
|
|||||||
- **RAM**: Minimum 6GB, recommended 8GB.
|
- **RAM**: Minimum 6GB, recommended 8GB.
|
||||||
- **CPU**: Minimum 2 cores, recommended 4 cores.
|
- **CPU**: Minimum 2 cores, recommended 4 cores.
|
||||||
- Immich runs on the `amd64` and `arm64` platforms.
|
- Immich runs on the `amd64` and `arm64` platforms.
|
||||||
Since `v2.6`, the machine learning container on `amd64` requires the `>= x86-64-v2` [microarchitecture level](https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels).
|
Since `v3`, the machine learning container on `amd64` requires the `>= x86-64-v2` [microarchitecture level](https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels).
|
||||||
Most CPUs released since ~2012 support this microarchitecture.
|
Most CPUs released since ~2012 support this microarchitecture.
|
||||||
If you are using a virtual machine, ensure you have selected a [supported microarchitecture](https://pve.proxmox.com/pve-docs/chapter-qm.html#_qemu_cpu_types).
|
If you are using a virtual machine, ensure you have selected a [supported microarchitecture](https://pve.proxmox.com/pve-docs/chapter-qm.html#_qemu_cpu_types).
|
||||||
|
If you are unable to support this instruction set, the last version to support `x86-64-v1` is `v2.7.5`.
|
||||||
|
Note that this release is no longer supported, and you must run a matching `immich-server` version.
|
||||||
- **Storage**: Recommended Unix-compatible filesystem (EXT4, ZFS, APFS, etc.) with support for user/group ownership and permissions.
|
- **Storage**: Recommended Unix-compatible filesystem (EXT4, ZFS, APFS, etc.) with support for user/group ownership and permissions.
|
||||||
- The generation of thumbnails and transcoded video can increase the size of the photo library by 10-20% on average.
|
- The generation of thumbnails and transcoded video can increase the size of the photo library by 10-20% on average.
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ Scroll to the bottom of the "**Details**" section and find the `IP Address` list
|
|||||||
|
|
||||||
## Step 4 - Configure Firewall Settings
|
## Step 4 - Configure Firewall Settings
|
||||||
|
|
||||||
Once your project completes the build process, your containers will start. In order to be able to access Immich from your browser, you need to configure the firewall settings for your Synology NAS.
|
Once your project completes the build process, your containers will start. In order to be able to access Immich from your browser, you need to configure the firewall settings for your Synology NAS to allow communication between the Immich containers.
|
||||||
|
|
||||||
Open "**Control Panel**" on your Synology NAS, and select "**Security**". Navigate to "**Firewall**"
|
Open "**Control Panel**" on your Synology NAS, and select "**Security**". Navigate to "**Firewall**"
|
||||||
|
|
||||||
@@ -74,6 +74,7 @@ Read the [Post Installation](/install/post-install.mdx) steps and [upgrade instr
|
|||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Updating Immich using Container Manager</summary>
|
<summary>Updating Immich using Container Manager</summary>
|
||||||
|
|
||||||
Check the post installation and upgrade instructions at the links above before proceeding with this section.
|
Check the post installation and upgrade instructions at the links above before proceeding with this section.
|
||||||
|
|
||||||
## Step 1. Backup
|
## Step 1. Backup
|
||||||
@@ -110,7 +111,7 @@ Go to **Project**, select **Action** then **Build**. This will download, unpack,
|
|||||||
|
|
||||||
## Step 5. Update firewall rule
|
## Step 5. Update firewall rule
|
||||||
|
|
||||||
The default behavior is to automatically start the containers once installed. If `immich_server` runs for a few seconds and then stops, it may be because the firewall rule no longer matches the server IP address.
|
Without a fixed subnet, the default behavior is to automatically start the containers once installed. If `immich_server` runs for a few seconds and then stops, it may be because the firewall rule no longer matches the server IP address.
|
||||||
|
|
||||||
Go to the **Container** section. Click on `immich_server` and scroll down on **General** to find the IP address.
|
Go to the **Container** section. Click on `immich_server` and scroll down on **General** to find the IP address.
|
||||||

|

|
||||||
@@ -123,4 +124,67 @@ In this example, the IP addresses mismatch and the firewall rule needs to be edi
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
To prevent future firewall issues, you may set a fixed subnet. [See Set Fixed Subnet](#set-fixed-subnet) for instructions.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details id="set-fixed-subnet">
|
||||||
|
<summary>Set Fixed Subnet</summary>
|
||||||
|
|
||||||
|
Docker by default assigns dynamic subnets to bridge networks which can change when rebuilding containers and can cause firewall rules to break. To avoid this, define a fixed subnet in your `docker-compose.yml`:
|
||||||
|
|
||||||
|
## Step 1. Determine current subnet
|
||||||
|
|
||||||
|
Go to the **Container** section. Click on `immich_server` and scroll down on **General** to find the IP address.
|
||||||
|

|
||||||
|
|
||||||
|
## Step 2. Add network configuration
|
||||||
|
|
||||||
|
Add the following network configuration at the end of your `docker-compose.yml` file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
networks:
|
||||||
|
immich-network:
|
||||||
|
driver: bridge
|
||||||
|
ipam:
|
||||||
|
config:
|
||||||
|
- subnet: 172.20.0.0/16
|
||||||
|
gateway: 172.20.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
If your docker container is running on a different subnet then update accordingly.
|
||||||
|
|
||||||
|
## Step 3. Add network to each service
|
||||||
|
|
||||||
|
Add the network to each service (immich-server, immich-machine-learning, redis, database):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
immich-server:
|
||||||
|
# other config options
|
||||||
|
networks:
|
||||||
|
- immich-network
|
||||||
|
|
||||||
|
immich-machine-learning:
|
||||||
|
# other config options
|
||||||
|
networks:
|
||||||
|
- immich-network
|
||||||
|
|
||||||
|
redis:
|
||||||
|
# other config options
|
||||||
|
networks:
|
||||||
|
- immich-network
|
||||||
|
|
||||||
|
database:
|
||||||
|
# other config options
|
||||||
|
networks:
|
||||||
|
- immich-network
|
||||||
|
```
|
||||||
|
|
||||||
|
Save your changes. Synology will ask if you want to save changes only or rebuild containers. Select rebuild containers.
|
||||||
|
|
||||||
|
## Step 4. Update Firewall Rules, if necessary
|
||||||
|
|
||||||
|
If your firewall rules were not already set for this subnet, the firewall rules will need to be updated. See [Step 4 - Configure Firewall Settings](#step-4---configure-firewall-settings).
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ const config = {
|
|||||||
url: 'https://docs.immich.app',
|
url: 'https://docs.immich.app',
|
||||||
baseUrl: '/',
|
baseUrl: '/',
|
||||||
onBrokenLinks: 'throw',
|
onBrokenLinks: 'throw',
|
||||||
onBrokenMarkdownLinks: 'warn',
|
|
||||||
favicon: 'img/favicon.png',
|
favicon: 'img/favicon.png',
|
||||||
|
|
||||||
// GitHub pages deployment config.
|
// GitHub pages deployment config.
|
||||||
@@ -29,6 +28,9 @@ const config = {
|
|||||||
// Mermaid diagrams
|
// Mermaid diagrams
|
||||||
markdown: {
|
markdown: {
|
||||||
mermaid: true,
|
mermaid: true,
|
||||||
|
hooks: {
|
||||||
|
onBrokenMarkdownLinks: 'warn',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
themes: ['@docusaurus/theme-mermaid'],
|
themes: ['@docusaurus/theme-mermaid'],
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# @generated - this file is auto-generated by `mise lock` https://mise.en.dev/dev-tools/mise-lock.html
|
||||||
|
|
||||||
|
[[tools.wrangler]]
|
||||||
|
version = "4.100.0"
|
||||||
|
backend = "npm:wrangler"
|
||||||
|
|
||||||
|
[tools.wrangler.options]
|
||||||
|
allow_builds = '["esbuild", "sharp", "workerd"]'
|
||||||
+2
-2
@@ -3,7 +3,7 @@ run = "pnpm install --filter documentation --frozen-lockfile"
|
|||||||
|
|
||||||
[tasks.start]
|
[tasks.start]
|
||||||
env._.path = "./node_modules/.bin"
|
env._.path = "./node_modules/.bin"
|
||||||
run = "docusaurus --port 3005"
|
run = "docusaurus start --port 3005"
|
||||||
|
|
||||||
[tasks.build]
|
[tasks.build]
|
||||||
env._.path = "./node_modules/.bin"
|
env._.path = "./node_modules/.bin"
|
||||||
@@ -28,4 +28,4 @@ run = "prettier --write ."
|
|||||||
run = "wrangler pages deploy build --project-name=${PROJECT_NAME} --branch=${BRANCH_NAME}"
|
run = "wrangler pages deploy build --project-name=${PROJECT_NAME} --branch=${BRANCH_NAME}"
|
||||||
|
|
||||||
[tools]
|
[tools]
|
||||||
wrangler = "4.66.0"
|
wrangler = "4.100.0"
|
||||||
|
|||||||
Vendored
+4
@@ -1,4 +1,8 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"label": "v3.0.0-rc.2",
|
||||||
|
"url": "https://docs.v3.0.0-rc.2.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v2.7.5",
|
"label": "v2.7.5",
|
||||||
"url": "https://docs.v2.7.5.archive.immich.app"
|
"url": "https://docs.v2.7.5.archive.immich.app"
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
FROM node:24.1.0-alpine3.20@sha256:8fe019e0d57dbdce5f5c27c0b63d2775cf34b00e3755a7dea969802d7e0c2b25
|
|
||||||
RUN corepack enable
|
|
||||||
ADD package.json *.ts ./
|
|
||||||
RUN pnpm install
|
|
||||||
EXPOSE 2286
|
|
||||||
CMD ["pnpm", "run", "start"]
|
|
||||||
@@ -83,9 +83,7 @@ volumes:
|
|||||||
model_cache:
|
model_cache:
|
||||||
prometheus_data:
|
prometheus_data:
|
||||||
grafana_data:
|
grafana_data:
|
||||||
pnpm_cache:
|
build_cache:
|
||||||
pnpm_store_server:
|
|
||||||
pnpm_store_web:
|
|
||||||
server_node_modules:
|
server_node_modules:
|
||||||
web_node_modules:
|
web_node_modules:
|
||||||
github_node_modules:
|
github_node_modules:
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ services:
|
|||||||
e2e-auth-server:
|
e2e-auth-server:
|
||||||
container_name: immich-e2e-auth-server
|
container_name: immich-e2e-auth-server
|
||||||
build:
|
build:
|
||||||
context: ../e2e-auth-server
|
context: ../
|
||||||
|
dockerfile: packages/e2e-auth-server/Dockerfile
|
||||||
ports:
|
ports:
|
||||||
- 2286:2286
|
- 2286:2286
|
||||||
|
|
||||||
@@ -44,7 +45,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich-e2e-redis
|
container_name: immich-e2e-redis
|
||||||
image: docker.io/valkey/valkey:9@sha256:3b55fbaa0cd93cf0d9d961f405e4dfcc70efe325e2d84da207a0a8e6d8fde4f9
|
image: docker.io/valkey/valkey:9@sha256:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
|
|
||||||
|
|||||||
+16
-1
@@ -1,11 +1,21 @@
|
|||||||
[tasks.install]
|
[tasks.install]
|
||||||
run = "pnpm install --filter immich-e2e --frozen-lockfile"
|
run = "pnpm install --filter immich-e2e --frozen-lockfile"
|
||||||
|
|
||||||
|
[tasks.build]
|
||||||
|
dir = "{{ config_root }}"
|
||||||
|
run = "docker compose build"
|
||||||
|
|
||||||
[tasks.test]
|
[tasks.test]
|
||||||
|
depends = ["//e2e:build", "//e2e:ci-setup"]
|
||||||
env._.path = "./node_modules/.bin"
|
env._.path = "./node_modules/.bin"
|
||||||
run = "vitest --run"
|
run = "vitest --run"
|
||||||
|
|
||||||
|
[tasks.playwright-install]
|
||||||
|
env._.path = "./node_modules/.bin"
|
||||||
|
run = "playwright install"
|
||||||
|
|
||||||
[tasks."test-web"]
|
[tasks."test-web"]
|
||||||
|
depends = ["//e2e:build", "//e2e:ci-setup", "//e2e:playwright-install"]
|
||||||
env._.path = "./node_modules/.bin"
|
env._.path = "./node_modules/.bin"
|
||||||
run = "playwright test"
|
run = "playwright test"
|
||||||
|
|
||||||
@@ -30,7 +40,12 @@ run = "tsc --noEmit"
|
|||||||
|
|
||||||
|
|
||||||
[tasks.ci-setup]
|
[tasks.ci-setup]
|
||||||
depends = ["//:sdk:install", "//:sdk:build", "//cli:install", "//cli:build"]
|
depends = [
|
||||||
|
"//:sdk:install",
|
||||||
|
"//:sdk:build",
|
||||||
|
"//packages/cli:install",
|
||||||
|
"//packages/cli:build",
|
||||||
|
]
|
||||||
run = { task = ":install" }
|
run = { task = ":install" }
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "2.7.5",
|
"version": "3.0.0-rc.2",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
"@playwright/test": "^1.44.1",
|
"@playwright/test": "^1.44.1",
|
||||||
"@socket.io/component-emitter": "^3.1.2",
|
"@socket.io/component-emitter": "^3.1.2",
|
||||||
"@types/luxon": "^3.4.2",
|
"@types/luxon": "^3.4.2",
|
||||||
"@types/node": "^24.12.2",
|
"@types/node": "^24.13.2",
|
||||||
"@types/pg": "^8.15.1",
|
"@types/pg": "^8.15.1",
|
||||||
"@types/pngjs": "^6.0.4",
|
"@types/pngjs": "^6.0.4",
|
||||||
"@types/supertest": "^7.0.0",
|
"@types/supertest": "^7.0.0",
|
||||||
@@ -54,7 +54,6 @@
|
|||||||
"typescript": "^6.0.0",
|
"typescript": "^6.0.0",
|
||||||
"typescript-eslint": "^8.28.0",
|
"typescript-eslint": "^8.28.0",
|
||||||
"utimes": "^5.2.1",
|
"utimes": "^5.2.1",
|
||||||
"vite-tsconfig-paths": "^6.1.1",
|
|
||||||
"vitest": "^4.0.0"
|
"vitest": "^4.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { LoginResponseDto, ManualJobName } from '@immich/sdk';
|
|||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { app, utils } from 'src/utils';
|
import { app, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
describe('/admin/database-backups', () => {
|
describe('/admin/database-backups', () => {
|
||||||
let cookie: string | undefined;
|
let cookie: string | undefined;
|
||||||
@@ -13,6 +13,9 @@ describe('/admin/database-backups', () => {
|
|||||||
admin = await utils.adminSetup({
|
admin = await utils.adminSetup({
|
||||||
onboarding: false,
|
onboarding: false,
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
await utils.resetBackups(admin.accessToken);
|
await utils.resetBackups(admin.accessToken);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ describe('/admin/maintenance', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
interval: 500,
|
interval: 500,
|
||||||
timeout: 10_000,
|
timeout: 60_000,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.toBeTruthy();
|
.toBeTruthy();
|
||||||
@@ -190,7 +190,7 @@ describe('/admin/maintenance', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
interval: 500,
|
interval: 500,
|
||||||
timeout: 10_000,
|
timeout: 60_000,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.toBeFalsy();
|
.toBeFalsy();
|
||||||
|
|||||||
@@ -504,13 +504,14 @@ describe('/albums', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not be able to share album with owner', async () => {
|
it('should deduplicate owner from albumUsers on create', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.post('/albums')
|
.post('/albums')
|
||||||
.send({ albumName: 'New album', albumUsers: [{ role: AlbumUserRole.Editor, userId: user1.userId }] })
|
.send({ albumName: 'New album', albumUsers: [{ role: AlbumUserRole.Editor, userId: user1.userId }] })
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
expect(status).toBe(400);
|
expect(status).toBe(201);
|
||||||
expect(body).toEqual(errorDto.badRequest('Cannot share album with owner'));
|
expect(body.albumUsers).toHaveLength(1);
|
||||||
|
expect(body.albumUsers[0]).toMatchObject({ role: AlbumUserRole.Owner, user: { id: user1.userId } });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -729,8 +730,8 @@ describe('/albums', () => {
|
|||||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
.send({ albumUsers: [{ userId: user1.userId, role: AlbumUserRole.Editor }] });
|
.send({ albumUsers: [{ userId: user1.userId, role: AlbumUserRole.Editor }] });
|
||||||
|
|
||||||
expect(status).toBe(400);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual(errorDto.badRequest('User already added'));
|
expect(body.albumUsers.length).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not be able to add existing user to shared album', async () => {
|
it('should not be able to add existing user to shared album', async () => {
|
||||||
@@ -744,8 +745,8 @@ describe('/albums', () => {
|
|||||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
.send({ albumUsers: [{ userId: user2.userId, role: AlbumUserRole.Editor }] });
|
.send({ albumUsers: [{ userId: user2.userId, role: AlbumUserRole.Editor }] });
|
||||||
|
|
||||||
expect(status).toBe(400);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual(errorDto.badRequest('User already added'));
|
expect(body.albumUsers.length).toEqual(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
getMyUser,
|
getMyUser,
|
||||||
LoginResponseDto,
|
LoginResponseDto,
|
||||||
SharedLinkType,
|
SharedLinkType,
|
||||||
updateConfig,
|
|
||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
import { exiftool } from 'exiftool-vendored';
|
import { exiftool } from 'exiftool-vendored';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
@@ -24,7 +23,6 @@ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|||||||
|
|
||||||
const locationAssetFilepath = `${testAssetDir}/metadata/gps-position/thompson-springs.jpg`;
|
const locationAssetFilepath = `${testAssetDir}/metadata/gps-position/thompson-springs.jpg`;
|
||||||
const ratingAssetFilepath = `${testAssetDir}/metadata/rating/mongolels.jpg`;
|
const ratingAssetFilepath = `${testAssetDir}/metadata/rating/mongolels.jpg`;
|
||||||
const facesAssetDir = `${testAssetDir}/metadata/faces`;
|
|
||||||
|
|
||||||
const readTags = async (bytes: Buffer, filename: string) => {
|
const readTags = async (bytes: Buffer, filename: string) => {
|
||||||
const filepath = join(tempDir, filename);
|
const filepath = join(tempDir, filename);
|
||||||
@@ -185,78 +183,6 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('faces', () => {
|
|
||||||
const metadataFaceTests = [
|
|
||||||
{
|
|
||||||
description: 'without orientation',
|
|
||||||
filename: 'portrait.jpg',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'adjusting face regions to orientation',
|
|
||||||
filename: 'portrait-orientation-6.jpg',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
// should produce same resulting face region coordinates for any orientation
|
|
||||||
const expectedFaces = [
|
|
||||||
{
|
|
||||||
name: 'Marie Curie',
|
|
||||||
birthDate: null,
|
|
||||||
isHidden: false,
|
|
||||||
faces: [
|
|
||||||
{
|
|
||||||
imageHeight: 700,
|
|
||||||
imageWidth: 840,
|
|
||||||
boundingBoxX1: 261,
|
|
||||||
boundingBoxX2: 356,
|
|
||||||
boundingBoxY1: 146,
|
|
||||||
boundingBoxY2: 284,
|
|
||||||
sourceType: 'exif',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Pierre Curie',
|
|
||||||
birthDate: null,
|
|
||||||
isHidden: false,
|
|
||||||
faces: [
|
|
||||||
{
|
|
||||||
imageHeight: 700,
|
|
||||||
imageWidth: 840,
|
|
||||||
boundingBoxX1: 536,
|
|
||||||
boundingBoxX2: 618,
|
|
||||||
boundingBoxY1: 83,
|
|
||||||
boundingBoxY2: 252,
|
|
||||||
sourceType: 'exif',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
it.each(metadataFaceTests)('should get the asset faces from $filename $description', async ({ filename }) => {
|
|
||||||
const config = await utils.getSystemConfig(admin.accessToken);
|
|
||||||
config.metadata.faces.import = true;
|
|
||||||
await updateConfig({ systemConfigDto: config }, { headers: asBearerAuth(admin.accessToken) });
|
|
||||||
|
|
||||||
const facesAsset = await utils.createAsset(admin.accessToken, {
|
|
||||||
assetData: {
|
|
||||||
filename,
|
|
||||||
bytes: await readFile(`${facesAssetDir}/${filename}`),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: facesAsset.id });
|
|
||||||
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.get(`/assets/${facesAsset.id}`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
|
||||||
|
|
||||||
expect(status).toBe(200);
|
|
||||||
expect(body.id).toEqual(facesAsset.id);
|
|
||||||
const sortedPeople = body.people.toSorted((a: any, b: any) => a.name.localeCompare(b.name));
|
|
||||||
expect(sortedPeople).toMatchObject(expectedFaces);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should work with a shared link', async () => {
|
it('should work with a shared link', async () => {
|
||||||
const sharedLink = await utils.createSharedLink(user1.accessToken, {
|
const sharedLink = await utils.createSharedLink(user1.accessToken, {
|
||||||
type: SharedLinkType.Individual,
|
type: SharedLinkType.Individual,
|
||||||
|
|||||||
@@ -0,0 +1,669 @@
|
|||||||
|
import {
|
||||||
|
AssetMediaResponseDto,
|
||||||
|
IntegrityReportResponseDto,
|
||||||
|
LoginResponseDto,
|
||||||
|
ManualJobName,
|
||||||
|
QueueCommand,
|
||||||
|
QueueName,
|
||||||
|
} from '@immich/sdk';
|
||||||
|
import { readFile } from 'node:fs/promises';
|
||||||
|
import { app, testAssetDir, utils } from 'src/utils';
|
||||||
|
import request from 'supertest';
|
||||||
|
import { afterEach, beforeAll, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
const assetFilepath = `${testAssetDir}/metadata/gps-position/thompson-springs.jpg`;
|
||||||
|
const asset1Filepath = `${testAssetDir}/albums/nature/el_torcal_rocks.jpg`;
|
||||||
|
const asset2Filepath = `${testAssetDir}/albums/nature/wood_anemones.jpg`;
|
||||||
|
|
||||||
|
describe('/admin/integrity', () => {
|
||||||
|
let admin: LoginResponseDto;
|
||||||
|
let asset: AssetMediaResponseDto;
|
||||||
|
|
||||||
|
let user1: LoginResponseDto;
|
||||||
|
let asset1: AssetMediaResponseDto;
|
||||||
|
|
||||||
|
let user2: LoginResponseDto;
|
||||||
|
let asset2: AssetMediaResponseDto;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await utils.resetDatabase();
|
||||||
|
admin = await utils.adminSetup();
|
||||||
|
|
||||||
|
user1 = await utils.userSetup(admin.accessToken, {
|
||||||
|
email: '1@example.com',
|
||||||
|
name: '1',
|
||||||
|
password: '1',
|
||||||
|
});
|
||||||
|
|
||||||
|
user2 = await utils.userSetup(admin.accessToken, {
|
||||||
|
email: '2@example.com',
|
||||||
|
name: '2',
|
||||||
|
password: '2',
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const queue of Object.values(QueueName)) {
|
||||||
|
if (queue === QueueName.IntegrityCheck) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
await utils.queueCommand(admin.accessToken, queue, {
|
||||||
|
command: QueueCommand.Pause,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
asset = await utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: {
|
||||||
|
filename: 'asset.jpg',
|
||||||
|
bytes: await readFile(assetFilepath),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
asset1 = await utils.createAsset(user1.accessToken, {
|
||||||
|
assetData: {
|
||||||
|
filename: 'asset.jpg',
|
||||||
|
bytes: await readFile(asset1Filepath),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
asset2 = await utils.createAsset(user2.accessToken, {
|
||||||
|
assetData: {
|
||||||
|
filename: 'asset.jpg',
|
||||||
|
bytes: await readFile(asset2Filepath),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.mkFolder('/data/bak');
|
||||||
|
await utils.copyFolder(`/data/upload/${admin.userId}`, `/data/bak/${admin.userId}`);
|
||||||
|
|
||||||
|
for (const queue of Object.values(QueueName)) {
|
||||||
|
if (queue === QueueName.IntegrityCheck) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
await utils.queueCommand(admin.accessToken, queue, {
|
||||||
|
command: QueueCommand.Empty,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.queueCommand(admin.accessToken, queue, {
|
||||||
|
command: QueueCommand.Resume,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await utils.deleteFolder(`/data/upload/${admin.userId}`);
|
||||||
|
await utils.copyFolder(`/data/bak/${admin.userId}`, `/data/upload/${admin.userId}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('POST /summary (& jobs)', async () => {
|
||||||
|
it.sequential('reports no issues', async () => {
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityUntrackedFiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityMissingFiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityChecksumMismatch,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityUntrackedFilesDeleteAll,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/admin/integrity/summary')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toEqual({
|
||||||
|
missing_file: 0,
|
||||||
|
untracked_file: 0,
|
||||||
|
checksum_mismatch: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it.sequential('should detect an untracked file (job: check untracked files)', async () => {
|
||||||
|
await utils.putTextFile('untracked', `/data/upload/${admin.userId}/untracked1.png`);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityUntrackedFiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/admin/integrity/summary')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
untracked_file: 1,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.sequential('should detect outdated untracked file reports (job: refresh untracked files)', async () => {
|
||||||
|
// these should not be detected:
|
||||||
|
await utils.putTextFile('untracked', `/data/upload/${admin.userId}/untracked2.png`);
|
||||||
|
await utils.putTextFile('untracked', `/data/upload/${admin.userId}/untracked3.png`);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityUntrackedFilesRefresh,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/admin/integrity/summary')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
untracked_file: 0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.sequential('should delete untracked files (job: delete all untracked file reports)', async () => {
|
||||||
|
await utils.putTextFile('untracked', `/data/upload/${admin.userId}/untracked1.png`);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityUntrackedFiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityUntrackedFilesDeleteAll,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/admin/integrity/summary')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
untracked_file: 0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.sequential('should detect a missing file and not a checksum mismatch (job: check missing files)', async () => {
|
||||||
|
await utils.deleteFolder(`/data/upload/${admin.userId}`);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityMissingFiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/admin/integrity/summary')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
missing_file: 1,
|
||||||
|
checksum_mismatch: 0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.sequential('should detect outdated missing file reports (job: refresh missing files)', async () => {
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityMissingFilesRefresh,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/admin/integrity/summary')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
missing_file: 0,
|
||||||
|
checksum_mismatch: 0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.sequential('should delete assets with missing files (job: delete all missing file reports)', async () => {
|
||||||
|
await utils.deleteFolder(`/data/upload/${user1.userId}`);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityMissingFiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status: listStatus, body: listBody } = await request(app)
|
||||||
|
.get('/admin/integrity/summary')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(listStatus).toBe(200);
|
||||||
|
expect(listBody).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
missing_file: 1,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityMissingFilesDeleteAll,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/admin/integrity/summary')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
missing_file: 0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(utils.getAssetInfo(user1.accessToken, asset1.id)).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
isTrashed: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.sequential('should detect a checksum mismatch (job: check file checksums)', async () => {
|
||||||
|
await utils.truncateFolder(`/data/upload/${admin.userId}`);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityChecksumMismatch,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/admin/integrity/summary')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
checksum_mismatch: 1,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.sequential('should detect outdated checksum mismatch reports (job: refresh file checksums)', async () => {
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityChecksumMismatchRefresh,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/admin/integrity/summary')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
checksum_mismatch: 0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.sequential(
|
||||||
|
'should delete assets with mismatched checksum (job: delete all checksum mismatch reports)',
|
||||||
|
async () => {
|
||||||
|
await utils.truncateFolder(`/data/upload/${user2.userId}`);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityChecksumMismatch,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status: listStatus, body: listBody } = await request(app)
|
||||||
|
.get('/admin/integrity/summary')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(listStatus).toBe(200);
|
||||||
|
expect(listBody).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
checksum_mismatch: 1,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityChecksumMismatchDeleteAll,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/admin/integrity/summary')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
checksum_mismatch: 0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(utils.getAssetInfo(user2.accessToken, asset2.id)).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
isTrashed: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('POST /report', async () => {
|
||||||
|
it.sequential('reports untracked files', async () => {
|
||||||
|
await utils.putTextFile('untracked', `/data/upload/${admin.userId}/untracked1.png`);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityUntrackedFiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/admin/integrity/report?type=untracked_file')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toEqual({
|
||||||
|
nextCursor: undefined,
|
||||||
|
items: expect.arrayContaining([
|
||||||
|
{
|
||||||
|
id: expect.any(String),
|
||||||
|
type: 'untracked_file',
|
||||||
|
path: `/data/upload/${admin.userId}/untracked1.png`,
|
||||||
|
assetId: null,
|
||||||
|
fileAssetId: null,
|
||||||
|
createdAt: expect.any(String),
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it.sequential('reports missing files', async () => {
|
||||||
|
await utils.deleteFolder(`/data/upload/${admin.userId}`);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityMissingFiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/admin/integrity/report?type=missing_file')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toEqual({
|
||||||
|
nextCursor: undefined,
|
||||||
|
items: expect.arrayContaining([
|
||||||
|
{
|
||||||
|
id: expect.any(String),
|
||||||
|
type: 'missing_file',
|
||||||
|
path: expect.any(String),
|
||||||
|
assetId: asset.id,
|
||||||
|
fileAssetId: null,
|
||||||
|
createdAt: expect.any(String),
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it.sequential('reports checksum mismatched files', async () => {
|
||||||
|
await utils.truncateFolder(`/data/upload/${admin.userId}`);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityChecksumMismatch,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/admin/integrity/report?type=checksum_mismatch')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toEqual({
|
||||||
|
nextCursor: undefined,
|
||||||
|
items: expect.arrayContaining([
|
||||||
|
{
|
||||||
|
id: expect.any(String),
|
||||||
|
type: 'checksum_mismatch',
|
||||||
|
path: expect.any(String),
|
||||||
|
assetId: asset.id,
|
||||||
|
fileAssetId: null,
|
||||||
|
createdAt: expect.any(String),
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('DELETE /report/:id', async () => {
|
||||||
|
it.sequential('delete untracked files', async () => {
|
||||||
|
await utils.putTextFile('untracked', `/data/upload/${admin.userId}/untracked1.png`);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityUntrackedFiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status: listStatus, body: listBody } = await request(app)
|
||||||
|
.get('/admin/integrity/report?type=untracked_file')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(listStatus).toBe(200);
|
||||||
|
|
||||||
|
const report = (listBody as IntegrityReportResponseDto).items.find(
|
||||||
|
(item) => item.path === `/data/upload/${admin.userId}/untracked1.png`,
|
||||||
|
)!;
|
||||||
|
|
||||||
|
const { status } = await request(app)
|
||||||
|
.delete(`/admin/integrity/report/${report.id}`)
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityUntrackedFiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status: listStatus2, body: listBody2 } = await request(app)
|
||||||
|
.get('/admin/integrity/report?type=untracked_file')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(listStatus2).toBe(200);
|
||||||
|
expect(listBody2).not.toBe(
|
||||||
|
expect.objectContaining({
|
||||||
|
items: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: report.id,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.sequential('delete assets missing files', async () => {
|
||||||
|
await utils.deleteFolder(`/data/upload/${admin.userId}`);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityMissingFiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status: listStatus, body: listBody } = await request(app)
|
||||||
|
.get('/admin/integrity/report?type=missing_file')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(listStatus).toBe(200);
|
||||||
|
expect(listBody.items.length).toBe(1);
|
||||||
|
|
||||||
|
const report = (listBody as IntegrityReportResponseDto).items[0];
|
||||||
|
|
||||||
|
const { status } = await request(app)
|
||||||
|
.delete(`/admin/integrity/report/${report.id}`)
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityMissingFiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status: listStatus2, body: listBody2 } = await request(app)
|
||||||
|
.get('/admin/integrity/report?type=missing_file')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(listStatus2).toBe(200);
|
||||||
|
expect(listBody2.items.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.sequential('delete assets with failing checksum', async () => {
|
||||||
|
await utils.truncateFolder(`/data/upload/${admin.userId}`);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityChecksumMismatch,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status: listStatus, body: listBody } = await request(app)
|
||||||
|
.get('/admin/integrity/report?type=checksum_mismatch')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(listStatus).toBe(200);
|
||||||
|
expect(listBody.items.length).toBe(1);
|
||||||
|
|
||||||
|
const report = (listBody as IntegrityReportResponseDto).items[0];
|
||||||
|
|
||||||
|
const { status } = await request(app)
|
||||||
|
.delete(`/admin/integrity/report/${report.id}`)
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityChecksumMismatch,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status: listStatus2, body: listBody2 } = await request(app)
|
||||||
|
.get('/admin/integrity/report?type=checksum_mismatch')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(listStatus2).toBe(200);
|
||||||
|
expect(listBody2.items.length).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GET /report/:type/csv', () => {
|
||||||
|
it.sequential('exports untracked files as csv', async () => {
|
||||||
|
await utils.putTextFile('untracked', `/data/upload/${admin.userId}/untracked1.png`);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityUntrackedFiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { status, headers, text } = await request(app)
|
||||||
|
.get('/admin/integrity/report/untracked_file/csv')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(headers['content-type']).toContain('text/csv');
|
||||||
|
expect(headers['content-disposition']).toContain('.csv');
|
||||||
|
expect(text).toContain('id,type,assetId,fileAssetId,path');
|
||||||
|
expect(text).toContain(`untracked_file`);
|
||||||
|
expect(text).toContain(`/data/upload/${admin.userId}/untracked1.png`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GET /report/:id/file', () => {
|
||||||
|
it.sequential('downloads untracked file', async () => {
|
||||||
|
await utils.putTextFile('untracked-content', `/data/upload/${admin.userId}/untracked1.png`);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityUntrackedFiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
const { body: listBody } = await request(app)
|
||||||
|
.get('/admin/integrity/report?type=untracked_file')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
const report = (listBody as IntegrityReportResponseDto).items.find(
|
||||||
|
(item) => item.path === `/data/upload/${admin.userId}/untracked1.png`,
|
||||||
|
)!;
|
||||||
|
|
||||||
|
const { status, headers, body } = await request(app)
|
||||||
|
.get(`/admin/integrity/report/${report.id}/file`)
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.buffer(true)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(headers['content-type']).toContain('application/octet-stream');
|
||||||
|
expect(body.toString()).toBe('untracked-content');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -2,6 +2,7 @@ import { AssetVisibility, LoginResponseDto } from '@immich/sdk';
|
|||||||
import { readFile } from 'node:fs/promises';
|
import { readFile } from 'node:fs/promises';
|
||||||
import { basename, join } from 'node:path';
|
import { basename, join } from 'node:path';
|
||||||
import { Socket } from 'socket.io-client';
|
import { Socket } from 'socket.io-client';
|
||||||
|
import { createUserDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { app, testAssetDir, utils } from 'src/utils';
|
import { app, testAssetDir, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
@@ -9,28 +10,48 @@ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|||||||
|
|
||||||
describe('/map', () => {
|
describe('/map', () => {
|
||||||
let websocket: Socket;
|
let websocket: Socket;
|
||||||
|
let partnerWebsocket: Socket;
|
||||||
let admin: LoginResponseDto;
|
let admin: LoginResponseDto;
|
||||||
|
let partner: LoginResponseDto;
|
||||||
|
let partnerArchivedAssetId: string;
|
||||||
|
let adminArchivedAssetId: string;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await utils.resetDatabase();
|
await utils.resetDatabase();
|
||||||
admin = await utils.adminSetup({ onboarding: false });
|
admin = await utils.adminSetup({ onboarding: false });
|
||||||
|
partner = await utils.userSetup(admin.accessToken, createUserDto.user1);
|
||||||
|
|
||||||
websocket = await utils.connectWebsocket(admin.accessToken);
|
websocket = await utils.connectWebsocket(admin.accessToken);
|
||||||
|
partnerWebsocket = await utils.connectWebsocket(partner.accessToken);
|
||||||
|
|
||||||
const files = ['formats/heic/IMG_2682.heic', 'metadata/gps-position/thompson-springs.jpg'];
|
const adminFiles = ['formats/heic/IMG_2682.heic', 'metadata/gps-position/thompson-springs.jpg'];
|
||||||
|
const adminArchivedFile = 'metadata/dates/datetimeoriginal-gps.jpg';
|
||||||
|
const partnerFile = 'metadata/gps-position/thompson-springs.jpg';
|
||||||
utils.resetEvents();
|
utils.resetEvents();
|
||||||
const uploadFile = async (input: string) => {
|
const uploadFile = async (accessToken: string, input: string) => {
|
||||||
const filepath = join(testAssetDir, input);
|
const filepath = join(testAssetDir, input);
|
||||||
const { id } = await utils.createAsset(admin.accessToken, {
|
const { id } = await utils.createAsset(accessToken, {
|
||||||
assetData: { bytes: await readFile(filepath), filename: basename(filepath) },
|
assetData: { bytes: await readFile(filepath), filename: basename(filepath) },
|
||||||
});
|
});
|
||||||
await utils.waitForWebsocketEvent({ event: 'assetUpload', id });
|
await utils.waitForWebsocketEvent({ event: 'assetUpload', id });
|
||||||
|
return id;
|
||||||
};
|
};
|
||||||
await Promise.all(files.map((f) => uploadFile(f)));
|
await Promise.all(adminFiles.map((f) => uploadFile(admin.accessToken, f)));
|
||||||
|
[adminArchivedAssetId, partnerArchivedAssetId] = await Promise.all([
|
||||||
|
uploadFile(admin.accessToken, adminArchivedFile),
|
||||||
|
uploadFile(partner.accessToken, partnerFile),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
utils.archiveAssets(admin.accessToken, [adminArchivedAssetId]),
|
||||||
|
utils.archiveAssets(partner.accessToken, [partnerArchivedAssetId]),
|
||||||
|
utils.createPartner(partner.accessToken, admin.userId),
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
utils.disconnectWebsocket(websocket);
|
utils.disconnectWebsocket(websocket);
|
||||||
|
utils.disconnectWebsocket(partnerWebsocket);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /map/markers', () => {
|
describe('GET /map/markers', () => {
|
||||||
@@ -40,7 +61,6 @@ describe('/map', () => {
|
|||||||
expect(body).toEqual(errorDto.unauthorized);
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO archive one of these assets
|
|
||||||
it('should get map markers for all non-archived assets', async () => {
|
it('should get map markers for all non-archived assets', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/map/markers')
|
.get('/map/markers')
|
||||||
@@ -69,7 +89,28 @@ describe('/map', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO archive one of these assets
|
it('should not expose partner archived asset locations', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/map/markers')
|
||||||
|
.query({ withPartners: true, isArchived: true })
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
const ids = body.map((m: { id: string }) => m.id);
|
||||||
|
expect(ids).not.toContain(partnerArchivedAssetId);
|
||||||
|
expect(ids).toContain(adminArchivedAssetId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should include own archived asset locations', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/map/markers')
|
||||||
|
.query({ isArchived: true })
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.map((m: { id: string }) => m.id)).toContain(adminArchivedAssetId);
|
||||||
|
});
|
||||||
|
|
||||||
it('should get all map markers', async () => {
|
it('should get all map markers', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/map/markers')
|
.get('/map/markers')
|
||||||
|
|||||||
@@ -259,17 +259,6 @@ describe('/search', () => {
|
|||||||
assets: [assetHeic],
|
assets: [assetHeic],
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
should: "should search city ('')",
|
|
||||||
deferred: () => ({
|
|
||||||
dto: {
|
|
||||||
city: '',
|
|
||||||
visibility: AssetVisibility.Timeline,
|
|
||||||
includeNull: true,
|
|
||||||
},
|
|
||||||
assets: [assetLast],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
should: 'should search city (null)',
|
should: 'should search city (null)',
|
||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
@@ -291,18 +280,6 @@ describe('/search', () => {
|
|||||||
assets: [assetDensity],
|
assets: [assetDensity],
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
should: "should search state ('')",
|
|
||||||
deferred: () => ({
|
|
||||||
dto: {
|
|
||||||
state: '',
|
|
||||||
visibility: AssetVisibility.Timeline,
|
|
||||||
withExif: true,
|
|
||||||
includeNull: true,
|
|
||||||
},
|
|
||||||
assets: [assetLast, assetNotocactus],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
should: 'should search state (null)',
|
should: 'should search state (null)',
|
||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
@@ -324,17 +301,6 @@ describe('/search', () => {
|
|||||||
assets: [assetFalcon],
|
assets: [assetFalcon],
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
should: "should search country ('')",
|
|
||||||
deferred: () => ({
|
|
||||||
dto: {
|
|
||||||
country: '',
|
|
||||||
visibility: AssetVisibility.Timeline,
|
|
||||||
includeNull: true,
|
|
||||||
},
|
|
||||||
assets: [assetLast],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
should: 'should search country (null)',
|
should: 'should search country (null)',
|
||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
@@ -441,7 +407,18 @@ describe('/search', () => {
|
|||||||
.get('/search/explore')
|
.get('/search/explore')
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual([{ fieldName: 'exifInfo.city', items: [] }]);
|
expect(Array.isArray(body)).toBe(true);
|
||||||
|
expect(body).toEqual(expect.arrayContaining([{ fieldName: 'exifInfo.city', items: [] }]));
|
||||||
|
expect(body).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
{
|
||||||
|
fieldName: 'createdAt',
|
||||||
|
items: expect.arrayContaining([
|
||||||
|
expect.objectContaining({ data: expect.objectContaining({ id: assetLast.id }) }),
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ describe('/server', () => {
|
|||||||
major: expect.any(Number),
|
major: expect.any(Number),
|
||||||
minor: expect.any(Number),
|
minor: expect.any(Number),
|
||||||
patch: expect.any(Number),
|
patch: expect.any(Number),
|
||||||
|
prerelease: expect.anything(),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -115,6 +116,7 @@ describe('/server', () => {
|
|||||||
oauthAutoLaunch: false,
|
oauthAutoLaunch: false,
|
||||||
ocr: false,
|
ocr: false,
|
||||||
passwordLogin: true,
|
passwordLogin: true,
|
||||||
|
realtimeTranscoding: false,
|
||||||
search: true,
|
search: true,
|
||||||
sidecar: true,
|
sidecar: true,
|
||||||
trash: true,
|
trash: true,
|
||||||
@@ -139,6 +141,7 @@ describe('/server', () => {
|
|||||||
maintenanceMode: false,
|
maintenanceMode: false,
|
||||||
mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json',
|
mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json',
|
||||||
mapLightStyleUrl: 'https://tiles.immich.cloud/v1/style/light.json',
|
mapLightStyleUrl: 'https://tiles.immich.cloud/v1/style/light.json',
|
||||||
|
minFaces: 3,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,18 +21,18 @@ describe('/system-config', () => {
|
|||||||
const response1 = await request(app)
|
const response1 = await request(app)
|
||||||
.put('/system-config')
|
.put('/system-config')
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
.send({ ...config, newVersionCheck: { enabled: false } });
|
.send({ ...config, newVersionCheck: { enabled: false, channel: 'stable' } });
|
||||||
|
|
||||||
expect(response1.status).toBe(200);
|
expect(response1.status).toBe(200);
|
||||||
expect(response1.body).toEqual({ ...config, newVersionCheck: { enabled: false } });
|
expect(response1.body).toEqual({ ...config, newVersionCheck: { enabled: false, channel: 'stable' } });
|
||||||
|
|
||||||
const response2 = await request(app)
|
const response2 = await request(app)
|
||||||
.put('/system-config')
|
.put('/system-config')
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
.send({ ...config, newVersionCheck: { enabled: true } });
|
.send({ ...config, newVersionCheck: { enabled: true, channel: 'stable' } });
|
||||||
|
|
||||||
expect(response2.status).toBe(200);
|
expect(response2.status).toBe(200);
|
||||||
expect(response2.body).toEqual({ ...config, newVersionCheck: { enabled: true } });
|
expect(response2.body).toEqual({ ...config, newVersionCheck: { enabled: true, channel: 'stable' } });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reject an invalid config entry', async () => {
|
it('should reject an invalid config entry', async () => {
|
||||||
|
|||||||
@@ -230,6 +230,21 @@ describe('/users', () => {
|
|||||||
const after = await getMyPreferences({ headers: asBearerAuth(admin.accessToken) });
|
const after = await getMyPreferences({ headers: asBearerAuth(admin.accessToken) });
|
||||||
expect(after).toMatchObject({ download: { includeEmbeddedVideos: true } });
|
expect(after).toMatchObject({ download: { includeEmbeddedVideos: true } });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should update minimum face count to display people', async () => {
|
||||||
|
const before = await getMyPreferences({ headers: asBearerAuth(admin.accessToken) });
|
||||||
|
expect(before).toMatchObject({ people: { minimumFaces: 3 } });
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.put('/users/me/preferences')
|
||||||
|
.send({ people: { minimumFaces: 2 } })
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toMatchObject({ people: { minimumFaces: 2 } });
|
||||||
|
|
||||||
|
const after = await getMyPreferences({ headers: asBearerAuth(admin.accessToken) });
|
||||||
|
expect(after).toMatchObject({ people: { minimumFaces: 2 } });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /users/:id', () => {
|
describe('GET /users/:id', () => {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { readFileSync } from 'node:fs';
|
|||||||
import { immichCli } from 'src/utils';
|
import { immichCli } from 'src/utils';
|
||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
const pkg = JSON.parse(readFileSync('../cli/package.json', 'utf8'));
|
const pkg = JSON.parse(readFileSync('../packages/cli/package.json', 'utf8'));
|
||||||
|
|
||||||
describe(`immich --version`, () => {
|
describe(`immich --version`, () => {
|
||||||
describe('immich --version', () => {
|
describe('immich --version', () => {
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { LoginResponseDto, ManualJobName, QueueName } from '@immich/sdk';
|
||||||
|
import { expect, test } from '@playwright/test';
|
||||||
|
import { utils } from 'src/utils';
|
||||||
|
|
||||||
|
test.describe.configure({ mode: 'serial' });
|
||||||
|
|
||||||
|
test.describe.skip('Integrity', () => {
|
||||||
|
let admin: LoginResponseDto;
|
||||||
|
|
||||||
|
test.beforeAll(async () => {
|
||||||
|
utils.initSdk();
|
||||||
|
await utils.resetDatabase();
|
||||||
|
admin = await utils.adminSetup();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('run integrity jobs to update stats', async ({ context, page }) => {
|
||||||
|
await utils.setAuthCookies(context, admin.accessToken);
|
||||||
|
|
||||||
|
await utils.createJob(admin.accessToken, {
|
||||||
|
name: ManualJobName.IntegrityUntrackedFiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
|
await page.goto('/admin/maintenance');
|
||||||
|
|
||||||
|
const count = page.getByText('Untracked Files').locator('..').locator('..').locator('div').nth(1);
|
||||||
|
|
||||||
|
const previousCount = Number.parseInt((await count.textContent()) ?? '');
|
||||||
|
|
||||||
|
await utils.mkFolder(`/data/upload/${admin.userId}`);
|
||||||
|
await utils.putTextFile('untracked', `/data/upload/${admin.userId}/untracked1.png`);
|
||||||
|
|
||||||
|
const checkButton = page.getByText('Integrity Report').locator('..').getByRole('button', { name: 'Check All' });
|
||||||
|
|
||||||
|
await checkButton.click();
|
||||||
|
await expect(checkButton).toBeEnabled();
|
||||||
|
|
||||||
|
await expect(count).toContainText((previousCount + 1).toString());
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -28,6 +28,7 @@ export function toColumnarFormat(assets: MockTimelineAsset[]): TimeBucketAssetRe
|
|||||||
ownerId: [],
|
ownerId: [],
|
||||||
ratio: [],
|
ratio: [],
|
||||||
thumbhash: [],
|
thumbhash: [],
|
||||||
|
createdAt: [],
|
||||||
fileCreatedAt: [],
|
fileCreatedAt: [],
|
||||||
localOffsetHours: [],
|
localOffsetHours: [],
|
||||||
isFavorite: [],
|
isFavorite: [],
|
||||||
@@ -54,8 +55,8 @@ export function toColumnarFormat(assets: MockTimelineAsset[]): TimeBucketAssetRe
|
|||||||
result.duration.push(asset.duration);
|
result.duration.push(asset.duration);
|
||||||
result.projectionType.push(asset.projectionType);
|
result.projectionType.push(asset.projectionType);
|
||||||
result.livePhotoVideoId.push(asset.livePhotoVideoId);
|
result.livePhotoVideoId.push(asset.livePhotoVideoId);
|
||||||
result.city.push(asset.city);
|
result.city?.push(asset.city);
|
||||||
result.country.push(asset.country);
|
result.country?.push(asset.country);
|
||||||
result.visibility.push(asset.visibility);
|
result.visibility.push(asset.visibility);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,7 +339,6 @@ export function toAssetResponseDto(asset: MockTimelineAsset, owner?: UserRespons
|
|||||||
livePhotoVideoId: asset.livePhotoVideoId,
|
livePhotoVideoId: asset.livePhotoVideoId,
|
||||||
tags: [],
|
tags: [],
|
||||||
people: [],
|
people: [],
|
||||||
unassignedFaces: [],
|
|
||||||
stack: asset.stack,
|
stack: asset.stack,
|
||||||
isOffline: false,
|
isOffline: false,
|
||||||
hasMetadata: true,
|
hasMetadata: true,
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ export const createMockStackAsset = (ownerId: string): AssetResponseDto => {
|
|||||||
livePhotoVideoId: null,
|
livePhotoVideoId: null,
|
||||||
tags: [],
|
tags: [],
|
||||||
people: [],
|
people: [],
|
||||||
unassignedFaces: [],
|
|
||||||
stack: undefined,
|
stack: undefined,
|
||||||
isOffline: false,
|
isOffline: false,
|
||||||
hasMetadata: true,
|
hasMetadata: true,
|
||||||
|
|||||||
@@ -536,7 +536,7 @@ test.describe('Timeline', () => {
|
|||||||
force: false,
|
force: false,
|
||||||
ids: [assetToTrash.id],
|
ids: [assetToTrash.id],
|
||||||
});
|
});
|
||||||
await page.locator('#asset-selection-app-bar').getByLabel('Close').click();
|
await page.locator('#control-bar').getByLabel('Close').click();
|
||||||
await page.getByText('Trash', { exact: true }).click();
|
await page.getByText('Trash', { exact: true }).click();
|
||||||
await timelineUtils.waitForTimelineLoad(page);
|
await timelineUtils.waitForTimelineLoad(page);
|
||||||
await thumbnailUtils.expectInViewport(page, assetToTrash.id);
|
await thumbnailUtils.expectInViewport(page, assetToTrash.id);
|
||||||
@@ -676,7 +676,7 @@ test.describe('Timeline', () => {
|
|||||||
ids: [assetToArchive.id],
|
ids: [assetToArchive.id],
|
||||||
});
|
});
|
||||||
await thumbnailUtils.expectThumbnailIsArchive(page, assetToArchive.id);
|
await thumbnailUtils.expectThumbnailIsArchive(page, assetToArchive.id);
|
||||||
await page.locator('#asset-selection-app-bar').getByLabel('Close').click();
|
await page.locator('#control-bar').getByLabel('Close').click();
|
||||||
await page.getByRole('link').getByText('Archive').click();
|
await page.getByRole('link').getByText('Archive').click();
|
||||||
await timelineUtils.waitForTimelineLoad(page);
|
await timelineUtils.waitForTimelineLoad(page);
|
||||||
await thumbnailUtils.expectInViewport(page, assetToArchive.id);
|
await thumbnailUtils.expectInViewport(page, assetToArchive.id);
|
||||||
@@ -823,7 +823,7 @@ test.describe('Timeline', () => {
|
|||||||
});
|
});
|
||||||
// ensure thumbnail still exists and has favorite icon
|
// ensure thumbnail still exists and has favorite icon
|
||||||
await thumbnailUtils.expectThumbnailIsFavorite(page, assetToFavorite.id);
|
await thumbnailUtils.expectThumbnailIsFavorite(page, assetToFavorite.id);
|
||||||
await page.locator('#asset-selection-app-bar').getByLabel('Close').click();
|
await page.locator('#control-bar').getByLabel('Close').click();
|
||||||
await page.getByRole('link').getByText('Favorites').click();
|
await page.getByRole('link').getByText('Favorites').click();
|
||||||
await timelineUtils.waitForTimelineLoad(page);
|
await timelineUtils.waitForTimelineLoad(page);
|
||||||
await pageUtils.goToAsset(page, assetToFavorite.fileCreatedAt);
|
await pageUtils.goToAsset(page, assetToFavorite.fileCreatedAt);
|
||||||
|
|||||||
+49
-4
@@ -90,7 +90,7 @@ export const tempDir = tmpdir();
|
|||||||
export const asBearerAuth = (accessToken: string) => ({ Authorization: `Bearer ${accessToken}` });
|
export const asBearerAuth = (accessToken: string) => ({ Authorization: `Bearer ${accessToken}` });
|
||||||
export const asKeyAuth = (key: string) => ({ 'x-api-key': key });
|
export const asKeyAuth = (key: string) => ({ 'x-api-key': key });
|
||||||
export const immichCli = (args: string[]) =>
|
export const immichCli = (args: string[]) =>
|
||||||
executeCommand('pnpm', ['exec', 'immich', '-d', `/${tempDir}/immich/`, ...args], { cwd: '../cli' }).promise;
|
executeCommand('pnpm', ['exec', 'immich', '-d', `/${tempDir}/immich/`, ...args], { cwd: '../packages/cli' }).promise;
|
||||||
export const dockerExec = (args: string[]) =>
|
export const dockerExec = (args: string[]) =>
|
||||||
executeCommand('docker', ['exec', '-i', 'immich-e2e-server', '/bin/bash', '-c', args.join(' ')]);
|
executeCommand('docker', ['exec', '-i', 'immich-e2e-server', '/bin/bash', '-c', args.join(' ')]);
|
||||||
export const immichAdmin = (args: string[]) => dockerExec([`immich-admin ${args.join(' ')}`]);
|
export const immichAdmin = (args: string[]) => dockerExec([`immich-admin ${args.join(' ')}`]);
|
||||||
@@ -192,6 +192,7 @@ export const utils = {
|
|||||||
'user',
|
'user',
|
||||||
'system_metadata',
|
'system_metadata',
|
||||||
'tag',
|
'tag',
|
||||||
|
'integrity_report',
|
||||||
];
|
];
|
||||||
|
|
||||||
const truncateTables = tables.filter((table) => table !== 'system_metadata');
|
const truncateTables = tables.filter((table) => table !== 'system_metadata');
|
||||||
@@ -559,15 +560,61 @@ export const utils = {
|
|||||||
mkdirSync(`${testAssetDir}/temp`, { recursive: true });
|
mkdirSync(`${testAssetDir}/temp`, { recursive: true });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
putFile(source: string, dest: string) {
|
||||||
|
return executeCommand('docker', ['cp', source, `immich-e2e-server:${dest}`]).promise;
|
||||||
|
},
|
||||||
|
|
||||||
|
async putTextFile(contents: string, dest: string) {
|
||||||
|
const dir = await mkdtemp(join(tmpdir(), 'test-'));
|
||||||
|
const fn = join(dir, 'file');
|
||||||
|
await pipeline(Readable.from(contents), createWriteStream(fn));
|
||||||
|
return executeCommand('docker', ['cp', fn, `immich-e2e-server:${dest}`]).promise;
|
||||||
|
},
|
||||||
|
|
||||||
async move(source: string, dest: string) {
|
async move(source: string, dest: string) {
|
||||||
return executeCommand('docker', ['exec', 'immich-e2e-server', 'mv', source, dest]).promise;
|
return executeCommand('docker', ['exec', 'immich-e2e-server', 'mv', source, dest]).promise;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async copyFolder(source: string, dest: string) {
|
||||||
|
return executeCommand('docker', ['exec', 'immich-e2e-server', 'cp', '-r', source, dest]).promise;
|
||||||
|
},
|
||||||
|
|
||||||
|
async deleteFile(path: string) {
|
||||||
|
return executeCommand('docker', ['exec', 'immich-e2e-server', 'rm', path]).promise;
|
||||||
|
},
|
||||||
|
|
||||||
|
async deleteFolder(path: string) {
|
||||||
|
return executeCommand('docker', ['exec', 'immich-e2e-server', 'rm', '-r', path]).promise;
|
||||||
|
},
|
||||||
|
|
||||||
|
async truncateFolder(path: string) {
|
||||||
|
return executeCommand('docker', [
|
||||||
|
'exec',
|
||||||
|
'immich-e2e-server',
|
||||||
|
'find',
|
||||||
|
path,
|
||||||
|
'-type',
|
||||||
|
'f',
|
||||||
|
'-exec',
|
||||||
|
'truncate',
|
||||||
|
'-s',
|
||||||
|
'1',
|
||||||
|
'{}',
|
||||||
|
';',
|
||||||
|
]).promise;
|
||||||
|
},
|
||||||
|
|
||||||
|
async mkFolder(path: string) {
|
||||||
|
return executeCommand('docker', ['exec', 'immich-e2e-server', 'mkdir', '-p', path]).promise;
|
||||||
|
},
|
||||||
|
|
||||||
createBackup: async (accessToken: string) => {
|
createBackup: async (accessToken: string) => {
|
||||||
await utils.createJob(accessToken, {
|
await utils.createJob(accessToken, {
|
||||||
name: ManualJobName.BackupDatabase,
|
name: ManualJobName.BackupDatabase,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(accessToken, 'backupDatabase');
|
||||||
|
|
||||||
return utils.poll(
|
return utils.poll(
|
||||||
() => request(app).get('/admin/database-backups').set('Authorization', `Bearer ${accessToken}`),
|
() => request(app).get('/admin/database-backups').set('Authorization', `Bearer ${accessToken}`),
|
||||||
({ status, body }) => status === 200 && body.backups.length === 1,
|
({ status, body }) => status === 200 && body.backups.length === 1,
|
||||||
@@ -577,10 +624,8 @@ export const utils = {
|
|||||||
|
|
||||||
resetBackups: async (accessToken: string) => {
|
resetBackups: async (accessToken: string) => {
|
||||||
const { backups } = await listDatabaseBackups({ headers: asBearerAuth(accessToken) });
|
const { backups } = await listDatabaseBackups({ headers: asBearerAuth(accessToken) });
|
||||||
|
|
||||||
const backupFiles = backups.map((b) => b.filename);
|
|
||||||
await deleteDatabaseBackup(
|
await deleteDatabaseBackup(
|
||||||
{ databaseBackupDeleteDto: { backups: backupFiles } },
|
{ databaseBackupDeleteDto: { backups: backups.map((dto) => dto.filename) } },
|
||||||
{ headers: asBearerAuth(accessToken) },
|
{ headers: asBearerAuth(accessToken) },
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
|
||||||
import { defineConfig } from 'vitest/config';
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
const skipDockerSetup = process.env.VITEST_DISABLE_DOCKER_SETUP === 'true';
|
const skipDockerSetup = process.env.VITEST_DISABLE_DOCKER_SETUP === 'true';
|
||||||
@@ -24,5 +23,7 @@ export default defineConfig({
|
|||||||
maxWorkers: 1,
|
maxWorkers: 1,
|
||||||
isolate: false,
|
isolate: false,
|
||||||
},
|
},
|
||||||
plugins: [tsconfigPaths()],
|
resolve: {
|
||||||
|
tsconfigPaths: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
|
||||||
import { defineConfig } from 'vitest/config';
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
const skipDockerSetup = process.env.VITEST_DISABLE_DOCKER_SETUP === 'true';
|
const skipDockerSetup = process.env.VITEST_DISABLE_DOCKER_SETUP === 'true';
|
||||||
@@ -24,5 +23,7 @@ export default defineConfig({
|
|||||||
maxWorkers: 1,
|
maxWorkers: 1,
|
||||||
isolate: false,
|
isolate: false,
|
||||||
},
|
},
|
||||||
plugins: [tsconfigPaths()],
|
resolve: {
|
||||||
|
tsconfigPaths: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
+13
-1
@@ -5,8 +5,10 @@
|
|||||||
"acknowledge": "Neem kennis",
|
"acknowledge": "Neem kennis",
|
||||||
"action": "Aksie",
|
"action": "Aksie",
|
||||||
"action_common_update": "Werk by",
|
"action_common_update": "Werk by",
|
||||||
|
"action_description": "’n Stel van aksies om op die gefiltreerde bates uit te voer",
|
||||||
"actions": "Aksies",
|
"actions": "Aksies",
|
||||||
"active": "Aktief",
|
"active": "Aktief",
|
||||||
|
"active_count": "Aktief: {count}",
|
||||||
"activity": "Aktiwiteite",
|
"activity": "Aktiwiteite",
|
||||||
"activity_changed": "Aktiwiteit is {enabled, select, true {geaktiveer} other {gedeaktiveer}}",
|
"activity_changed": "Aktiwiteit is {enabled, select, true {geaktiveer} other {gedeaktiveer}}",
|
||||||
"add": "Voeg toe",
|
"add": "Voeg toe",
|
||||||
@@ -14,6 +16,9 @@
|
|||||||
"add_a_location": "Voeg ’n ligging toe",
|
"add_a_location": "Voeg ’n ligging toe",
|
||||||
"add_a_name": "Voeg ’n naam toe",
|
"add_a_name": "Voeg ’n naam toe",
|
||||||
"add_a_title": "Voeg ’n titel toe",
|
"add_a_title": "Voeg ’n titel toe",
|
||||||
|
"add_action": "Voeg aksie toe",
|
||||||
|
"add_action_description": "Klik om ’n aksie toe te voeg om uit te voer",
|
||||||
|
"add_assets": "Voeg bates by",
|
||||||
"add_birthday": "Voeg ’n verjaarsdag toe",
|
"add_birthday": "Voeg ’n verjaarsdag toe",
|
||||||
"add_endpoint": "Voeg eindpunt toe",
|
"add_endpoint": "Voeg eindpunt toe",
|
||||||
"add_exclusion_pattern": "Voeg uitsluitingspatroon toe",
|
"add_exclusion_pattern": "Voeg uitsluitingspatroon toe",
|
||||||
@@ -27,9 +32,13 @@
|
|||||||
"add_to_album": "Voeg toe tot album",
|
"add_to_album": "Voeg toe tot album",
|
||||||
"add_to_album_bottom_sheet_added": "Tot {album} toegevoeg",
|
"add_to_album_bottom_sheet_added": "Tot {album} toegevoeg",
|
||||||
"add_to_album_bottom_sheet_already_exists": "Reeds in {album}",
|
"add_to_album_bottom_sheet_already_exists": "Reeds in {album}",
|
||||||
|
"add_to_album_bottom_sheet_some_local_assets": "Sommige plaaslike bates kon nie toe gevoeg word tot die album nie",
|
||||||
|
"add_to_album_toggle": "Wissel seleksie vir {album}",
|
||||||
"add_to_albums": "Voeg toe tot albums",
|
"add_to_albums": "Voeg toe tot albums",
|
||||||
"add_to_albums_count": "Voeg toe tot albums ({count})",
|
"add_to_albums_count": "Voeg toe tot albums ({count})",
|
||||||
|
"add_to_bottom_bar": "Voeg toe",
|
||||||
"add_to_shared_album": "Voeg toe tot gedeelde album",
|
"add_to_shared_album": "Voeg toe tot gedeelde album",
|
||||||
|
"add_upload_to_stack": "Voeg oplaai by stapel",
|
||||||
"add_url": "Voeg bronadres toe",
|
"add_url": "Voeg bronadres toe",
|
||||||
"added_to_archive": "Tot argief toegevoeg",
|
"added_to_archive": "Tot argief toegevoeg",
|
||||||
"added_to_favorites": "Tot gunstelinge toegevoeg",
|
"added_to_favorites": "Tot gunstelinge toegevoeg",
|
||||||
@@ -46,6 +55,7 @@
|
|||||||
"backup_database": "Skep Databasisstortlêer",
|
"backup_database": "Skep Databasisstortlêer",
|
||||||
"backup_database_enable_description": "Aktiveer databasisstortlêers",
|
"backup_database_enable_description": "Aktiveer databasisstortlêers",
|
||||||
"backup_keep_last_amount": "Aantal vorige stortlêers om te hou",
|
"backup_keep_last_amount": "Aantal vorige stortlêers om te hou",
|
||||||
|
"backup_onboarding_2_description": "plaaslike kopieë op verskillende toestelle. Dit sluit die hooflêers en ’n rugsteun van daardie lêers plaaslik in.",
|
||||||
"backup_onboarding_3_description": "totale kopieë van u data, insluitend die oorspronklike lêers. Dit sluit 1 kopie op ’n ander perseel en 2 lokale kopieë in.",
|
"backup_onboarding_3_description": "totale kopieë van u data, insluitend die oorspronklike lêers. Dit sluit 1 kopie op ’n ander perseel en 2 lokale kopieë in.",
|
||||||
"backup_onboarding_description": "’n <backblaze-link>3-2-1-rugsteunstrategie</backblaze-link> word sterk aanbeveel om u data veilig te hou. Hou kopieë van u foto’s/video’s sowel as die Immich-databasis vir ’n volledige rugsteunoplossing.",
|
"backup_onboarding_description": "’n <backblaze-link>3-2-1-rugsteunstrategie</backblaze-link> word sterk aanbeveel om u data veilig te hou. Hou kopieë van u foto’s/video’s sowel as die Immich-databasis vir ’n volledige rugsteunoplossing.",
|
||||||
"backup_onboarding_footer": "Lees hierdie <link>dokument</link> vir meer inligting oor hoe om ’n rugsteunkopie van Immich te maak.",
|
"backup_onboarding_footer": "Lees hierdie <link>dokument</link> vir meer inligting oor hoe om ’n rugsteunkopie van Immich te maak.",
|
||||||
@@ -61,6 +71,7 @@
|
|||||||
"confirm_reprocess_all_faces": "Is u seker u wil alle gesigte herverwerk? Dit sal ook genoemde mense skoonmaak.",
|
"confirm_reprocess_all_faces": "Is u seker u wil alle gesigte herverwerk? Dit sal ook genoemde mense skoonmaak.",
|
||||||
"confirm_user_password_reset": "Is u seker u wil {user} se wagwoord terugstel?",
|
"confirm_user_password_reset": "Is u seker u wil {user} se wagwoord terugstel?",
|
||||||
"confirm_user_pin_code_reset": "Is u seker u wil {user} se PIN-kode herstel?",
|
"confirm_user_pin_code_reset": "Is u seker u wil {user} se PIN-kode herstel?",
|
||||||
|
"copy_config_to_clipboard_description": "Kopieer die huidige stelselkonfigurasie as ’n JSON‑objek na die klipbord",
|
||||||
"create_job": "Skep taak",
|
"create_job": "Skep taak",
|
||||||
"cron_expression": "Cron-uitdrukking",
|
"cron_expression": "Cron-uitdrukking",
|
||||||
"cron_expression_description": "Stel die skanderingsinterval in met die cron-formaat. Kyk gerus na bv. <link>Crontab Guru</link> vir meer inligting",
|
"cron_expression_description": "Stel die skanderingsinterval in met die cron-formaat. Kyk gerus na bv. <link>Crontab Guru</link> vir meer inligting",
|
||||||
@@ -68,6 +79,8 @@
|
|||||||
"disable_login": "Deaktiveer aantekening",
|
"disable_login": "Deaktiveer aantekening",
|
||||||
"duplicate_detection_job_description": "Begin masjienleer op items om soortgelyke beelde op te spoor. Maak staat op Slimsoek",
|
"duplicate_detection_job_description": "Begin masjienleer op items om soortgelyke beelde op te spoor. Maak staat op Slimsoek",
|
||||||
"exclusion_pattern_description": "Met uitsluitingspatrone kan u lêers en vouers ignoreer wanneer u u biblioteek skandeer. Dit is nuttig as u vouers het wat lêers bevat wat u nie wil invoer nie, soos RAW-lêers.",
|
"exclusion_pattern_description": "Met uitsluitingspatrone kan u lêers en vouers ignoreer wanneer u u biblioteek skandeer. Dit is nuttig as u vouers het wat lêers bevat wat u nie wil invoer nie, soos RAW-lêers.",
|
||||||
|
"export_config_as_json_description": "Laai die huidige stelselkonfigurasie af as ’n JSON‑lêer",
|
||||||
|
"external_libraries_page_description": "Admin eksterne biblioteekbladsy",
|
||||||
"face_detection": "Gesigherkenning",
|
"face_detection": "Gesigherkenning",
|
||||||
"face_detection_description": "Identifiseer die gesigte in media d.m.v. masjienleer. Vir video’s word slegs die duimnael oorweeg. “Herlaai” (ver)werk al die media weer. “Stel terug” verwyder alle huidige gesigdata. “Onverwerk” plaas items in die ry wat nog nie verwerk is nie. Geïdentifiseerde gesigte sal ná voltooiing van Gesigidentifikasie vir Gesigherkenning in die ry geplaas word om hulle in bestaande of nuwe persone te groepeer.",
|
"face_detection_description": "Identifiseer die gesigte in media d.m.v. masjienleer. Vir video’s word slegs die duimnael oorweeg. “Herlaai” (ver)werk al die media weer. “Stel terug” verwyder alle huidige gesigdata. “Onverwerk” plaas items in die ry wat nog nie verwerk is nie. Geïdentifiseerde gesigte sal ná voltooiing van Gesigidentifikasie vir Gesigherkenning in die ry geplaas word om hulle in bestaande of nuwe persone te groepeer.",
|
||||||
"facial_recognition_job_description": "Groepeer gesigte in mense. Die stap is vinniger nadat Gesigherkenning klaar is. “Herstel” (her-)groepeer alle gesigte. “Vermiste” plaas gesigte in ry wat nie ’n persoon gekoppel het nie.",
|
"facial_recognition_job_description": "Groepeer gesigte in mense. Die stap is vinniger nadat Gesigherkenning klaar is. “Herstel” (her-)groepeer alle gesigte. “Vermiste” plaas gesigte in ry wat nie ’n persoon gekoppel het nie.",
|
||||||
@@ -189,7 +202,6 @@
|
|||||||
"unsupported_field_type": "Onondersteunde veldtipe",
|
"unsupported_field_type": "Onondersteunde veldtipe",
|
||||||
"unsupported_file_type": "Lêer {file} kan nie opgelaai word nie omdat die lêertipe {type} nie ondersteun word nie.",
|
"unsupported_file_type": "Lêer {file} kan nie opgelaai word nie omdat die lêertipe {type} nie ondersteun word nie.",
|
||||||
"untagged": "Sonder etiket",
|
"untagged": "Sonder etiket",
|
||||||
"untitled_workflow": "Naamlose werkvloei",
|
|
||||||
"up_next": "Volgende",
|
"up_next": "Volgende",
|
||||||
"update_location_action_prompt": "Werk die ligging van {count} gekose items by met:",
|
"update_location_action_prompt": "Werk die ligging van {count} gekose items by met:",
|
||||||
"updated_at": "Bygewerk",
|
"updated_at": "Bygewerk",
|
||||||
|
|||||||
+147
-23
@@ -1,14 +1,14 @@
|
|||||||
{
|
{
|
||||||
"about": "حول",
|
"about": "حول",
|
||||||
"account": "حساب",
|
"account": "الحساب",
|
||||||
"account_settings": "إعدادات الحساب",
|
"account_settings": "إعدادات الحساب",
|
||||||
"acknowledge": "أُدرك ذلك",
|
"acknowledge": "أُدرك ذلك",
|
||||||
"action": "إجراء",
|
"action": "الإجراء",
|
||||||
"action_common_update": "تحديث",
|
"action_common_update": "تحديث",
|
||||||
"action_description": "مجموعة من الفعاليات التي ستنفذ على الأصول التي تم تصفيتها",
|
"action_description": "مجموعة إجراءات لتنفيذها على المحتويات المصفاة",
|
||||||
"actions": "عمليات",
|
"actions": "عمليات",
|
||||||
"active": "نشط",
|
"active": "نشط",
|
||||||
"active_count": "فعال: {count}",
|
"active_count": "نشط: {count}",
|
||||||
"activity": "نشاط",
|
"activity": "نشاط",
|
||||||
"activity_changed": "النشاط {enabled, select, true {مُفْعل} other {معطّل}}",
|
"activity_changed": "النشاط {enabled, select, true {مُفْعل} other {معطّل}}",
|
||||||
"add": "إضافة",
|
"add": "إضافة",
|
||||||
@@ -22,13 +22,12 @@
|
|||||||
"add_birthday": "أضف تاريخ الميلاد",
|
"add_birthday": "أضف تاريخ الميلاد",
|
||||||
"add_endpoint": "اضف نقطة نهاية",
|
"add_endpoint": "اضف نقطة نهاية",
|
||||||
"add_exclusion_pattern": "إضافة نمط إستثناء",
|
"add_exclusion_pattern": "إضافة نمط إستثناء",
|
||||||
"add_filter": "اضف تصفية",
|
|
||||||
"add_filter_description": "اضغط لاضافة شرط تصفية",
|
|
||||||
"add_location": "إضافة موقع",
|
"add_location": "إضافة موقع",
|
||||||
"add_more_users": "إضافة مستخدمين آخرين",
|
"add_more_users": "إضافة مستخدمين آخرين",
|
||||||
"add_partner": "أضف شريكًا",
|
"add_partner": "أضف شريكًا",
|
||||||
"add_path": "إضافة مسار",
|
"add_path": "إضافة مسار",
|
||||||
"add_photos": "إضافة صور",
|
"add_photos": "إضافة صور",
|
||||||
|
"add_step": "اضف خطوة",
|
||||||
"add_tag": "اضف علامة",
|
"add_tag": "اضف علامة",
|
||||||
"add_to": "إضافة إلى…",
|
"add_to": "إضافة إلى…",
|
||||||
"add_to_album": "إضافة إلى ألبوم",
|
"add_to_album": "إضافة إلى ألبوم",
|
||||||
@@ -42,7 +41,6 @@
|
|||||||
"add_to_shared_album": "إضافة إلى ألبوم مشارك",
|
"add_to_shared_album": "إضافة إلى ألبوم مشارك",
|
||||||
"add_upload_to_stack": "اضف رفع الى حزمة",
|
"add_upload_to_stack": "اضف رفع الى حزمة",
|
||||||
"add_url": "إضافة رابط",
|
"add_url": "إضافة رابط",
|
||||||
"add_workflow_step": "اضف خطوة سير عمل",
|
|
||||||
"added_to_archive": "أُضيفت للأرشيف",
|
"added_to_archive": "أُضيفت للأرشيف",
|
||||||
"added_to_favorites": "أُضيفت للمفضلات",
|
"added_to_favorites": "أُضيفت للمفضلات",
|
||||||
"added_to_favorites_count": "تم إضافة {count, number} إلى المفضلات",
|
"added_to_favorites_count": "تم إضافة {count, number} إلى المفضلات",
|
||||||
@@ -61,8 +59,8 @@
|
|||||||
"backup_onboarding_1_description": "نسخة خارج الموقع في موقع آخر.",
|
"backup_onboarding_1_description": "نسخة خارج الموقع في موقع آخر.",
|
||||||
"backup_onboarding_2_description": "نسخ محلية على أجهزة مختلفة. يشمل ذلك الملفات الرئيسية ونسخة احتياطية محلية منها.",
|
"backup_onboarding_2_description": "نسخ محلية على أجهزة مختلفة. يشمل ذلك الملفات الرئيسية ونسخة احتياطية محلية منها.",
|
||||||
"backup_onboarding_3_description": "إجمالي نُسخ بياناتك، بما في ذلك الملفات الأصلية. يشمل ذلك نسخةً واحدةً خارج الموقع ونسختين محليتين.",
|
"backup_onboarding_3_description": "إجمالي نُسخ بياناتك، بما في ذلك الملفات الأصلية. يشمل ذلك نسخةً واحدةً خارج الموقع ونسختين محليتين.",
|
||||||
"backup_onboarding_description": "يُنصح باتباع <backblaze-link>استراتيجية النسخ الاحتياطي 3-2- 1</backblaze-link> لحماية بياناتك. احتفظ بنسخ احتياطية من صورك/فيديوهاتك المحمّلة، بالإضافة إلى قاعدة بيانات Immich، لضمان حل نسخ احتياطي شامل.",
|
"backup_onboarding_description": "يُنصح باتباع استراتيجية النسخ الاحتياطي <backblaze-link>3-2-1 backup strategy</backblaze-link> لحماية بياناتك. يجب عليك الاحتفاظ بنسخ من الصور/مقاطع الفيديو المرفوعة بالإضافة إلى قاعدة بيانات Immich للحصول على حل نسخ احتياطي شامل.",
|
||||||
"backup_onboarding_footer": "لمزيد من المعلومات حول النسخ الاحتياطي لـ <link>Immich</link>، يرجى الرجوع إلى <link>الوثائق</link>.",
|
"backup_onboarding_footer": "لمزيد من المعلومات حول النسخ الاحتياطي لـ Immich، يرجى الرجوع إلى <link>الوثائق</link>.",
|
||||||
"backup_onboarding_parts_title": "يتضمن النسخ الاحتياطي 3-2-1 ما يلي:",
|
"backup_onboarding_parts_title": "يتضمن النسخ الاحتياطي 3-2-1 ما يلي:",
|
||||||
"backup_onboarding_title": "النسخ الاحتياطية",
|
"backup_onboarding_title": "النسخ الاحتياطية",
|
||||||
"backup_settings": "إعدادات تفريغ قاعدة البيانات",
|
"backup_settings": "إعدادات تفريغ قاعدة البيانات",
|
||||||
@@ -81,6 +79,7 @@
|
|||||||
"cron_expression_description": "اضبط الفاصل الزمني للفحص باستخدام تنسيق cron. لمزيد من المعلومات يُرجى الرجوع إلى <link>Crontab Guru</link> على سبيل المثال",
|
"cron_expression_description": "اضبط الفاصل الزمني للفحص باستخدام تنسيق cron. لمزيد من المعلومات يُرجى الرجوع إلى <link>Crontab Guru</link> على سبيل المثال",
|
||||||
"cron_expression_presets": "الإعدادات المسبقة لتعبير Cron",
|
"cron_expression_presets": "الإعدادات المسبقة لتعبير Cron",
|
||||||
"disable_login": "تعطيل تسجيل الدخول",
|
"disable_login": "تعطيل تسجيل الدخول",
|
||||||
|
"download_csv": "حمل CSV",
|
||||||
"duplicate_detection_job_description": "بدء التعلم الآلي على المحتوى للعثور على الصور المتشابهة. يعتمد على البحث الذكي",
|
"duplicate_detection_job_description": "بدء التعلم الآلي على المحتوى للعثور على الصور المتشابهة. يعتمد على البحث الذكي",
|
||||||
"exclusion_pattern_description": "تتيح لك أنماط الاستبعاد تجاهل الملفات والمجلدات عند فحص مكتبتك. يعد هذا مفيدًا إذا كان لديك مجلدات تحتوي على ملفات لا تريد استيرادها، مثل ملفات RAW.",
|
"exclusion_pattern_description": "تتيح لك أنماط الاستبعاد تجاهل الملفات والمجلدات عند فحص مكتبتك. يعد هذا مفيدًا إذا كان لديك مجلدات تحتوي على ملفات لا تريد استيرادها، مثل ملفات RAW.",
|
||||||
"export_config_as_json_description": "تحميل اعدادات النظام الحالية كملف بصيغة JSON",
|
"export_config_as_json_description": "تحميل اعدادات النظام الحالية كملف بصيغة JSON",
|
||||||
@@ -190,9 +189,25 @@
|
|||||||
"machine_learning_smart_search_enabled": "تفعيل البحث الذكي",
|
"machine_learning_smart_search_enabled": "تفعيل البحث الذكي",
|
||||||
"machine_learning_smart_search_enabled_description": "إذا تم تعطيله، فلن يتم ترميز الصور للبحث الذكي.",
|
"machine_learning_smart_search_enabled_description": "إذا تم تعطيله، فلن يتم ترميز الصور للبحث الذكي.",
|
||||||
"machine_learning_url_description": "عنوان URL لخادم التعلم الآلي. إذا تم توفير أكثر من عنوان URL واحد، سيتم محاولة الاتصال بكل خادم على حدة حتى يستجيب أحدهم بنجاح، بدءًا من الأول إلى الأخير. سيتم تجاهل الخوادم التي لا تستجيب مؤقتًا حتى تعود للعمل.",
|
"machine_learning_url_description": "عنوان URL لخادم التعلم الآلي. إذا تم توفير أكثر من عنوان URL واحد، سيتم محاولة الاتصال بكل خادم على حدة حتى يستجيب أحدهم بنجاح، بدءًا من الأول إلى الأخير. سيتم تجاهل الخوادم التي لا تستجيب مؤقتًا حتى تعود للعمل.",
|
||||||
|
"maintenance_backup_management": "إدارة النسخ الاحتياطي",
|
||||||
"maintenance_delete_backup": "حذف النسخ الاحتياطي",
|
"maintenance_delete_backup": "حذف النسخ الاحتياطي",
|
||||||
"maintenance_delete_backup_description": "هذا الملف سيتم حذفه بشكل لا رجعه فيه.",
|
"maintenance_delete_backup_description": "هذا الملف سيتم حذفه بشكل لا رجعه فيه.",
|
||||||
"maintenance_delete_error": "فشل حذف النسخ الاحتياطي.",
|
"maintenance_delete_error": "فشل حذف النسخ الاحتياطي.",
|
||||||
|
"maintenance_integrity_check": "تحقق",
|
||||||
|
"maintenance_integrity_check_all": "تحديد الكل",
|
||||||
|
"maintenance_integrity_checksum_mismatch": "عدم تطابق رمز التحقق",
|
||||||
|
"maintenance_integrity_checksum_mismatch_description": "الملفات التي لا يتطابق المجموع التدقيقي لها على القرص مع المجموع التدقيقي المخزن في قاعدة بيانات Immich.",
|
||||||
|
"maintenance_integrity_checksum_mismatch_job": "التحقق من عدم تطابق المجموع التدقيقي",
|
||||||
|
"maintenance_integrity_checksum_mismatch_refresh_job": "تحديث تقارير عدم تطابق المجموع التدقيقي",
|
||||||
|
"maintenance_integrity_missing_file": "الملفات المفقودة",
|
||||||
|
"maintenance_integrity_missing_file_description": "الملفات التي يتتبعها Immich في قاعدة بياناته ولكنها غير موجودة في نظام الملفات.",
|
||||||
|
"maintenance_integrity_missing_file_job": "التحقق من الملفات المفقودة",
|
||||||
|
"maintenance_integrity_missing_file_refresh_job": "تحديث تقارير الملفات المفقودة",
|
||||||
|
"maintenance_integrity_report": "تقرير السلامة",
|
||||||
|
"maintenance_integrity_untracked_file": "الملفات غير المتتبعة",
|
||||||
|
"maintenance_integrity_untracked_file_description": "الملفات الموجودة في مجلدات Immich بدون وجود سجلات لها.",
|
||||||
|
"maintenance_integrity_untracked_file_job": "التحقق من الملفات غير المتتبعة",
|
||||||
|
"maintenance_integrity_untracked_file_refresh_job": "تحديث تقارير الملفات غير المتتبعة",
|
||||||
"maintenance_restore_backup": "استعادة النسخ الاحتياطي",
|
"maintenance_restore_backup": "استعادة النسخ الاحتياطي",
|
||||||
"maintenance_restore_backup_description": "سيتم مسح بيانات Immich واستعادتها من النسخة الاحتياطي المختار. سيتم إنشاء نسخة احتياطية قبل المتابعة.",
|
"maintenance_restore_backup_description": "سيتم مسح بيانات Immich واستعادتها من النسخة الاحتياطي المختار. سيتم إنشاء نسخة احتياطية قبل المتابعة.",
|
||||||
"maintenance_restore_backup_different_version": "هذا النسخ الاحتياطي تم انشائه باستخدام اصدار مختلف من Immich!",
|
"maintenance_restore_backup_different_version": "هذا النسخ الاحتياطي تم انشائه باستخدام اصدار مختلف من Immich!",
|
||||||
@@ -267,6 +282,8 @@
|
|||||||
"notification_enable_email_notifications": "تفعيل إشعارات البريد الإلكتروني",
|
"notification_enable_email_notifications": "تفعيل إشعارات البريد الإلكتروني",
|
||||||
"notification_settings": "إعدادات الإشعارات",
|
"notification_settings": "إعدادات الإشعارات",
|
||||||
"notification_settings_description": "إدارة إعدادات الإشعارات، بما في ذلك البريد الإلكتروني",
|
"notification_settings_description": "إدارة إعدادات الإشعارات، بما في ذلك البريد الإلكتروني",
|
||||||
|
"oauth_allow_insecure_requests": "السماح بالطلبات الغير الآمنة",
|
||||||
|
"oauth_allow_insecure_requests_description": "تحذير: هذا يعطل التحقق من صحة شهادة أمن طبقة النقل لطلبات الترخيص المفتوح وقد يعرضك الهجمات الوسطية.",
|
||||||
"oauth_auto_launch": "التشغيل التلقائي",
|
"oauth_auto_launch": "التشغيل التلقائي",
|
||||||
"oauth_auto_launch_description": "ابدأ تدفق تسجيل الدخول OAuth تلقائيًا عند الانتقال إلى صفحة تسجيل الدخول",
|
"oauth_auto_launch_description": "ابدأ تدفق تسجيل الدخول OAuth تلقائيًا عند الانتقال إلى صفحة تسجيل الدخول",
|
||||||
"oauth_auto_register": "التسجيل التلقائي",
|
"oauth_auto_register": "التسجيل التلقائي",
|
||||||
@@ -274,9 +291,11 @@
|
|||||||
"oauth_button_text": "نص الزر",
|
"oauth_button_text": "نص الزر",
|
||||||
"oauth_client_secret_description": "مطلوب للعميل السري، او اذا PKCE(مفتاح الاثبات لتبادل الكود) ليس مدعوم من العميل العام.",
|
"oauth_client_secret_description": "مطلوب للعميل السري، او اذا PKCE(مفتاح الاثبات لتبادل الكود) ليس مدعوم من العميل العام.",
|
||||||
"oauth_enable_description": "تسجيل الدخول باستخدام OAuth",
|
"oauth_enable_description": "تسجيل الدخول باستخدام OAuth",
|
||||||
|
"oauth_end_session_url_description": "إعادة توجيه المستخدم إلى معرف الموارد الموحد (URI) هذا عند تسجيل الخروج.",
|
||||||
"oauth_mobile_redirect_uri": "عنوان URI لإعادة التوجيه على الهاتف",
|
"oauth_mobile_redirect_uri": "عنوان URI لإعادة التوجيه على الهاتف",
|
||||||
"oauth_mobile_redirect_uri_override": "تجاوز عنوان URI لإعادة التوجيه على الهاتف",
|
"oauth_mobile_redirect_uri_override": "تجاوز عنوان URI لإعادة التوجيه على الهاتف",
|
||||||
"oauth_mobile_redirect_uri_override_description": "قم بتفعيله عندما لا يسمح موفر OAuth بمعرف URI للجوال، مثل ''{callback}''",
|
"oauth_mobile_redirect_uri_override_description": "قم بتفعيله عندما لا يسمح موفر OAuth بمعرف URI للجوال، مثل ''{callback}''",
|
||||||
|
"oauth_prompt_description": "مُعامل التوجيه (مثلselect_account, login, consent)",
|
||||||
"oauth_role_claim": "المطالبة بالدور(صلاحيات)",
|
"oauth_role_claim": "المطالبة بالدور(صلاحيات)",
|
||||||
"oauth_role_claim_description": "منح وصول المسؤول تلقائيًا بناءً على وجود هذا الطلب. قد يكون الطلب إما 'مستخدم' أو 'مسؤول'.",
|
"oauth_role_claim_description": "منح وصول المسؤول تلقائيًا بناءً على وجود هذا الطلب. قد يكون الطلب إما 'مستخدم' أو 'مسؤول'.",
|
||||||
"oauth_settings": "OAuth",
|
"oauth_settings": "OAuth",
|
||||||
@@ -303,6 +322,8 @@
|
|||||||
"refreshing_all_libraries": "تحديث كافة المكتبات",
|
"refreshing_all_libraries": "تحديث كافة المكتبات",
|
||||||
"registration": "تسجيل المدير",
|
"registration": "تسجيل المدير",
|
||||||
"registration_description": "بما أنك أول مستخدم في النظام، سيتم تعيينك كمسؤول وستكون مسؤولًا عن المهام الإدارية، وسيتم إنشاء مستخدمين إضافيين بواسطتك.",
|
"registration_description": "بما أنك أول مستخدم في النظام، سيتم تعيينك كمسؤول وستكون مسؤولًا عن المهام الإدارية، وسيتم إنشاء مستخدمين إضافيين بواسطتك.",
|
||||||
|
"release_channel_release_candidate": "إصدار مرشح",
|
||||||
|
"release_channel_stable": "مستقر",
|
||||||
"remove_failed_jobs": "ازالة العمليات التي فشلت",
|
"remove_failed_jobs": "ازالة العمليات التي فشلت",
|
||||||
"require_password_change_on_login": "الطلب من المستخدم تغيير كلمة المرور عند تسجيل الدخول الأول",
|
"require_password_change_on_login": "الطلب من المستخدم تغيير كلمة المرور عند تسجيل الدخول الأول",
|
||||||
"reset_settings_to_default": "إعادة ضبط الإعدادات إلى الوضع الافتراضي",
|
"reset_settings_to_default": "إعادة ضبط الإعدادات إلى الوضع الافتراضي",
|
||||||
@@ -333,7 +354,7 @@
|
|||||||
"storage_template_migration_description": "قم بتطبيق القالب الحالي <link>{template}</link> على المحتويات التي تم رفعها سابقًا",
|
"storage_template_migration_description": "قم بتطبيق القالب الحالي <link>{template}</link> على المحتويات التي تم رفعها سابقًا",
|
||||||
"storage_template_migration_info": "تغييرات النموذج الخزني ستغير جميع الصيغ الى احرف صغيرة. تغييرات النموذج ستنطبق فقط على المحتويات الجديدة. لتطبيق النموذج على المحتويات التي تم رفعها سابقًا، قم بتشغيل <link>{job}</link>.",
|
"storage_template_migration_info": "تغييرات النموذج الخزني ستغير جميع الصيغ الى احرف صغيرة. تغييرات النموذج ستنطبق فقط على المحتويات الجديدة. لتطبيق النموذج على المحتويات التي تم رفعها سابقًا، قم بتشغيل <link>{job}</link>.",
|
||||||
"storage_template_migration_job": "وظيفة تهجير قالب التخزين",
|
"storage_template_migration_job": "وظيفة تهجير قالب التخزين",
|
||||||
"storage_template_more_details": "لمزيد من التفاصيل حول هذه الميزة، يرجى الرجوع إلى <template-link>Storage Template</template-link> و <implications-link>implications</implications-link>.",
|
"storage_template_more_details": "لمزيد من التفاصيل حول هذه الميزة، يرجى الرجوع إلى <template-link>Storage Template</template-link> و <implications-link>implications</implications-link>",
|
||||||
"storage_template_onboarding_description_v2": "عند التفعيل. هذه الخاصية ستقوم بالترتيب التلقائي للملفات بناء على نموذج معرف من قبل المستخدم. رجاء اطلع على <link>التوثيق</link>.",
|
"storage_template_onboarding_description_v2": "عند التفعيل. هذه الخاصية ستقوم بالترتيب التلقائي للملفات بناء على نموذج معرف من قبل المستخدم. رجاء اطلع على <link>التوثيق</link>.",
|
||||||
"storage_template_path_length": "الحد التقريبي لطول المسار: <b>{length, number}</b>/{limit, number}",
|
"storage_template_path_length": "الحد التقريبي لطول المسار: <b>{length, number}</b>/{limit, number}",
|
||||||
"storage_template_settings": "قالب التخزين",
|
"storage_template_settings": "قالب التخزين",
|
||||||
@@ -397,6 +418,10 @@
|
|||||||
"transcoding_preferred_hardware_device_description": "ينطبق فقط على VAAPI وQSV. يضبط عقدة dri المستخدمة لتحويل ترميز الأجهزة.",
|
"transcoding_preferred_hardware_device_description": "ينطبق فقط على VAAPI وQSV. يضبط عقدة dri المستخدمة لتحويل ترميز الأجهزة.",
|
||||||
"transcoding_preset_preset": "الضبط المُسبق (-preset)",
|
"transcoding_preset_preset": "الضبط المُسبق (-preset)",
|
||||||
"transcoding_preset_preset_description": "سرعة الضغط. تؤدي الإعدادات المسبقة الأبطأ إلى إنتاج ملفات أصغر حجمًا، وزيادة الجودة عند استهداف معدل بت معين. يتجاهل VP9 السرعات الأعلى من 'الأسرع'.",
|
"transcoding_preset_preset_description": "سرعة الضغط. تؤدي الإعدادات المسبقة الأبطأ إلى إنتاج ملفات أصغر حجمًا، وزيادة الجودة عند استهداف معدل بت معين. يتجاهل VP9 السرعات الأعلى من 'الأسرع'.",
|
||||||
|
"transcoding_realtime": "تحويل الترميز في الوقت الفعلي [تجريبي]",
|
||||||
|
"transcoding_realtime_description": "يتيح إجراء تحويل الترميز في الوقت الفعلي أثناء بث الفيديو. كما يمكّن من تبديل الجودة، ولكنه قد يتسبب في زيادة زمن تأخير التشغيل وحدوث تقطيع، اعتماداً على قدرات الخادم.",
|
||||||
|
"transcoding_realtime_enabled": "تفعيل تحويل الترميز في الوقت الفعلي",
|
||||||
|
"transcoding_realtime_enabled_description": "في حال تعطيله، سيرفض الخادم بدء جلسات تحويل ترميز جديدة في الوقت الفعلي.",
|
||||||
"transcoding_reference_frames": "الإطارات المرجعية",
|
"transcoding_reference_frames": "الإطارات المرجعية",
|
||||||
"transcoding_reference_frames_description": "عدد الإطارات التي يجب الرجوع إليها عند ضغط إطار معين. تعمل القيم الأعلى على تحسين كفاءة الضغط، ولكنها تبطئ عملية التشفير. 0 يضبط هذه القيمة تلقائيًا.",
|
"transcoding_reference_frames_description": "عدد الإطارات التي يجب الرجوع إليها عند ضغط إطار معين. تعمل القيم الأعلى على تحسين كفاءة الضغط، ولكنها تبطئ عملية التشفير. 0 يضبط هذه القيمة تلقائيًا.",
|
||||||
"transcoding_required_description": "فقط مقاطع الفيديو ذات التنسيق غير المقبول",
|
"transcoding_required_description": "فقط مقاطع الفيديو ذات التنسيق غير المقبول",
|
||||||
@@ -440,6 +465,8 @@
|
|||||||
"user_settings_description": "إدارة إعدادات المستخدم",
|
"user_settings_description": "إدارة إعدادات المستخدم",
|
||||||
"user_successfully_removed": "المستخدم {email} تمت ازالته بنجاح.",
|
"user_successfully_removed": "المستخدم {email} تمت ازالته بنجاح.",
|
||||||
"users_page_description": "صفحة ادارة المستخدمين",
|
"users_page_description": "صفحة ادارة المستخدمين",
|
||||||
|
"version_check_channel": "قناة الإصدار",
|
||||||
|
"version_check_channel_description": "اختر قناة الإصدار التي ترغب في تلقي إعلانات الإصدارات لها",
|
||||||
"version_check_enabled_description": "تفعيل التحقق من الإصدارات الجديدة",
|
"version_check_enabled_description": "تفعيل التحقق من الإصدارات الجديدة",
|
||||||
"version_check_implications": "تعتمد ميزة التحقق من الإصدار على التواصل الدوري مع {server}",
|
"version_check_implications": "تعتمد ميزة التحقق من الإصدار على التواصل الدوري مع {server}",
|
||||||
"version_check_settings": "التحقق من الإصدار",
|
"version_check_settings": "التحقق من الإصدار",
|
||||||
@@ -560,13 +587,14 @@
|
|||||||
"asset_added_to_album": "تمت إضافته إلى الألبوم",
|
"asset_added_to_album": "تمت إضافته إلى الألبوم",
|
||||||
"asset_adding_to_album": "جارٍ الإضافة إلى الألبوم…",
|
"asset_adding_to_album": "جارٍ الإضافة إلى الألبوم…",
|
||||||
"asset_created": "انشئ اصل",
|
"asset_created": "انشئ اصل",
|
||||||
|
"asset_day_count": "{date}: {count, plural, zero {لا توجد ملفات} one {ملف واحد} two {ملفان} few {# ملفات} many {# ملفًا} other {# ملف}}",
|
||||||
"asset_description_updated": "تم تحديث وصف المحتوى",
|
"asset_description_updated": "تم تحديث وصف المحتوى",
|
||||||
"asset_filename_is_offline": "الأصل {filename} غير متصل",
|
"asset_filename_is_offline": "الأصل {filename} غير متصل",
|
||||||
"asset_has_unassigned_faces": "يحتوي الأصل على وجوه غير مخصصة",
|
"asset_has_unassigned_faces": "يحتوي الأصل على وجوه غير مخصصة",
|
||||||
"asset_hashing": "التجزئة…",
|
"asset_hashing": "التجزئة…",
|
||||||
"asset_list_group_by_sub_title": "تنظيم بواسطة",
|
"asset_list_group_by_sub_title": "تنظيم بواسطة",
|
||||||
"asset_list_layout_settings_dynamic_layout_title": "تخطيط ديناميكي",
|
"asset_list_layout_settings_dynamic_layout_title": "تخطيط ديناميكي",
|
||||||
"asset_list_layout_settings_group_automatically": "تلقائي",
|
"asset_list_layout_settings_group_automatically": "تلقائيا",
|
||||||
"asset_list_layout_settings_group_by": "مجموعة الأصول حسب",
|
"asset_list_layout_settings_group_by": "مجموعة الأصول حسب",
|
||||||
"asset_list_layout_settings_group_by_month_day": "شهر + يوم",
|
"asset_list_layout_settings_group_by_month_day": "شهر + يوم",
|
||||||
"asset_list_layout_sub_title": "تصميم",
|
"asset_list_layout_sub_title": "تصميم",
|
||||||
@@ -689,6 +717,7 @@
|
|||||||
"backup_settings_subtitle": "إدارة إعدادات التحميل",
|
"backup_settings_subtitle": "إدارة إعدادات التحميل",
|
||||||
"backup_upload_details_page_more_details": "اضغط لتفاصيل اضافية",
|
"backup_upload_details_page_more_details": "اضغط لتفاصيل اضافية",
|
||||||
"backward": "الى الوراء",
|
"backward": "الى الوراء",
|
||||||
|
"battery_optimization_backup_reliability": "إيقاف تحسين البطارية يزيد من استقرار النسخ الاحتياطي في الخلفية",
|
||||||
"biometric_auth_enabled": "المصادقة البايومترية مفعله",
|
"biometric_auth_enabled": "المصادقة البايومترية مفعله",
|
||||||
"biometric_locked_out": "لقد قفلت عنك المصادقة البيومترية",
|
"biometric_locked_out": "لقد قفلت عنك المصادقة البيومترية",
|
||||||
"biometric_no_options": "لا توجد خيارات بايومترية متوفرة",
|
"biometric_no_options": "لا توجد خيارات بايومترية متوفرة",
|
||||||
@@ -696,6 +725,7 @@
|
|||||||
"birthdate_saved": "تم حفظ تاريخ الميلاد بنجاح",
|
"birthdate_saved": "تم حفظ تاريخ الميلاد بنجاح",
|
||||||
"birthdate_set_description": "يتم استخدام تاريخ الميلاد لحساب عمر هذا الشخص وقت التقاط الصورة.",
|
"birthdate_set_description": "يتم استخدام تاريخ الميلاد لحساب عمر هذا الشخص وقت التقاط الصورة.",
|
||||||
"blurred_background": "خلفية مشوشة",
|
"blurred_background": "خلفية مشوشة",
|
||||||
|
"browse_templates": "تصفح القوالب",
|
||||||
"bugs_and_feature_requests": "الأخطاء وطلبات الميزات",
|
"bugs_and_feature_requests": "الأخطاء وطلبات الميزات",
|
||||||
"build": "يبني",
|
"build": "يبني",
|
||||||
"build_image": "بناء الصورة",
|
"build_image": "بناء الصورة",
|
||||||
@@ -729,6 +759,7 @@
|
|||||||
"cannot_update_the_description": "لا يمكن تحديث الوصف",
|
"cannot_update_the_description": "لا يمكن تحديث الوصف",
|
||||||
"cast": "بث",
|
"cast": "بث",
|
||||||
"cast_description": "ضبط وجهات البث المتوفرة",
|
"cast_description": "ضبط وجهات البث المتوفرة",
|
||||||
|
"change": "تغيير",
|
||||||
"change_date": "غيّر التاريخ",
|
"change_date": "غيّر التاريخ",
|
||||||
"change_description": "تغيير الوصف",
|
"change_description": "تغيير الوصف",
|
||||||
"change_display_order": "تغيير ترتيب العرض",
|
"change_display_order": "تغيير ترتيب العرض",
|
||||||
@@ -757,6 +788,7 @@
|
|||||||
"check_corrupt_asset_backup_description": "قم بإجراء هذا الفحص فقط عبر شبكة Wi-Fi وبعد نسخ جميع الأصول احتياطيًا. قد يستغرق الإجراء بضع دقائق.",
|
"check_corrupt_asset_backup_description": "قم بإجراء هذا الفحص فقط عبر شبكة Wi-Fi وبعد نسخ جميع الأصول احتياطيًا. قد يستغرق الإجراء بضع دقائق.",
|
||||||
"check_logs": "تحقق من السجلات",
|
"check_logs": "تحقق من السجلات",
|
||||||
"checksum": "مجموع التحقق",
|
"checksum": "مجموع التحقق",
|
||||||
|
"choose": "اختيار",
|
||||||
"choose_matching_people_to_merge": "اختر الأشخاص المتطابقين لدمجهم",
|
"choose_matching_people_to_merge": "اختر الأشخاص المتطابقين لدمجهم",
|
||||||
"city": "المدينة",
|
"city": "المدينة",
|
||||||
"cleanup_confirm_description": "Immich وجد {count} اصول (انشئت قبل {date}) تم خزنها احتياطيا الى الخادم. ازالة النسخ المحلية من هذا الجهاز?",
|
"cleanup_confirm_description": "Immich وجد {count} اصول (انشئت قبل {date}) تم خزنها احتياطيا الى الخادم. ازالة النسخ المحلية من هذا الجهاز?",
|
||||||
@@ -774,6 +806,7 @@
|
|||||||
"clear": "إخلاء",
|
"clear": "إخلاء",
|
||||||
"clear_all": "إخلاء الكل",
|
"clear_all": "إخلاء الكل",
|
||||||
"clear_all_recent_searches": "مسح جميع عمليات البحث الأخيرة",
|
"clear_all_recent_searches": "مسح جميع عمليات البحث الأخيرة",
|
||||||
|
"clear_failed_count": "فشل المسح ({count})",
|
||||||
"clear_file_cache": "مسح ذاكرة التخزين المؤقت للملفات",
|
"clear_file_cache": "مسح ذاكرة التخزين المؤقت للملفات",
|
||||||
"clear_message": "إخلاء الرسالة",
|
"clear_message": "إخلاء الرسالة",
|
||||||
"clear_value": "إخلاء القيمة",
|
"clear_value": "إخلاء القيمة",
|
||||||
@@ -805,6 +838,7 @@
|
|||||||
"comments_are_disabled": "التعليقات معطلة",
|
"comments_are_disabled": "التعليقات معطلة",
|
||||||
"common_create_new_album": "إنشاء ألبوم جديد",
|
"common_create_new_album": "إنشاء ألبوم جديد",
|
||||||
"completed": "اكتمل",
|
"completed": "اكتمل",
|
||||||
|
"configuration": "اعدادات",
|
||||||
"confirm": "تأكيد",
|
"confirm": "تأكيد",
|
||||||
"confirm_admin_password": "تأكيد كلمة مرور المسؤول",
|
"confirm_admin_password": "تأكيد كلمة مرور المسؤول",
|
||||||
"confirm_delete_face": "هل أنت متأكد من حذف وجه {name} من الأصول؟",
|
"confirm_delete_face": "هل أنت متأكد من حذف وجه {name} من الأصول؟",
|
||||||
@@ -819,6 +853,7 @@
|
|||||||
"contain": "محتواة",
|
"contain": "محتواة",
|
||||||
"context": "السياق",
|
"context": "السياق",
|
||||||
"continue": "متابعة",
|
"continue": "متابعة",
|
||||||
|
"control_bottom_app_bar_add_tags": "اضافة علامات",
|
||||||
"control_bottom_app_bar_create_new_album": "إنشاء ألبوم جديد",
|
"control_bottom_app_bar_create_new_album": "إنشاء ألبوم جديد",
|
||||||
"control_bottom_app_bar_delete_from_immich": "حذف من Immich",
|
"control_bottom_app_bar_delete_from_immich": "حذف من Immich",
|
||||||
"control_bottom_app_bar_delete_from_local": "حذف من الجهاز",
|
"control_bottom_app_bar_delete_from_local": "حذف من الجهاز",
|
||||||
@@ -832,6 +867,7 @@
|
|||||||
"copy_error": "نسخ الخطأ",
|
"copy_error": "نسخ الخطأ",
|
||||||
"copy_file_path": "نسخ مسار الملف",
|
"copy_file_path": "نسخ مسار الملف",
|
||||||
"copy_image": "نسخ الصورة",
|
"copy_image": "نسخ الصورة",
|
||||||
|
"copy_json": "نسخ JSON",
|
||||||
"copy_link": "نسخ الرابط",
|
"copy_link": "نسخ الرابط",
|
||||||
"copy_link_to_clipboard": "انسخ الرابط إلى الحافظة",
|
"copy_link_to_clipboard": "انسخ الرابط إلى الحافظة",
|
||||||
"copy_password": "نسخ كلمة المرور",
|
"copy_password": "نسخ كلمة المرور",
|
||||||
@@ -881,22 +917,23 @@
|
|||||||
"cutoff_date_description": "احتفظ بالصور من آخر…",
|
"cutoff_date_description": "احتفظ بالصور من آخر…",
|
||||||
"cutoff_day": "{count, plural, one {يوم} other {ايام}}",
|
"cutoff_day": "{count, plural, one {يوم} other {ايام}}",
|
||||||
"cutoff_year": "{count, plural, one {سنة} other {سنوات}}",
|
"cutoff_year": "{count, plural, one {سنة} other {سنوات}}",
|
||||||
"daily_title_text_date": "E ، MMM DD",
|
|
||||||
"daily_title_text_date_year": "E ، MMM DD ، yyyy",
|
|
||||||
"dark": "معتم",
|
"dark": "معتم",
|
||||||
"dark_theme": "تبديل المظهر إلى الداكن",
|
"dark_theme": "تبديل المظهر إلى الداكن",
|
||||||
"date": "تاريخ",
|
"date": "تاريخ",
|
||||||
"date_after": "التارخ بعد",
|
"date_after": "التارخ بعد",
|
||||||
"date_and_time": "التاريخ و الوقت",
|
"date_and_time": "التاريخ و الوقت",
|
||||||
"date_before": "التاريخ قبل",
|
"date_before": "التاريخ قبل",
|
||||||
"date_format": "E ، Lll D ، Y • H: MM A",
|
"date_of_birth": "تاريخ الميلاد",
|
||||||
"date_of_birth_saved": "تم حفظ تاريخ الميلاد بنجاح",
|
"date_of_birth_saved": "تم حفظ تاريخ الميلاد بنجاح",
|
||||||
"date_range": "نطاق الموعد",
|
"date_range": "نطاق الموعد",
|
||||||
|
"date_time_original": "تاريخ/وقت الاصلي",
|
||||||
"day": "يوم",
|
"day": "يوم",
|
||||||
"days": "ايام",
|
"days": "ايام",
|
||||||
"deduplicate_all": "إلغاء تكرار الكل",
|
"deduplicate_all": "إلغاء تكرار الكل",
|
||||||
"default_locale": "الإعدادات المحلية الافتراضية",
|
"default_locale": "الإعدادات المحلية الافتراضية",
|
||||||
"default_locale_description": "تنسيق التواريخ والأرقام بناءً على الإعدادات المحلية للمتصفح",
|
"default_locale_description": "تنسيق التواريخ والأرقام بناءً على الإعدادات المحلية للمتصفح",
|
||||||
|
"default_quality_subtitle": "الجودة المستخدمة عند الضغط على \"مشاركة\". اضغط مطولاً على زر المشاركة لاختيار الجودة في كل مرة.",
|
||||||
|
"default_share_quality": "جودة المشاركة الافتراضية",
|
||||||
"delete": "حذف",
|
"delete": "حذف",
|
||||||
"delete_action_confirmation_message": "هل انت متأكد من حذف هذا الملف؟ هذا سؤدي الى نقل الملف الى سلة مهملات الخادم وسيتم اشعارك ان كنت تريد حذفه على الجهاز",
|
"delete_action_confirmation_message": "هل انت متأكد من حذف هذا الملف؟ هذا سؤدي الى نقل الملف الى سلة مهملات الخادم وسيتم اشعارك ان كنت تريد حذفه على الجهاز",
|
||||||
"delete_action_prompt": "تم حذف {count}",
|
"delete_action_prompt": "تم حذف {count}",
|
||||||
@@ -970,7 +1007,10 @@
|
|||||||
"downloading_asset_filename": "جاري تنزيل الاصل {filename}",
|
"downloading_asset_filename": "جاري تنزيل الاصل {filename}",
|
||||||
"downloading_from_icloud": "التنزيل من iCloud",
|
"downloading_from_icloud": "التنزيل من iCloud",
|
||||||
"downloading_media": "تنزيل الوسائط",
|
"downloading_media": "تنزيل الوسائط",
|
||||||
|
"drag_to_reorder": "اسحب لإعادة الترتيب",
|
||||||
"drop_files_to_upload": "قم بإسقاط الملفات في أي مكان لرفعها",
|
"drop_files_to_upload": "قم بإسقاط الملفات في أي مكان لرفعها",
|
||||||
|
"duplicate": "تكرار",
|
||||||
|
"duplicate_workflow": "تكرار سير العمل",
|
||||||
"duplicates": "التكرارات",
|
"duplicates": "التكرارات",
|
||||||
"duplicates_description": "قم بحل كل مجموعة من خلال الإشارة إلى التكرارات، إن وجدت.",
|
"duplicates_description": "قم بحل كل مجموعة من خلال الإشارة إلى التكرارات، إن وجدت.",
|
||||||
"duration": "المدة",
|
"duration": "المدة",
|
||||||
@@ -1072,6 +1112,7 @@
|
|||||||
"failed_to_remove_product_key": "تعذر إزالة مفتاح المنتج",
|
"failed_to_remove_product_key": "تعذر إزالة مفتاح المنتج",
|
||||||
"failed_to_reset_pin_code": "فشل اعادة تعيين رمز الPIN",
|
"failed_to_reset_pin_code": "فشل اعادة تعيين رمز الPIN",
|
||||||
"failed_to_stack_assets": "فشل في تكديس المحتويات",
|
"failed_to_stack_assets": "فشل في تكديس المحتويات",
|
||||||
|
"failed_to_tag_assets": "فشل في وضع علامات على الأصول",
|
||||||
"failed_to_unstack_assets": "فشل في فصل المحتويات",
|
"failed_to_unstack_assets": "فشل في فصل المحتويات",
|
||||||
"failed_to_update_notification_status": "فشل في تحديث حالة الإشعار",
|
"failed_to_update_notification_status": "فشل في تحديث حالة الإشعار",
|
||||||
"incorrect_email_or_password": "بريد أو كلمة مرور غير صحيحة",
|
"incorrect_email_or_password": "بريد أو كلمة مرور غير صحيحة",
|
||||||
@@ -1191,15 +1232,18 @@
|
|||||||
"export_as_json": "تصدير كـ JSON",
|
"export_as_json": "تصدير كـ JSON",
|
||||||
"export_database": "تصدير قاعدة البيانات",
|
"export_database": "تصدير قاعدة البيانات",
|
||||||
"export_database_description": "تصدير قاعدة البيانات من نوع SQLite",
|
"export_database_description": "تصدير قاعدة البيانات من نوع SQLite",
|
||||||
|
"exposure_time": "وقت التعرض",
|
||||||
"extension": "الإمتداد",
|
"extension": "الإمتداد",
|
||||||
"external": "خارجي",
|
"external": "خارجي",
|
||||||
"external_libraries": "المكتبات الخارجية",
|
"external_libraries": "المكتبات الخارجية",
|
||||||
"external_network": "شبكة خارجية",
|
"external_network": "شبكة خارجية",
|
||||||
"external_network_sheet_info": "عندما لا يتواجد على شبكة Wi-Fi المفضلة، فإنه سيتصل بالخادم من خلال أول عناوين URL أدناه التي يمكنه الوصول إليها، بدءًا من الأعلى إلى الأسفل",
|
"external_network_sheet_info": "عندما لا يتواجد على شبكة Wi-Fi المفضلة، فإنه سيتصل بالخادم من خلال أول عناوين URL أدناه التي يمكنه الوصول إليها، بدءًا من الأعلى إلى الأسفل",
|
||||||
|
"f_number": "الفتحة البؤرية",
|
||||||
"face_unassigned": "غير معين",
|
"face_unassigned": "غير معين",
|
||||||
"failed": "فشل",
|
"failed": "فشل",
|
||||||
"failed_count": "فشل: {count}",
|
"failed_count": "فشل: {count}",
|
||||||
"failed_to_authenticate": "فشل في المصادقة",
|
"failed_to_authenticate": "فشل في المصادقة",
|
||||||
|
"failed_to_delete_file": "فشل حذف الملف",
|
||||||
"failed_to_load_assets": "فشل تحميل الأصول",
|
"failed_to_load_assets": "فشل تحميل الأصول",
|
||||||
"failed_to_load_folder": "فشل تحميل المجلد",
|
"failed_to_load_folder": "فشل تحميل المجلد",
|
||||||
"favorite": "مفضل",
|
"favorite": "مفضل",
|
||||||
@@ -1213,7 +1257,6 @@
|
|||||||
"features_setting_description": "إدارة ميزات التطبيق",
|
"features_setting_description": "إدارة ميزات التطبيق",
|
||||||
"file_name_or_extension": "اسم الملف أو امتداده",
|
"file_name_or_extension": "اسم الملف أو امتداده",
|
||||||
"file_name_text": "أسم الملف",
|
"file_name_text": "أسم الملف",
|
||||||
"file_name_with_value": "اسم الملف: {file_name}",
|
|
||||||
"file_size": "حجم الملف",
|
"file_size": "حجم الملف",
|
||||||
"filename": "اسم الملف",
|
"filename": "اسم الملف",
|
||||||
"filetype": "نوع الملف",
|
"filetype": "نوع الملف",
|
||||||
@@ -1226,6 +1269,7 @@
|
|||||||
"find_them_fast": "يمكنك العثور عليها بسرعة بالاسم من خلال البحث",
|
"find_them_fast": "يمكنك العثور عليها بسرعة بالاسم من خلال البحث",
|
||||||
"first": "الاول",
|
"first": "الاول",
|
||||||
"fix_incorrect_match": "إصلاح المطابقة غير الصحيحة",
|
"fix_incorrect_match": "إصلاح المطابقة غير الصحيحة",
|
||||||
|
"focal_length": "البعد البؤري",
|
||||||
"folder": "مجلد",
|
"folder": "مجلد",
|
||||||
"folder_not_found": "لم يتم العثور على المجلد",
|
"folder_not_found": "لم يتم العثور على المجلد",
|
||||||
"folders": "المجلدات",
|
"folders": "المجلدات",
|
||||||
@@ -1236,6 +1280,7 @@
|
|||||||
"free_up_space_description": "نقل الصور والفديوات التي تم خزنها احتياطياالى سلة المهملات الخاصه بجهازك لتحرير المساحة. نسخك على اىخادم ستبقى بأمان.",
|
"free_up_space_description": "نقل الصور والفديوات التي تم خزنها احتياطياالى سلة المهملات الخاصه بجهازك لتحرير المساحة. نسخك على اىخادم ستبقى بأمان.",
|
||||||
"free_up_space_settings_subtitle": "تحرير خزن الجهاز",
|
"free_up_space_settings_subtitle": "تحرير خزن الجهاز",
|
||||||
"full_path": "مسار كامل:{path}",
|
"full_path": "مسار كامل:{path}",
|
||||||
|
"full_path_or_folder": "المسار الكامل او المجلد",
|
||||||
"gcast_enabled": "كوكل كاست",
|
"gcast_enabled": "كوكل كاست",
|
||||||
"gcast_enabled_description": "تقوم هذه الميزة بتحميل الموارد الخارجية من Google حتى تعمل.",
|
"gcast_enabled_description": "تقوم هذه الميزة بتحميل الموارد الخارجية من Google حتى تعمل.",
|
||||||
"general": "عام",
|
"general": "عام",
|
||||||
@@ -1329,6 +1374,7 @@
|
|||||||
"individual_share": "حصة فردية",
|
"individual_share": "حصة فردية",
|
||||||
"individual_shares": "المشاركات الفردية",
|
"individual_shares": "المشاركات الفردية",
|
||||||
"info": "معلومات",
|
"info": "معلومات",
|
||||||
|
"integrity_checks": "فحوصات السلامة",
|
||||||
"interval": {
|
"interval": {
|
||||||
"day_at_onepm": "كل يوم الساعة الواحدة ظهرا",
|
"day_at_onepm": "كل يوم الساعة الواحدة ظهرا",
|
||||||
"hours": "كل {hours, plural, one {ساعة} other {{hours, number} ساعة}}",
|
"hours": "كل {hours, plural, one {ساعة} other {{hours, number} ساعة}}",
|
||||||
@@ -1345,6 +1391,7 @@
|
|||||||
"ios_debug_info_no_sync_yet": "لم يتم تشغيل أي مهمة مزامنة في الخلفية حتى الآن",
|
"ios_debug_info_no_sync_yet": "لم يتم تشغيل أي مهمة مزامنة في الخلفية حتى الآن",
|
||||||
"ios_debug_info_processes_queued": "{count, plural, one {{count} عملية خلفية ادخلتةفي طابور} other {{count} عمليات خلفية ادخلت في طابور}}",
|
"ios_debug_info_processes_queued": "{count, plural, one {{count} عملية خلفية ادخلتةفي طابور} other {{count} عمليات خلفية ادخلت في طابور}}",
|
||||||
"ios_debug_info_processing_ran_at": "المعالجة جرت في {dateTime}",
|
"ios_debug_info_processing_ran_at": "المعالجة جرت في {dateTime}",
|
||||||
|
"iso": "ISO",
|
||||||
"items_count": "{count, plural, one {# عنصر} other {# عناصر}}",
|
"items_count": "{count, plural, one {# عنصر} other {# عناصر}}",
|
||||||
"jobs": "الوظائف",
|
"jobs": "الوظائف",
|
||||||
"json_editor": "محرر JSON",
|
"json_editor": "محرر JSON",
|
||||||
@@ -1375,6 +1422,7 @@
|
|||||||
"leave": "مغادرة",
|
"leave": "مغادرة",
|
||||||
"leave_album": "اترك الالبوم",
|
"leave_album": "اترك الالبوم",
|
||||||
"lens_model": "نموذج العدسات",
|
"lens_model": "نموذج العدسات",
|
||||||
|
"less": "أقل",
|
||||||
"let_others_respond": "دع الآخرين يستجيبون",
|
"let_others_respond": "دع الآخرين يستجيبون",
|
||||||
"level": "المستوى",
|
"level": "المستوى",
|
||||||
"library": "مكتبة",
|
"library": "مكتبة",
|
||||||
@@ -1392,11 +1440,14 @@
|
|||||||
"light_theme": "التبديل إلى المظهر الفاتح",
|
"light_theme": "التبديل إلى المظهر الفاتح",
|
||||||
"like": "اعجاب",
|
"like": "اعجاب",
|
||||||
"like_deleted": "تم حذف الإعجاب",
|
"like_deleted": "تم حذف الإعجاب",
|
||||||
|
"link": "رابط",
|
||||||
"link_motion_video": "رابط فيديو الحركة",
|
"link_motion_video": "رابط فيديو الحركة",
|
||||||
"link_to_docs": "لمزيد من المعلومات، يُرجى الرجوع إلى <link>الوثائق</link>.",
|
"link_to_docs": "لمزيد من المعلومات، يُرجى الرجوع إلى <link>الوثائق</link>.",
|
||||||
"link_to_oauth": "الربط مع OAuth",
|
"link_to_oauth": "الربط مع OAuth",
|
||||||
"linked_oauth_account": "حساب مرتبط بـ OAuth",
|
"linked_oauth_account": "حساب مرتبط بـ OAuth",
|
||||||
"list": "قائمة",
|
"list": "قائمة",
|
||||||
|
"live": "حي",
|
||||||
|
"load_more": "تحميل المزيد",
|
||||||
"loading": "تحميل",
|
"loading": "تحميل",
|
||||||
"loading_search_results_failed": "فشل تحميل نتائج البحث",
|
"loading_search_results_failed": "فشل تحميل نتائج البحث",
|
||||||
"local": "محلّي",
|
"local": "محلّي",
|
||||||
@@ -1497,7 +1548,6 @@
|
|||||||
"map_location_picker_page_use_location": "استخدم هذا الموقع",
|
"map_location_picker_page_use_location": "استخدم هذا الموقع",
|
||||||
"map_location_service_disabled_content": "يجب تمكين خدمة الموقع لعرض الأصول من موقعك الحالي.هل تريد تمكينه الآن؟",
|
"map_location_service_disabled_content": "يجب تمكين خدمة الموقع لعرض الأصول من موقعك الحالي.هل تريد تمكينه الآن؟",
|
||||||
"map_location_service_disabled_title": "خدمة الموقع معطل",
|
"map_location_service_disabled_title": "خدمة الموقع معطل",
|
||||||
"map_marker_for_images": "علامة الخريطة للصور الملتقطة في {city}، {country}",
|
|
||||||
"map_marker_with_image": "علامة الخريطة مع الصورة",
|
"map_marker_with_image": "علامة الخريطة مع الصورة",
|
||||||
"map_no_location_permission_content": "هناك حاجة إلى إذن الموقع لعرض الأصول من موقعك الحالي.هل تريد السماح به الآن؟",
|
"map_no_location_permission_content": "هناك حاجة إلى إذن الموقع لعرض الأصول من موقعك الحالي.هل تريد السماح به الآن؟",
|
||||||
"map_no_location_permission_title": "تم رفض إذن الموقع",
|
"map_no_location_permission_title": "تم رفض إذن الموقع",
|
||||||
@@ -1518,6 +1568,38 @@
|
|||||||
"marked_all_as_read": "تم تحديد الكل كمقروء",
|
"marked_all_as_read": "تم تحديد الكل كمقروء",
|
||||||
"matches": "تطابقات",
|
"matches": "تطابقات",
|
||||||
"matching_assets": "الاصول المطابقة",
|
"matching_assets": "الاصول المطابقة",
|
||||||
|
"media_chrome": {
|
||||||
|
"auto": "تلقائي",
|
||||||
|
"captions": "التعليقات التوضيحية",
|
||||||
|
"captions_off": "اطفاء",
|
||||||
|
"closed_captions": "الترجمة المرئية المغلقة",
|
||||||
|
"decode_error": "فشل فك التشفير",
|
||||||
|
"disable_captions": "تعطيل الترجمة",
|
||||||
|
"enable_captions": "تفعيل الترجمة",
|
||||||
|
"enter_fullscreen_mode": "ادخل إلى وضع ملء الشاشة",
|
||||||
|
"exit_fullscreen_mode": "اخرج من وضع ملء الشاشة",
|
||||||
|
"loop": "حلقة",
|
||||||
|
"media_error_description": "خطأ في الوسائط تسبب في ايقاف التشغيل. قد تكون الوسائط تالفة او ان متصفحك لا يدعم الصيغة.",
|
||||||
|
"media_loading": "تحميل الوسائط",
|
||||||
|
"mute": "كتم",
|
||||||
|
"network_error": "خطأ في الشبكة",
|
||||||
|
"network_error_description": "خطأ في الشبكة تسبب في فشل تنزيل الوسائط.",
|
||||||
|
"not_supported_error": "المصدر غير مدعوم",
|
||||||
|
"playback_rate": "معدل التشغيل",
|
||||||
|
"playback_rate_current": "معدل التشغيل الحالي",
|
||||||
|
"playback_rate_value": "معدل التشغيل {playbackRate}",
|
||||||
|
"playback_time": "وقت التشغيل",
|
||||||
|
"quality": "جودة",
|
||||||
|
"second": "ثانية",
|
||||||
|
"seconds": "ثواني",
|
||||||
|
"time_value_of_total_time": "{currentTime} من {totalTime}",
|
||||||
|
"time_value_remaining": "{time} متبقي",
|
||||||
|
"unmute": "الغاء الكتم",
|
||||||
|
"unsupported_error_description": "حدث خطأ غير مدعوم. حدث فشل في الخادم او الشبكة, او المتصفح الخاص بك لا يدعم هذه الصيغة.",
|
||||||
|
"video_not_loaded_unknown_time": "الفيديو غير محمل, الوقت غير معلوم.",
|
||||||
|
"video_player": "مشغل الفيديو",
|
||||||
|
"volume": "حجم"
|
||||||
|
},
|
||||||
"media_type": "نوع الوسائط",
|
"media_type": "نوع الوسائط",
|
||||||
"memories": "الذكريات",
|
"memories": "الذكريات",
|
||||||
"memories_all_caught_up": "كل شيء محدث",
|
"memories_all_caught_up": "كل شيء محدث",
|
||||||
@@ -1534,6 +1616,8 @@
|
|||||||
"merge_people_prompt": "هل تريد دمج هؤلاء الناس؟ هذا الإجراء لا رجعة فيه.",
|
"merge_people_prompt": "هل تريد دمج هؤلاء الناس؟ هذا الإجراء لا رجعة فيه.",
|
||||||
"merge_people_successfully": "تم دمج الأشخاص بنجاح",
|
"merge_people_successfully": "تم دمج الأشخاص بنجاح",
|
||||||
"merged_people_count": "دمج {count, plural, one {شخص واحد} other {# أشخاص}}",
|
"merged_people_count": "دمج {count, plural, one {شخص واحد} other {# أشخاص}}",
|
||||||
|
"minFaces": "الحد الأدنى للوجوه",
|
||||||
|
"minFaces_description": "الحد الأدنى لعدد الوجوه المتعرف عليها لكي يتم عرض الشخص",
|
||||||
"minimize": "تصغير",
|
"minimize": "تصغير",
|
||||||
"minute": "دقيقة",
|
"minute": "دقيقة",
|
||||||
"minutes": "دقائق",
|
"minutes": "دقائق",
|
||||||
@@ -1543,9 +1627,10 @@
|
|||||||
"mobile_app": "تطبيق الجوال",
|
"mobile_app": "تطبيق الجوال",
|
||||||
"mobile_app_download_onboarding_note": "قم بتنزيل التطبيق المصاحب للهاتف المحمول باستخدام الخيارات التالية",
|
"mobile_app_download_onboarding_note": "قم بتنزيل التطبيق المصاحب للهاتف المحمول باستخدام الخيارات التالية",
|
||||||
"model": "نموذج",
|
"model": "نموذج",
|
||||||
|
"modify_date": "تغيير التاريخ",
|
||||||
"month": "شهر",
|
"month": "شهر",
|
||||||
"monthly_title_text_date_format": "ط ط ط",
|
|
||||||
"more": "المزيد",
|
"more": "المزيد",
|
||||||
|
"motion": "حركة",
|
||||||
"move": "تحريك",
|
"move": "تحريك",
|
||||||
"move_down": "انزل الى الاسفل",
|
"move_down": "انزل الى الاسفل",
|
||||||
"move_off_locked_folder": "تحريك خارج المجلد المقفل",
|
"move_off_locked_folder": "تحريك خارج المجلد المقفل",
|
||||||
@@ -1562,6 +1647,8 @@
|
|||||||
"multiselect_grid_edit_gps_err_read_only": "لا يمكن تعديل موقع الأصول (المواد) للقراءة فقط، سوف يتخطى",
|
"multiselect_grid_edit_gps_err_read_only": "لا يمكن تعديل موقع الأصول (المواد) للقراءة فقط، سوف يتخطى",
|
||||||
"mute_memories": "كتم الذكريات",
|
"mute_memories": "كتم الذكريات",
|
||||||
"my_albums": "ألبوماتي",
|
"my_albums": "ألبوماتي",
|
||||||
|
"my_immich_description": "نسخ الصفحة الحالية كرابط Immich الخاص بي",
|
||||||
|
"my_immich_title": "رابط Immich الخاص بي",
|
||||||
"name": "الاسم",
|
"name": "الاسم",
|
||||||
"name_or_nickname": "الاسم أو اللقب",
|
"name_or_nickname": "الاسم أو اللقب",
|
||||||
"name_required": "الاسم مطلوب",
|
"name_required": "الاسم مطلوب",
|
||||||
@@ -1589,7 +1676,6 @@
|
|||||||
"next": "التالي",
|
"next": "التالي",
|
||||||
"next_memory": "الذكرى التالية",
|
"next_memory": "الذكرى التالية",
|
||||||
"no": "لا",
|
"no": "لا",
|
||||||
"no_actions_added": "لم تتم إضافة إجراءات حتى الان",
|
|
||||||
"no_albums_found": "لم يتم ايجاد البومات",
|
"no_albums_found": "لم يتم ايجاد البومات",
|
||||||
"no_albums_message": "قم بإنشاء ألبوم لتنظيم الصور ومقاطع الفيديو الخاصة بك",
|
"no_albums_message": "قم بإنشاء ألبوم لتنظيم الصور ومقاطع الفيديو الخاصة بك",
|
||||||
"no_albums_with_name_yet": "يبدو أنه ليس لديك أي ألبومات بهذا الاسم حتى الآن.",
|
"no_albums_with_name_yet": "يبدو أنه ليس لديك أي ألبومات بهذا الاسم حتى الآن.",
|
||||||
@@ -1606,7 +1692,6 @@
|
|||||||
"no_exif_info_available": "لا تتوفر معلومات exif",
|
"no_exif_info_available": "لا تتوفر معلومات exif",
|
||||||
"no_explore_results_message": "قم برفع المزيد من الصور لاستكشاف مجموعتك.",
|
"no_explore_results_message": "قم برفع المزيد من الصور لاستكشاف مجموعتك.",
|
||||||
"no_favorites_message": "أضف المفضلة للعثور بسرعة على أفضل الصور ومقاطع الفيديو",
|
"no_favorites_message": "أضف المفضلة للعثور بسرعة على أفضل الصور ومقاطع الفيديو",
|
||||||
"no_filters_added": "لم تتم إضافة أي فلتر بعد",
|
|
||||||
"no_libraries_message": "إنشاء مكتبة خارجية لعرض الصور ومقاطع الفيديو الخاصة بك",
|
"no_libraries_message": "إنشاء مكتبة خارجية لعرض الصور ومقاطع الفيديو الخاصة بك",
|
||||||
"no_local_assets_found": "لم يتم العثور على أي اصول محلية تتطابق مع قيمة التحقق هذه",
|
"no_local_assets_found": "لم يتم العثور على أي اصول محلية تتطابق مع قيمة التحقق هذه",
|
||||||
"no_location_set": "لم يتم تحديد موقع",
|
"no_location_set": "لم يتم تحديد موقع",
|
||||||
@@ -1619,6 +1704,7 @@
|
|||||||
"no_results": "لا يوجد نتائج",
|
"no_results": "لا يوجد نتائج",
|
||||||
"no_results_description": "جرب كلمة رئيسية مرادفة أو أكثر عمومية",
|
"no_results_description": "جرب كلمة رئيسية مرادفة أو أكثر عمومية",
|
||||||
"no_shared_albums_message": "قم بإنشاء ألبوم لمشاركة الصور ومقاطع الفيديو مع الأشخاص في شبكتك",
|
"no_shared_albums_message": "قم بإنشاء ألبوم لمشاركة الصور ومقاطع الفيديو مع الأشخاص في شبكتك",
|
||||||
|
"no_steps": "لم يتم اضافة خطوات بعد",
|
||||||
"no_uploads_in_progress": "لا يوجد اي ملفات قيد الرفع",
|
"no_uploads_in_progress": "لا يوجد اي ملفات قيد الرفع",
|
||||||
"none": "لا يوجد",
|
"none": "لا يوجد",
|
||||||
"not_allowed": "غير مسموح",
|
"not_allowed": "غير مسموح",
|
||||||
@@ -1627,6 +1713,7 @@
|
|||||||
"not_selected": "لم يختار",
|
"not_selected": "لم يختار",
|
||||||
"notes": "ملاحظات",
|
"notes": "ملاحظات",
|
||||||
"nothing_here_yet": "لا يوجد شيء هنا بعد",
|
"nothing_here_yet": "لا يوجد شيء هنا بعد",
|
||||||
|
"notification_backup_reliability": "فعل الإشعارات للزيادة من استقرار النسخ الاحتياطي في الخلفية",
|
||||||
"notification_permission_dialog_content": "لتمكين الإخطارات ، انتقل إلى الإعدادات و اختار السماح.",
|
"notification_permission_dialog_content": "لتمكين الإخطارات ، انتقل إلى الإعدادات و اختار السماح.",
|
||||||
"notification_permission_list_tile_content": "منح إذن لتمكين الإخطارات.",
|
"notification_permission_list_tile_content": "منح إذن لتمكين الإخطارات.",
|
||||||
"notification_permission_list_tile_enable_button": "تمكين الإخطارات",
|
"notification_permission_list_tile_enable_button": "تمكين الإخطارات",
|
||||||
@@ -1664,6 +1751,7 @@
|
|||||||
"organize_into_albums": "ترتيب في ألبومات",
|
"organize_into_albums": "ترتيب في ألبومات",
|
||||||
"organize_into_albums_description": "أضف الصور الموجودة إلى الألبومات باستخدام إعدادات النسخ المتزامن الحالية",
|
"organize_into_albums_description": "أضف الصور الموجودة إلى الألبومات باستخدام إعدادات النسخ المتزامن الحالية",
|
||||||
"organize_your_library": "تنظيم مكتبتك",
|
"organize_your_library": "تنظيم مكتبتك",
|
||||||
|
"orientation": "اتجاه",
|
||||||
"original": "أصلي",
|
"original": "أصلي",
|
||||||
"other": "أخرى",
|
"other": "أخرى",
|
||||||
"other_devices": "أجهزة أخرى",
|
"other_devices": "أجهزة أخرى",
|
||||||
@@ -1755,6 +1843,8 @@
|
|||||||
"play_original_video_setting_description": "تفضيل تشغيل مقاطع الفيديو الأصلية بدلاً من مقاطع الفيديو المحولة. إذا لم يكن الملف الأصلي متوافقًا، فقد لا يتم تشغيله بشكل صحيح.",
|
"play_original_video_setting_description": "تفضيل تشغيل مقاطع الفيديو الأصلية بدلاً من مقاطع الفيديو المحولة. إذا لم يكن الملف الأصلي متوافقًا، فقد لا يتم تشغيله بشكل صحيح.",
|
||||||
"play_transcoded_video": "تشغيل الفيديو المُعاد ترميزه",
|
"play_transcoded_video": "تشغيل الفيديو المُعاد ترميزه",
|
||||||
"please_auth_to_access": "الرجاء القيام بالمصادقة للوصول",
|
"please_auth_to_access": "الرجاء القيام بالمصادقة للوصول",
|
||||||
|
"plugin_method_filter_type": "تصفية",
|
||||||
|
"plugin_method_filter_type_description": "هذه الوسيلة تستطيع تصفية الاحداث و تمنع تشغيل الخطوات التالية شرطيا",
|
||||||
"port": "المنفذ",
|
"port": "المنفذ",
|
||||||
"preferences_settings_subtitle": "ادارة تفضيلات التطبيق",
|
"preferences_settings_subtitle": "ادارة تفضيلات التطبيق",
|
||||||
"preferences_settings_title": "التفضيلات",
|
"preferences_settings_title": "التفضيلات",
|
||||||
@@ -1776,6 +1866,7 @@
|
|||||||
"profile_drawer_readonly_mode": "تم تفعيل وضع القراءة فقط. اضغط مطولا على رمز صورة المستخدم للخروج.",
|
"profile_drawer_readonly_mode": "تم تفعيل وضع القراءة فقط. اضغط مطولا على رمز صورة المستخدم للخروج.",
|
||||||
"profile_image_of_user": "صورة الملف الشخصي لـ {user}",
|
"profile_image_of_user": "صورة الملف الشخصي لـ {user}",
|
||||||
"profile_picture_set": "مجموعة الصور الشخصية.",
|
"profile_picture_set": "مجموعة الصور الشخصية.",
|
||||||
|
"projection_type": "نوع العرض",
|
||||||
"public_album": "الألبوم العام",
|
"public_album": "الألبوم العام",
|
||||||
"public_share": "مشاركة عامة",
|
"public_share": "مشاركة عامة",
|
||||||
"purchase_account_info": "داعم",
|
"purchase_account_info": "داعم",
|
||||||
@@ -1853,6 +1944,7 @@
|
|||||||
"remove_assets_title": "هل تريد إزالة المحتويات؟",
|
"remove_assets_title": "هل تريد إزالة المحتويات؟",
|
||||||
"remove_custom_date_range": "إزالة النطاق الزمني المخصص",
|
"remove_custom_date_range": "إزالة النطاق الزمني المخصص",
|
||||||
"remove_deleted_assets": "إزالة الملفات الغير متصلة",
|
"remove_deleted_assets": "إزالة الملفات الغير متصلة",
|
||||||
|
"remove_filter": "ازالة التصفية",
|
||||||
"remove_from_album": "إزالة من الألبوم",
|
"remove_from_album": "إزالة من الألبوم",
|
||||||
"remove_from_album_action_prompt": "تم ازالة {count} من الالبوم",
|
"remove_from_album_action_prompt": "تم ازالة {count} من الالبوم",
|
||||||
"remove_from_favorites": "إزالة من المفضلة",
|
"remove_from_favorites": "إزالة من المفضلة",
|
||||||
@@ -1926,6 +2018,8 @@
|
|||||||
"scan_settings": "إعدادات الفحص",
|
"scan_settings": "إعدادات الفحص",
|
||||||
"scanning": "جاري البحث",
|
"scanning": "جاري البحث",
|
||||||
"scanning_for_album": "جارٍ الفحص عن ألبوم...",
|
"scanning_for_album": "جارٍ الفحص عن ألبوم...",
|
||||||
|
"screencast_mode_description": "إظهار مؤشرات أحداث لوحة المفاتيح والماوس على الشاشة",
|
||||||
|
"screencast_mode_title": "تبديل وضع تسجيل الشاشة",
|
||||||
"search": "البحث",
|
"search": "البحث",
|
||||||
"search_albums": "البحث في الألبومات",
|
"search_albums": "البحث في الألبومات",
|
||||||
"search_by_context": "البحث حسب السياق",
|
"search_by_context": "البحث حسب السياق",
|
||||||
@@ -1933,6 +2027,8 @@
|
|||||||
"search_by_description_example": "يوم المشي لمسافات طويلة في سابا",
|
"search_by_description_example": "يوم المشي لمسافات طويلة في سابا",
|
||||||
"search_by_filename": "البحث بإسم الملف أو نوعه",
|
"search_by_filename": "البحث بإسم الملف أو نوعه",
|
||||||
"search_by_filename_example": "كـ IMG_1234.JPG أو PNG",
|
"search_by_filename_example": "كـ IMG_1234.JPG أو PNG",
|
||||||
|
"search_by_full_path": "بحث بالمسار الكامل او المجلد",
|
||||||
|
"search_by_full_path_example": "/احمد/مشاريع/طباعة_ثلاثية_الابعاد/2026-07-01 - يمكنك البحث عن مشاريع, طباعة_ثلاثية_الابعاد, 2026 الخ.",
|
||||||
"search_by_ocr": "البحث عن طريق التعرف البصري على الحروف",
|
"search_by_ocr": "البحث عن طريق التعرف البصري على الحروف",
|
||||||
"search_by_ocr_example": "لاتيه",
|
"search_by_ocr_example": "لاتيه",
|
||||||
"search_camera_lens_model": "بحث نموذج العدسة...",
|
"search_camera_lens_model": "بحث نموذج العدسة...",
|
||||||
@@ -2009,6 +2105,7 @@
|
|||||||
"select_person": "اختر شخص",
|
"select_person": "اختر شخص",
|
||||||
"select_person_to_tag": "اختر شخص لوضع علامة",
|
"select_person_to_tag": "اختر شخص لوضع علامة",
|
||||||
"select_photos": "تحديد الصور",
|
"select_photos": "تحديد الصور",
|
||||||
|
"select_quality": "تحديد الدقة",
|
||||||
"select_trash_all": "تحديد حذف الكلِ",
|
"select_trash_all": "تحديد حذف الكلِ",
|
||||||
"select_user_for_sharing_page_err_album": "فشل في إنشاء ألبوم",
|
"select_user_for_sharing_page_err_album": "فشل في إنشاء ألبوم",
|
||||||
"selected": "التحديد",
|
"selected": "التحديد",
|
||||||
@@ -2072,6 +2169,8 @@
|
|||||||
"share_assets_selected": "اختيار {count}",
|
"share_assets_selected": "اختيار {count}",
|
||||||
"share_dialog_preparing": "تحضير...",
|
"share_dialog_preparing": "تحضير...",
|
||||||
"share_link": "مشاركة رابط",
|
"share_link": "مشاركة رابط",
|
||||||
|
"share_original": "استخدام الملف الأصلي",
|
||||||
|
"share_preview": "استخدام الصورة المصغرة",
|
||||||
"shared": "مُشتَرك",
|
"shared": "مُشتَرك",
|
||||||
"shared_album_activities_input_disable": "التعليق معطل",
|
"shared_album_activities_input_disable": "التعليق معطل",
|
||||||
"shared_album_activity_remove_content": "هل تريد حذف هذا النشاط؟",
|
"shared_album_activity_remove_content": "هل تريد حذف هذا النشاط؟",
|
||||||
@@ -2140,7 +2239,9 @@
|
|||||||
"show_in_timeline": "إظهار في المخطط الزمني",
|
"show_in_timeline": "إظهار في المخطط الزمني",
|
||||||
"show_in_timeline_setting_description": "إظهار الصور ومقاطع الفيديو من هذا المستخدم في المخطط الزمني الخاص بك",
|
"show_in_timeline_setting_description": "إظهار الصور ومقاطع الفيديو من هذا المستخدم في المخطط الزمني الخاص بك",
|
||||||
"show_keyboard_shortcuts": "إظهار اختصارات لوحة المفاتيح",
|
"show_keyboard_shortcuts": "إظهار اختصارات لوحة المفاتيح",
|
||||||
|
"show_less": "اضهر اقل",
|
||||||
"show_metadata": "إظهار البيانات الوصفية",
|
"show_metadata": "إظهار البيانات الوصفية",
|
||||||
|
"show_more_fields": "{count, plural, one {اضهر #حقل اكثر} other {اضهر # حقول اكثر}}",
|
||||||
"show_or_hide_info": "إظهار أو إخفاء المعلومات",
|
"show_or_hide_info": "إظهار أو إخفاء المعلومات",
|
||||||
"show_password": "إظهار كلمة المرور",
|
"show_password": "إظهار كلمة المرور",
|
||||||
"show_person_options": "إظهار خيارات الشخص",
|
"show_person_options": "إظهار خيارات الشخص",
|
||||||
@@ -2148,6 +2249,7 @@
|
|||||||
"show_schema": "أظهر المخطط",
|
"show_schema": "أظهر المخطط",
|
||||||
"show_search_options": "إظهار خيارات البحث",
|
"show_search_options": "إظهار خيارات البحث",
|
||||||
"show_shared_links": "عرض الروابط المشتركة",
|
"show_shared_links": "عرض الروابط المشتركة",
|
||||||
|
"show_slideshow_metadata_overlay": "عرض معلومات الصورة",
|
||||||
"show_slideshow_transition": "إظهار انتقال عرض الشرائح",
|
"show_slideshow_transition": "إظهار انتقال عرض الشرائح",
|
||||||
"show_supporter_badge": "شارة المؤيد",
|
"show_supporter_badge": "شارة المؤيد",
|
||||||
"show_supporter_badge_description": "إظهار شارة المؤيد",
|
"show_supporter_badge_description": "إظهار شارة المؤيد",
|
||||||
@@ -2163,9 +2265,14 @@
|
|||||||
"skip_to_folders": "تخطي إلى المجلدات",
|
"skip_to_folders": "تخطي إلى المجلدات",
|
||||||
"skip_to_tags": "تخطي إلى العلامات",
|
"skip_to_tags": "تخطي إلى العلامات",
|
||||||
"slideshow": "عرض الشرائح",
|
"slideshow": "عرض الشرائح",
|
||||||
|
"slideshow_metadata_overlay_mode": "محتوى التراكب",
|
||||||
|
"slideshow_metadata_overlay_mode_description_only": "وصف فقط",
|
||||||
|
"slideshow_metadata_overlay_mode_full": "كامل",
|
||||||
"slideshow_repeat": "اعادة عرض الشرائح",
|
"slideshow_repeat": "اعادة عرض الشرائح",
|
||||||
"slideshow_repeat_description": "العودة إلى البداية عند انتهاء عرض الشرائح",
|
"slideshow_repeat_description": "العودة إلى البداية عند انتهاء عرض الشرائح",
|
||||||
"slideshow_settings": "إعدادات عرض الشرائح",
|
"slideshow_settings": "إعدادات عرض الشرائح",
|
||||||
|
"smart_album": "ألبوم ذكي",
|
||||||
|
"some_assets_already_have_a_location_warning": "بعض الملفات المحددة تحتوي بالفعل على موقع جغرافي",
|
||||||
"sort_albums_by": "رتب الألبومات حسب...",
|
"sort_albums_by": "رتب الألبومات حسب...",
|
||||||
"sort_created": "تاريخ الإنشاء",
|
"sort_created": "تاريخ الإنشاء",
|
||||||
"sort_items": "عدد العناصر",
|
"sort_items": "عدد العناصر",
|
||||||
@@ -2188,6 +2295,11 @@
|
|||||||
"start_date_before_end_date": "يجب أن يكون تاريخ بدء الفترة قبل تاريخ نهايتها",
|
"start_date_before_end_date": "يجب أن يكون تاريخ بدء الفترة قبل تاريخ نهايتها",
|
||||||
"state": "الولاية",
|
"state": "الولاية",
|
||||||
"status": "الحالة",
|
"status": "الحالة",
|
||||||
|
"step_delete": "حذف خطوة",
|
||||||
|
"step_delete_confirm": "هل انت متاكد من انك تريد حذف هذه الخطوة؟",
|
||||||
|
"step_details": "تفاصيل الخطوة",
|
||||||
|
"steps": "خطوات",
|
||||||
|
"steps_count": "{count, plural, one {# خطوة} other {# خطوات}}",
|
||||||
"stop_casting": "ايقاف البث",
|
"stop_casting": "ايقاف البث",
|
||||||
"stop_motion_photo": "إيقاف حركة الصورة",
|
"stop_motion_photo": "إيقاف حركة الصورة",
|
||||||
"stop_photo_sharing": "توقف عن مشاركة صورك؟",
|
"stop_photo_sharing": "توقف عن مشاركة صورك؟",
|
||||||
@@ -2214,6 +2326,8 @@
|
|||||||
"sync_status": "حالة النسخ المتزامن",
|
"sync_status": "حالة النسخ المتزامن",
|
||||||
"sync_status_subtitle": "عرض وإدارة نظام النسخ المتزامن",
|
"sync_status_subtitle": "عرض وإدارة نظام النسخ المتزامن",
|
||||||
"sync_upload_album_setting_subtitle": "انشئ و ارفع صورك و فديوهاتك الالبومات المختارة في Immich",
|
"sync_upload_album_setting_subtitle": "انشئ و ارفع صورك و فديوهاتك الالبومات المختارة في Immich",
|
||||||
|
"system_theme": "سمة النظام",
|
||||||
|
"system_theme_command_description": "استعمل سمة النظام ({value})",
|
||||||
"tag": "العلامة",
|
"tag": "العلامة",
|
||||||
"tag_assets": "أصول العلامة",
|
"tag_assets": "أصول العلامة",
|
||||||
"tag_created": "تم إنشاء العلامة: {tag}",
|
"tag_created": "تم إنشاء العلامة: {tag}",
|
||||||
@@ -2279,11 +2393,13 @@
|
|||||||
"trash_page_title": "سلة المهملات ({count})",
|
"trash_page_title": "سلة المهملات ({count})",
|
||||||
"trashed_items_will_be_permanently_deleted_after": "سيتم حذفُ العناصر المحذوفة نِهائيًا بعد {days, plural, one {# يوم} other {# أيام }}.",
|
"trashed_items_will_be_permanently_deleted_after": "سيتم حذفُ العناصر المحذوفة نِهائيًا بعد {days, plural, one {# يوم} other {# أيام }}.",
|
||||||
"trigger": "مفعِل",
|
"trigger": "مفعِل",
|
||||||
"trigger_asset_uploaded": "تم رفع الاصل",
|
"trigger_asset_metadata_extraction": "استخراج البيانات الوصفية للملفات",
|
||||||
|
"trigger_asset_metadata_extraction_description": "يتم تفعيله عند استخراج البيانات الوصفية (EXIF) للملف",
|
||||||
|
"trigger_asset_uploaded": "رفع الاصل",
|
||||||
"trigger_asset_uploaded_description": "يتم تفعيله عند تحميل أصل جديد",
|
"trigger_asset_uploaded_description": "يتم تفعيله عند تحميل أصل جديد",
|
||||||
"trigger_description": "حدث يبدأ سير العمل",
|
"trigger_description": "حدث يبدأ سير العمل",
|
||||||
"trigger_person_recognized": "تم التعرف على شخص",
|
"trigger_person_recognized": "تم التعرف على شخص",
|
||||||
"trigger_person_recognized_description": "يتم تفعيله عند اكتشاف شخص",
|
"trigger_person_recognized_description": "يتم تفعيله عند التعرف على شخص",
|
||||||
"trigger_type": "نوع المفعل",
|
"trigger_type": "نوع المفعل",
|
||||||
"troubleshoot": "استكشاف المشاكل",
|
"troubleshoot": "استكشاف المشاكل",
|
||||||
"type": "النوع",
|
"type": "النوع",
|
||||||
@@ -2319,13 +2435,13 @@
|
|||||||
"unsupported_field_type": "نوع حقل غير مدعوم",
|
"unsupported_field_type": "نوع حقل غير مدعوم",
|
||||||
"unsupported_file_type": "لا يمكن رفع الملف {file} لأن نوع الملف {type} غير مدعوم.",
|
"unsupported_file_type": "لا يمكن رفع الملف {file} لأن نوع الملف {type} غير مدعوم.",
|
||||||
"untagged": "غير مُعَلَّم",
|
"untagged": "غير مُعَلَّم",
|
||||||
"untitled_workflow": "خطة سير عمل بدون عنوان",
|
|
||||||
"up_next": "التالي",
|
"up_next": "التالي",
|
||||||
"update_location_action_prompt": "تحديث موقع {count} عناصر محددة على النحو التالي:",
|
"update_location_action_prompt": "تحديث موقع {count} عناصر محددة على النحو التالي:",
|
||||||
"updated_at": "تم التحديث",
|
"updated_at": "تم التحديث",
|
||||||
"updated_password": "تم تحديث كلمة المرور",
|
"updated_password": "تم تحديث كلمة المرور",
|
||||||
"upload": "رفع",
|
"upload": "رفع",
|
||||||
"upload_concurrency": "الرفع المتزامن",
|
"upload_concurrency": "الرفع المتزامن",
|
||||||
|
"upload_day_count": "{date}: {count, plural, one {# رفع الملف} other {# رفع الملفات}}",
|
||||||
"upload_details": "تفاصيل الرفع",
|
"upload_details": "تفاصيل الرفع",
|
||||||
"upload_dialog_info": "هل تريد النسخ الاحتياطي للأصول (الأصول) المحددة إلى الخادم؟",
|
"upload_dialog_info": "هل تريد النسخ الاحتياطي للأصول (الأصول) المحددة إلى الخادم؟",
|
||||||
"upload_dialog_title": "تحميل الأصول",
|
"upload_dialog_title": "تحميل الأصول",
|
||||||
@@ -2341,6 +2457,8 @@
|
|||||||
"upload_to_immich": "الرفع الىImmich ({count})",
|
"upload_to_immich": "الرفع الىImmich ({count})",
|
||||||
"uploading": "جاري الرفع",
|
"uploading": "جاري الرفع",
|
||||||
"uploading_media": "رفع الوسائط",
|
"uploading_media": "رفع الوسائط",
|
||||||
|
"uploads": "عمليات الرفع",
|
||||||
|
"uploads_count": "{count, plural, one {# رفع الملف} other {# رفع الملفات}}",
|
||||||
"url": "عنوان URL",
|
"url": "عنوان URL",
|
||||||
"usage": "الاستخدام",
|
"usage": "الاستخدام",
|
||||||
"use_biometric": "استخدم البايومتري",
|
"use_biometric": "استخدم البايومتري",
|
||||||
@@ -2348,6 +2466,7 @@
|
|||||||
"use_browser_locale_description": "تنسيق التواريخ والأوقات والأرقام وفقًا لإعدادات اللغة في متصفحك",
|
"use_browser_locale_description": "تنسيق التواريخ والأوقات والأرقام وفقًا لإعدادات اللغة في متصفحك",
|
||||||
"use_current_connection": "استخدم الاتصال الحالي",
|
"use_current_connection": "استخدم الاتصال الحالي",
|
||||||
"use_custom_date_range": "استخدم النطاق الزمني المخصص بدلاً من ذلك",
|
"use_custom_date_range": "استخدم النطاق الزمني المخصص بدلاً من ذلك",
|
||||||
|
"use_template": "استخدام القالب",
|
||||||
"user": "مستخدم",
|
"user": "مستخدم",
|
||||||
"user_has_been_deleted": "هذا المستخدم تم حذفه.",
|
"user_has_been_deleted": "هذا المستخدم تم حذفه.",
|
||||||
"user_id": "معرف المستخدم",
|
"user_id": "معرف المستخدم",
|
||||||
@@ -2377,6 +2496,7 @@
|
|||||||
"video": "فيديو",
|
"video": "فيديو",
|
||||||
"video_hover_setting": "تشغيل الصورة المصغرة للفيديو عند التمرير",
|
"video_hover_setting": "تشغيل الصورة المصغرة للفيديو عند التمرير",
|
||||||
"video_hover_setting_description": "تشغيل الصورة المصغرة للفيديو عند تحريك الماوس فوق العنصر. حتى عند التعطيل، يمكن بدء التشغيل عن طريق التمرير فوق رمز التشغيل.",
|
"video_hover_setting_description": "تشغيل الصورة المصغرة للفيديو عند تحريك الماوس فوق العنصر. حتى عند التعطيل، يمكن بدء التشغيل عن طريق التمرير فوق رمز التشغيل.",
|
||||||
|
"video_quality": "جودة الفيديو",
|
||||||
"videos": "فيديوهات",
|
"videos": "فيديوهات",
|
||||||
"videos_count": "{count, plural, one {# مقطع فيديو } other {# مقاطع الفيديو }}",
|
"videos_count": "{count, plural, one {# مقطع فيديو } other {# مقاطع الفيديو }}",
|
||||||
"videos_only": "الفديوات فقط",
|
"videos_only": "الفديوات فقط",
|
||||||
@@ -2409,8 +2529,10 @@
|
|||||||
"week": "أسبوع",
|
"week": "أسبوع",
|
||||||
"welcome": "مرحباً",
|
"welcome": "مرحباً",
|
||||||
"welcome_to_immich": "مرحباً بك في Immich",
|
"welcome_to_immich": "مرحباً بك في Immich",
|
||||||
|
"when": "عندما",
|
||||||
"width": "عُرض",
|
"width": "عُرض",
|
||||||
"wifi_name": "اسم شبكة Wi-Fi",
|
"wifi_name": "اسم شبكة Wi-Fi",
|
||||||
|
"workflow": "سير العمل",
|
||||||
"workflow_delete_prompt": "متأكد من حذف سير العمل هذا؟",
|
"workflow_delete_prompt": "متأكد من حذف سير العمل هذا؟",
|
||||||
"workflow_deleted": "تم حذف سير العمل",
|
"workflow_deleted": "تم حذف سير العمل",
|
||||||
"workflow_description": "وصف سير العمل",
|
"workflow_description": "وصف سير العمل",
|
||||||
@@ -2420,11 +2542,13 @@
|
|||||||
"workflow_name": "اسم سير العمل",
|
"workflow_name": "اسم سير العمل",
|
||||||
"workflow_navigation_prompt": "متاكد من المغادرة بدون حفظ التغييرات؟",
|
"workflow_navigation_prompt": "متاكد من المغادرة بدون حفظ التغييرات؟",
|
||||||
"workflow_summary": "ملخص سير العمل",
|
"workflow_summary": "ملخص سير العمل",
|
||||||
|
"workflow_templates": "قوالب سير العمل",
|
||||||
"workflow_update_success": "تم تحديث سير العمل بنجاح",
|
"workflow_update_success": "تم تحديث سير العمل بنجاح",
|
||||||
"workflow_updated": "تم تحديث سير العمل",
|
"workflow_updated": "تم تحديث سير العمل",
|
||||||
"workflows": "سير العمل",
|
"workflows": "سير العمليات",
|
||||||
"workflows_help_text": "تعمل سير العمل على أتمتة الإجراءات على أصولك بناءً على المفعلات والفلاتر",
|
"workflows_help_text": "تعمل سير العمل على أتمتة الإجراءات على أصولك بناءً على المفعلات والفلاتر",
|
||||||
"wrong_pin_code": "رمز التعريف الشخصي خاطئ",
|
"wrong_pin_code": "رمز التعريف الشخصي خاطئ",
|
||||||
|
"x_of_total": "{x}\\{total}",
|
||||||
"year": "سنة",
|
"year": "سنة",
|
||||||
"years_ago": "{years, plural, one {# سنة} other {# سنوات}} مضت",
|
"years_ago": "{years, plural, one {# سنة} other {# سنوات}} مضت",
|
||||||
"yes": "نعم",
|
"yes": "نعم",
|
||||||
|
|||||||
+24
-2
@@ -5,6 +5,7 @@
|
|||||||
"acknowledge": "Təsdiq et",
|
"acknowledge": "Təsdiq et",
|
||||||
"action": "Əməliyyat",
|
"action": "Əməliyyat",
|
||||||
"action_common_update": "Yenilə",
|
"action_common_update": "Yenilə",
|
||||||
|
"action_description": "Filtrlənmiş aktivliklər üzərində yerinə yetiriləcək əməliyyatlar toplusu",
|
||||||
"actions": "Əməliyyatlar",
|
"actions": "Əməliyyatlar",
|
||||||
"active": "Aktiv",
|
"active": "Aktiv",
|
||||||
"active_count": "Aktiv: {count}",
|
"active_count": "Aktiv: {count}",
|
||||||
@@ -15,6 +16,9 @@
|
|||||||
"add_a_location": "Məkan əlavə et",
|
"add_a_location": "Məkan əlavə et",
|
||||||
"add_a_name": "Ad əlavə et",
|
"add_a_name": "Ad əlavə et",
|
||||||
"add_a_title": "Başlıq əlavə et",
|
"add_a_title": "Başlıq əlavə et",
|
||||||
|
"add_action": "Yeni əməliyyat əlavə et",
|
||||||
|
"add_action_description": "Əməliyyat əlavə etmək üçün klikləyin",
|
||||||
|
"add_assets": "Aktivlik əlavə et",
|
||||||
"add_birthday": "Doğum günü əlavə et",
|
"add_birthday": "Doğum günü əlavə et",
|
||||||
"add_endpoint": "Son nöqtə əlavə et",
|
"add_endpoint": "Son nöqtə əlavə et",
|
||||||
"add_exclusion_pattern": "Çıxarma nümunəsi əlavə et",
|
"add_exclusion_pattern": "Çıxarma nümunəsi əlavə et",
|
||||||
@@ -46,7 +50,7 @@
|
|||||||
"authentication_settings": "Səlahiyyətləndirmə parametrləri",
|
"authentication_settings": "Səlahiyyətləndirmə parametrləri",
|
||||||
"authentication_settings_description": "Şifrə, OAuth və digər səlahiyyətləndirmə parametrləri",
|
"authentication_settings_description": "Şifrə, OAuth və digər səlahiyyətləndirmə parametrləri",
|
||||||
"authentication_settings_disable_all": "Bütün giriş etmə metodlarını söndürmək istədiyinizdən əminsinizmi? Giriş etmə funksiyası tamamilə söndürüləcəkdir.",
|
"authentication_settings_disable_all": "Bütün giriş etmə metodlarını söndürmək istədiyinizdən əminsinizmi? Giriş etmə funksiyası tamamilə söndürüləcəkdir.",
|
||||||
"authentication_settings_reenable": "Yenidən aktiv etmək üçün <link> Server Əmri</link> -ni istifadə edin.",
|
"authentication_settings_reenable": "Yenidən aktiv etmək üçün <link> Server Əmri</link>-ni istifadə edin.",
|
||||||
"background_task_job": "Arxa plan tapşırıqları",
|
"background_task_job": "Arxa plan tapşırıqları",
|
||||||
"backup_database": "Verilənlər bazasının dump-ını yaradın",
|
"backup_database": "Verilənlər bazasının dump-ını yaradın",
|
||||||
"backup_database_enable_description": "Verilənlər bazasının artıq nüsxələrini aktiv et",
|
"backup_database_enable_description": "Verilənlər bazasının artıq nüsxələrini aktiv et",
|
||||||
@@ -54,6 +58,7 @@
|
|||||||
"backup_onboarding_1_description": "buludda və ya başqa fiziki yerdə saytdan kənar surət.",
|
"backup_onboarding_1_description": "buludda və ya başqa fiziki yerdə saytdan kənar surət.",
|
||||||
"backup_onboarding_2_description": "müxtəlif cihazlarda yerli nüsxələr. Bura əsas fayllar və həmin faylların ehtiyat lokal nüsxəsi daxildir.",
|
"backup_onboarding_2_description": "müxtəlif cihazlarda yerli nüsxələr. Bura əsas fayllar və həmin faylların ehtiyat lokal nüsxəsi daxildir.",
|
||||||
"backup_onboarding_3_description": "orijinal fayllar da daxil olmaqla məlumatlarınızın ümumi surətləri. Buraya 1 kənar nüsxə və 2 lokal nüsxə daxildir.",
|
"backup_onboarding_3_description": "orijinal fayllar da daxil olmaqla məlumatlarınızın ümumi surətləri. Buraya 1 kənar nüsxə və 2 lokal nüsxə daxildir.",
|
||||||
|
"backup_onboarding_description": "<backblaze-link>3-2-1 yedəkləmə strategiyası</backblaze-link> məlumatlarınızı qorumaq üçün tövsiyə olunur. Yüklədiyiniz şəkil və videoların, həmçinin Immich verilənlər bazasının surətlərini saxlamalısınız ki, hərtərəfli yedəkləmə həlli əldə edəsiniz.",
|
||||||
"backup_onboarding_footer": "Immich-in ehtiyat nüsxəsini çıxarmaq haqqında ətraflı məlumat üçün <link>sənədlərə</link> müraciət edin.",
|
"backup_onboarding_footer": "Immich-in ehtiyat nüsxəsini çıxarmaq haqqında ətraflı məlumat üçün <link>sənədlərə</link> müraciət edin.",
|
||||||
"backup_onboarding_parts_title": "3-2-1 ehtiyat nüsxəsinə aşağıdakılar daxildir:",
|
"backup_onboarding_parts_title": "3-2-1 ehtiyat nüsxəsinə aşağıdakılar daxildir:",
|
||||||
"backup_onboarding_title": "Ehtiyat surətlər",
|
"backup_onboarding_title": "Ehtiyat surətlər",
|
||||||
@@ -61,14 +66,31 @@
|
|||||||
"backup_settings_description": "Verilənlər bazasının ehtiyat nüsxə parametrlərini idarə et",
|
"backup_settings_description": "Verilənlər bazasının ehtiyat nüsxə parametrlərini idarə et",
|
||||||
"cleared_jobs": "{job} üçün tapşırıqlar silindi",
|
"cleared_jobs": "{job} üçün tapşırıqlar silindi",
|
||||||
"config_set_by_file": "Konfiqurasiya hal-hazırda konfiqurasiya faylı ilə təyin olunub",
|
"config_set_by_file": "Konfiqurasiya hal-hazırda konfiqurasiya faylı ilə təyin olunub",
|
||||||
"confirm_delete_library": "{library} kitabxanasını silmək istədiyinizdən əminmisiniz?",
|
"confirm_delete_library": "{library} kitabxanasını silmək istədiyinizə əminmisiniz?",
|
||||||
"confirm_email_below": "Təsdiqləmək üçün aşağıya {email} yazın",
|
"confirm_email_below": "Təsdiqləmək üçün aşağıya {email} yazın",
|
||||||
|
"confirm_reprocess_all_faces": "Bütün üzləri yenidən emal etmək istədiyinizə əminsiniz? Bu, həmçinin adlandırılmış şəxsləri siləcək.",
|
||||||
"confirm_user_password_reset": "{user} adlı istifadəçinin şifrəsini sıfırlamaq istədiyinizdən əminmisiniz?",
|
"confirm_user_password_reset": "{user} adlı istifadəçinin şifrəsini sıfırlamaq istədiyinizdən əminmisiniz?",
|
||||||
|
"confirm_user_pin_code_reset": "{user} istifadəçisinin PIN kodunu sıfırlamaq istədiyinizə əminsiniz?",
|
||||||
|
"copy_config_to_clipboard_description": "Cari sistem konfiqurasiyasını JSON obyekt kimi mübadilə buferinə kopyalayın",
|
||||||
|
"create_job": "İş yarat",
|
||||||
|
"cron_expression": "Cron ifadəsi",
|
||||||
|
"cron_expression_description": "Cron formatından istifadə edərək skan intervalını təyin edin. Ətraflı məlumat üçün nümunələrə baxa bilərsiniz. <link>Crontab Guru</link>",
|
||||||
|
"cron_expression_presets": "Cron ifadəsi ön ayarları",
|
||||||
"disable_login": "Giriş etməni söndür",
|
"disable_login": "Giriş etməni söndür",
|
||||||
"duplicate_detection_job_description": "Bənzər şəkilləri tapmaq üçün maşın öyrənməsini işə salın. Bu prosses Smart Search funksiyasına əsaslanır",
|
"duplicate_detection_job_description": "Bənzər şəkilləri tapmaq üçün maşın öyrənməsini işə salın. Bu prosses Smart Search funksiyasına əsaslanır",
|
||||||
|
"exclusion_pattern_description": "İstisna nümunələri kitabxananızı skan edərkən faylları və qovluqları nəzərə almamağa imkan verir. Bu, RAW faylları kimi idxal etmək istəmədiyiniz faylları olan qovluqlarınız olduqda faydalıdır.",
|
||||||
|
"export_config_as_json_description": "Cari sistem konfiqurasiyasını JSON faylı kimi endirin",
|
||||||
|
"external_libraries_page_description": "Admin xarici kitabxana səhifəsi",
|
||||||
"face_detection": "Üz tanıma",
|
"face_detection": "Üz tanıma",
|
||||||
|
"failed_job_command": "{command} əmri {job} işi üçün uğursuz oldu",
|
||||||
"force_delete_user_warning": "XƏBƏRDARLIQ: Bu əməliyyat istifadəçi və bütün məlumatları siləcəkdir. Bu prossesi və silinən faylları geri qaytarmaq olmaz.",
|
"force_delete_user_warning": "XƏBƏRDARLIQ: Bu əməliyyat istifadəçi və bütün məlumatları siləcəkdir. Bu prossesi və silinən faylları geri qaytarmaq olmaz.",
|
||||||
|
"image_format": "Format",
|
||||||
"image_format_description": "WebP, JPEG faylına görə daha kiçik həcmə sahibdir, lakin onu kodlaşdırmaq daha çox vaxt alır.",
|
"image_format_description": "WebP, JPEG faylına görə daha kiçik həcmə sahibdir, lakin onu kodlaşdırmaq daha çox vaxt alır.",
|
||||||
|
"image_fullsize_description": "Böyüdülmüş halda istifadə edilən, metadata-sı silinmiş tam ölçülü şəkil",
|
||||||
|
"image_fullsize_enabled": "Tam ölçülü şəkil generasiyasını aktiv et",
|
||||||
|
"image_fullsize_enabled_description": "Veb üçün uyğun olmayan formatlar üçün tam ölçülü şəkil yaradın. “Daxili önizləməyə üstünlük ver” aktiv olduqda, daxili önizləmələr çevrilmədən birbaşa istifadə olunur. JPEG kimi veb üçün uyğun formatlara təsir etmir.",
|
||||||
|
"image_fullsize_quality_description": "Tam ölçülü şəkil keyfiyyəti (1-100). Daha yüksək dəyər daha yaxşı keyfiyyət verir, lakin daha böyük ölçülü fayl yaradır.",
|
||||||
|
"image_fullsize_title": "Tam ölçülü şəkil tənzimləmələri",
|
||||||
"image_preview_title": "Önizləmə parametrləri",
|
"image_preview_title": "Önizləmə parametrləri",
|
||||||
"image_quality": "Keyfiyyət",
|
"image_quality": "Keyfiyyət",
|
||||||
"image_resolution": "Çözümlülük",
|
"image_resolution": "Çözümlülük",
|
||||||
|
|||||||
+1964
-39
File diff suppressed because it is too large
Load Diff
+150
-25
@@ -22,13 +22,12 @@
|
|||||||
"add_birthday": "Добави дата на раждане",
|
"add_birthday": "Добави дата на раждане",
|
||||||
"add_endpoint": "Добави крайна точка",
|
"add_endpoint": "Добави крайна точка",
|
||||||
"add_exclusion_pattern": "Добави модел за изключване",
|
"add_exclusion_pattern": "Добави модел за изключване",
|
||||||
"add_filter": "Добави филтър",
|
|
||||||
"add_filter_description": "Натиснете за да добавите условие за филтър",
|
|
||||||
"add_location": "Дoбави местоположение",
|
"add_location": "Дoбави местоположение",
|
||||||
"add_more_users": "Добави още потребители",
|
"add_more_users": "Добави още потребители",
|
||||||
"add_partner": "Добави партньор",
|
"add_partner": "Добави партньор",
|
||||||
"add_path": "Добави път",
|
"add_path": "Добави път",
|
||||||
"add_photos": "Добави снимки",
|
"add_photos": "Добави снимки",
|
||||||
|
"add_step": "Добави стъпка",
|
||||||
"add_tag": "Добави маркер",
|
"add_tag": "Добави маркер",
|
||||||
"add_to": "Добави към…",
|
"add_to": "Добави към…",
|
||||||
"add_to_album": "Добави към албум",
|
"add_to_album": "Добави към албум",
|
||||||
@@ -42,7 +41,6 @@
|
|||||||
"add_to_shared_album": "Добави към споделен албум",
|
"add_to_shared_album": "Добави към споделен албум",
|
||||||
"add_upload_to_stack": "Добави качените в група",
|
"add_upload_to_stack": "Добави качените в група",
|
||||||
"add_url": "Добави URL",
|
"add_url": "Добави URL",
|
||||||
"add_workflow_step": "Добави стъпка от работния процес",
|
|
||||||
"added_to_archive": "Добавено към архива",
|
"added_to_archive": "Добавено към архива",
|
||||||
"added_to_favorites": "Добавени към любимите ви",
|
"added_to_favorites": "Добавени към любимите ви",
|
||||||
"added_to_favorites_count": "Добавени {count, number} към любими",
|
"added_to_favorites_count": "Добавени {count, number} към любими",
|
||||||
@@ -81,6 +79,7 @@
|
|||||||
"cron_expression_description": "Настрой интервала на сканиране използвайки cron формата. За повече информация <link>Crontab Guru</link>",
|
"cron_expression_description": "Настрой интервала на сканиране използвайки cron формата. За повече информация <link>Crontab Guru</link>",
|
||||||
"cron_expression_presets": "Примерни Cron изрази",
|
"cron_expression_presets": "Примерни Cron изрази",
|
||||||
"disable_login": "Изключете вписването",
|
"disable_login": "Изключете вписването",
|
||||||
|
"download_csv": "Изтегли CSV",
|
||||||
"duplicate_detection_job_description": "Стартиране машинно обучение върху елементи, за откриване на подобни изображения. Разчита на Интелигентно Търсене",
|
"duplicate_detection_job_description": "Стартиране машинно обучение върху елементи, за откриване на подобни изображения. Разчита на Интелигентно Търсене",
|
||||||
"exclusion_pattern_description": "Модели за изключване позволяват да игнорирате файлове и папки, когато сканирате вашата библиотека. Това е потребно, ако имате папки, които съдържат файлове, които не искате да импортирате. Примерно - RAW файлове.",
|
"exclusion_pattern_description": "Модели за изключване позволяват да игнорирате файлове и папки, когато сканирате вашата библиотека. Това е потребно, ако имате папки, които съдържат файлове, които не искате да импортирате. Примерно - RAW файлове.",
|
||||||
"export_config_as_json_description": "Запази текущата системна конфигурация като JSON файл",
|
"export_config_as_json_description": "Запази текущата системна конфигурация като JSON файл",
|
||||||
@@ -190,9 +189,25 @@
|
|||||||
"machine_learning_smart_search_enabled": "Включване на Интелигентно Търсене",
|
"machine_learning_smart_search_enabled": "Включване на Интелигентно Търсене",
|
||||||
"machine_learning_smart_search_enabled_description": "Ако е деактивирано, изображенията няма да бъдат кодирани за Интелигентно Търсене.",
|
"machine_learning_smart_search_enabled_description": "Ако е деактивирано, изображенията няма да бъдат кодирани за Интелигентно Търсене.",
|
||||||
"machine_learning_url_description": "URL на сървъра за машинно обучение. Ако са предоставени повече от един URL, всеки сървър ще бъде опитан един по един, докато един отговори успешно, в реда от първия до последния. Сървъри, които не отговорят, ще бъдат временно игнорирани, докато не се върнат онлайн.",
|
"machine_learning_url_description": "URL на сървъра за машинно обучение. Ако са предоставени повече от един URL, всеки сървър ще бъде опитан един по един, докато един отговори успешно, в реда от първия до последния. Сървъри, които не отговорят, ще бъдат временно игнорирани, докато не се върнат онлайн.",
|
||||||
|
"maintenance_backup_management": "Управление на архивирането",
|
||||||
"maintenance_delete_backup": "Изтриване на архив",
|
"maintenance_delete_backup": "Изтриване на архив",
|
||||||
"maintenance_delete_backup_description": "Този файл ще бъде безвъзвратно изтрит.",
|
"maintenance_delete_backup_description": "Този файл ще бъде безвъзвратно изтрит.",
|
||||||
"maintenance_delete_error": "Неуспешно изтриване на архив.",
|
"maintenance_delete_error": "Неуспешно изтриване на архив.",
|
||||||
|
"maintenance_integrity_check": "Проверка",
|
||||||
|
"maintenance_integrity_check_all": "Провери всички",
|
||||||
|
"maintenance_integrity_checksum_mismatch": "Несъответствие на контролната сума",
|
||||||
|
"maintenance_integrity_checksum_mismatch_description": "Файлове, чиято контролна сума не съвпада със запазената в базата данни на Immich.",
|
||||||
|
"maintenance_integrity_checksum_mismatch_job": "Проверка на контролните суми",
|
||||||
|
"maintenance_integrity_checksum_mismatch_refresh_job": "Обнови докладите за проверка на конторлните суми",
|
||||||
|
"maintenance_integrity_missing_file": "Липсващи файлове",
|
||||||
|
"maintenance_integrity_missing_file_description": "Файлове, които Immich следи в базата си данни, но не са налични във файловата система.",
|
||||||
|
"maintenance_integrity_missing_file_job": "Проверка за липсващи файлове",
|
||||||
|
"maintenance_integrity_missing_file_refresh_job": "Обнови докладите за липсващи файлове",
|
||||||
|
"maintenance_integrity_report": "Отчет за непокътнатост",
|
||||||
|
"maintenance_integrity_untracked_file": "Непроследени файлове",
|
||||||
|
"maintenance_integrity_untracked_file_description": "Файлове в папките на Immich, за които Immich няма никакви записи.",
|
||||||
|
"maintenance_integrity_untracked_file_job": "Проверка за непроследени файлове",
|
||||||
|
"maintenance_integrity_untracked_file_refresh_job": "Обнови докладите за непроследени файлове",
|
||||||
"maintenance_restore_backup": "Възстановяване на архив",
|
"maintenance_restore_backup": "Възстановяване на архив",
|
||||||
"maintenance_restore_backup_description": "Immich ще изтрие всички текущи данни и после ще възстанови данните от избрания архив. Първо ще направи нов архив.",
|
"maintenance_restore_backup_description": "Immich ще изтрие всички текущи данни и после ще възстанови данните от избрания архив. Първо ще направи нов архив.",
|
||||||
"maintenance_restore_backup_different_version": "Този архив е създаден с различна версия на Immich!",
|
"maintenance_restore_backup_different_version": "Този архив е създаден с различна версия на Immich!",
|
||||||
@@ -267,6 +282,8 @@
|
|||||||
"notification_enable_email_notifications": "Включване на имейл известията",
|
"notification_enable_email_notifications": "Включване на имейл известията",
|
||||||
"notification_settings": "Настройки на известията",
|
"notification_settings": "Настройки на известията",
|
||||||
"notification_settings_description": "Управление на настройките за известия, вкл. имейл",
|
"notification_settings_description": "Управление на настройките за известия, вкл. имейл",
|
||||||
|
"oauth_allow_insecure_requests": "Разрешаване на несигурни заявки",
|
||||||
|
"oauth_allow_insecure_requests_description": "ПРЕДУПРЕЖДЕНИЕ: Това изключва проверката за валидност на TLS сертификата при OAuth заявки и отваря възможност за атака от типа \"човек по средата\".",
|
||||||
"oauth_auto_launch": "Автоматично стартиране",
|
"oauth_auto_launch": "Автоматично стартиране",
|
||||||
"oauth_auto_launch_description": "Автоматично стартиране на вход чрез OAuth, когато се отвори страницата за вход",
|
"oauth_auto_launch_description": "Автоматично стартиране на вход чрез OAuth, когато се отвори страницата за вход",
|
||||||
"oauth_auto_register": "Автоматична регистрация",
|
"oauth_auto_register": "Автоматична регистрация",
|
||||||
@@ -274,9 +291,11 @@
|
|||||||
"oauth_button_text": "Текст на бутона",
|
"oauth_button_text": "Текст на бутона",
|
||||||
"oauth_client_secret_description": "Задължително за поверителен клиент или когато не се поддържа PKCE (Proof Key for Code Exchange) за публичен клиент.",
|
"oauth_client_secret_description": "Задължително за поверителен клиент или когато не се поддържа PKCE (Proof Key for Code Exchange) за публичен клиент.",
|
||||||
"oauth_enable_description": "Влизане с OAuth",
|
"oauth_enable_description": "Влизане с OAuth",
|
||||||
|
"oauth_end_session_url_description": "Пренасочване на потребителя към този URI адрес, когато излезе от системата.",
|
||||||
"oauth_mobile_redirect_uri": "URI за мобилно пренасочване",
|
"oauth_mobile_redirect_uri": "URI за мобилно пренасочване",
|
||||||
"oauth_mobile_redirect_uri_override": "URI пренасочване за мобилни устройства",
|
"oauth_mobile_redirect_uri_override": "URI пренасочване за мобилни устройства",
|
||||||
"oauth_mobile_redirect_uri_override_description": "Разреши когато доставчика за OAuth удостоверяване не позволява за мобилни URI идентификатори, като ''{callback}''",
|
"oauth_mobile_redirect_uri_override_description": "Разреши когато доставчика за OAuth удостоверяване не позволява за мобилни URI идентификатори, като ''{callback}''",
|
||||||
|
"oauth_prompt_description": "Параметър за подкана (напр. select_account, login, consent)",
|
||||||
"oauth_role_claim": "Потвърждение на роля",
|
"oauth_role_claim": "Потвърждение на роля",
|
||||||
"oauth_role_claim_description": "Автоматично предоставяне на административни права при наличие на това потвържение. Потвърждението може да има стойност 'user' или 'admin'.",
|
"oauth_role_claim_description": "Автоматично предоставяне на административни права при наличие на това потвържение. Потвърждението може да има стойност 'user' или 'admin'.",
|
||||||
"oauth_settings": "OAuth",
|
"oauth_settings": "OAuth",
|
||||||
@@ -303,6 +322,8 @@
|
|||||||
"refreshing_all_libraries": "Опресняване на всички библиотеки",
|
"refreshing_all_libraries": "Опресняване на всички библиотеки",
|
||||||
"registration": "Администраторска регистрация",
|
"registration": "Администраторска регистрация",
|
||||||
"registration_description": "Тъй като сте първият потребител в системата, ще бъдете назначен като администратор и ще отговаряте за административните задачи, а допълнителните потребители ще бъдат създадени от вас.",
|
"registration_description": "Тъй като сте първият потребител в системата, ще бъдете назначен като администратор и ще отговаряте за административните задачи, а допълнителните потребители ще бъдат създадени от вас.",
|
||||||
|
"release_channel_release_candidate": "Предварителна версия",
|
||||||
|
"release_channel_stable": "Стабилна версия",
|
||||||
"remove_failed_jobs": "Премахване на неуспешни задачи",
|
"remove_failed_jobs": "Премахване на неуспешни задачи",
|
||||||
"require_password_change_on_login": "Изискване за промяна паролата при първо влизане",
|
"require_password_change_on_login": "Изискване за промяна паролата при първо влизане",
|
||||||
"reset_settings_to_default": "Възстановяване на настройките по подразбиране",
|
"reset_settings_to_default": "Възстановяване на настройките по подразбиране",
|
||||||
@@ -397,6 +418,10 @@
|
|||||||
"transcoding_preferred_hardware_device_description": "Прилага се само за VAAPI и QSV. Задава dri възела, използван за хардуерно транскодиране.",
|
"transcoding_preferred_hardware_device_description": "Прилага се само за VAAPI и QSV. Задава dri възела, използван за хардуерно транскодиране.",
|
||||||
"transcoding_preset_preset": "Предварително зададени(-preset)",
|
"transcoding_preset_preset": "Предварително зададени(-preset)",
|
||||||
"transcoding_preset_preset_description": "Скорост на компресия. По-бавните предварително зададени настройки създават по-малки файлове и повишават качеството при насочване към определен битрейт. VP9 игнорира скорости над „по-бързо“.",
|
"transcoding_preset_preset_description": "Скорост на компресия. По-бавните предварително зададени настройки създават по-малки файлове и повишават качеството при насочване към определен битрейт. VP9 игнорира скорости над „по-бързо“.",
|
||||||
|
"transcoding_realtime": "Транскодиране в реално време [ЕКСПЕРИМЕНТАЛНО]",
|
||||||
|
"transcoding_realtime_description": "Позволява транскодиране на видео по време на възпроизвеждане. Разрешава превключване на качеството, но може да предизвика по-голямо забавяне или накъсване на възпроизвеждането според възможностите на сървъра.",
|
||||||
|
"transcoding_realtime_enabled": "Включи транскодиране в реално време",
|
||||||
|
"transcoding_realtime_enabled_description": "Ако е изключено, сървъра ще отказва нова сесия за транскодиране в реално време.",
|
||||||
"transcoding_reference_frames": "Референтни кадри",
|
"transcoding_reference_frames": "Референтни кадри",
|
||||||
"transcoding_reference_frames_description": "Броят кадри за препратка при компресиране на даден кадър. По-високите стойности подобряват ефективността на компресията, но забавят кодирането. 0 задава тази стойност автоматично.",
|
"transcoding_reference_frames_description": "Броят кадри за препратка при компресиране на даден кадър. По-високите стойности подобряват ефективността на компресията, но забавят кодирането. 0 задава тази стойност автоматично.",
|
||||||
"transcoding_required_description": "Само видеа, които не са в приет формат",
|
"transcoding_required_description": "Само видеа, които не са в приет формат",
|
||||||
@@ -440,6 +465,8 @@
|
|||||||
"user_settings_description": "Управление на потребителските настройки",
|
"user_settings_description": "Управление на потребителските настройки",
|
||||||
"user_successfully_removed": "Потребител {email} е успешно премахнат.",
|
"user_successfully_removed": "Потребител {email} е успешно премахнат.",
|
||||||
"users_page_description": "Страница за администриране на потребители",
|
"users_page_description": "Страница за администриране на потребители",
|
||||||
|
"version_check_channel": "Канал за обновявания",
|
||||||
|
"version_check_channel_description": "Посочете канал, по който да получавате известия за нова версия",
|
||||||
"version_check_enabled_description": "Активирай проверка на версията",
|
"version_check_enabled_description": "Активирай проверка на версията",
|
||||||
"version_check_implications": "Функцията за проверка на версията разчита на периодична комуникация с {server}",
|
"version_check_implications": "Функцията за проверка на версията разчита на периодична комуникация с {server}",
|
||||||
"version_check_settings": "Проверка на версията",
|
"version_check_settings": "Проверка на версията",
|
||||||
@@ -560,6 +587,7 @@
|
|||||||
"asset_added_to_album": "Добавено в албум",
|
"asset_added_to_album": "Добавено в албум",
|
||||||
"asset_adding_to_album": "Добавяне в албум…",
|
"asset_adding_to_album": "Добавяне в албум…",
|
||||||
"asset_created": "Обектът е създаден",
|
"asset_created": "Обектът е създаден",
|
||||||
|
"asset_day_count": "{date}: {count, plural, one {# обект} other {# обекта}}",
|
||||||
"asset_description_updated": "Описанието на елемента е обновено",
|
"asset_description_updated": "Описанието на елемента е обновено",
|
||||||
"asset_filename_is_offline": "Активът {filename} е офлайн",
|
"asset_filename_is_offline": "Активът {filename} е офлайн",
|
||||||
"asset_has_unassigned_faces": "Елементът има незададени лица",
|
"asset_has_unassigned_faces": "Елементът има незададени лица",
|
||||||
@@ -689,6 +717,7 @@
|
|||||||
"backup_settings_subtitle": "Управление на настройките за качване",
|
"backup_settings_subtitle": "Управление на настройките за качване",
|
||||||
"backup_upload_details_page_more_details": "Повече подробности",
|
"backup_upload_details_page_more_details": "Повече подробности",
|
||||||
"backward": "Назад",
|
"backward": "Назад",
|
||||||
|
"battery_optimization_backup_reliability": "Изключване на оптимизацията на използване на батерията може да подобри надеждността на архивиране във фонов режим",
|
||||||
"biometric_auth_enabled": "Включена биометрично удостоверяване",
|
"biometric_auth_enabled": "Включена биометрично удостоверяване",
|
||||||
"biometric_locked_out": "Няма достъп до биометрично удостоверяване",
|
"biometric_locked_out": "Няма достъп до биометрично удостоверяване",
|
||||||
"biometric_no_options": "Няма биометрична автентикация",
|
"biometric_no_options": "Няма биометрична автентикация",
|
||||||
@@ -696,6 +725,7 @@
|
|||||||
"birthdate_saved": "Датата на раждане е запазена успешно",
|
"birthdate_saved": "Датата на раждане е запазена успешно",
|
||||||
"birthdate_set_description": "Датата на раждане се използва за изчисляване на възрастта на този човек към момента на снимката.",
|
"birthdate_set_description": "Датата на раждане се използва за изчисляване на възрастта на този човек към момента на снимката.",
|
||||||
"blurred_background": "Замъглен заден фон",
|
"blurred_background": "Замъглен заден фон",
|
||||||
|
"browse_templates": "Разглеждане на шаблони",
|
||||||
"bugs_and_feature_requests": "Бъгове и заявки за функции",
|
"bugs_and_feature_requests": "Бъгове и заявки за функции",
|
||||||
"build": "Версия",
|
"build": "Версия",
|
||||||
"build_image": "Docker версия",
|
"build_image": "Docker версия",
|
||||||
@@ -729,6 +759,7 @@
|
|||||||
"cannot_update_the_description": "Описанието не може да бъде обновено",
|
"cannot_update_the_description": "Описанието не може да бъде обновено",
|
||||||
"cast": "Поточно предаване",
|
"cast": "Поточно предаване",
|
||||||
"cast_description": "Настройка на наличните цели за предаване",
|
"cast_description": "Настройка на наличните цели за предаване",
|
||||||
|
"change": "Промени",
|
||||||
"change_date": "Промени датата",
|
"change_date": "Промени датата",
|
||||||
"change_description": "Промени описанието",
|
"change_description": "Промени описанието",
|
||||||
"change_display_order": "Промени реда на показване",
|
"change_display_order": "Промени реда на показване",
|
||||||
@@ -757,6 +788,7 @@
|
|||||||
"check_corrupt_asset_backup_description": "Изпълни тази проверка само при Wi-Fi и след архивиране на всички обекти. Процедурата може да продължи няколко минути.",
|
"check_corrupt_asset_backup_description": "Изпълни тази проверка само при Wi-Fi и след архивиране на всички обекти. Процедурата може да продължи няколко минути.",
|
||||||
"check_logs": "Провери логовете",
|
"check_logs": "Провери логовете",
|
||||||
"checksum": "Контролна сума",
|
"checksum": "Контролна сума",
|
||||||
|
"choose": "Избeри",
|
||||||
"choose_matching_people_to_merge": "Изберете подходящи хора за сливане",
|
"choose_matching_people_to_merge": "Изберете подходящи хора за сливане",
|
||||||
"city": "Град",
|
"city": "Град",
|
||||||
"cleanup_confirm_description": "Immich намери {count} обекта (създадени преди {date}), които са архивирани на сървъра. Да се премахнат ли локалните копия от това устройство?",
|
"cleanup_confirm_description": "Immich намери {count} обекта (създадени преди {date}), които са архивирани на сървъра. Да се премахнат ли локалните копия от това устройство?",
|
||||||
@@ -774,6 +806,7 @@
|
|||||||
"clear": "Изчисти",
|
"clear": "Изчисти",
|
||||||
"clear_all": "Изчисти всичко",
|
"clear_all": "Изчисти всичко",
|
||||||
"clear_all_recent_searches": "Изчистете всички скорошни търсения",
|
"clear_all_recent_searches": "Изчистете всички скорошни търсения",
|
||||||
|
"clear_failed_count": "Неуспешно изчистване на ({count})",
|
||||||
"clear_file_cache": "Изчистване на кеша на файловете",
|
"clear_file_cache": "Изчистване на кеша на файловете",
|
||||||
"clear_message": "Изчисти съобщението",
|
"clear_message": "Изчисти съобщението",
|
||||||
"clear_value": "Изчисти стойността",
|
"clear_value": "Изчисти стойността",
|
||||||
@@ -805,6 +838,7 @@
|
|||||||
"comments_are_disabled": "Коментарите са деактивирани",
|
"comments_are_disabled": "Коментарите са деактивирани",
|
||||||
"common_create_new_album": "Създай нов албум",
|
"common_create_new_album": "Създай нов албум",
|
||||||
"completed": "Завършено",
|
"completed": "Завършено",
|
||||||
|
"configuration": "Конфигурация",
|
||||||
"confirm": "Потвърди",
|
"confirm": "Потвърди",
|
||||||
"confirm_admin_password": "Потвърждаване на паролата на администратора",
|
"confirm_admin_password": "Потвърждаване на паролата на администратора",
|
||||||
"confirm_delete_face": "Сигурни ли сте, че искате да изтриете лицето на {name} от актива?",
|
"confirm_delete_face": "Сигурни ли сте, че искате да изтриете лицето на {name} от актива?",
|
||||||
@@ -819,6 +853,7 @@
|
|||||||
"contain": "В рамките на",
|
"contain": "В рамките на",
|
||||||
"context": "Контекст",
|
"context": "Контекст",
|
||||||
"continue": "Продължи",
|
"continue": "Продължи",
|
||||||
|
"control_bottom_app_bar_add_tags": "Добавяне на етикети",
|
||||||
"control_bottom_app_bar_create_new_album": "Създай нов албум",
|
"control_bottom_app_bar_create_new_album": "Създай нов албум",
|
||||||
"control_bottom_app_bar_delete_from_immich": "Премахни от Immich съръра",
|
"control_bottom_app_bar_delete_from_immich": "Премахни от Immich съръра",
|
||||||
"control_bottom_app_bar_delete_from_local": "Премахни от устройството",
|
"control_bottom_app_bar_delete_from_local": "Премахни от устройството",
|
||||||
@@ -832,6 +867,7 @@
|
|||||||
"copy_error": "Грешка при копирането",
|
"copy_error": "Грешка при копирането",
|
||||||
"copy_file_path": "Копирай пътя на файла",
|
"copy_file_path": "Копирай пътя на файла",
|
||||||
"copy_image": "Копиране на изображението",
|
"copy_image": "Копиране на изображението",
|
||||||
|
"copy_json": "Копирай JSON",
|
||||||
"copy_link": "Копиране на линк",
|
"copy_link": "Копиране на линк",
|
||||||
"copy_link_to_clipboard": "Копиране на връзката в клипборда",
|
"copy_link_to_clipboard": "Копиране на връзката в клипборда",
|
||||||
"copy_password": "Копиране на парола",
|
"copy_password": "Копиране на парола",
|
||||||
@@ -881,22 +917,23 @@
|
|||||||
"cutoff_date_description": "Запазване на снимки от последните…",
|
"cutoff_date_description": "Запазване на снимки от последните…",
|
||||||
"cutoff_day": "{count, plural, one {ден} other {дни}}",
|
"cutoff_day": "{count, plural, one {ден} other {дни}}",
|
||||||
"cutoff_year": "{count, plural, one {година} other {години}}",
|
"cutoff_year": "{count, plural, one {година} other {години}}",
|
||||||
"daily_title_text_date": "E, dd MMM",
|
|
||||||
"daily_title_text_date_year": "E, dd MMM yyyy",
|
|
||||||
"dark": "Тъмен",
|
"dark": "Тъмен",
|
||||||
"dark_theme": "Премини към тъмна тема",
|
"dark_theme": "Премини към тъмна тема",
|
||||||
"date": "Дата",
|
"date": "Дата",
|
||||||
"date_after": "Дата след",
|
"date_after": "Дата след",
|
||||||
"date_and_time": "Дата и час",
|
"date_and_time": "Дата и час",
|
||||||
"date_before": "Дата преди",
|
"date_before": "Дата преди",
|
||||||
"date_format": "E, d LLL y • h:mm a",
|
"date_of_birth": "Дата на раждане",
|
||||||
"date_of_birth_saved": "Дата на раждане е записана успешно",
|
"date_of_birth_saved": "Дата на раждане е записана успешно",
|
||||||
"date_range": "Период от време",
|
"date_range": "Период от време",
|
||||||
|
"date_time_original": "Дата/Час на оригинала",
|
||||||
"day": "Ден",
|
"day": "Ден",
|
||||||
"days": "Дни",
|
"days": "Дни",
|
||||||
"deduplicate_all": "Дедупликиране на всички",
|
"deduplicate_all": "Дедупликиране на всички",
|
||||||
"default_locale": "Език по подразбиране",
|
"default_locale": "Език по подразбиране",
|
||||||
"default_locale_description": "Формат на дата и числа според езиковата настройка на браузъра",
|
"default_locale_description": "Формат на дата и числа според езиковата настройка на браузъра",
|
||||||
|
"default_quality_subtitle": "Качество при споделяне на файлове. Задръжте бутона за споделяне, за да изберете качеството.",
|
||||||
|
"default_share_quality": "Качество по подразбиране при споделяне на файлове",
|
||||||
"delete": "Изтрий",
|
"delete": "Изтрий",
|
||||||
"delete_action_confirmation_message": "Сигурни ли сте, че искате да изтриете този обект? Следва преместване на обекта в коша за отпадъци на сървъра и ще получите предложение обекта да бъде изтрит локално",
|
"delete_action_confirmation_message": "Сигурни ли сте, че искате да изтриете този обект? Следва преместване на обекта в коша за отпадъци на сървъра и ще получите предложение обекта да бъде изтрит локално",
|
||||||
"delete_action_prompt": "{count} са изтрити",
|
"delete_action_prompt": "{count} са изтрити",
|
||||||
@@ -970,7 +1007,10 @@
|
|||||||
"downloading_asset_filename": "Изтегляне на файл {filename}",
|
"downloading_asset_filename": "Изтегляне на файл {filename}",
|
||||||
"downloading_from_icloud": "Сваляне от iCloud",
|
"downloading_from_icloud": "Сваляне от iCloud",
|
||||||
"downloading_media": "Изтегляне на медия",
|
"downloading_media": "Изтегляне на медия",
|
||||||
|
"drag_to_reorder": "Плъзнете, за да пренаредите",
|
||||||
"drop_files_to_upload": "Пуснете файловете, за да ги качите",
|
"drop_files_to_upload": "Пуснете файловете, за да ги качите",
|
||||||
|
"duplicate": "Направи копие",
|
||||||
|
"duplicate_workflow": "Дублиране на работен процес",
|
||||||
"duplicates": "Дубликати",
|
"duplicates": "Дубликати",
|
||||||
"duplicates_description": "Изберете всяка група, като посочите кои, ако има такива, са дубликати.",
|
"duplicates_description": "Изберете всяка група, като посочите кои, ако има такива, са дубликати.",
|
||||||
"duration": "Продължителност",
|
"duration": "Продължителност",
|
||||||
@@ -1072,6 +1112,7 @@
|
|||||||
"failed_to_remove_product_key": "Неуспешно премахване на продуктовия ключ",
|
"failed_to_remove_product_key": "Неуспешно премахване на продуктовия ключ",
|
||||||
"failed_to_reset_pin_code": "Неуспешно нулиране на ПИН кода",
|
"failed_to_reset_pin_code": "Неуспешно нулиране на ПИН кода",
|
||||||
"failed_to_stack_assets": "Неуспешно подреждане на обекти",
|
"failed_to_stack_assets": "Неуспешно подреждане на обекти",
|
||||||
|
"failed_to_tag_assets": "Неуспешно добавяне на етикет",
|
||||||
"failed_to_unstack_assets": "Неуспешно премахване на подредбата на обекти",
|
"failed_to_unstack_assets": "Неуспешно премахване на подредбата на обекти",
|
||||||
"failed_to_update_notification_status": "Неуспешно обновяване на състоянието на известията",
|
"failed_to_update_notification_status": "Неуспешно обновяване на състоянието на известията",
|
||||||
"incorrect_email_or_password": "Неправилен имейл или парола",
|
"incorrect_email_or_password": "Неправилен имейл или парола",
|
||||||
@@ -1191,15 +1232,18 @@
|
|||||||
"export_as_json": "Експортиране като JSON",
|
"export_as_json": "Експортиране като JSON",
|
||||||
"export_database": "Експорт на базата данни",
|
"export_database": "Експорт на базата данни",
|
||||||
"export_database_description": "Експорт на базата данни SQLite",
|
"export_database_description": "Експорт на базата данни SQLite",
|
||||||
|
"exposure_time": "Време на експозиция",
|
||||||
"extension": "Разширение",
|
"extension": "Разширение",
|
||||||
"external": "Външно",
|
"external": "Външно",
|
||||||
"external_libraries": "Външни библиотеки",
|
"external_libraries": "Външни библиотеки",
|
||||||
"external_network": "Външна мрежа",
|
"external_network": "Външна мрежа",
|
||||||
"external_network_sheet_info": "Когато няма връзка с предпочитаната Wi-Fi мрежа, приложението ще опитва да се свърже със сървъра чрез първия достъпен URL адрес, започвайки отгоре надолу",
|
"external_network_sheet_info": "Когато няма връзка с предпочитаната Wi-Fi мрежа, приложението ще опитва да се свърже със сървъра чрез първия достъпен URL адрес, започвайки отгоре надолу",
|
||||||
|
"f_number": "F-число",
|
||||||
"face_unassigned": "Незададено",
|
"face_unassigned": "Незададено",
|
||||||
"failed": "Неуспешно",
|
"failed": "Неуспешно",
|
||||||
"failed_count": "Неуспешни: {count}",
|
"failed_count": "Неуспешни: {count}",
|
||||||
"failed_to_authenticate": "Неуспешна автентикация",
|
"failed_to_authenticate": "Неуспешна автентикация",
|
||||||
|
"failed_to_delete_file": "Неуспешно изтриване на файл",
|
||||||
"failed_to_load_assets": "Неуспешно зареждане на елементи",
|
"failed_to_load_assets": "Неуспешно зареждане на елементи",
|
||||||
"failed_to_load_folder": "Неуспешно зареждане на папка",
|
"failed_to_load_folder": "Неуспешно зареждане на папка",
|
||||||
"favorite": "Любим",
|
"favorite": "Любим",
|
||||||
@@ -1213,7 +1257,6 @@
|
|||||||
"features_setting_description": "Управление на функциите на приложението",
|
"features_setting_description": "Управление на функциите на приложението",
|
||||||
"file_name_or_extension": "Име на файл или разширение",
|
"file_name_or_extension": "Име на файл или разширение",
|
||||||
"file_name_text": "Имe на файл",
|
"file_name_text": "Имe на файл",
|
||||||
"file_name_with_value": "Име на файл: {file_name}",
|
|
||||||
"file_size": "Размер на файла",
|
"file_size": "Размер на файла",
|
||||||
"filename": "Име на файл",
|
"filename": "Име на файл",
|
||||||
"filetype": "Тип на файл",
|
"filetype": "Тип на файл",
|
||||||
@@ -1226,6 +1269,7 @@
|
|||||||
"find_them_fast": "Намерете ги бързо по име с търсене",
|
"find_them_fast": "Намерете ги бързо по име с търсене",
|
||||||
"first": "Първи",
|
"first": "Първи",
|
||||||
"fix_incorrect_match": "Поправяне на неправилно съвпадение",
|
"fix_incorrect_match": "Поправяне на неправилно съвпадение",
|
||||||
|
"focal_length": "Фокусно разстояние",
|
||||||
"folder": "Папка",
|
"folder": "Папка",
|
||||||
"folder_not_found": "Папката не е намерена",
|
"folder_not_found": "Папката не е намерена",
|
||||||
"folders": "Папки",
|
"folders": "Папки",
|
||||||
@@ -1236,6 +1280,7 @@
|
|||||||
"free_up_space_description": "Преместете архивираните снимки и видеа в кошчето на устройството, за да освободите място. Копията на сървъра ще бъдат запазени.",
|
"free_up_space_description": "Преместете архивираните снимки и видеа в кошчето на устройството, за да освободите място. Копията на сървъра ще бъдат запазени.",
|
||||||
"free_up_space_settings_subtitle": "Освобождаване на място за съхранение на устройството",
|
"free_up_space_settings_subtitle": "Освобождаване на място за съхранение на устройството",
|
||||||
"full_path": "Пълен път: {path}",
|
"full_path": "Пълен път: {path}",
|
||||||
|
"full_path_or_folder": "Пълен път или папка",
|
||||||
"gcast_enabled": "Gооgle Cast",
|
"gcast_enabled": "Gооgle Cast",
|
||||||
"gcast_enabled_description": "За да работи тази функция зарежда външни ресурси от Google.",
|
"gcast_enabled_description": "За да работи тази функция зарежда външни ресурси от Google.",
|
||||||
"general": "Общи",
|
"general": "Общи",
|
||||||
@@ -1329,6 +1374,7 @@
|
|||||||
"individual_share": "Индивидуално споделяне",
|
"individual_share": "Индивидуално споделяне",
|
||||||
"individual_shares": "Индивидуални споделяния",
|
"individual_shares": "Индивидуални споделяния",
|
||||||
"info": "Информация",
|
"info": "Информация",
|
||||||
|
"integrity_checks": "Проверка за непокътнатост",
|
||||||
"interval": {
|
"interval": {
|
||||||
"day_at_onepm": "Всеки ден в 13:00",
|
"day_at_onepm": "Всеки ден в 13:00",
|
||||||
"hours": "Всеки {hours, plural, one {час} other {{hours, number} часа}}",
|
"hours": "Всеки {hours, plural, one {час} other {{hours, number} часа}}",
|
||||||
@@ -1345,6 +1391,7 @@
|
|||||||
"ios_debug_info_no_sync_yet": "Все още не е изпълнявана задача за фонова синхронизация",
|
"ios_debug_info_no_sync_yet": "Все още не е изпълнявана задача за фонова синхронизация",
|
||||||
"ios_debug_info_processes_queued": "{count, plural, one {{count} фонов процес} many {{count} фонови процеса} other {{count} фонови процеса}} в опашката",
|
"ios_debug_info_processes_queued": "{count, plural, one {{count} фонов процес} many {{count} фонови процеса} other {{count} фонови процеса}} в опашката",
|
||||||
"ios_debug_info_processing_ran_at": "Започната обработка на {dateTime}",
|
"ios_debug_info_processing_ran_at": "Започната обработка на {dateTime}",
|
||||||
|
"iso": "ISO",
|
||||||
"items_count": "{count, plural, one {# елемент} other {# елементи}}",
|
"items_count": "{count, plural, one {# елемент} other {# елементи}}",
|
||||||
"jobs": "Задачи",
|
"jobs": "Задачи",
|
||||||
"json_editor": "JSON редактор",
|
"json_editor": "JSON редактор",
|
||||||
@@ -1375,6 +1422,7 @@
|
|||||||
"leave": "Излез",
|
"leave": "Излез",
|
||||||
"leave_album": "Напускане на албума",
|
"leave_album": "Напускане на албума",
|
||||||
"lens_model": "Модел леща",
|
"lens_model": "Модел леща",
|
||||||
|
"less": "По-малко",
|
||||||
"let_others_respond": "Позволете на другите да отговорят",
|
"let_others_respond": "Позволете на другите да отговорят",
|
||||||
"level": "Ниво",
|
"level": "Ниво",
|
||||||
"library": "Библиотека",
|
"library": "Библиотека",
|
||||||
@@ -1392,11 +1440,14 @@
|
|||||||
"light_theme": "Премини към светла тема",
|
"light_theme": "Премини към светла тема",
|
||||||
"like": "Харесайте",
|
"like": "Харесайте",
|
||||||
"like_deleted": "Като изтрит",
|
"like_deleted": "Като изтрит",
|
||||||
|
"link": "Връзка",
|
||||||
"link_motion_video": "Линк към видео",
|
"link_motion_video": "Линк към видео",
|
||||||
"link_to_docs": "За повече информация вижте <link>документацията</link>.",
|
"link_to_docs": "За повече информация вижте <link>документацията</link>.",
|
||||||
"link_to_oauth": "Линк към OAuth",
|
"link_to_oauth": "Линк към OAuth",
|
||||||
"linked_oauth_account": "Свързан OAuth акаунт",
|
"linked_oauth_account": "Свързан OAuth акаунт",
|
||||||
"list": "Лист",
|
"list": "Лист",
|
||||||
|
"live": "Живот",
|
||||||
|
"load_more": "Зареди още",
|
||||||
"loading": "Зареждане",
|
"loading": "Зареждане",
|
||||||
"loading_search_results_failed": "Зареждането на резултатите от търсенето е неуспешно",
|
"loading_search_results_failed": "Зареждането на резултатите от търсенето е неуспешно",
|
||||||
"local": "Локално",
|
"local": "Локално",
|
||||||
@@ -1497,7 +1548,7 @@
|
|||||||
"map_location_picker_page_use_location": "Използвай това местоположение",
|
"map_location_picker_page_use_location": "Използвай това местоположение",
|
||||||
"map_location_service_disabled_content": "За да се показват обектите от текущото място, трябва да бъде включена услугата за местоположение. Искате ли да я включите сега?",
|
"map_location_service_disabled_content": "За да се показват обектите от текущото място, трябва да бъде включена услугата за местоположение. Искате ли да я включите сега?",
|
||||||
"map_location_service_disabled_title": "Услугата за местоположение е изключена",
|
"map_location_service_disabled_title": "Услугата за местоположение е изключена",
|
||||||
"map_marker_for_images": "Маркери на картата за снимки направени в {city}, {country}",
|
"map_marker_for_image": "Маркер на картата за снимка, направена в {city}, {country}",
|
||||||
"map_marker_with_image": "Маркер на картата с изображение",
|
"map_marker_with_image": "Маркер на картата с изображение",
|
||||||
"map_no_location_permission_content": "За да се показват обектите от текущото място, трябва разрешение за определяне на местоположението. Искате ли да предоставите разрешение сега?",
|
"map_no_location_permission_content": "За да се показват обектите от текущото място, трябва разрешение за определяне на местоположението. Искате ли да предоставите разрешение сега?",
|
||||||
"map_no_location_permission_title": "Отказан достъп до местоположение",
|
"map_no_location_permission_title": "Отказан достъп до местоположение",
|
||||||
@@ -1518,6 +1569,38 @@
|
|||||||
"marked_all_as_read": "Всички маркирани като прочетени",
|
"marked_all_as_read": "Всички маркирани като прочетени",
|
||||||
"matches": "Съвпадения",
|
"matches": "Съвпадения",
|
||||||
"matching_assets": "Съвпадащи обекти",
|
"matching_assets": "Съвпадащи обекти",
|
||||||
|
"media_chrome": {
|
||||||
|
"auto": "Авто",
|
||||||
|
"captions": "Субтитри",
|
||||||
|
"captions_off": "Изключенo",
|
||||||
|
"closed_captions": "субтитри",
|
||||||
|
"decode_error": "Грешка при декодиране",
|
||||||
|
"disable_captions": "Изключи субтитри",
|
||||||
|
"enable_captions": "Включи субтитри",
|
||||||
|
"enter_fullscreen_mode": "На цял екран",
|
||||||
|
"exit_fullscreen_mode": "Изход от цял екран",
|
||||||
|
"loop": "Повтаряй",
|
||||||
|
"media_error_description": "Възпроизвеждането е спряно поради грешка във файла. Може би файлът е повреден или браузъра не поддържа този формат.",
|
||||||
|
"media_loading": "зареждане на медия",
|
||||||
|
"mute": "Без звук",
|
||||||
|
"network_error": "Грешка в мрежата",
|
||||||
|
"network_error_description": "Прекъсване на зареждането поради грешка в мрежата.",
|
||||||
|
"not_supported_error": "Този източник не се поддържа",
|
||||||
|
"playback_rate": "Скорост на възпроизвеждане",
|
||||||
|
"playback_rate_current": "текуща скорост на възпроизвеждане",
|
||||||
|
"playback_rate_value": "Скорост на възпроизвеждане {playbackRate}",
|
||||||
|
"playback_time": "продължителност",
|
||||||
|
"quality": "Качество",
|
||||||
|
"second": "секунда",
|
||||||
|
"seconds": "секунди",
|
||||||
|
"time_value_of_total_time": "{currentTime} от {totalTime}",
|
||||||
|
"time_value_remaining": "{time} остават",
|
||||||
|
"unmute": "Включи звук",
|
||||||
|
"unsupported_error_description": "Възникна непоправима грешка. Проблем в сървъра или мрежата, възможно е браузъра да не поддържа този формат.",
|
||||||
|
"video_not_loaded_unknown_time": "не е заредено видео, неизвестно време.",
|
||||||
|
"video_player": "видеоплеер",
|
||||||
|
"volume": "сила на звука"
|
||||||
|
},
|
||||||
"media_type": "Вид медия",
|
"media_type": "Вид медия",
|
||||||
"memories": "Спомени",
|
"memories": "Спомени",
|
||||||
"memories_all_caught_up": "Това е всичко за днес",
|
"memories_all_caught_up": "Това е всичко за днес",
|
||||||
@@ -1534,6 +1617,8 @@
|
|||||||
"merge_people_prompt": "Искате ли да слеете тези хора? Това действие е необратимо.",
|
"merge_people_prompt": "Искате ли да слеете тези хора? Това действие е необратимо.",
|
||||||
"merge_people_successfully": "Успешно сливане на хора",
|
"merge_people_successfully": "Успешно сливане на хора",
|
||||||
"merged_people_count": "Слят {count, plural, one {# човек} other {# човека}}",
|
"merged_people_count": "Слят {count, plural, one {# човек} other {# човека}}",
|
||||||
|
"minFaces": "Минимум лица",
|
||||||
|
"minFaces_description": "Минималният брой разпознати лица, за да бъде показан човек като разпознат",
|
||||||
"minimize": "Минимизиране",
|
"minimize": "Минимизиране",
|
||||||
"minute": "Минута",
|
"minute": "Минута",
|
||||||
"minutes": "Минути",
|
"minutes": "Минути",
|
||||||
@@ -1543,9 +1628,10 @@
|
|||||||
"mobile_app": "Мобилно приложение",
|
"mobile_app": "Мобилно приложение",
|
||||||
"mobile_app_download_onboarding_note": "Свалете мобилното приложение Immich с някоя от следните опции",
|
"mobile_app_download_onboarding_note": "Свалете мобилното приложение Immich с някоя от следните опции",
|
||||||
"model": "Модел",
|
"model": "Модел",
|
||||||
|
"modify_date": "Дата на промянa",
|
||||||
"month": "Месец",
|
"month": "Месец",
|
||||||
"monthly_title_text_date_format": "MMMM г",
|
|
||||||
"more": "Още",
|
"more": "Още",
|
||||||
|
"motion": "Движение",
|
||||||
"move": "Премести",
|
"move": "Премести",
|
||||||
"move_down": "Премести надолу",
|
"move_down": "Премести надолу",
|
||||||
"move_off_locked_folder": "Извади от заключената папка",
|
"move_off_locked_folder": "Извади от заключената папка",
|
||||||
@@ -1562,6 +1648,8 @@
|
|||||||
"multiselect_grid_edit_gps_err_read_only": "Не може да се редактира местоположението на обект само за четене, пропускане",
|
"multiselect_grid_edit_gps_err_read_only": "Не може да се редактира местоположението на обект само за четене, пропускане",
|
||||||
"mute_memories": "Изключване на звука на спомените",
|
"mute_memories": "Изключване на звука на спомените",
|
||||||
"my_albums": "Мои албуми",
|
"my_albums": "Мои албуми",
|
||||||
|
"my_immich_description": "Копирай адреса на текущата страница като връзка към моя Immich",
|
||||||
|
"my_immich_title": "Връзка към моя Immich",
|
||||||
"name": "Име",
|
"name": "Име",
|
||||||
"name_or_nickname": "Име или прякор",
|
"name_or_nickname": "Име или прякор",
|
||||||
"name_required": "Задължително е Име",
|
"name_required": "Задължително е Име",
|
||||||
@@ -1589,7 +1677,6 @@
|
|||||||
"next": "Следващо",
|
"next": "Следващо",
|
||||||
"next_memory": "Следващ спомен",
|
"next_memory": "Следващ спомен",
|
||||||
"no": "Не",
|
"no": "Не",
|
||||||
"no_actions_added": "Все още не са добавени действия",
|
|
||||||
"no_albums_found": "Не са намерени албуми",
|
"no_albums_found": "Не са намерени албуми",
|
||||||
"no_albums_message": "Създайте албум за организиране на снимки и видеоклипове",
|
"no_albums_message": "Създайте албум за организиране на снимки и видеоклипове",
|
||||||
"no_albums_with_name_yet": "Изглежда, че все още нямате албуми с това име.",
|
"no_albums_with_name_yet": "Изглежда, че все още нямате албуми с това име.",
|
||||||
@@ -1606,7 +1693,6 @@
|
|||||||
"no_exif_info_available": "Няма exif информация",
|
"no_exif_info_available": "Няма exif информация",
|
||||||
"no_explore_results_message": "Качете още снимки, за да разгледате колекцията си.",
|
"no_explore_results_message": "Качете още снимки, за да разгледате колекцията си.",
|
||||||
"no_favorites_message": "Добавете в любими, за да намирате бързо най-добрите си снимки и видеоклипове",
|
"no_favorites_message": "Добавете в любими, за да намирате бързо най-добрите си снимки и видеоклипове",
|
||||||
"no_filters_added": "Все още не са добавени филтри",
|
|
||||||
"no_libraries_message": "Създайте външна библиотека за да разглеждате снимки и видеоклипове",
|
"no_libraries_message": "Създайте външна библиотека за да разглеждате снимки и видеоклипове",
|
||||||
"no_local_assets_found": "Не е намерен локален обект с такава контролна сума",
|
"no_local_assets_found": "Не е намерен локален обект с такава контролна сума",
|
||||||
"no_location_set": "Не е зададено местоположение",
|
"no_location_set": "Не е зададено местоположение",
|
||||||
@@ -1619,6 +1705,7 @@
|
|||||||
"no_results": "Няма резултати",
|
"no_results": "Няма резултати",
|
||||||
"no_results_description": "Опитайте със синоним или по-обща ключова дума",
|
"no_results_description": "Опитайте със синоним или по-обща ключова дума",
|
||||||
"no_shared_albums_message": "Създайте албум, за да споделяте снимки и видеоклипове с хората в мрежата си",
|
"no_shared_albums_message": "Създайте албум, за да споделяте снимки и видеоклипове с хората в мрежата си",
|
||||||
|
"no_steps": "Все още няма добавени стъпки",
|
||||||
"no_uploads_in_progress": "Няма качване в момента",
|
"no_uploads_in_progress": "Няма качване в момента",
|
||||||
"none": "Нищо",
|
"none": "Нищо",
|
||||||
"not_allowed": "Не е разрешено",
|
"not_allowed": "Не е разрешено",
|
||||||
@@ -1627,6 +1714,7 @@
|
|||||||
"not_selected": "Не е избрано",
|
"not_selected": "Не е избрано",
|
||||||
"notes": "Бележки",
|
"notes": "Бележки",
|
||||||
"nothing_here_yet": "Засега тук няма нищо",
|
"nothing_here_yet": "Засега тук няма нищо",
|
||||||
|
"notification_backup_reliability": "Позволете известията, за да подобрите надеждността на архивиране във фонов режим",
|
||||||
"notification_permission_dialog_content": "За да включиш известията, отиди в Настройки и избери Разреши.",
|
"notification_permission_dialog_content": "За да включиш известията, отиди в Настройки и избери Разреши.",
|
||||||
"notification_permission_list_tile_content": "Дай разрешение за активиране на известията.",
|
"notification_permission_list_tile_content": "Дай разрешение за активиране на известията.",
|
||||||
"notification_permission_list_tile_enable_button": "Разреши известията",
|
"notification_permission_list_tile_enable_button": "Разреши известията",
|
||||||
@@ -1664,6 +1752,7 @@
|
|||||||
"organize_into_albums": "Подредете в албуми",
|
"organize_into_albums": "Подредете в албуми",
|
||||||
"organize_into_albums_description": "Добавете наличните снимки в албуми, като използвате текущите настройки за синхронизиране",
|
"organize_into_albums_description": "Добавете наличните снимки в албуми, като използвате текущите настройки за синхронизиране",
|
||||||
"organize_your_library": "Организиране на вашата библиотека",
|
"organize_your_library": "Организиране на вашата библиотека",
|
||||||
|
"orientation": "Ориентация",
|
||||||
"original": "оригинал",
|
"original": "оригинал",
|
||||||
"other": "Други",
|
"other": "Други",
|
||||||
"other_devices": "Други устройства",
|
"other_devices": "Други устройства",
|
||||||
@@ -1755,6 +1844,8 @@
|
|||||||
"play_original_video_setting_description": "Предпочитане на показване на оригиналното видео, вместо транскодирани. Ако формата на оригиналния файл не се поддържа, възпроизвеждането може да бъде неправилно.",
|
"play_original_video_setting_description": "Предпочитане на показване на оригиналното видео, вместо транскодирани. Ако формата на оригиналния файл не се поддържа, възпроизвеждането може да бъде неправилно.",
|
||||||
"play_transcoded_video": "Покажи транскодирано видео",
|
"play_transcoded_video": "Покажи транскодирано видео",
|
||||||
"please_auth_to_access": "Моля, удостовери за достъп",
|
"please_auth_to_access": "Моля, удостовери за достъп",
|
||||||
|
"plugin_method_filter_type": "Филтър",
|
||||||
|
"plugin_method_filter_type_description": "Този метод може да филтрира събития и по условие да спира изпълнението на следващи стъпки",
|
||||||
"port": "Порт",
|
"port": "Порт",
|
||||||
"preferences_settings_subtitle": "Управление на предпочитанията на приложението",
|
"preferences_settings_subtitle": "Управление на предпочитанията на приложението",
|
||||||
"preferences_settings_title": "Предпочитания",
|
"preferences_settings_title": "Предпочитания",
|
||||||
@@ -1776,6 +1867,7 @@
|
|||||||
"profile_drawer_readonly_mode": "Режима само за четене е активиран. С дълго натискане върху картиката-аватар на потребителя ще деактивирате само за четене.",
|
"profile_drawer_readonly_mode": "Режима само за четене е активиран. С дълго натискане върху картиката-аватар на потребителя ще деактивирате само за четене.",
|
||||||
"profile_image_of_user": "Профилна снимка на {user}",
|
"profile_image_of_user": "Профилна снимка на {user}",
|
||||||
"profile_picture_set": "Профилната снимка е сложена.",
|
"profile_picture_set": "Профилната снимка е сложена.",
|
||||||
|
"projection_type": "Тип проекция",
|
||||||
"public_album": "Публичен албум",
|
"public_album": "Публичен албум",
|
||||||
"public_share": "Публично споделяне",
|
"public_share": "Публично споделяне",
|
||||||
"purchase_account_info": "Поддръжник",
|
"purchase_account_info": "Поддръжник",
|
||||||
@@ -1853,6 +1945,7 @@
|
|||||||
"remove_assets_title": "Премахване на елементите?",
|
"remove_assets_title": "Премахване на елементите?",
|
||||||
"remove_custom_date_range": "Премахни зададения диапазон от дати",
|
"remove_custom_date_range": "Премахни зададения диапазон от дати",
|
||||||
"remove_deleted_assets": "Премахни Изтритите Елементи",
|
"remove_deleted_assets": "Премахни Изтритите Елементи",
|
||||||
|
"remove_filter": "Премахни филтър",
|
||||||
"remove_from_album": "Премахни от албума",
|
"remove_from_album": "Премахни от албума",
|
||||||
"remove_from_album_action_prompt": "{count} са премахнати от албума",
|
"remove_from_album_action_prompt": "{count} са премахнати от албума",
|
||||||
"remove_from_favorites": "Премахни от Любими",
|
"remove_from_favorites": "Премахни от Любими",
|
||||||
@@ -1926,6 +2019,8 @@
|
|||||||
"scan_settings": "Сканирай настройките",
|
"scan_settings": "Сканирай настройките",
|
||||||
"scanning": "Сканиране",
|
"scanning": "Сканиране",
|
||||||
"scanning_for_album": "Сканирай за албум...",
|
"scanning_for_album": "Сканирай за албум...",
|
||||||
|
"screencast_mode_description": "Показване на екрана на индикатори за събития от клавиатурата и мишката",
|
||||||
|
"screencast_mode_title": "Превключване на режима на скрийнкаст",
|
||||||
"search": "Търсене",
|
"search": "Търсене",
|
||||||
"search_albums": "Търси албуми",
|
"search_albums": "Търси албуми",
|
||||||
"search_by_context": "Търси по контекст",
|
"search_by_context": "Търси по контекст",
|
||||||
@@ -1933,6 +2028,8 @@
|
|||||||
"search_by_description_example": "Разходка в Сапа",
|
"search_by_description_example": "Разходка в Сапа",
|
||||||
"search_by_filename": "Търси по име на файла или разширение",
|
"search_by_filename": "Търси по име на файла или разширение",
|
||||||
"search_by_filename_example": "например IMG_1234.JPG или PNG",
|
"search_by_filename_example": "например IMG_1234.JPG или PNG",
|
||||||
|
"search_by_full_path": "Търсене по пълен път или папка",
|
||||||
|
"search_by_full_path_example": "/John/Projects/3D_Printing/2026-07-01 - търсена за Projects, 3D, Printing, 2026 и т.н.",
|
||||||
"search_by_ocr": "Търсене на текст",
|
"search_by_ocr": "Търсене на текст",
|
||||||
"search_by_ocr_example": "Lattе",
|
"search_by_ocr_example": "Lattе",
|
||||||
"search_camera_lens_model": "Търсене на модел на обектива...",
|
"search_camera_lens_model": "Търсене на модел на обектива...",
|
||||||
@@ -2009,6 +2106,7 @@
|
|||||||
"select_person": "Изберете човек",
|
"select_person": "Изберете човек",
|
||||||
"select_person_to_tag": "Избери лице, което да маркираш",
|
"select_person_to_tag": "Избери лице, което да маркираш",
|
||||||
"select_photos": "Изберете снимки",
|
"select_photos": "Изберете снимки",
|
||||||
|
"select_quality": "Изберете качество",
|
||||||
"select_trash_all": "Изберете всичко за кошчето",
|
"select_trash_all": "Изберете всичко за кошчето",
|
||||||
"select_user_for_sharing_page_err_album": "Създаването на албум не бе успешно",
|
"select_user_for_sharing_page_err_album": "Създаването на албум не бе успешно",
|
||||||
"selected": "Избрано",
|
"selected": "Избрано",
|
||||||
@@ -2072,6 +2170,8 @@
|
|||||||
"share_assets_selected": "{count} избрани",
|
"share_assets_selected": "{count} избрани",
|
||||||
"share_dialog_preparing": "Подготовка...",
|
"share_dialog_preparing": "Подготовка...",
|
||||||
"share_link": "Връзка за споделяне",
|
"share_link": "Връзка за споделяне",
|
||||||
|
"share_original": "Използвай оригинала (голям размер)",
|
||||||
|
"share_preview": "Използвай миниатюра (намален размер)",
|
||||||
"shared": "Споделено",
|
"shared": "Споделено",
|
||||||
"shared_album_activities_input_disable": "Коментарите са изключени",
|
"shared_album_activities_input_disable": "Коментарите са изключени",
|
||||||
"shared_album_activity_remove_content": "Искате ли да изтриете тази активност?",
|
"shared_album_activity_remove_content": "Искате ли да изтриете тази активност?",
|
||||||
@@ -2140,7 +2240,9 @@
|
|||||||
"show_in_timeline": "Показване във времевата линия",
|
"show_in_timeline": "Показване във времевата линия",
|
||||||
"show_in_timeline_setting_description": "Показване на снимки и видеа от този потребител във времевата линия",
|
"show_in_timeline_setting_description": "Показване на снимки и видеа от този потребител във времевата линия",
|
||||||
"show_keyboard_shortcuts": "Покажи клавишни комбинации",
|
"show_keyboard_shortcuts": "Покажи клавишни комбинации",
|
||||||
|
"show_less": "Покажи по-малко",
|
||||||
"show_metadata": "Покажи метаданни",
|
"show_metadata": "Покажи метаданни",
|
||||||
|
"show_more_fields": "{count, plural, one {Покажи още # поле} other {Покажи още # полета}}",
|
||||||
"show_or_hide_info": "Покажи или скрий информацията",
|
"show_or_hide_info": "Покажи или скрий информацията",
|
||||||
"show_password": "Покажи паролата",
|
"show_password": "Покажи паролата",
|
||||||
"show_person_options": "Показване на опции за лица",
|
"show_person_options": "Показване на опции за лица",
|
||||||
@@ -2148,6 +2250,7 @@
|
|||||||
"show_schema": "Покажи схема",
|
"show_schema": "Покажи схема",
|
||||||
"show_search_options": "Показване на опциите за търсене",
|
"show_search_options": "Показване на опциите за търсене",
|
||||||
"show_shared_links": "Покажи споделени линкове",
|
"show_shared_links": "Покажи споделени линкове",
|
||||||
|
"show_slideshow_metadata_overlay": "Покажи информационния слой",
|
||||||
"show_slideshow_transition": "Покажи прехода на слайдшоуто",
|
"show_slideshow_transition": "Покажи прехода на слайдшоуто",
|
||||||
"show_supporter_badge": "Значка поддръжник",
|
"show_supporter_badge": "Значка поддръжник",
|
||||||
"show_supporter_badge_description": "Покажи значка поддръжник",
|
"show_supporter_badge_description": "Покажи значка поддръжник",
|
||||||
@@ -2163,31 +2266,41 @@
|
|||||||
"skip_to_folders": "Премини към папките",
|
"skip_to_folders": "Премини към папките",
|
||||||
"skip_to_tags": "Премини към етикетите",
|
"skip_to_tags": "Премини към етикетите",
|
||||||
"slideshow": "Слайдшоу",
|
"slideshow": "Слайдшоу",
|
||||||
|
"slideshow_metadata_overlay_mode": "Съдържание на слоя с информация",
|
||||||
|
"slideshow_metadata_overlay_mode_description_only": "Само описание",
|
||||||
|
"slideshow_metadata_overlay_mode_full": "Пълна",
|
||||||
"slideshow_repeat": "Повтаряй слайдшоуто",
|
"slideshow_repeat": "Повтаряй слайдшоуто",
|
||||||
"slideshow_repeat_description": "Започвай отново, когато слайдшоуто приключи",
|
"slideshow_repeat_description": "Започвай отново, когато слайдшоуто приключи",
|
||||||
"slideshow_settings": "Настройки за слайдшоу",
|
"slideshow_settings": "Настройки за слайдшоу",
|
||||||
|
"smart_album": "Умен албум",
|
||||||
|
"some_assets_already_have_a_location_warning": "Някои от избраните файлове вече имат местоположение",
|
||||||
"sort_albums_by": "Сортиране на албуми по...",
|
"sort_albums_by": "Сортиране на албуми по...",
|
||||||
"sort_created": "Дата на създаване",
|
"sort_created": "Дата на създаване",
|
||||||
"sort_items": "Брой елементи",
|
"sort_items": "Брой елементи",
|
||||||
"sort_modified": "Дата на промяна",
|
"sort_modified": "Дата на промяна",
|
||||||
"sort_newest": "Най-нови снимки",
|
"sort_newest": "Най-нова снимка",
|
||||||
"sort_oldest": "Най-старата снимка",
|
"sort_oldest": "Най-стара снимка",
|
||||||
"sort_people_by_similarity": "Сортиране на хора по прилика",
|
"sort_people_by_similarity": "Сортиране на хора по прилика",
|
||||||
"sort_recent": "Най-новата снимка",
|
"sort_recent": "Най-скорошна снимка",
|
||||||
"sort_title": "Заглавие",
|
"sort_title": "Заглавие",
|
||||||
"source": "Код",
|
"source": "Код",
|
||||||
"stack": "Събери",
|
"stack": "Групирай",
|
||||||
"stack_action_prompt": "{count} са групирани",
|
"stack_action_prompt": "{count} са групирани",
|
||||||
"stack_duplicates": "Подреждане на дубликати",
|
"stack_duplicates": "Групирай дубликати",
|
||||||
"stack_select_one_photo": "Избери една главна снимка за събраните снимки",
|
"stack_select_one_photo": "Избери главна снимка за групировката",
|
||||||
"stack_selected_photos": "Подреждане на избрани снимки",
|
"stack_selected_photos": "Групиране на избрани снимки",
|
||||||
"stacked_assets_count": "Събрани {count, plural, one {# елемент} other {# елементи}}",
|
"stacked_assets_count": "Групиран(и) {count, plural, one {# елемент} other {# елементи}}",
|
||||||
"stacktrace": "Следа на събраните",
|
"stacktrace": "Проследяване на стека (stacktrace)",
|
||||||
"start": "Старт",
|
"start": "Начало",
|
||||||
"start_date": "Начална дата",
|
"start_date": "Начална дата",
|
||||||
"start_date_before_end_date": "Началната дата трябва да бъде преди крайната дата",
|
"start_date_before_end_date": "Началната дата трябва да бъде преди крайната дата",
|
||||||
"state": "Щат",
|
"state": "Област (щат)",
|
||||||
"status": "Статус",
|
"status": "Статус",
|
||||||
|
"step_delete": "Премахни стъпката",
|
||||||
|
"step_delete_confirm": "Сигурни ли сте, че искате да премахнете тази стъпка?",
|
||||||
|
"step_details": "Подробности за стъпката",
|
||||||
|
"steps": "Стъпки",
|
||||||
|
"steps_count": "{count, plural, one {# стъпка} other {# стъпки}}",
|
||||||
"stop_casting": "Спри предаването",
|
"stop_casting": "Спри предаването",
|
||||||
"stop_motion_photo": "Снимка със стоп кадър",
|
"stop_motion_photo": "Снимка със стоп кадър",
|
||||||
"stop_photo_sharing": "Да спра ли споделянето на вашите снимки?",
|
"stop_photo_sharing": "Да спра ли споделянето на вашите снимки?",
|
||||||
@@ -2214,6 +2327,8 @@
|
|||||||
"sync_status": "Състояние на синхронизацията",
|
"sync_status": "Състояние на синхронизацията",
|
||||||
"sync_status_subtitle": "Преглед и управление на системата за синхронизация",
|
"sync_status_subtitle": "Преглед и управление на системата за синхронизация",
|
||||||
"sync_upload_album_setting_subtitle": "Създавайте и зареждайте снимки и видеа в избрани албуми в Immich",
|
"sync_upload_album_setting_subtitle": "Създавайте и зареждайте снимки и видеа в избрани албуми в Immich",
|
||||||
|
"system_theme": "Тема от системата",
|
||||||
|
"system_theme_command_description": "Използвай системната тема ({value})",
|
||||||
"tag": "Таг",
|
"tag": "Таг",
|
||||||
"tag_assets": "Тагни елементи",
|
"tag_assets": "Тагни елементи",
|
||||||
"tag_created": "Създаден етикет: {tag}",
|
"tag_created": "Създаден етикет: {tag}",
|
||||||
@@ -2279,11 +2394,13 @@
|
|||||||
"trash_page_title": "В коша ({count})",
|
"trash_page_title": "В коша ({count})",
|
||||||
"trashed_items_will_be_permanently_deleted_after": "Изхвърлените в кошчето елементи ще бъдат изтрити за постоянно след {days, plural, one {# ден} other {# дни}}.",
|
"trashed_items_will_be_permanently_deleted_after": "Изхвърлените в кошчето елементи ще бъдат изтрити за постоянно след {days, plural, one {# ден} other {# дни}}.",
|
||||||
"trigger": "Тригер",
|
"trigger": "Тригер",
|
||||||
"trigger_asset_uploaded": "Обектът е зареден",
|
"trigger_asset_metadata_extraction": "Извличане на метаданни от елементи",
|
||||||
|
"trigger_asset_metadata_extraction_description": "Задействан, когато EXIF метаданните от даден елемент са извлечени",
|
||||||
|
"trigger_asset_uploaded": "Качване на файлове",
|
||||||
"trigger_asset_uploaded_description": "Сработва при зареждане на нов обект",
|
"trigger_asset_uploaded_description": "Сработва при зареждане на нов обект",
|
||||||
"trigger_description": "Събитие, което стартира работния процес",
|
"trigger_description": "Събитие, което стартира работния процес",
|
||||||
"trigger_person_recognized": "Разпознато е лице",
|
"trigger_person_recognized": "Разпознато е лице",
|
||||||
"trigger_person_recognized_description": "Сработва при разпознаване на лице",
|
"trigger_person_recognized_description": "Задействан при разпознаване на лице",
|
||||||
"trigger_type": "Тип на тригера",
|
"trigger_type": "Тип на тригера",
|
||||||
"troubleshoot": "Отстраняване на проблеми",
|
"troubleshoot": "Отстраняване на проблеми",
|
||||||
"type": "Тип",
|
"type": "Тип",
|
||||||
@@ -2319,13 +2436,13 @@
|
|||||||
"unsupported_field_type": "Типа на полето не се поддържа",
|
"unsupported_field_type": "Типа на полето не се поддържа",
|
||||||
"unsupported_file_type": "Файлът {file} не може да бъде зареден, защото неговият тип {type} не се поддържа.",
|
"unsupported_file_type": "Файлът {file} не може да бъде зареден, защото неговият тип {type} не се поддържа.",
|
||||||
"untagged": "Немаркирани",
|
"untagged": "Немаркирани",
|
||||||
"untitled_workflow": "Работен процес без име",
|
|
||||||
"up_next": "Следващ",
|
"up_next": "Следващ",
|
||||||
"update_location_action_prompt": "Обнови координатите на {count} избрани обекта с:",
|
"update_location_action_prompt": "Обнови координатите на {count} избрани обекта с:",
|
||||||
"updated_at": "Обновено",
|
"updated_at": "Обновено",
|
||||||
"updated_password": "Паролата е променена",
|
"updated_password": "Паролата е променена",
|
||||||
"upload": "Качване",
|
"upload": "Качване",
|
||||||
"upload_concurrency": "Успоредни качвания",
|
"upload_concurrency": "Успоредни качвания",
|
||||||
|
"upload_day_count": "{date}: {count, plural, one {# качване} other {# качвания}}",
|
||||||
"upload_details": "Детайли за качването",
|
"upload_details": "Детайли за качването",
|
||||||
"upload_dialog_info": "Искате ли да архивирате на сървъра избраните обекти?",
|
"upload_dialog_info": "Искате ли да архивирате на сървъра избраните обекти?",
|
||||||
"upload_dialog_title": "Качи обект",
|
"upload_dialog_title": "Качи обект",
|
||||||
@@ -2341,6 +2458,8 @@
|
|||||||
"upload_to_immich": "Казване в Immich ({count})",
|
"upload_to_immich": "Казване в Immich ({count})",
|
||||||
"uploading": "Качваме",
|
"uploading": "Качваме",
|
||||||
"uploading_media": "Качване на медийни файлове",
|
"uploading_media": "Качване на медийни файлове",
|
||||||
|
"uploads": "Качвания",
|
||||||
|
"uploads_count": "{count, plural, one {# качване} other {# качвания}}",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
"usage": "Потребление",
|
"usage": "Потребление",
|
||||||
"use_biometric": "Използвай биометрия",
|
"use_biometric": "Използвай биометрия",
|
||||||
@@ -2348,6 +2467,7 @@
|
|||||||
"use_browser_locale_description": "Формат на дата, време и числа според езиковата настройка на браузъра",
|
"use_browser_locale_description": "Формат на дата, време и числа според езиковата настройка на браузъра",
|
||||||
"use_current_connection": "Използвай текущата връзка",
|
"use_current_connection": "Използвай текущата връзка",
|
||||||
"use_custom_date_range": "Използвайте собствен диапазон от дати вместо това",
|
"use_custom_date_range": "Използвайте собствен диапазон от дати вместо това",
|
||||||
|
"use_template": "Използвайте шаблон",
|
||||||
"user": "Потребител",
|
"user": "Потребител",
|
||||||
"user_has_been_deleted": "Този потребител е премахнат.",
|
"user_has_been_deleted": "Този потребител е премахнат.",
|
||||||
"user_id": "Потребител ИД",
|
"user_id": "Потребител ИД",
|
||||||
@@ -2377,6 +2497,7 @@
|
|||||||
"video": "Видеоклип",
|
"video": "Видеоклип",
|
||||||
"video_hover_setting": "Възпроизвеждане на видеоклип при посочване с мишката",
|
"video_hover_setting": "Възпроизвеждане на видеоклип при посочване с мишката",
|
||||||
"video_hover_setting_description": "Възпроизвеждане на видеоклипа, когато мишката се движи над елемента. Дори когато е деактивирано, възпроизвеждането може да бъде стартирано чрез задържане на курсора на мишката върху иконата за възпроизвеждане.",
|
"video_hover_setting_description": "Възпроизвеждане на видеоклипа, когато мишката се движи над елемента. Дори когато е деактивирано, възпроизвеждането може да бъде стартирано чрез задържане на курсора на мишката върху иконата за възпроизвеждане.",
|
||||||
|
"video_quality": "Качество на видеото",
|
||||||
"videos": "Видеоклипове",
|
"videos": "Видеоклипове",
|
||||||
"videos_count": "{count, plural, one {# Видео} other {# Видеа}}",
|
"videos_count": "{count, plural, one {# Видео} other {# Видеа}}",
|
||||||
"videos_only": "Само видеа",
|
"videos_only": "Само видеа",
|
||||||
@@ -2409,8 +2530,10 @@
|
|||||||
"week": "Седмица",
|
"week": "Седмица",
|
||||||
"welcome": "Добре дошли",
|
"welcome": "Добре дошли",
|
||||||
"welcome_to_immich": "Добре дошли в Immich",
|
"welcome_to_immich": "Добре дошли в Immich",
|
||||||
|
"when": "Когато",
|
||||||
"width": "Ширинa",
|
"width": "Ширинa",
|
||||||
"wifi_name": "Wi-Fi мрежа",
|
"wifi_name": "Wi-Fi мрежа",
|
||||||
|
"workflow": "Работен процес",
|
||||||
"workflow_delete_prompt": "Наистина ли искате да изтриете този работен процес?",
|
"workflow_delete_prompt": "Наистина ли искате да изтриете този работен процес?",
|
||||||
"workflow_deleted": "Работния процес е изтрит",
|
"workflow_deleted": "Работния процес е изтрит",
|
||||||
"workflow_description": "Описание на работния процес",
|
"workflow_description": "Описание на работния процес",
|
||||||
@@ -2420,11 +2543,13 @@
|
|||||||
"workflow_name": "Име на работния процес",
|
"workflow_name": "Име на работния процес",
|
||||||
"workflow_navigation_prompt": "Наистина ли искате да излезете без да съхраните промените?",
|
"workflow_navigation_prompt": "Наистина ли искате да излезете без да съхраните промените?",
|
||||||
"workflow_summary": "Обобщение за работния процес",
|
"workflow_summary": "Обобщение за работния процес",
|
||||||
|
"workflow_templates": "Шаблони на работния процес",
|
||||||
"workflow_update_success": "Работният процес е успешно обновен",
|
"workflow_update_success": "Работният процес е успешно обновен",
|
||||||
"workflow_updated": "Работният процес е обновен",
|
"workflow_updated": "Работният процес е обновен",
|
||||||
"workflows": "Работни процеси",
|
"workflows": "Работни процеси",
|
||||||
"workflows_help_text": "Работните процеси автоматизират действията с вашите обекти чрез тригери и филтри",
|
"workflows_help_text": "Работните процеси автоматизират действията с вашите обекти чрез тригери и филтри",
|
||||||
"wrong_pin_code": "Грешен PIN код",
|
"wrong_pin_code": "Грешен PIN код",
|
||||||
|
"x_of_total": "{x}/{total}",
|
||||||
"year": "Година",
|
"year": "Година",
|
||||||
"years_ago": "преди {years, plural, one {# година} other {# години}}",
|
"years_ago": "преди {years, plural, one {# година} other {# години}}",
|
||||||
"yes": "Да",
|
"yes": "Да",
|
||||||
|
|||||||
+4
-3
@@ -22,13 +22,12 @@
|
|||||||
"add_birthday": "জন্মদিন যোগ করুন",
|
"add_birthday": "জন্মদিন যোগ করুন",
|
||||||
"add_endpoint": "এন্ডপয়েন্ট যোগ করুন",
|
"add_endpoint": "এন্ডপয়েন্ট যোগ করুন",
|
||||||
"add_exclusion_pattern": "বহির্ভূতকরণ নমুনা",
|
"add_exclusion_pattern": "বহির্ভূতকরণ নমুনা",
|
||||||
"add_filter": "ফিল্টার যোগ করুন",
|
|
||||||
"add_filter_description": "একটি ফিল্টার শর্ত যোগ করতে ক্লিক করুন",
|
|
||||||
"add_location": "অবস্থান যুক্ত করুন",
|
"add_location": "অবস্থান যুক্ত করুন",
|
||||||
"add_more_users": "আরো ব্যবহারকারী যুক্ত করুন",
|
"add_more_users": "আরো ব্যবহারকারী যুক্ত করুন",
|
||||||
"add_partner": "অংশীদার যোগ করুন",
|
"add_partner": "অংশীদার যোগ করুন",
|
||||||
"add_path": "পাথ যুক্ত করুন",
|
"add_path": "পাথ যুক্ত করুন",
|
||||||
"add_photos": "ছবি যুক্ত করুন",
|
"add_photos": "ছবি যুক্ত করুন",
|
||||||
|
"add_step": "ধাপ যোগ করুন",
|
||||||
"add_tag": "ট্যাগ যুক্ত করুন",
|
"add_tag": "ট্যাগ যুক্ত করুন",
|
||||||
"add_to": "যুক্ত করুন…",
|
"add_to": "যুক্ত করুন…",
|
||||||
"add_to_album": "এলবাম এ যোগ করুন",
|
"add_to_album": "এলবাম এ যোগ করুন",
|
||||||
@@ -42,7 +41,6 @@
|
|||||||
"add_to_shared_album": "শেয়ার করা অ্যালবামে যোগ করুন",
|
"add_to_shared_album": "শেয়ার করা অ্যালবামে যোগ করুন",
|
||||||
"add_upload_to_stack": "আপলোড স্ট্যাকে যোগ করুন",
|
"add_upload_to_stack": "আপলোড স্ট্যাকে যোগ করুন",
|
||||||
"add_url": "লিঙ্ক যোগ করুন",
|
"add_url": "লিঙ্ক যোগ করুন",
|
||||||
"add_workflow_step": "কাজের ধাপ যোগ করুন",
|
|
||||||
"added_to_archive": "আর্কাইভ এ যোগ করা হয়েছে",
|
"added_to_archive": "আর্কাইভ এ যোগ করা হয়েছে",
|
||||||
"added_to_favorites": "ফেভারিটে যোগ করা হয়েছে",
|
"added_to_favorites": "ফেভারিটে যোগ করা হয়েছে",
|
||||||
"added_to_favorites_count": "পছন্দের তালিকায় {count, number} যোগ করা হয়েছে",
|
"added_to_favorites_count": "পছন্দের তালিকায় {count, number} যোগ করা হয়েছে",
|
||||||
@@ -81,6 +79,7 @@
|
|||||||
"cron_expression_description": "Cron ফরম্যাট ব্যবহার করে স্ক্যানিং ইন্টারভ্যাল নির্ধারণ করুন। আরও তথ্যের জন্য দয়া করে <link>Crontab Guru</link> দেখুন",
|
"cron_expression_description": "Cron ফরম্যাট ব্যবহার করে স্ক্যানিং ইন্টারভ্যাল নির্ধারণ করুন। আরও তথ্যের জন্য দয়া করে <link>Crontab Guru</link> দেখুন",
|
||||||
"cron_expression_presets": "Cron এক্সপ্রেশন প্রিসেট",
|
"cron_expression_presets": "Cron এক্সপ্রেশন প্রিসেট",
|
||||||
"disable_login": "লগইন অক্ষম করুন",
|
"disable_login": "লগইন অক্ষম করুন",
|
||||||
|
"download_csv": "CSV ডাউনলোড করুন",
|
||||||
"duplicate_detection_job_description": "সদৃশ ছবি শনাক্ত করতে অ্যাসেটগুলোর উপর মেশিন লার্নিং চালান। এটি Smart Search-এর উপর নির্ভর করে",
|
"duplicate_detection_job_description": "সদৃশ ছবি শনাক্ত করতে অ্যাসেটগুলোর উপর মেশিন লার্নিং চালান। এটি Smart Search-এর উপর নির্ভর করে",
|
||||||
"exclusion_pattern_description": "এক্সক্লুশন প্যাটার্ন ব্যবহার করে লাইব্রেরি স্ক্যান করার সময় নির্দিষ্ট ফাইল ও ফোল্ডার উপেক্ষা করা যায়। এটি তখনই উপকারী যখন কিছু ফোল্ডারে এমন ফাইল থাকে যা আপনি ইমপোর্ট করতে চান না, যেমন RAW ফাইল।",
|
"exclusion_pattern_description": "এক্সক্লুশন প্যাটার্ন ব্যবহার করে লাইব্রেরি স্ক্যান করার সময় নির্দিষ্ট ফাইল ও ফোল্ডার উপেক্ষা করা যায়। এটি তখনই উপকারী যখন কিছু ফোল্ডারে এমন ফাইল থাকে যা আপনি ইমপোর্ট করতে চান না, যেমন RAW ফাইল।",
|
||||||
"export_config_as_json_description": "বর্তমান সিস্টেম কনফিগারেশনটিকে একটি JSON ফাইল হিসেবে ডাউনলোড করুন",
|
"export_config_as_json_description": "বর্তমান সিস্টেম কনফিগারেশনটিকে একটি JSON ফাইল হিসেবে ডাউনলোড করুন",
|
||||||
@@ -190,9 +189,11 @@
|
|||||||
"machine_learning_smart_search_enabled": "স্মার্ট সার্চ সক্ষম করুন",
|
"machine_learning_smart_search_enabled": "স্মার্ট সার্চ সক্ষম করুন",
|
||||||
"machine_learning_smart_search_enabled_description": "নিষ্ক্রিয় থাকলে, স্মার্ট সার্চের জন্য ছবিগুলো এনকোড (encode) করা হবে না।",
|
"machine_learning_smart_search_enabled_description": "নিষ্ক্রিয় থাকলে, স্মার্ট সার্চের জন্য ছবিগুলো এনকোড (encode) করা হবে না।",
|
||||||
"machine_learning_url_description": "মেশিন লার্নিং সার্ভারের URL। যদি একের বেশি URL প্রদান করা হয়, তবে একটি সফলভাবে সাড়া না দেওয়া পর্যন্ত প্রতিটি সার্ভারে এক এক করে চেষ্টা করা হবে (প্রথম থেকে শেষ ক্রমানুসারে)। যে সার্ভারগুলো সাড়া দেবে না, সেগুলো পুনরায় সচল হওয়া পর্যন্ত সাময়িকভাবে উপেক্ষা করা হবে।",
|
"machine_learning_url_description": "মেশিন লার্নিং সার্ভারের URL। যদি একের বেশি URL প্রদান করা হয়, তবে একটি সফলভাবে সাড়া না দেওয়া পর্যন্ত প্রতিটি সার্ভারে এক এক করে চেষ্টা করা হবে (প্রথম থেকে শেষ ক্রমানুসারে)। যে সার্ভারগুলো সাড়া দেবে না, সেগুলো পুনরায় সচল হওয়া পর্যন্ত সাময়িকভাবে উপেক্ষা করা হবে।",
|
||||||
|
"maintenance_backup_management": "ব্যাকআপ ব্যবস্থাপনা",
|
||||||
"maintenance_delete_backup": "ব্যাকআপ (Backup)মুছুন",
|
"maintenance_delete_backup": "ব্যাকআপ (Backup)মুছুন",
|
||||||
"maintenance_delete_backup_description": "এই ফাইলটি চিরতরে মুছে ফেলা হবে।",
|
"maintenance_delete_backup_description": "এই ফাইলটি চিরতরে মুছে ফেলা হবে।",
|
||||||
"maintenance_delete_error": "ব্যাকআপ মুছে ফেলতে ব্যর্থ হয়েছে।",
|
"maintenance_delete_error": "ব্যাকআপ মুছে ফেলতে ব্যর্থ হয়েছে।",
|
||||||
|
"maintenance_integrity_check": "যাচাই",
|
||||||
"maintenance_restore_backup": "ব্যাকআপ পুনরুদ্ধার(Restore) করুন",
|
"maintenance_restore_backup": "ব্যাকআপ পুনরুদ্ধার(Restore) করুন",
|
||||||
"maintenance_restore_backup_description": "Immich মুছে ফেলা হবে এবং নির্বাচিত ব্যাকআপ থেকে পুনরুদ্ধার করা হবে। কার্যক্রম চালিয়ে যাওয়ার আগে একটি ব্যাকআপ তৈরি করা হবে।",
|
"maintenance_restore_backup_description": "Immich মুছে ফেলা হবে এবং নির্বাচিত ব্যাকআপ থেকে পুনরুদ্ধার করা হবে। কার্যক্রম চালিয়ে যাওয়ার আগে একটি ব্যাকআপ তৈরি করা হবে।",
|
||||||
"maintenance_restore_backup_different_version": "এই ব্যাকআপটি Immich-এর একটি ভিন্ন সংস্করণের মাধ্যমে তৈরি করা হয়েছিল!",
|
"maintenance_restore_backup_different_version": "এই ব্যাকআপটি Immich-এর একটি ভিন্ন সংস্করণের মাধ্যমে তৈরি করা হয়েছিল!",
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
+142
-17
@@ -22,13 +22,12 @@
|
|||||||
"add_birthday": "Afegeix la data de naixement",
|
"add_birthday": "Afegeix la data de naixement",
|
||||||
"add_endpoint": "afegir endpoint",
|
"add_endpoint": "afegir endpoint",
|
||||||
"add_exclusion_pattern": "Afegir un patró d'exclusió",
|
"add_exclusion_pattern": "Afegir un patró d'exclusió",
|
||||||
"add_filter": "Afegir filtre",
|
|
||||||
"add_filter_description": "Feu clic per afegir una condició de filtre",
|
|
||||||
"add_location": "Afegir la ubicació",
|
"add_location": "Afegir la ubicació",
|
||||||
"add_more_users": "Afegir més usuaris",
|
"add_more_users": "Afegir més usuaris",
|
||||||
"add_partner": "Afegir company/a",
|
"add_partner": "Afegir company/a",
|
||||||
"add_path": "Afegir una ruta",
|
"add_path": "Afegir una ruta",
|
||||||
"add_photos": "Afegir fotografies",
|
"add_photos": "Afegir fotografies",
|
||||||
|
"add_step": "Afegeix pas",
|
||||||
"add_tag": "Afegir una etiqueta",
|
"add_tag": "Afegir una etiqueta",
|
||||||
"add_to": "Afegir a…",
|
"add_to": "Afegir a…",
|
||||||
"add_to_album": "Afegir a un l'àlbum",
|
"add_to_album": "Afegir a un l'àlbum",
|
||||||
@@ -42,7 +41,6 @@
|
|||||||
"add_to_shared_album": "Afegir a un àlbum compartit",
|
"add_to_shared_album": "Afegir a un àlbum compartit",
|
||||||
"add_upload_to_stack": "Afegeix la càrrega a la pila",
|
"add_upload_to_stack": "Afegeix la càrrega a la pila",
|
||||||
"add_url": "Afegir URL",
|
"add_url": "Afegir URL",
|
||||||
"add_workflow_step": "Afegeix un pas del flux de treball",
|
|
||||||
"added_to_archive": "Afegir a l'arxiu",
|
"added_to_archive": "Afegir a l'arxiu",
|
||||||
"added_to_favorites": "Afegit als preferits",
|
"added_to_favorites": "Afegit als preferits",
|
||||||
"added_to_favorites_count": "{count, number} afegits als preferits",
|
"added_to_favorites_count": "{count, number} afegits als preferits",
|
||||||
@@ -81,6 +79,7 @@
|
|||||||
"cron_expression_description": "Estableix l'interval d'escaneig amb el format cron. Per obtenir més informació, consulteu, p.e <link>Crontab Guru</link>",
|
"cron_expression_description": "Estableix l'interval d'escaneig amb el format cron. Per obtenir més informació, consulteu, p.e <link>Crontab Guru</link>",
|
||||||
"cron_expression_presets": "Ajustos predefinits d'expressions Cron",
|
"cron_expression_presets": "Ajustos predefinits d'expressions Cron",
|
||||||
"disable_login": "Deshabiliteu l'inici de sessió",
|
"disable_login": "Deshabiliteu l'inici de sessió",
|
||||||
|
"download_csv": "Descarregar CSV",
|
||||||
"duplicate_detection_job_description": "Executa l'aprenentatge automàtic en els elements per a detectar imatges semblants. Fa servir la cerca intel·ligent",
|
"duplicate_detection_job_description": "Executa l'aprenentatge automàtic en els elements per a detectar imatges semblants. Fa servir la cerca intel·ligent",
|
||||||
"exclusion_pattern_description": "Els patrons d'exclusió permeten ignorar fitxers i carpetes quan escanegeu una llibreria. Això és útil si teniu carpetes que contenen fitxer que no voleu importar, com els fitxers RAW.",
|
"exclusion_pattern_description": "Els patrons d'exclusió permeten ignorar fitxers i carpetes quan escanegeu una llibreria. Això és útil si teniu carpetes que contenen fitxer que no voleu importar, com els fitxers RAW.",
|
||||||
"export_config_as_json_description": "Baixa la configuració actual del sistema com a fitxer JSON",
|
"export_config_as_json_description": "Baixa la configuració actual del sistema com a fitxer JSON",
|
||||||
@@ -190,9 +189,25 @@
|
|||||||
"machine_learning_smart_search_enabled": "Activa la cerca intel·ligent",
|
"machine_learning_smart_search_enabled": "Activa la cerca intel·ligent",
|
||||||
"machine_learning_smart_search_enabled_description": "Si està desactivada, les imatges no es codificaran per la cerca intel·ligent.",
|
"machine_learning_smart_search_enabled_description": "Si està desactivada, les imatges no es codificaran per la cerca intel·ligent.",
|
||||||
"machine_learning_url_description": "L'URL del servidor d'aprenentatge automàtic. Si es proporciona més d'un URL, s'intentarà accedir a cada servidor en ordre fins que un d'ells respongui correctament.",
|
"machine_learning_url_description": "L'URL del servidor d'aprenentatge automàtic. Si es proporciona més d'un URL, s'intentarà accedir a cada servidor en ordre fins que un d'ells respongui correctament.",
|
||||||
|
"maintenance_backup_management": "Gestió de còpies de seguretat",
|
||||||
"maintenance_delete_backup": "Elimina la còpia de seguretat",
|
"maintenance_delete_backup": "Elimina la còpia de seguretat",
|
||||||
"maintenance_delete_backup_description": "Aquest fitxer s'eliminarà de forma permanent.",
|
"maintenance_delete_backup_description": "Aquest fitxer s'eliminarà de forma permanent.",
|
||||||
"maintenance_delete_error": "No s'ha pogut suprimir la còpia de seguretat.",
|
"maintenance_delete_error": "No s'ha pogut suprimir la còpia de seguretat.",
|
||||||
|
"maintenance_integrity_check": "Verificació",
|
||||||
|
"maintenance_integrity_check_all": "Verificar tot",
|
||||||
|
"maintenance_integrity_checksum_mismatch": "Checksum incorrecte",
|
||||||
|
"maintenance_integrity_checksum_mismatch_description": "Fitxers els quals la suma de verificació al disc no coincideix amb la que Immich té emmagatzemada a la base de dades.",
|
||||||
|
"maintenance_integrity_checksum_mismatch_job": "Comprovar checksums",
|
||||||
|
"maintenance_integrity_checksum_mismatch_refresh_job": "Actualitzar errors de checksums",
|
||||||
|
"maintenance_integrity_missing_file": "Manquen fitxers",
|
||||||
|
"maintenance_integrity_missing_file_description": "Fitxers que l'Immich té registrats a la seva base de dades però que no existeixen al sistema de fitxers.",
|
||||||
|
"maintenance_integrity_missing_file_job": "Verificar fitxers que falten",
|
||||||
|
"maintenance_integrity_missing_file_refresh_job": "Refrescar informe de fitxers desapareguts",
|
||||||
|
"maintenance_integrity_report": "Informe Integritat",
|
||||||
|
"maintenance_integrity_untracked_file": "Arxius no rastrejats",
|
||||||
|
"maintenance_integrity_untracked_file_description": "Fitxers presents als directoris d'Immich que Immich no en té cap registre.",
|
||||||
|
"maintenance_integrity_untracked_file_job": "Consulta de fitxers no rastrejats",
|
||||||
|
"maintenance_integrity_untracked_file_refresh_job": "Actualitza els informes de fitxers no rastrejats",
|
||||||
"maintenance_restore_backup": "Restaura la còpia de seguretat",
|
"maintenance_restore_backup": "Restaura la còpia de seguretat",
|
||||||
"maintenance_restore_backup_description": "Immich s'esborrarà i es restaurarà des de la còpia de seguretat escollida. Es crearà una còpia de seguretat abans de continuar.",
|
"maintenance_restore_backup_description": "Immich s'esborrarà i es restaurarà des de la còpia de seguretat escollida. Es crearà una còpia de seguretat abans de continuar.",
|
||||||
"maintenance_restore_backup_different_version": "Aquesta còpia de seguretat s'ha creat amb una versió diferent d'Immich!",
|
"maintenance_restore_backup_different_version": "Aquesta còpia de seguretat s'ha creat amb una versió diferent d'Immich!",
|
||||||
@@ -267,6 +282,8 @@
|
|||||||
"notification_enable_email_notifications": "Habilita les notificacions de correu electrònic",
|
"notification_enable_email_notifications": "Habilita les notificacions de correu electrònic",
|
||||||
"notification_settings": "Configuració de notificacions",
|
"notification_settings": "Configuració de notificacions",
|
||||||
"notification_settings_description": "Gestiona la configuració de notificacions, incloent-hi el correu electrònic",
|
"notification_settings_description": "Gestiona la configuració de notificacions, incloent-hi el correu electrònic",
|
||||||
|
"oauth_allow_insecure_requests": "Permet sol·licituds no segures",
|
||||||
|
"oauth_allow_insecure_requests_description": "AVÍS: Això inhabilita la validació de certificats TLS per a les sol·licituds OAuth i us pot exposar a atacs MITM.",
|
||||||
"oauth_auto_launch": "Execució automàtica",
|
"oauth_auto_launch": "Execució automàtica",
|
||||||
"oauth_auto_launch_description": "Inicia el flux d'inici de sessió OAuth automàticament en accedir a la pàgina d'inici de sessió",
|
"oauth_auto_launch_description": "Inicia el flux d'inici de sessió OAuth automàticament en accedir a la pàgina d'inici de sessió",
|
||||||
"oauth_auto_register": "Registre automàtic",
|
"oauth_auto_register": "Registre automàtic",
|
||||||
@@ -274,9 +291,11 @@
|
|||||||
"oauth_button_text": "Text del botó",
|
"oauth_button_text": "Text del botó",
|
||||||
"oauth_client_secret_description": "Requerit per clients confidencials, o si PKCE (Proof Key for Code Exchange) no està suportat pel client públic.",
|
"oauth_client_secret_description": "Requerit per clients confidencials, o si PKCE (Proof Key for Code Exchange) no està suportat pel client públic.",
|
||||||
"oauth_enable_description": "Iniciar sessió amb OAuth",
|
"oauth_enable_description": "Iniciar sessió amb OAuth",
|
||||||
|
"oauth_end_session_url_description": "Redirigeix l'usuari a aquest URI quan tanqui la sessió.",
|
||||||
"oauth_mobile_redirect_uri": "URI de redirecció mòbil",
|
"oauth_mobile_redirect_uri": "URI de redirecció mòbil",
|
||||||
"oauth_mobile_redirect_uri_override": "Sobreescriu l'URI de redirecció mòbil",
|
"oauth_mobile_redirect_uri_override": "Sobreescriu l'URI de redirecció mòbil",
|
||||||
"oauth_mobile_redirect_uri_override_description": "Habilita quan el proveïdor d'OAuth no permet una URI mòbil, com ara ''{callback}''",
|
"oauth_mobile_redirect_uri_override_description": "Habilita quan el proveïdor d'OAuth no permet una URI mòbil, com ara ''{callback}''",
|
||||||
|
"oauth_prompt_description": "Paràmetre de sol·licitud (per exemple, select_account, login, consent)",
|
||||||
"oauth_role_claim": "Concessió de rol",
|
"oauth_role_claim": "Concessió de rol",
|
||||||
"oauth_role_claim_description": "Atorgar accés d'administrador automàticament segons la presència d'aquesta concessió. La concessió pot ser 'usuari' o 'admin'.",
|
"oauth_role_claim_description": "Atorgar accés d'administrador automàticament segons la presència d'aquesta concessió. La concessió pot ser 'usuari' o 'admin'.",
|
||||||
"oauth_settings": "OAuth",
|
"oauth_settings": "OAuth",
|
||||||
@@ -303,6 +322,8 @@
|
|||||||
"refreshing_all_libraries": "Actualitzant totes les biblioteques",
|
"refreshing_all_libraries": "Actualitzant totes les biblioteques",
|
||||||
"registration": "Registre d'administrador",
|
"registration": "Registre d'administrador",
|
||||||
"registration_description": "Com que ets el primer usuari del sistema, seràs designat com a administrador i seràs responsable de les tasques administratives. També seràs l'encarregat de crear usuaris addicionals.",
|
"registration_description": "Com que ets el primer usuari del sistema, seràs designat com a administrador i seràs responsable de les tasques administratives. També seràs l'encarregat de crear usuaris addicionals.",
|
||||||
|
"release_channel_release_candidate": "Candidat a versió",
|
||||||
|
"release_channel_stable": "Estable",
|
||||||
"remove_failed_jobs": "Eliminar treballs fallits",
|
"remove_failed_jobs": "Eliminar treballs fallits",
|
||||||
"require_password_change_on_login": "Requerir que l'usuari canviï la contrasenya en el primer inici de sessió",
|
"require_password_change_on_login": "Requerir que l'usuari canviï la contrasenya en el primer inici de sessió",
|
||||||
"reset_settings_to_default": "Restablir configuracions per defecte",
|
"reset_settings_to_default": "Restablir configuracions per defecte",
|
||||||
@@ -397,6 +418,10 @@
|
|||||||
"transcoding_preferred_hardware_device_description": "S'aplica només a VAAPI i QSV. Estableix el node dri utilitzat per a la transcodificació de maquinari.",
|
"transcoding_preferred_hardware_device_description": "S'aplica només a VAAPI i QSV. Estableix el node dri utilitzat per a la transcodificació de maquinari.",
|
||||||
"transcoding_preset_preset": "Preestablert (-preset)",
|
"transcoding_preset_preset": "Preestablert (-preset)",
|
||||||
"transcoding_preset_preset_description": "Velocitat de compressió. Els valors predefinits més lents produeixen fitxers més petits i augmenten la qualitat quan s'orienta a una taxa de bits determinada. VP9 ignora les velocitats superiors a 'més ràpides'.",
|
"transcoding_preset_preset_description": "Velocitat de compressió. Els valors predefinits més lents produeixen fitxers més petits i augmenten la qualitat quan s'orienta a una taxa de bits determinada. VP9 ignora les velocitats superiors a 'més ràpides'.",
|
||||||
|
"transcoding_realtime": "Transcodificació en temps real [EXPERIMENTAL]",
|
||||||
|
"transcoding_realtime_description": "Permet que la transcodificació es realitzi en temps real mentre es retransmet el vídeo. Habilita el canvi de qualitat, però pot causar una latència de reproducció més alta i entretallats segons les capacitats del servidor.",
|
||||||
|
"transcoding_realtime_enabled": "Activa transcodificació en temps real",
|
||||||
|
"transcoding_realtime_enabled_description": "Si està desactivat, el servidor rebutjarà iniciar noves sessions de transcodificació en temps real.",
|
||||||
"transcoding_reference_frames": "Fotogrames de referència",
|
"transcoding_reference_frames": "Fotogrames de referència",
|
||||||
"transcoding_reference_frames_description": "El nombre de fotogrames a fer referència en comprimir un fotograma determinat. Els valors més alts milloren l'eficiència de la compressió, però alenteixen la codificació. 0 estableix aquest valor automàticament.",
|
"transcoding_reference_frames_description": "El nombre de fotogrames a fer referència en comprimir un fotograma determinat. Els valors més alts milloren l'eficiència de la compressió, però alenteixen la codificació. 0 estableix aquest valor automàticament.",
|
||||||
"transcoding_required_description": "Només vídeos que no tenen un format acceptat",
|
"transcoding_required_description": "Només vídeos que no tenen un format acceptat",
|
||||||
@@ -440,6 +465,8 @@
|
|||||||
"user_settings_description": "Gestiona la configuració dels usuaris",
|
"user_settings_description": "Gestiona la configuració dels usuaris",
|
||||||
"user_successfully_removed": "L'usuari {email} s'ha eliminat correctament.",
|
"user_successfully_removed": "L'usuari {email} s'ha eliminat correctament.",
|
||||||
"users_page_description": "Pàgina d'usuaris de l'administrador",
|
"users_page_description": "Pàgina d'usuaris de l'administrador",
|
||||||
|
"version_check_channel": "Canal de publicació",
|
||||||
|
"version_check_channel_description": "Tria el canal de publicació del qual vols rebre avisos de noves versions",
|
||||||
"version_check_enabled_description": "Activa la comprovació de la versió",
|
"version_check_enabled_description": "Activa la comprovació de la versió",
|
||||||
"version_check_implications": "La funció de comprovació de versions depèn de comunicacions periòdiques amb {server}",
|
"version_check_implications": "La funció de comprovació de versions depèn de comunicacions periòdiques amb {server}",
|
||||||
"version_check_settings": "Comprovació de versió",
|
"version_check_settings": "Comprovació de versió",
|
||||||
@@ -461,7 +488,7 @@
|
|||||||
"advanced_settings_prefer_remote_title": "Prefereix imatges remotes",
|
"advanced_settings_prefer_remote_title": "Prefereix imatges remotes",
|
||||||
"advanced_settings_proxy_headers_subtitle": "Definiu les capçaleres de proxy que Immich per enviar amb cada sol·licitud de xarxa",
|
"advanced_settings_proxy_headers_subtitle": "Definiu les capçaleres de proxy que Immich per enviar amb cada sol·licitud de xarxa",
|
||||||
"advanced_settings_proxy_headers_title": "Capçaleres de proxy particulars [EXPERIMENTAL]",
|
"advanced_settings_proxy_headers_title": "Capçaleres de proxy particulars [EXPERIMENTAL]",
|
||||||
"advanced_settings_readonly_mode_subtitle": "Habilita el només de lectura mode on les fotos poden ser només vist, a coses els agrada seleccionant imatges múltiples, compartint, càsting, elimina és tot discapacitat. Habilita/Desactiva només de lectura via avatar d'usuari des de la pantalla major",
|
"advanced_settings_readonly_mode_subtitle": "Activa el mode de només lectura, en què les fotos només es poden veure. Accions com seleccionar múltiples imatges, compartir, enviar a un dispositiu o eliminar queden desactivades. Activa o desactiva el mode de només lectura des de l’avatar d’usuari de la pantalla principal",
|
||||||
"advanced_settings_readonly_mode_title": "Mode de només lectura",
|
"advanced_settings_readonly_mode_title": "Mode de només lectura",
|
||||||
"advanced_settings_self_signed_ssl_subtitle": "Omet la verificació del certificat SSL del servidor. Requerit per a certificats autosignats.",
|
"advanced_settings_self_signed_ssl_subtitle": "Omet la verificació del certificat SSL del servidor. Requerit per a certificats autosignats.",
|
||||||
"advanced_settings_self_signed_ssl_title": "Permet certificats SSL autosignats [EXPERIMENTAL]",
|
"advanced_settings_self_signed_ssl_title": "Permet certificats SSL autosignats [EXPERIMENTAL]",
|
||||||
@@ -560,6 +587,7 @@
|
|||||||
"asset_added_to_album": "Afegit a l'àlbum",
|
"asset_added_to_album": "Afegit a l'àlbum",
|
||||||
"asset_adding_to_album": "Afegint a l'àlbum…",
|
"asset_adding_to_album": "Afegint a l'àlbum…",
|
||||||
"asset_created": "Recurs creat",
|
"asset_created": "Recurs creat",
|
||||||
|
"asset_day_count": "{date}: {count, plural, one {# element} other {# elements}}",
|
||||||
"asset_description_updated": "La descripció del recurs s'ha actualitzat",
|
"asset_description_updated": "La descripció del recurs s'ha actualitzat",
|
||||||
"asset_filename_is_offline": "L'element {filename} està fora de línia",
|
"asset_filename_is_offline": "L'element {filename} està fora de línia",
|
||||||
"asset_has_unassigned_faces": "L'element té cares no assignades",
|
"asset_has_unassigned_faces": "L'element té cares no assignades",
|
||||||
@@ -689,6 +717,7 @@
|
|||||||
"backup_settings_subtitle": "Administra la configuració de pujada",
|
"backup_settings_subtitle": "Administra la configuració de pujada",
|
||||||
"backup_upload_details_page_more_details": "Toqueu per obtenir més detalls",
|
"backup_upload_details_page_more_details": "Toqueu per obtenir més detalls",
|
||||||
"backward": "Enrere",
|
"backward": "Enrere",
|
||||||
|
"battery_optimization_backup_reliability": "Desactivar les optimitzacions de la bateria pot millorar la fiabilitat de la còpia de seguretat en segon pla",
|
||||||
"biometric_auth_enabled": "Autentificació biomètrica activada",
|
"biometric_auth_enabled": "Autentificació biomètrica activada",
|
||||||
"biometric_locked_out": "Esteu bloquejats fora de l'autenticació biomètrica",
|
"biometric_locked_out": "Esteu bloquejats fora de l'autenticació biomètrica",
|
||||||
"biometric_no_options": "No hi ha opcions biomètriques disponibles",
|
"biometric_no_options": "No hi ha opcions biomètriques disponibles",
|
||||||
@@ -696,9 +725,10 @@
|
|||||||
"birthdate_saved": "Data de naixement guardada amb èxit",
|
"birthdate_saved": "Data de naixement guardada amb èxit",
|
||||||
"birthdate_set_description": "La data de naixement s'utilitza per calcular l'edat d'aquesta persona en el moment d'una foto.",
|
"birthdate_set_description": "La data de naixement s'utilitza per calcular l'edat d'aquesta persona en el moment d'una foto.",
|
||||||
"blurred_background": "Fons difuminat",
|
"blurred_background": "Fons difuminat",
|
||||||
|
"browse_templates": "Explorar plantilles",
|
||||||
"bugs_and_feature_requests": "Errors i sol·licituds de funcions",
|
"bugs_and_feature_requests": "Errors i sol·licituds de funcions",
|
||||||
"build": "Construeix",
|
"build": "Número de compilació",
|
||||||
"build_image": "Construeix la imatge",
|
"build_image": "Versió de la imatge compilada",
|
||||||
"bulk_delete_duplicates_confirmation": "Esteu segurs que voleu suprimir de manera massiva {count, plural, one {# recurs duplicat} other {# recursos duplicats}}? Això mantindrà el recurs més gran de cada grup i esborrarà permanentment tots els altres duplicats. No podeu desfer aquesta acció!",
|
"bulk_delete_duplicates_confirmation": "Esteu segurs que voleu suprimir de manera massiva {count, plural, one {# recurs duplicat} other {# recursos duplicats}}? Això mantindrà el recurs més gran de cada grup i esborrarà permanentment tots els altres duplicats. No podeu desfer aquesta acció!",
|
||||||
"bulk_keep_duplicates_confirmation": "Esteu segur que voleu mantenir {count, plural, one {# recurs duplicat} other {# recursos duplicats}}? Això resoldrà tots els grups duplicats sense eliminar res.",
|
"bulk_keep_duplicates_confirmation": "Esteu segur que voleu mantenir {count, plural, one {# recurs duplicat} other {# recursos duplicats}}? Això resoldrà tots els grups duplicats sense eliminar res.",
|
||||||
"bulk_trash_duplicates_confirmation": "Esteu segur que voleu enviar a les escombraries {count, plural, one {# recurs duplicat} other {# recursos duplicats}}? Això mantindrà el recurs més gran de cada grup i eliminarà la resta de duplicats.",
|
"bulk_trash_duplicates_confirmation": "Esteu segur que voleu enviar a les escombraries {count, plural, one {# recurs duplicat} other {# recursos duplicats}}? Això mantindrà el recurs més gran de cada grup i eliminarà la resta de duplicats.",
|
||||||
@@ -729,6 +759,7 @@
|
|||||||
"cannot_update_the_description": "No es pot actualitzar la descripció",
|
"cannot_update_the_description": "No es pot actualitzar la descripció",
|
||||||
"cast": "Emet",
|
"cast": "Emet",
|
||||||
"cast_description": "Configurar les destinacions de transmissió disponibles",
|
"cast_description": "Configurar les destinacions de transmissió disponibles",
|
||||||
|
"change": "Canvia",
|
||||||
"change_date": "Canvia la data",
|
"change_date": "Canvia la data",
|
||||||
"change_description": "Canvia la descripció",
|
"change_description": "Canvia la descripció",
|
||||||
"change_display_order": "Canvia l'ordre de visualització",
|
"change_display_order": "Canvia l'ordre de visualització",
|
||||||
@@ -757,6 +788,7 @@
|
|||||||
"check_corrupt_asset_backup_description": "Executeu aquesta comprovació només mitjançant Wi-Fi i un cop s'hagi fet una còpia de seguretat de tots els actius. El procediment pot trigar uns minuts.",
|
"check_corrupt_asset_backup_description": "Executeu aquesta comprovació només mitjançant Wi-Fi i un cop s'hagi fet una còpia de seguretat de tots els actius. El procediment pot trigar uns minuts.",
|
||||||
"check_logs": "Comprovar els registres",
|
"check_logs": "Comprovar els registres",
|
||||||
"checksum": "Suma de control",
|
"checksum": "Suma de control",
|
||||||
|
"choose": "Tria",
|
||||||
"choose_matching_people_to_merge": "Trieu les persones que coincideixin per combinar-les",
|
"choose_matching_people_to_merge": "Trieu les persones que coincideixin per combinar-les",
|
||||||
"city": "Ciutat",
|
"city": "Ciutat",
|
||||||
"cleanup_confirm_description": "Immich ha trobat {count} recursos (creats abans del {date}) carregats adequadament al servidor. Eliminar les còpies locals d'aquest dispositiu?",
|
"cleanup_confirm_description": "Immich ha trobat {count} recursos (creats abans del {date}) carregats adequadament al servidor. Eliminar les còpies locals d'aquest dispositiu?",
|
||||||
@@ -774,6 +806,7 @@
|
|||||||
"clear": "Buida",
|
"clear": "Buida",
|
||||||
"clear_all": "Neteja-ho tot",
|
"clear_all": "Neteja-ho tot",
|
||||||
"clear_all_recent_searches": "Esborra totes les cerques recents",
|
"clear_all_recent_searches": "Esborra totes les cerques recents",
|
||||||
|
"clear_failed_count": "Buida fallades ({count})",
|
||||||
"clear_file_cache": "Buida la memòria cau de fitxers",
|
"clear_file_cache": "Buida la memòria cau de fitxers",
|
||||||
"clear_message": "Neteja el missatge",
|
"clear_message": "Neteja el missatge",
|
||||||
"clear_value": "Neteja el valor",
|
"clear_value": "Neteja el valor",
|
||||||
@@ -805,6 +838,7 @@
|
|||||||
"comments_are_disabled": "Els comentaris estan desactivats",
|
"comments_are_disabled": "Els comentaris estan desactivats",
|
||||||
"common_create_new_album": "Crea un àlbum nou",
|
"common_create_new_album": "Crea un àlbum nou",
|
||||||
"completed": "Completat",
|
"completed": "Completat",
|
||||||
|
"configuration": "Configuració",
|
||||||
"confirm": "Confirmar",
|
"confirm": "Confirmar",
|
||||||
"confirm_admin_password": "Confirmeu la contrasenya d'administrador",
|
"confirm_admin_password": "Confirmeu la contrasenya d'administrador",
|
||||||
"confirm_delete_face": "Estàs segur que vols eliminar la cara de {name} de les cares reconegudes?",
|
"confirm_delete_face": "Estàs segur que vols eliminar la cara de {name} de les cares reconegudes?",
|
||||||
@@ -819,6 +853,7 @@
|
|||||||
"contain": "Contingut",
|
"contain": "Contingut",
|
||||||
"context": "Context",
|
"context": "Context",
|
||||||
"continue": "Continuar",
|
"continue": "Continuar",
|
||||||
|
"control_bottom_app_bar_add_tags": "Afegeix etiquetes",
|
||||||
"control_bottom_app_bar_create_new_album": "Crea un àlbum nou",
|
"control_bottom_app_bar_create_new_album": "Crea un àlbum nou",
|
||||||
"control_bottom_app_bar_delete_from_immich": "Suprimeix del Immich",
|
"control_bottom_app_bar_delete_from_immich": "Suprimeix del Immich",
|
||||||
"control_bottom_app_bar_delete_from_local": "Suprimeix del dispositiu",
|
"control_bottom_app_bar_delete_from_local": "Suprimeix del dispositiu",
|
||||||
@@ -832,6 +867,7 @@
|
|||||||
"copy_error": "Error de còpia",
|
"copy_error": "Error de còpia",
|
||||||
"copy_file_path": "Copia la ruta del fitxer",
|
"copy_file_path": "Copia la ruta del fitxer",
|
||||||
"copy_image": "Còpia imatge",
|
"copy_image": "Còpia imatge",
|
||||||
|
"copy_json": "Copia el JSON",
|
||||||
"copy_link": "Còpia l'enllaç",
|
"copy_link": "Còpia l'enllaç",
|
||||||
"copy_link_to_clipboard": "Còpia l'enllaç al porta-retalls",
|
"copy_link_to_clipboard": "Còpia l'enllaç al porta-retalls",
|
||||||
"copy_password": "Còpia la contrasenya",
|
"copy_password": "Còpia la contrasenya",
|
||||||
@@ -881,22 +917,23 @@
|
|||||||
"cutoff_date_description": "Manté fotos des de l'últim…",
|
"cutoff_date_description": "Manté fotos des de l'últim…",
|
||||||
"cutoff_day": "{count, plural, one {dia} other {dies}}",
|
"cutoff_day": "{count, plural, one {dia} other {dies}}",
|
||||||
"cutoff_year": "{count, plural, one {any} other {anys}}",
|
"cutoff_year": "{count, plural, one {any} other {anys}}",
|
||||||
"daily_title_text_date": "E, dd MMM",
|
|
||||||
"daily_title_text_date_year": "E, dd MMM, yyyy",
|
|
||||||
"dark": "Fosc",
|
"dark": "Fosc",
|
||||||
"dark_theme": "Canvia a tema fosc",
|
"dark_theme": "Canvia a tema fosc",
|
||||||
"date": "Data",
|
"date": "Data",
|
||||||
"date_after": "Data posterior a",
|
"date_after": "Data posterior a",
|
||||||
"date_and_time": "Data i hora",
|
"date_and_time": "Data i hora",
|
||||||
"date_before": "Data anterior a",
|
"date_before": "Data anterior a",
|
||||||
"date_format": "E, d LLL, y • hh:mm",
|
"date_of_birth": "Data de naixement",
|
||||||
"date_of_birth_saved": "Data de naixement guardada amb èxit",
|
"date_of_birth_saved": "Data de naixement guardada amb èxit",
|
||||||
"date_range": "Interval de dates",
|
"date_range": "Interval de dates",
|
||||||
|
"date_time_original": "Data/Hora original",
|
||||||
"day": "Dia",
|
"day": "Dia",
|
||||||
"days": "Dies",
|
"days": "Dies",
|
||||||
"deduplicate_all": "Desduplica-ho tot",
|
"deduplicate_all": "Desduplica-ho tot",
|
||||||
"default_locale": "Configuració regional predeterminada",
|
"default_locale": "Configuració regional predeterminada",
|
||||||
"default_locale_description": "Format de dades i números en funció de la configuració local",
|
"default_locale_description": "Format de dades i números en funció de la configuració local",
|
||||||
|
"default_quality_subtitle": "Qualitat utilitzada a l’hora de tocar la compartició. Premeu el botó de compartir per triar cada vegada.",
|
||||||
|
"default_share_quality": "Qualitat de comparticio per defecte",
|
||||||
"delete": "Esborrar",
|
"delete": "Esborrar",
|
||||||
"delete_action_confirmation_message": "Segur que vols eliminar aquest recurs? Aquesta acció el mourà a la paperera del servidor, i et preguntarà si el vols eliminar localment",
|
"delete_action_confirmation_message": "Segur que vols eliminar aquest recurs? Aquesta acció el mourà a la paperera del servidor, i et preguntarà si el vols eliminar localment",
|
||||||
"delete_action_prompt": "{count} eliminats",
|
"delete_action_prompt": "{count} eliminats",
|
||||||
@@ -970,7 +1007,10 @@
|
|||||||
"downloading_asset_filename": "Descarregant l'element {filename}",
|
"downloading_asset_filename": "Descarregant l'element {filename}",
|
||||||
"downloading_from_icloud": "Descarregant des d'iCloud",
|
"downloading_from_icloud": "Descarregant des d'iCloud",
|
||||||
"downloading_media": "Descàrrega multimèdia",
|
"downloading_media": "Descàrrega multimèdia",
|
||||||
|
"drag_to_reorder": "Arrossegueu per reordenar",
|
||||||
"drop_files_to_upload": "Deixeu els fitxers a qualsevol lloc per pujar-los",
|
"drop_files_to_upload": "Deixeu els fitxers a qualsevol lloc per pujar-los",
|
||||||
|
"duplicate": "Duplica",
|
||||||
|
"duplicate_workflow": "Duplica el flux de treball",
|
||||||
"duplicates": "Duplicats",
|
"duplicates": "Duplicats",
|
||||||
"duplicates_description": "Resol cada grup indicant, si n'hi ha, quins són duplicats.",
|
"duplicates_description": "Resol cada grup indicant, si n'hi ha, quins són duplicats.",
|
||||||
"duration": "Durada",
|
"duration": "Durada",
|
||||||
@@ -1072,6 +1112,7 @@
|
|||||||
"failed_to_remove_product_key": "No s'ha pogut eliminar la clau del producte",
|
"failed_to_remove_product_key": "No s'ha pogut eliminar la clau del producte",
|
||||||
"failed_to_reset_pin_code": "No s'ha pogut reiniciar el codi PIN",
|
"failed_to_reset_pin_code": "No s'ha pogut reiniciar el codi PIN",
|
||||||
"failed_to_stack_assets": "No s'han pogut apilar els elements",
|
"failed_to_stack_assets": "No s'han pogut apilar els elements",
|
||||||
|
"failed_to_tag_assets": "Ha fallat l'assignació d'etiquetes",
|
||||||
"failed_to_unstack_assets": "No s'han pogut desapilar els elements",
|
"failed_to_unstack_assets": "No s'han pogut desapilar els elements",
|
||||||
"failed_to_update_notification_status": "Error en actualitzar l'estat de les notificacions",
|
"failed_to_update_notification_status": "Error en actualitzar l'estat de les notificacions",
|
||||||
"incorrect_email_or_password": "Correu electrònic o contrasenya incorrectes",
|
"incorrect_email_or_password": "Correu electrònic o contrasenya incorrectes",
|
||||||
@@ -1191,15 +1232,18 @@
|
|||||||
"export_as_json": "Exportar com a JSON",
|
"export_as_json": "Exportar com a JSON",
|
||||||
"export_database": "Exportar base de dades",
|
"export_database": "Exportar base de dades",
|
||||||
"export_database_description": "Exportar la base de dades SQLite",
|
"export_database_description": "Exportar la base de dades SQLite",
|
||||||
|
"exposure_time": "Temps d'exposició",
|
||||||
"extension": "Extensió",
|
"extension": "Extensió",
|
||||||
"external": "Extern",
|
"external": "Extern",
|
||||||
"external_libraries": "Llibreries externes",
|
"external_libraries": "Llibreries externes",
|
||||||
"external_network": "Xarxa externa",
|
"external_network": "Xarxa externa",
|
||||||
"external_network_sheet_info": "Quan no estigui a la xarxa Wi-Fi preferida, l'aplicació es connectarà al servidor mitjançant el primer dels URL següents a què pot arribar, començant de dalt a baix",
|
"external_network_sheet_info": "Quan no estigui a la xarxa Wi-Fi preferida, l'aplicació es connectarà al servidor mitjançant el primer dels URL següents a què pot arribar, començant de dalt a baix",
|
||||||
|
"f_number": "Obertura",
|
||||||
"face_unassigned": "Sense assignar",
|
"face_unassigned": "Sense assignar",
|
||||||
"failed": "Fallat",
|
"failed": "Fallat",
|
||||||
"failed_count": "Fallits: {count}",
|
"failed_count": "Fallits: {count}",
|
||||||
"failed_to_authenticate": "No s'ha pogut autenticar",
|
"failed_to_authenticate": "No s'ha pogut autenticar",
|
||||||
|
"failed_to_delete_file": "No s'ha pogut esborrar el fitxer",
|
||||||
"failed_to_load_assets": "Error carregant recursos",
|
"failed_to_load_assets": "Error carregant recursos",
|
||||||
"failed_to_load_folder": "No s'ha pogut carregar la carpeta",
|
"failed_to_load_folder": "No s'ha pogut carregar la carpeta",
|
||||||
"favorite": "Preferit",
|
"favorite": "Preferit",
|
||||||
@@ -1213,7 +1257,6 @@
|
|||||||
"features_setting_description": "Administrar les funcions de l'aplicació",
|
"features_setting_description": "Administrar les funcions de l'aplicació",
|
||||||
"file_name_or_extension": "Nom de l'arxiu o extensió",
|
"file_name_or_extension": "Nom de l'arxiu o extensió",
|
||||||
"file_name_text": "Nom del fitxer",
|
"file_name_text": "Nom del fitxer",
|
||||||
"file_name_with_value": "Nom del fitxer: {file_name}",
|
|
||||||
"file_size": "Mida del fitxer",
|
"file_size": "Mida del fitxer",
|
||||||
"filename": "Nom del fitxer",
|
"filename": "Nom del fitxer",
|
||||||
"filetype": "Tipus d'arxiu",
|
"filetype": "Tipus d'arxiu",
|
||||||
@@ -1226,6 +1269,7 @@
|
|||||||
"find_them_fast": "Trobeu-los ràpidament pel nom amb la cerca",
|
"find_them_fast": "Trobeu-los ràpidament pel nom amb la cerca",
|
||||||
"first": "Primer",
|
"first": "Primer",
|
||||||
"fix_incorrect_match": "Corregiu la coincidència incorrecta",
|
"fix_incorrect_match": "Corregiu la coincidència incorrecta",
|
||||||
|
"focal_length": "Longitud focal",
|
||||||
"folder": "Carpeta",
|
"folder": "Carpeta",
|
||||||
"folder_not_found": "Carpeta no trobada",
|
"folder_not_found": "Carpeta no trobada",
|
||||||
"folders": "Carpetes",
|
"folders": "Carpetes",
|
||||||
@@ -1236,6 +1280,7 @@
|
|||||||
"free_up_space_description": "Mou fotos i videos que ja tinguen còpia al servidor a la paperera del teu dispositiu per alliberar espai. Les còpies del servidor no es modificaran.",
|
"free_up_space_description": "Mou fotos i videos que ja tinguen còpia al servidor a la paperera del teu dispositiu per alliberar espai. Les còpies del servidor no es modificaran.",
|
||||||
"free_up_space_settings_subtitle": "Alliberar espai del dispositiu",
|
"free_up_space_settings_subtitle": "Alliberar espai del dispositiu",
|
||||||
"full_path": "Ruta completa: {path}",
|
"full_path": "Ruta completa: {path}",
|
||||||
|
"full_path_or_folder": "Camí sencer o carpeta",
|
||||||
"gcast_enabled": "Google Cast",
|
"gcast_enabled": "Google Cast",
|
||||||
"gcast_enabled_description": "Aquesta funció carrega recursos externs de Google per funcionar.",
|
"gcast_enabled_description": "Aquesta funció carrega recursos externs de Google per funcionar.",
|
||||||
"general": "General",
|
"general": "General",
|
||||||
@@ -1329,6 +1374,7 @@
|
|||||||
"individual_share": "Compartit individualment",
|
"individual_share": "Compartit individualment",
|
||||||
"individual_shares": "Espais individuals",
|
"individual_shares": "Espais individuals",
|
||||||
"info": "Informació",
|
"info": "Informació",
|
||||||
|
"integrity_checks": "Verificacions d'integritat",
|
||||||
"interval": {
|
"interval": {
|
||||||
"day_at_onepm": "Cada dia a les 13h",
|
"day_at_onepm": "Cada dia a les 13h",
|
||||||
"hours": "Cada {hours, plural, one {hour} other {{hours, number} hours}}",
|
"hours": "Cada {hours, plural, one {hour} other {{hours, number} hours}}",
|
||||||
@@ -1345,6 +1391,7 @@
|
|||||||
"ios_debug_info_no_sync_yet": "Encara no s'ha executat cap tasca de sincronització en segon pla",
|
"ios_debug_info_no_sync_yet": "Encara no s'ha executat cap tasca de sincronització en segon pla",
|
||||||
"ios_debug_info_processes_queued": "{count, plural, one {Un procés en segon pla a la cua} other {{count} processos en segon pla a la cua}}",
|
"ios_debug_info_processes_queued": "{count, plural, one {Un procés en segon pla a la cua} other {{count} processos en segon pla a la cua}}",
|
||||||
"ios_debug_info_processing_ran_at": "El processament s'ha executat {dateTime}",
|
"ios_debug_info_processing_ran_at": "El processament s'ha executat {dateTime}",
|
||||||
|
"iso": "ISO",
|
||||||
"items_count": "{count, plural, one {# element} other {# elements}}",
|
"items_count": "{count, plural, one {# element} other {# elements}}",
|
||||||
"jobs": "Tasques",
|
"jobs": "Tasques",
|
||||||
"json_editor": "Editor JSON",
|
"json_editor": "Editor JSON",
|
||||||
@@ -1375,6 +1422,7 @@
|
|||||||
"leave": "Marxar",
|
"leave": "Marxar",
|
||||||
"leave_album": "Abandonar àlbum",
|
"leave_album": "Abandonar àlbum",
|
||||||
"lens_model": "Model de lents",
|
"lens_model": "Model de lents",
|
||||||
|
"less": "Menys",
|
||||||
"let_others_respond": "Deixa que els altres responguin",
|
"let_others_respond": "Deixa que els altres responguin",
|
||||||
"level": "Nivell",
|
"level": "Nivell",
|
||||||
"library": "Bibilioteca",
|
"library": "Bibilioteca",
|
||||||
@@ -1392,11 +1440,14 @@
|
|||||||
"light_theme": "Canviar a tema clar",
|
"light_theme": "Canviar a tema clar",
|
||||||
"like": "M'agrada",
|
"like": "M'agrada",
|
||||||
"like_deleted": "M'agrada suprimit",
|
"like_deleted": "M'agrada suprimit",
|
||||||
|
"link": "Enllaç",
|
||||||
"link_motion_video": "Enllaçar vídeo en moviment",
|
"link_motion_video": "Enllaçar vídeo en moviment",
|
||||||
"link_to_docs": "Per més informació, mirar la <link>documentation</link>.",
|
"link_to_docs": "Per més informació, mirar la <link>documentation</link>.",
|
||||||
"link_to_oauth": "Enllaç a OAuth",
|
"link_to_oauth": "Enllaç a OAuth",
|
||||||
"linked_oauth_account": "Compte OAuth enllaçat",
|
"linked_oauth_account": "Compte OAuth enllaçat",
|
||||||
"list": "Llista",
|
"list": "Llista",
|
||||||
|
"live": "En viu",
|
||||||
|
"load_more": "Carregar més",
|
||||||
"loading": "Carregant",
|
"loading": "Carregant",
|
||||||
"loading_search_results_failed": "No s'han pogut carregar els resultats de la cerca",
|
"loading_search_results_failed": "No s'han pogut carregar els resultats de la cerca",
|
||||||
"local": "Local",
|
"local": "Local",
|
||||||
@@ -1497,7 +1548,7 @@
|
|||||||
"map_location_picker_page_use_location": "Utilitzar aquesta ubicació",
|
"map_location_picker_page_use_location": "Utilitzar aquesta ubicació",
|
||||||
"map_location_service_disabled_content": "El servei de localització s'ha d'activar per mostrar els elements de la teva ubicació actual. Vols activar-lo ara?",
|
"map_location_service_disabled_content": "El servei de localització s'ha d'activar per mostrar els elements de la teva ubicació actual. Vols activar-lo ara?",
|
||||||
"map_location_service_disabled_title": "Servei de localització desactivat",
|
"map_location_service_disabled_title": "Servei de localització desactivat",
|
||||||
"map_marker_for_images": "Marcador de mapa per a imatges fetes a {city}, {country}",
|
"map_marker_for_image": "Marcador de mapa per a imatge obtinguda a {city}, {country}",
|
||||||
"map_marker_with_image": "Marcador de mapa amb imatge",
|
"map_marker_with_image": "Marcador de mapa amb imatge",
|
||||||
"map_no_location_permission_content": "Es necessita el permís de localització per mostrar els elements de la teva ubicació actual. Vols permetre-ho ara?",
|
"map_no_location_permission_content": "Es necessita el permís de localització per mostrar els elements de la teva ubicació actual. Vols permetre-ho ara?",
|
||||||
"map_no_location_permission_title": "Permís de localització denegat",
|
"map_no_location_permission_title": "Permís de localització denegat",
|
||||||
@@ -1518,6 +1569,38 @@
|
|||||||
"marked_all_as_read": "Marcat tot com a llegit",
|
"marked_all_as_read": "Marcat tot com a llegit",
|
||||||
"matches": "Coincidències",
|
"matches": "Coincidències",
|
||||||
"matching_assets": "Recursos Coincidents",
|
"matching_assets": "Recursos Coincidents",
|
||||||
|
"media_chrome": {
|
||||||
|
"auto": "Auto",
|
||||||
|
"captions": "Llegendes",
|
||||||
|
"captions_off": "Desactivat",
|
||||||
|
"closed_captions": "Llegendes tancades",
|
||||||
|
"decode_error": "Error de decodificació",
|
||||||
|
"disable_captions": "Desactivar llegendes",
|
||||||
|
"enable_captions": "Activar llegendes",
|
||||||
|
"enter_fullscreen_mode": "Activar mode pantalla sencera",
|
||||||
|
"exit_fullscreen_mode": "Desactivar mode pantalla sencera",
|
||||||
|
"loop": "Bucle",
|
||||||
|
"media_error_description": "Un error dels mitjans ha provocat l'aturada de la reproducció. El mitjà pot estar corromput o el navegador no suporta el format.",
|
||||||
|
"media_loading": "carregant el mitjà",
|
||||||
|
"mute": "Silencia",
|
||||||
|
"network_error": "Error de xarxa",
|
||||||
|
"network_error_description": "Un error de xarxa ha provocat la fallada de la descarrega.",
|
||||||
|
"not_supported_error": "Font origen no suportada",
|
||||||
|
"playback_rate": "Velocitat de reproducció",
|
||||||
|
"playback_rate_current": "velocitat actual de reproducció",
|
||||||
|
"playback_rate_value": "Velocitat de reproducció {playbackRate}",
|
||||||
|
"playback_time": "temps de reproducció",
|
||||||
|
"quality": "Quallitat",
|
||||||
|
"second": "segon",
|
||||||
|
"seconds": "segons",
|
||||||
|
"time_value_of_total_time": "{currentTime} de {totalTime}",
|
||||||
|
"time_value_remaining": "{time} restant",
|
||||||
|
"unmute": "Activa so",
|
||||||
|
"unsupported_error_description": "Un error no suportat ha passat. El servidor o la xarxa han fallat, o el vostre navegador no accepta aquest format.",
|
||||||
|
"video_not_loaded_unknown_time": "vídeo no carregat, temps desconegut.",
|
||||||
|
"video_player": "reproductor de vídeo",
|
||||||
|
"volume": "volum"
|
||||||
|
},
|
||||||
"media_type": "Tipus de mitjà",
|
"media_type": "Tipus de mitjà",
|
||||||
"memories": "Records",
|
"memories": "Records",
|
||||||
"memories_all_caught_up": "Posat al dia",
|
"memories_all_caught_up": "Posat al dia",
|
||||||
@@ -1534,6 +1617,8 @@
|
|||||||
"merge_people_prompt": "Vols combinar aquestes persones? Aquesta acció és irreversible.",
|
"merge_people_prompt": "Vols combinar aquestes persones? Aquesta acció és irreversible.",
|
||||||
"merge_people_successfully": "Persones combinades amb èxit",
|
"merge_people_successfully": "Persones combinades amb èxit",
|
||||||
"merged_people_count": "Combinades {count, plural, one {# persona} other {# persones}}",
|
"merged_people_count": "Combinades {count, plural, one {# persona} other {# persones}}",
|
||||||
|
"minFaces": "Nombre mínim de cares",
|
||||||
|
"minFaces_description": "El nombre mínim de cares reconegudes perquè es mostri una persona",
|
||||||
"minimize": "Minimitza",
|
"minimize": "Minimitza",
|
||||||
"minute": "Minut",
|
"minute": "Minut",
|
||||||
"minutes": "Minuts",
|
"minutes": "Minuts",
|
||||||
@@ -1543,9 +1628,10 @@
|
|||||||
"mobile_app": "Aplicació mòbil",
|
"mobile_app": "Aplicació mòbil",
|
||||||
"mobile_app_download_onboarding_note": "Descarregar la App de mòbil fent servir les seguents opcions",
|
"mobile_app_download_onboarding_note": "Descarregar la App de mòbil fent servir les seguents opcions",
|
||||||
"model": "Model",
|
"model": "Model",
|
||||||
|
"modify_date": "Canvia la data",
|
||||||
"month": "Mes",
|
"month": "Mes",
|
||||||
"monthly_title_text_date_format": "MMMM a",
|
|
||||||
"more": "Més",
|
"more": "Més",
|
||||||
|
"motion": "Moviment",
|
||||||
"move": "Moure",
|
"move": "Moure",
|
||||||
"move_down": "Moure cap avall",
|
"move_down": "Moure cap avall",
|
||||||
"move_off_locked_folder": "Moure fora de la carpeta bloquejada",
|
"move_off_locked_folder": "Moure fora de la carpeta bloquejada",
|
||||||
@@ -1562,6 +1648,8 @@
|
|||||||
"multiselect_grid_edit_gps_err_read_only": "No es pot canviar la localització de fitxers de només lectura, saltant",
|
"multiselect_grid_edit_gps_err_read_only": "No es pot canviar la localització de fitxers de només lectura, saltant",
|
||||||
"mute_memories": "Silenciar records",
|
"mute_memories": "Silenciar records",
|
||||||
"my_albums": "Els meus àlbums",
|
"my_albums": "Els meus àlbums",
|
||||||
|
"my_immich_description": "Copia la pàgina actual com a enllaç de My Immich",
|
||||||
|
"my_immich_title": "Enllaç My Immich",
|
||||||
"name": "Nom",
|
"name": "Nom",
|
||||||
"name_or_nickname": "Nom o sobrenom",
|
"name_or_nickname": "Nom o sobrenom",
|
||||||
"name_required": "El nom és obligatori",
|
"name_required": "El nom és obligatori",
|
||||||
@@ -1589,7 +1677,6 @@
|
|||||||
"next": "Següent",
|
"next": "Següent",
|
||||||
"next_memory": "Següent record",
|
"next_memory": "Següent record",
|
||||||
"no": "No",
|
"no": "No",
|
||||||
"no_actions_added": "Encara no s'han afegit accions",
|
|
||||||
"no_albums_found": "No s'han trobat àlbums",
|
"no_albums_found": "No s'han trobat àlbums",
|
||||||
"no_albums_message": "Creeu un àlbum per organitzar les vostres fotos i vídeos",
|
"no_albums_message": "Creeu un àlbum per organitzar les vostres fotos i vídeos",
|
||||||
"no_albums_with_name_yet": "Sembla que encara no tens cap àlbum amb aquest nom.",
|
"no_albums_with_name_yet": "Sembla que encara no tens cap àlbum amb aquest nom.",
|
||||||
@@ -1606,7 +1693,6 @@
|
|||||||
"no_exif_info_available": "No hi ha informació d'exif disponible",
|
"no_exif_info_available": "No hi ha informació d'exif disponible",
|
||||||
"no_explore_results_message": "Penja més fotos per explorar la teva col·lecció.",
|
"no_explore_results_message": "Penja més fotos per explorar la teva col·lecció.",
|
||||||
"no_favorites_message": "Afegiu preferits per trobar les millors fotos i vídeos a l'instant",
|
"no_favorites_message": "Afegiu preferits per trobar les millors fotos i vídeos a l'instant",
|
||||||
"no_filters_added": "Encara no s'han afegit filtres",
|
|
||||||
"no_libraries_message": "Creeu una llibreria externa per veure les vostres fotos i vídeos",
|
"no_libraries_message": "Creeu una llibreria externa per veure les vostres fotos i vídeos",
|
||||||
"no_local_assets_found": "No s'ha trobat cap recurs local amb aquest checksum",
|
"no_local_assets_found": "No s'ha trobat cap recurs local amb aquest checksum",
|
||||||
"no_location_set": "No s'ha definit cap ubicació",
|
"no_location_set": "No s'ha definit cap ubicació",
|
||||||
@@ -1619,6 +1705,7 @@
|
|||||||
"no_results": "Sense resultats",
|
"no_results": "Sense resultats",
|
||||||
"no_results_description": "Proveu un sinònim o una paraula clau més general",
|
"no_results_description": "Proveu un sinònim o una paraula clau més general",
|
||||||
"no_shared_albums_message": "Creeu un àlbum per compartir fotos i vídeos amb persones a la vostra xarxa",
|
"no_shared_albums_message": "Creeu un àlbum per compartir fotos i vídeos amb persones a la vostra xarxa",
|
||||||
|
"no_steps": "Encara no s'ha afegit cap pas",
|
||||||
"no_uploads_in_progress": "Cap pujada en progrés",
|
"no_uploads_in_progress": "Cap pujada en progrés",
|
||||||
"none": "Cap",
|
"none": "Cap",
|
||||||
"not_allowed": "No permès",
|
"not_allowed": "No permès",
|
||||||
@@ -1627,6 +1714,7 @@
|
|||||||
"not_selected": "No seleccionat",
|
"not_selected": "No seleccionat",
|
||||||
"notes": "Notes",
|
"notes": "Notes",
|
||||||
"nothing_here_yet": "No hi ha res encara",
|
"nothing_here_yet": "No hi ha res encara",
|
||||||
|
"notification_backup_reliability": "Activa les notificacions per millorar la fiabilitat de les còpies de seguretat en segon pla",
|
||||||
"notification_permission_dialog_content": "Per activar les notificacions, aneu a Configuració i seleccioneu permet.",
|
"notification_permission_dialog_content": "Per activar les notificacions, aneu a Configuració i seleccioneu permet.",
|
||||||
"notification_permission_list_tile_content": "Atorga permís per a activar les notificacions.",
|
"notification_permission_list_tile_content": "Atorga permís per a activar les notificacions.",
|
||||||
"notification_permission_list_tile_enable_button": "Activa les notificacions",
|
"notification_permission_list_tile_enable_button": "Activa les notificacions",
|
||||||
@@ -1664,6 +1752,7 @@
|
|||||||
"organize_into_albums": "Organitzar en àlbums",
|
"organize_into_albums": "Organitzar en àlbums",
|
||||||
"organize_into_albums_description": "Posar fotos existents en àlbums utilitzant la configuració de sincronització actual",
|
"organize_into_albums_description": "Posar fotos existents en àlbums utilitzant la configuració de sincronització actual",
|
||||||
"organize_your_library": "Organitzeu la llibreria",
|
"organize_your_library": "Organitzeu la llibreria",
|
||||||
|
"orientation": "Orientació",
|
||||||
"original": "original",
|
"original": "original",
|
||||||
"other": "Altres",
|
"other": "Altres",
|
||||||
"other_devices": "Altres dispositius",
|
"other_devices": "Altres dispositius",
|
||||||
@@ -1755,6 +1844,8 @@
|
|||||||
"play_original_video_setting_description": "Preferir la reproducció del video original sobre el video recodificat. Si el video original no es compatible potser no es reprodueixi correctament.",
|
"play_original_video_setting_description": "Preferir la reproducció del video original sobre el video recodificat. Si el video original no es compatible potser no es reprodueixi correctament.",
|
||||||
"play_transcoded_video": "Veure el video recodificat",
|
"play_transcoded_video": "Veure el video recodificat",
|
||||||
"please_auth_to_access": "Per favor, autentica't per accedir",
|
"please_auth_to_access": "Per favor, autentica't per accedir",
|
||||||
|
"plugin_method_filter_type": "Filtre",
|
||||||
|
"plugin_method_filter_type_description": "Aquest mètode pot filtrar esdeveniments i, condicionat, evitar que s'executin els passos següents",
|
||||||
"port": "Port",
|
"port": "Port",
|
||||||
"preferences_settings_subtitle": "Gestiona les preferències de l'aplicació",
|
"preferences_settings_subtitle": "Gestiona les preferències de l'aplicació",
|
||||||
"preferences_settings_title": "Preferències",
|
"preferences_settings_title": "Preferències",
|
||||||
@@ -1776,6 +1867,7 @@
|
|||||||
"profile_drawer_readonly_mode": "Mode només lectura. Feu pulsació llarga a la icona de l'avatar d'usuari per sortir.",
|
"profile_drawer_readonly_mode": "Mode només lectura. Feu pulsació llarga a la icona de l'avatar d'usuari per sortir.",
|
||||||
"profile_image_of_user": "Imatge de perfil de {user}",
|
"profile_image_of_user": "Imatge de perfil de {user}",
|
||||||
"profile_picture_set": "Imatge de perfil configurada.",
|
"profile_picture_set": "Imatge de perfil configurada.",
|
||||||
|
"projection_type": "Tipus de Projecció",
|
||||||
"public_album": "Àlbum públic",
|
"public_album": "Àlbum públic",
|
||||||
"public_share": "Compartit públicament",
|
"public_share": "Compartit públicament",
|
||||||
"purchase_account_info": "Contribuent",
|
"purchase_account_info": "Contribuent",
|
||||||
@@ -1853,6 +1945,7 @@
|
|||||||
"remove_assets_title": "Eliminar els elements?",
|
"remove_assets_title": "Eliminar els elements?",
|
||||||
"remove_custom_date_range": "Elimina l'interval de dates personalitzat",
|
"remove_custom_date_range": "Elimina l'interval de dates personalitzat",
|
||||||
"remove_deleted_assets": "Suprimeix fitxers fora de línia",
|
"remove_deleted_assets": "Suprimeix fitxers fora de línia",
|
||||||
|
"remove_filter": "Elimina el filtre",
|
||||||
"remove_from_album": "Treu de l'àlbum",
|
"remove_from_album": "Treu de l'àlbum",
|
||||||
"remove_from_album_action_prompt": "{count} eliminats de l'àlbum",
|
"remove_from_album_action_prompt": "{count} eliminats de l'àlbum",
|
||||||
"remove_from_favorites": "Eliminar dels preferits",
|
"remove_from_favorites": "Eliminar dels preferits",
|
||||||
@@ -1926,6 +2019,8 @@
|
|||||||
"scan_settings": "Configuració d'escaneig",
|
"scan_settings": "Configuració d'escaneig",
|
||||||
"scanning": "Escanejant",
|
"scanning": "Escanejant",
|
||||||
"scanning_for_album": "S'està buscant l'àlbum...",
|
"scanning_for_album": "S'està buscant l'àlbum...",
|
||||||
|
"screencast_mode_description": "Mostra els indicadors d'esdeveniments del teclat i del ratolí a la pantalla",
|
||||||
|
"screencast_mode_title": "Activa/desactiva el mode de captura de pantalla",
|
||||||
"search": "Cerca",
|
"search": "Cerca",
|
||||||
"search_albums": "Buscar àlbums",
|
"search_albums": "Buscar àlbums",
|
||||||
"search_by_context": "Buscar per context",
|
"search_by_context": "Buscar per context",
|
||||||
@@ -1933,6 +2028,8 @@
|
|||||||
"search_by_description_example": "Jornada de senderisme a Sapa",
|
"search_by_description_example": "Jornada de senderisme a Sapa",
|
||||||
"search_by_filename": "Cerca per nom de fitxer o extensió",
|
"search_by_filename": "Cerca per nom de fitxer o extensió",
|
||||||
"search_by_filename_example": "per exemple IMG_1234.JPG o PNG",
|
"search_by_filename_example": "per exemple IMG_1234.JPG o PNG",
|
||||||
|
"search_by_full_path": "Cerca per camí complert o carpeta",
|
||||||
|
"search_by_full_path_example": "/John/Projects/3D_Printing/2026-07-01 - pots buscar Projectes, 3D, Impressió, 2026 etc.",
|
||||||
"search_by_ocr": "Buscar per OCR",
|
"search_by_ocr": "Buscar per OCR",
|
||||||
"search_by_ocr_example": "Després",
|
"search_by_ocr_example": "Després",
|
||||||
"search_camera_lens_model": "Buscar model de lents....",
|
"search_camera_lens_model": "Buscar model de lents....",
|
||||||
@@ -2009,6 +2106,7 @@
|
|||||||
"select_person": "Seleccionar persona",
|
"select_person": "Seleccionar persona",
|
||||||
"select_person_to_tag": "Selecciona una persona per etiquetar",
|
"select_person_to_tag": "Selecciona una persona per etiquetar",
|
||||||
"select_photos": "Tria fotografies",
|
"select_photos": "Tria fotografies",
|
||||||
|
"select_quality": "Selecciona qualitat",
|
||||||
"select_trash_all": "Envia la selecció a la paperera",
|
"select_trash_all": "Envia la selecció a la paperera",
|
||||||
"select_user_for_sharing_page_err_album": "Error al crear l'àlbum",
|
"select_user_for_sharing_page_err_album": "Error al crear l'àlbum",
|
||||||
"selected": "Seleccionat",
|
"selected": "Seleccionat",
|
||||||
@@ -2072,6 +2170,8 @@
|
|||||||
"share_assets_selected": "{count} seleccionats",
|
"share_assets_selected": "{count} seleccionats",
|
||||||
"share_dialog_preparing": "S'està preparant...",
|
"share_dialog_preparing": "S'està preparant...",
|
||||||
"share_link": "Compartir Enllaç",
|
"share_link": "Compartir Enllaç",
|
||||||
|
"share_original": "Ús original (gran)",
|
||||||
|
"share_preview": "Ús de la miniatura (petit)",
|
||||||
"shared": "Compartit",
|
"shared": "Compartit",
|
||||||
"shared_album_activities_input_disable": "Els comentaris estan desactivats",
|
"shared_album_activities_input_disable": "Els comentaris estan desactivats",
|
||||||
"shared_album_activity_remove_content": "Voleu eliminar aquesta activitat?",
|
"shared_album_activity_remove_content": "Voleu eliminar aquesta activitat?",
|
||||||
@@ -2140,7 +2240,9 @@
|
|||||||
"show_in_timeline": "Mostra a la cronologia",
|
"show_in_timeline": "Mostra a la cronologia",
|
||||||
"show_in_timeline_setting_description": "Mostra fotos i vídeos d'aquest usuari a la cronologia",
|
"show_in_timeline_setting_description": "Mostra fotos i vídeos d'aquest usuari a la cronologia",
|
||||||
"show_keyboard_shortcuts": "Mostra dreceres de teclat",
|
"show_keyboard_shortcuts": "Mostra dreceres de teclat",
|
||||||
|
"show_less": "Mostra'n menys",
|
||||||
"show_metadata": "Mostra metadades",
|
"show_metadata": "Mostra metadades",
|
||||||
|
"show_more_fields": "{count, plural, one {Veure # camp més} other {Veure # camps més}}",
|
||||||
"show_or_hide_info": "Mostra o amaga informació",
|
"show_or_hide_info": "Mostra o amaga informació",
|
||||||
"show_password": "Mostra contrasenya",
|
"show_password": "Mostra contrasenya",
|
||||||
"show_person_options": "Mostra opcions de la persona",
|
"show_person_options": "Mostra opcions de la persona",
|
||||||
@@ -2148,6 +2250,7 @@
|
|||||||
"show_schema": "Mostrar esquema",
|
"show_schema": "Mostrar esquema",
|
||||||
"show_search_options": "Mostra opcions de cerca",
|
"show_search_options": "Mostra opcions de cerca",
|
||||||
"show_shared_links": "Mostra els enllaços compartits",
|
"show_shared_links": "Mostra els enllaços compartits",
|
||||||
|
"show_slideshow_metadata_overlay": "Mostra informació sobre la imatge",
|
||||||
"show_slideshow_transition": "Mostra la transició de la presentació de diapositives",
|
"show_slideshow_transition": "Mostra la transició de la presentació de diapositives",
|
||||||
"show_supporter_badge": "Insígnia de contribuent",
|
"show_supporter_badge": "Insígnia de contribuent",
|
||||||
"show_supporter_badge_description": "Mostra una insígnia de contributor",
|
"show_supporter_badge_description": "Mostra una insígnia de contributor",
|
||||||
@@ -2163,9 +2266,14 @@
|
|||||||
"skip_to_folders": "Anar a carpetes",
|
"skip_to_folders": "Anar a carpetes",
|
||||||
"skip_to_tags": "Anar a etiquetes",
|
"skip_to_tags": "Anar a etiquetes",
|
||||||
"slideshow": "Diapositives",
|
"slideshow": "Diapositives",
|
||||||
|
"slideshow_metadata_overlay_mode": "Contingut de superposició",
|
||||||
|
"slideshow_metadata_overlay_mode_description_only": "Descripció només",
|
||||||
|
"slideshow_metadata_overlay_mode_full": "Tot",
|
||||||
"slideshow_repeat": "Repeteix la presentació de diapositives",
|
"slideshow_repeat": "Repeteix la presentació de diapositives",
|
||||||
"slideshow_repeat_description": "Torna al principi quan acaba la presentació de diapositives",
|
"slideshow_repeat_description": "Torna al principi quan acaba la presentació de diapositives",
|
||||||
"slideshow_settings": "Configuració de diapositives",
|
"slideshow_settings": "Configuració de diapositives",
|
||||||
|
"smart_album": "Àlbum inteŀligent",
|
||||||
|
"some_assets_already_have_a_location_warning": "Alguns dels actius seleccionats ja tenen una ubicació",
|
||||||
"sort_albums_by": "Ordena àlbums per...",
|
"sort_albums_by": "Ordena àlbums per...",
|
||||||
"sort_created": "Data de creació",
|
"sort_created": "Data de creació",
|
||||||
"sort_items": "Quantitat d'elements",
|
"sort_items": "Quantitat d'elements",
|
||||||
@@ -2188,6 +2296,11 @@
|
|||||||
"start_date_before_end_date": "La data d'inici ha de ser abans de la data de fi",
|
"start_date_before_end_date": "La data d'inici ha de ser abans de la data de fi",
|
||||||
"state": "Regió",
|
"state": "Regió",
|
||||||
"status": "Estat",
|
"status": "Estat",
|
||||||
|
"step_delete": "Elimina el pas",
|
||||||
|
"step_delete_confirm": "Esteu segur que voleu eliminar el pas?",
|
||||||
|
"step_details": "Detalls del pas",
|
||||||
|
"steps": "Passos",
|
||||||
|
"steps_count": "{count, plural, one {# pas} other {# passos}}",
|
||||||
"stop_casting": "Atura la transmisió",
|
"stop_casting": "Atura la transmisió",
|
||||||
"stop_motion_photo": "Atura foto en moviment",
|
"stop_motion_photo": "Atura foto en moviment",
|
||||||
"stop_photo_sharing": "Deixar de compartir les teves fotos?",
|
"stop_photo_sharing": "Deixar de compartir les teves fotos?",
|
||||||
@@ -2214,6 +2327,8 @@
|
|||||||
"sync_status": "Estat de la incronització",
|
"sync_status": "Estat de la incronització",
|
||||||
"sync_status_subtitle": "Observa i administra el sistema de sincronització",
|
"sync_status_subtitle": "Observa i administra el sistema de sincronització",
|
||||||
"sync_upload_album_setting_subtitle": "Creeu i pugeu les seves fotos i vídeos als àlbums seleccionats a Immich",
|
"sync_upload_album_setting_subtitle": "Creeu i pugeu les seves fotos i vídeos als àlbums seleccionats a Immich",
|
||||||
|
"system_theme": "Tema del sistema",
|
||||||
|
"system_theme_command_description": "Utilitza el tema del sistema ({value})",
|
||||||
"tag": "Etiqueta",
|
"tag": "Etiqueta",
|
||||||
"tag_assets": "Etiquetar actius",
|
"tag_assets": "Etiquetar actius",
|
||||||
"tag_created": "Etiqueta creada: {tag}",
|
"tag_created": "Etiqueta creada: {tag}",
|
||||||
@@ -2279,11 +2394,13 @@
|
|||||||
"trash_page_title": "Paperera ({count})",
|
"trash_page_title": "Paperera ({count})",
|
||||||
"trashed_items_will_be_permanently_deleted_after": "Els elements que s'enviïn a la paperera s'eliminaran permanentment després de {days, plural, one {# dia} other {# dies}}.",
|
"trashed_items_will_be_permanently_deleted_after": "Els elements que s'enviïn a la paperera s'eliminaran permanentment després de {days, plural, one {# dia} other {# dies}}.",
|
||||||
"trigger": "Disparador",
|
"trigger": "Disparador",
|
||||||
"trigger_asset_uploaded": "Mitjà Carregat",
|
"trigger_asset_metadata_extraction": "Extracció de metadades d'actius",
|
||||||
|
"trigger_asset_metadata_extraction_description": "Disparat quan s'extreuen les metadades EXIF d'un actiu",
|
||||||
|
"trigger_asset_uploaded": "Càrrega de Mitjans",
|
||||||
"trigger_asset_uploaded_description": "Es dispara quan un nou mitjà es puge al servidor",
|
"trigger_asset_uploaded_description": "Es dispara quan un nou mitjà es puge al servidor",
|
||||||
"trigger_description": "L'esdeveniment que inicia l'automatització",
|
"trigger_description": "L'esdeveniment que inicia l'automatització",
|
||||||
"trigger_person_recognized": "Persona identificada",
|
"trigger_person_recognized": "Persona identificada",
|
||||||
"trigger_person_recognized_description": "Es dispara quan es detecta una persona",
|
"trigger_person_recognized_description": "Disparat quan es reconeix una persona",
|
||||||
"trigger_type": "Tipus de disparador",
|
"trigger_type": "Tipus de disparador",
|
||||||
"troubleshoot": "Solució de problemes",
|
"troubleshoot": "Solució de problemes",
|
||||||
"type": "Tipus",
|
"type": "Tipus",
|
||||||
@@ -2319,13 +2436,13 @@
|
|||||||
"unsupported_field_type": "Tipus de camp no suportat",
|
"unsupported_field_type": "Tipus de camp no suportat",
|
||||||
"unsupported_file_type": "No es pot carregar el fitxer {file} perquè el seu tipus de fitxer {type} no és compatible.",
|
"unsupported_file_type": "No es pot carregar el fitxer {file} perquè el seu tipus de fitxer {type} no és compatible.",
|
||||||
"untagged": "Sense etiqueta",
|
"untagged": "Sense etiqueta",
|
||||||
"untitled_workflow": "Automatització sense títol",
|
|
||||||
"up_next": "Pròxim",
|
"up_next": "Pròxim",
|
||||||
"update_location_action_prompt": "Actualitza la ubicació de {count} elements seleccionats amb:",
|
"update_location_action_prompt": "Actualitza la ubicació de {count} elements seleccionats amb:",
|
||||||
"updated_at": "Actualitzat",
|
"updated_at": "Actualitzat",
|
||||||
"updated_password": "Contrasenya actualitzada",
|
"updated_password": "Contrasenya actualitzada",
|
||||||
"upload": "Pujar",
|
"upload": "Pujar",
|
||||||
"upload_concurrency": "Concurrència de pujades",
|
"upload_concurrency": "Concurrència de pujades",
|
||||||
|
"upload_day_count": "{date}: {count, plural, one {# pujada} other {# pujades}}",
|
||||||
"upload_details": "Detalls de la Pujada",
|
"upload_details": "Detalls de la Pujada",
|
||||||
"upload_dialog_info": "Vols fer còpia de seguretat dels elements seleccionats al servidor?",
|
"upload_dialog_info": "Vols fer còpia de seguretat dels elements seleccionats al servidor?",
|
||||||
"upload_dialog_title": "Puja elements",
|
"upload_dialog_title": "Puja elements",
|
||||||
@@ -2341,6 +2458,8 @@
|
|||||||
"upload_to_immich": "Puja a Immich ({count})",
|
"upload_to_immich": "Puja a Immich ({count})",
|
||||||
"uploading": "Pujant",
|
"uploading": "Pujant",
|
||||||
"uploading_media": "Pujant mitjans",
|
"uploading_media": "Pujant mitjans",
|
||||||
|
"uploads": "Pujades",
|
||||||
|
"uploads_count": "{count, plural, one {# pujada} other {# pujades}}",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
"usage": "Ús",
|
"usage": "Ús",
|
||||||
"use_biometric": "Empra biometria",
|
"use_biometric": "Empra biometria",
|
||||||
@@ -2348,6 +2467,7 @@
|
|||||||
"use_browser_locale_description": "Formatejar dates, hores i números segons la llengua i regió del navegador",
|
"use_browser_locale_description": "Formatejar dates, hores i números segons la llengua i regió del navegador",
|
||||||
"use_current_connection": "Utilitza la connexió actual",
|
"use_current_connection": "Utilitza la connexió actual",
|
||||||
"use_custom_date_range": "Fes servir un rang de dates personalitzat",
|
"use_custom_date_range": "Fes servir un rang de dates personalitzat",
|
||||||
|
"use_template": "Utilitza la plantilla",
|
||||||
"user": "Usuari",
|
"user": "Usuari",
|
||||||
"user_has_been_deleted": "Aquest usuari ha sigut eliminat.",
|
"user_has_been_deleted": "Aquest usuari ha sigut eliminat.",
|
||||||
"user_id": "ID d'usuari",
|
"user_id": "ID d'usuari",
|
||||||
@@ -2377,6 +2497,7 @@
|
|||||||
"video": "Vídeo",
|
"video": "Vídeo",
|
||||||
"video_hover_setting": "Reprodueix la miniatura en passar el ratolí",
|
"video_hover_setting": "Reprodueix la miniatura en passar el ratolí",
|
||||||
"video_hover_setting_description": "Reprodueix la miniatura quan el ratolí plana sobre l'element. Fins i tot quan estigui deshabilitat, la reproducció s'iniciarà planant sobre el botó de reproducció.",
|
"video_hover_setting_description": "Reprodueix la miniatura quan el ratolí plana sobre l'element. Fins i tot quan estigui deshabilitat, la reproducció s'iniciarà planant sobre el botó de reproducció.",
|
||||||
|
"video_quality": "Qualitat del vídeo",
|
||||||
"videos": "Vídeos",
|
"videos": "Vídeos",
|
||||||
"videos_count": "{count, plural, one {# vídeo} other {# vídeos}}",
|
"videos_count": "{count, plural, one {# vídeo} other {# vídeos}}",
|
||||||
"videos_only": "Només videos",
|
"videos_only": "Només videos",
|
||||||
@@ -2409,8 +2530,10 @@
|
|||||||
"week": "Setmana",
|
"week": "Setmana",
|
||||||
"welcome": "Benvingut",
|
"welcome": "Benvingut",
|
||||||
"welcome_to_immich": "Benvingut a immich",
|
"welcome_to_immich": "Benvingut a immich",
|
||||||
|
"when": "Quan",
|
||||||
"width": "Amplada",
|
"width": "Amplada",
|
||||||
"wifi_name": "Nom Wi-Fi",
|
"wifi_name": "Nom Wi-Fi",
|
||||||
|
"workflow": "Flux de treball",
|
||||||
"workflow_delete_prompt": "Segur que vols eliminar aquesta automatització?",
|
"workflow_delete_prompt": "Segur que vols eliminar aquesta automatització?",
|
||||||
"workflow_deleted": "Automatització eliminada",
|
"workflow_deleted": "Automatització eliminada",
|
||||||
"workflow_description": "Descripció de l'automatització",
|
"workflow_description": "Descripció de l'automatització",
|
||||||
@@ -2420,11 +2543,13 @@
|
|||||||
"workflow_name": "Nom de l'automatització",
|
"workflow_name": "Nom de l'automatització",
|
||||||
"workflow_navigation_prompt": "Segur que vols sortir sense desar els canvis?",
|
"workflow_navigation_prompt": "Segur que vols sortir sense desar els canvis?",
|
||||||
"workflow_summary": "Resum de l'automatització",
|
"workflow_summary": "Resum de l'automatització",
|
||||||
|
"workflow_templates": "Plantilles de fluxos de treball",
|
||||||
"workflow_update_success": "Automatització actualitzada amb èxit",
|
"workflow_update_success": "Automatització actualitzada amb èxit",
|
||||||
"workflow_updated": "Automatització actualitzada",
|
"workflow_updated": "Automatització actualitzada",
|
||||||
"workflows": "Automatitzacions",
|
"workflows": "Automatitzacions",
|
||||||
"workflows_help_text": "Les automatitzacions realitzen accions automàticament sobre els teus mitjans basant-se en disparadors i filtres",
|
"workflows_help_text": "Les automatitzacions realitzen accions automàticament sobre els teus mitjans basant-se en disparadors i filtres",
|
||||||
"wrong_pin_code": "Codi PIN incorrecte",
|
"wrong_pin_code": "Codi PIN incorrecte",
|
||||||
|
"x_of_total": "{x}/{total}",
|
||||||
"year": "Any",
|
"year": "Any",
|
||||||
"years_ago": "Fa {years, plural, one {# any} other {# anys}}",
|
"years_ago": "Fa {years, plural, one {# any} other {# anys}}",
|
||||||
"yes": "Sí",
|
"yes": "Sí",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user