diff --git a/.devcontainer/mobile/container-compose-overrides.yml b/.devcontainer/mobile/container-compose-overrides.yml index d6cd95018f..99e41cbece 100644 --- a/.devcontainer/mobile/container-compose-overrides.yml +++ b/.devcontainer/mobile/container-compose-overrides.yml @@ -6,28 +6,35 @@ services: - IMMICH_SERVER_URL=http://127.0.0.1:2283/ volumes: !override # bind mount host to /workspaces/immich - ..:/workspaces/immich - - cli_node_modules:/workspaces/immich/cli/node_modules - - e2e_node_modules:/workspaces/immich/e2e/node_modules - - open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules - - server_node_modules:/workspaces/immich/server/node_modules - - web_node_modules:/workspaces/immich/web/node_modules - - ${UPLOAD_LOCATION}/photos:/data + - ${UPLOAD_LOCATION:-upload-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data + - pnpm-store:/usr/src/app/.pnpm-store + - server-node_modules:/usr/src/app/server/node_modules + - web-node_modules:/usr/src/app/web/node_modules + - github-node_modules:/usr/src/app/.github/node_modules + - cli-node_modules:/usr/src/app/cli/node_modules + - docs-node_modules:/usr/src/app/docs/node_modules + - e2e-node_modules:/usr/src/app/e2e/node_modules + - sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules + - app-node_modules:/usr/src/app/node_modules + - sveltekit:/usr/src/app/web/.svelte-kit + - coverage:/usr/src/app/web/coverage - /etc/localtime:/etc/localtime:ro - + immich-web: + env_file: !reset [] + immich-machine-learning: + env_file: !reset [] database: + env_file: !reset [] + environment: !override + POSTGRES_PASSWORD: ${DB_PASSWORD-postgres} + POSTGRES_USER: ${DB_USERNAME-postgres} + POSTGRES_DB: ${DB_DATABASE_NAME-immich} + POSTGRES_INITDB_ARGS: '--data-checksums' + POSTGRES_HOST_AUTH_METHOD: md5 volumes: - - ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data - + - ${UPLOAD_LOCATION:-postgres-devcontainer-volume}${UPLOAD_LOCATION:+/postgres}:/var/lib/postgresql/data + redis: + env_file: !reset [] volumes: - # Node modules for each service to avoid conflicts and ensure consistent dependencies - cli_node_modules: - e2e_node_modules: - open_api_node_modules: - server_node_modules: - web_node_modules: - - # UPLOAD_LOCATION must be set to a absolute path or vol-upload - vol-upload: - - # DB_DATA_LOCATION must be set to a absolute path or vol-database - vol-database: + upload-devcontainer-volume: + postgres-devcontainer-volume: diff --git a/.devcontainer/mobile/devcontainer.json b/.devcontainer/mobile/devcontainer.json index 0dbcc8e9c8..140a2ecac3 100644 --- a/.devcontainer/mobile/devcontainer.json +++ b/.devcontainer/mobile/devcontainer.json @@ -40,7 +40,7 @@ "userEnvProbe": "loginInteractiveShell", "remoteEnv": { // The location where your uploaded files are stored - "UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./Library}", + "UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./library}", // Connection secret for postgres. You should change it to a random password // Please use only the characters `A-Za-z0-9`, without special characters or spaces "DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}", diff --git a/.github/.nvmrc b/.github/.nvmrc index e2228113dd..442c7587a9 100644 --- a/.github/.nvmrc +++ b/.github/.nvmrc @@ -1 +1 @@ -22.19.0 +22.20.0 diff --git a/.github/labeler.yml b/.github/labeler.yml index c0c52f1d7e..d8923a3035 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -6,7 +6,6 @@ cli: documentation: - changed-files: - any-glob-to-any-file: - - docs/blob/** - docs/docs/** - docs/src/** - docs/static/** diff --git a/.github/workflows/build-mobile.yml b/.github/workflows/build-mobile.yml index 1b57731b23..f7e9ad5731 100644 --- a/.github/workflows/build-mobile.yml +++ b/.github/workflows/build-mobile.yml @@ -36,7 +36,7 @@ jobs: steps: - name: Check what should run id: check - uses: immich-app/devtools/actions/pre-job@24820aa4ef67959b0dcf69a438cccf00d7c7042b # pre-job-action-v1.0.1 + uses: immich-app/devtools/actions/pre-job@5f91b52dfbb92b8d96ca411ab59c896cd59714ca # pre-job-action-v1.1.0 with: filters: | mobile: @@ -55,7 +55,7 @@ jobs: runs-on: mich steps: - - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: ref: ${{ inputs.ref || github.sha }} persist-credentials: false @@ -66,14 +66,14 @@ jobs: working-directory: ./mobile run: printf "%s" $KEY_JKS | base64 -d > android/key.jks - - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: 'zulu' java-version: '17' - name: Restore Gradle Cache id: cache-gradle-restore - uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: | ~/.gradle/caches @@ -130,7 +130,7 @@ jobs: - name: Save Gradle Cache id: cache-gradle-save - uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 if: github.ref == 'refs/heads/main' with: path: | diff --git a/.github/workflows/cache-cleanup.yml b/.github/workflows/cache-cleanup.yml index cdff8ed931..0b4366ba04 100644 --- a/.github/workflows/cache-cleanup.yml +++ b/.github/workflows/cache-cleanup.yml @@ -19,7 +19,7 @@ jobs: actions: write steps: - name: Check out code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 777f1898ec..51e023056d 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -29,15 +29,15 @@ jobs: working-directory: ./cli steps: - - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Setup pnpm - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version-file: './cli/.nvmrc' registry-url: 'https://registry.npmjs.org' @@ -50,7 +50,7 @@ jobs: - run: pnpm install --frozen-lockfile - run: pnpm build - - run: pnpm publish + - run: pnpm publish --no-git-checks if: ${{ github.event_name == 'release' }} env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} @@ -65,7 +65,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false @@ -76,7 +76,7 @@ jobs: uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 - name: Login to GitHub Container Registry - uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 if: ${{ !github.event.pull_request.head.repo.fork }} with: registry: ghcr.io diff --git a/.github/workflows/close-duplicates.yml b/.github/workflows/close-duplicates.yml index 8470e0e18c..ba360b50dc 100644 --- a/.github/workflows/close-duplicates.yml +++ b/.github/workflows/close-duplicates.yml @@ -35,7 +35,7 @@ jobs: needs: [get_body, should_run] if: ${{ needs.should_run.outputs.should_run == 'true' }} container: - image: ghcr.io/immich-app/mdq:main@sha256:d8ae47cf2e6cf4e2559bd57a60b73674fe44f897cba2c2bddff2987a05be10a4 + image: ghcr.io/immich-app/mdq:main@sha256:6b8450bfc06770af1af66bce9bf2ced7d1d9b90df1a59fc4c83a17777a9f6723 outputs: checked: ${{ steps.get_checkbox.outputs.checked }} steps: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 503dd30d9a..c0cb9e1da1 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -44,13 +44,13 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3 + uses: github/codeql-action/init@755f44910c12a3d7ca0d8c6e42c048b3362f7cec # v3.30.8 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -63,7 +63,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3 + uses: github/codeql-action/autobuild@755f44910c12a3d7ca0d8c6e42c048b3362f7cec # v3.30.8 # ℹ️ 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 @@ -76,6 +76,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3 + uses: github/codeql-action/analyze@755f44910c12a3d7ca0d8c6e42c048b3362f7cec # v3.30.8 with: category: '/language:${{matrix.language}}' diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index a630d27809..7175dc0a89 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Check what should run id: check - uses: immich-app/devtools/actions/pre-job@24820aa4ef67959b0dcf69a438cccf00d7c7042b # pre-job-action-v1.0.1 + uses: immich-app/devtools/actions/pre-job@5f91b52dfbb92b8d96ca411ab59c896cd59714ca # pre-job-action-v1.1.0 with: filters: | server: @@ -53,7 +53,7 @@ jobs: suffix: ['', '-cuda', '-rocm', '-openvino', '-armnn', '-rknn'] steps: - name: Login to GitHub Container Registry - uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 with: registry: ghcr.io username: ${{ github.repository_owner }} @@ -82,7 +82,7 @@ jobs: suffix: [''] steps: - name: Login to GitHub Container Registry - uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 with: registry: ghcr.io username: ${{ github.repository_owner }} @@ -124,7 +124,7 @@ jobs: tag-suffix: '-rocm' platforms: linux/amd64 runner-mapping: '{"linux/amd64": "mich"}' - uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@129aeda75a450666ce96e8bc8126652e717917a7 # multi-runner-build-workflow-0.1.1 + uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@946acac326940f8badf09ccf591d9cb345d6a689 # multi-runner-build-workflow-v0.2.1 permissions: contents: read actions: read @@ -147,7 +147,7 @@ jobs: name: Build and Push Server needs: pre-job if: ${{ fromJSON(needs.pre-job.outputs.should_run).server == true }} - uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@129aeda75a450666ce96e8bc8126652e717917a7 # multi-runner-build-workflow-0.1.1 + uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@946acac326940f8badf09ccf591d9cb345d6a689 # multi-runner-build-workflow-v0.2.1 permissions: contents: read actions: read diff --git a/.github/workflows/docs-build.yml b/.github/workflows/docs-build.yml index 9a35a0ae91..537282b789 100644 --- a/.github/workflows/docs-build.yml +++ b/.github/workflows/docs-build.yml @@ -22,7 +22,7 @@ jobs: steps: - name: Check what should run id: check - uses: immich-app/devtools/actions/pre-job@24820aa4ef67959b0dcf69a438cccf00d7c7042b # pre-job-action-v1.0.1 + uses: immich-app/devtools/actions/pre-job@5f91b52dfbb92b8d96ca411ab59c896cd59714ca # pre-job-action-v1.1.0 with: filters: | docs: @@ -47,15 +47,15 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Setup pnpm - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version-file: './docs/.nvmrc' cache: 'pnpm' diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index b504b811e3..6643c5abfd 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -20,7 +20,7 @@ jobs: run: echo 'The triggering workflow did not succeed' && exit 1 - name: Get artifact id: get-artifact - uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: | let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ @@ -38,7 +38,7 @@ jobs: return { found: true, id: matchArtifact.id }; - name: Determine deploy parameters id: parameters - uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: HEAD_SHA: ${{ github.event.workflow_run.head_sha }} with: @@ -108,13 +108,13 @@ jobs: if: ${{ fromJson(needs.checks.outputs.artifact).found && fromJson(needs.checks.outputs.parameters).shouldDeploy }} steps: - name: Checkout code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Load parameters id: parameters - uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: PARAM_JSON: ${{ needs.checks.outputs.parameters }} with: @@ -125,7 +125,7 @@ jobs: core.setOutput("shouldDeploy", parameters.shouldDeploy); - name: Download artifact - uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: ARTIFACT_JSON: ${{ needs.checks.outputs.artifact }} with: diff --git a/.github/workflows/docs-destroy.yml b/.github/workflows/docs-destroy.yml index 37653c0990..c4b9a9fff3 100644 --- a/.github/workflows/docs-destroy.yml +++ b/.github/workflows/docs-destroy.yml @@ -14,7 +14,7 @@ jobs: pull-requests: write steps: - name: Checkout code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false diff --git a/.github/workflows/fix-format.yml b/.github/workflows/fix-format.yml index bec34c2713..dd61764bbc 100644 --- a/.github/workflows/fix-format.yml +++ b/.github/workflows/fix-format.yml @@ -16,23 +16,23 @@ jobs: steps: - name: Generate a token id: generate-token - uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 + uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 with: app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} - name: 'Checkout' - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: ref: ${{ github.event.pull_request.head.ref }} token: ${{ steps.generate-token.outputs.token }} persist-credentials: true - name: Setup pnpm - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version-file: './server/.nvmrc' cache: 'pnpm' @@ -48,7 +48,7 @@ jobs: message: 'chore: fix formatting' - name: Remove label - uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 if: always() with: script: | diff --git a/.github/workflows/merge-translations.yml b/.github/workflows/merge-translations.yml index d65567d400..d494460320 100644 --- a/.github/workflows/merge-translations.yml +++ b/.github/workflows/merge-translations.yml @@ -58,7 +58,7 @@ jobs: - name: Generate a token id: generate_token if: ${{ inputs.skip != true }} - uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 + uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 with: app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index ad73c78cf8..af92a3e215 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -11,4 +11,4 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0 + - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1 diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 835e88938f..5a369ad13b 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -49,26 +49,26 @@ jobs: steps: - name: Generate a token id: generate-token - uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 + uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 with: app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} - name: Checkout - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: token: ${{ steps.generate-token.outputs.token }} persist-credentials: true ref: main - name: Install uv - uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 + uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 - name: Setup pnpm - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version-file: './server/.nvmrc' cache: 'pnpm' @@ -111,24 +111,24 @@ jobs: steps: - name: Generate a token id: generate-token - uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 + uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 with: app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} - name: Checkout - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: token: ${{ steps.generate-token.outputs.token }} persist-credentials: false - name: Download APK - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: name: release-apk-signed - name: Create draft release - uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3 + uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1 with: draft: true tag_name: ${{ env.IMMICH_VERSION }} diff --git a/.github/workflows/preview-label.yaml b/.github/workflows/preview-label.yaml index 1d9a0060ad..594214f820 100644 --- a/.github/workflows/preview-label.yaml +++ b/.github/workflows/preview-label.yaml @@ -24,7 +24,7 @@ jobs: permissions: pull-requests: write steps: - - uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: | github.rest.issues.removeLabel({ diff --git a/.github/workflows/sdk.yml b/.github/workflows/sdk.yml index 460e7da4a7..6d768efaaf 100644 --- a/.github/workflows/sdk.yml +++ b/.github/workflows/sdk.yml @@ -16,15 +16,15 @@ jobs: run: working-directory: ./open-api/typescript-sdk steps: - - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Setup pnpm - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 # Setup .npmrc file to publish to npm - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version-file: './open-api/typescript-sdk/.nvmrc' registry-url: 'https://registry.npmjs.org' @@ -35,6 +35,6 @@ jobs: - name: Build run: pnpm build - name: Publish - run: pnpm publish + run: pnpm publish --no-git-checks env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml index f5e68fb42d..3e7223be74 100644 --- a/.github/workflows/static_analysis.yml +++ b/.github/workflows/static_analysis.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Check what should run id: check - uses: immich-app/devtools/actions/pre-job@24820aa4ef67959b0dcf69a438cccf00d7c7042b # pre-job-action-v1.0.1 + uses: immich-app/devtools/actions/pre-job@5f91b52dfbb92b8d96ca411ab59c896cd59714ca # pre-job-action-v1.1.0 with: filters: | mobile: @@ -42,7 +42,7 @@ jobs: working-directory: ./mobile steps: - name: Checkout code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 773d14e171..d95ede71e4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Check what should run id: check - uses: immich-app/devtools/actions/pre-job@24820aa4ef67959b0dcf69a438cccf00d7c7042b # pre-job-action-v1.0.1 + uses: immich-app/devtools/actions/pre-job@5f91b52dfbb92b8d96ca411ab59c896cd59714ca # pre-job-action-v1.1.0 with: filters: | i18n: @@ -56,13 +56,13 @@ jobs: working-directory: ./server steps: - name: Checkout code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Setup pnpm - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version-file: './server/.nvmrc' cache: 'pnpm' @@ -93,13 +93,13 @@ jobs: working-directory: ./cli steps: - name: Checkout code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Setup pnpm - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version-file: './cli/.nvmrc' cache: 'pnpm' @@ -133,13 +133,13 @@ jobs: working-directory: ./cli steps: - name: Checkout code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Setup pnpm - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version-file: './cli/.nvmrc' cache: 'pnpm' @@ -168,13 +168,13 @@ jobs: working-directory: ./web steps: - name: Checkout code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Setup pnpm - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version-file: './web/.nvmrc' cache: 'pnpm' @@ -185,7 +185,7 @@ jobs: - name: Run pnpm install run: pnpm rebuild && pnpm install --frozen-lockfile - name: Run linter - run: pnpm lint:p + run: pnpm lint if: ${{ !cancelled() }} - name: Run formatter run: pnpm format @@ -205,13 +205,13 @@ jobs: working-directory: ./web steps: - name: Checkout code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Setup pnpm - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version-file: './web/.nvmrc' cache: 'pnpm' @@ -236,13 +236,13 @@ jobs: contents: read steps: - name: Checkout code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Setup pnpm - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version-file: './web/.nvmrc' cache: 'pnpm' @@ -277,13 +277,13 @@ jobs: working-directory: ./e2e steps: - name: Checkout code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Setup pnpm - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version-file: './e2e/.nvmrc' cache: 'pnpm' @@ -316,13 +316,13 @@ jobs: working-directory: ./server steps: - name: Checkout code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Setup pnpm - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version-file: './server/.nvmrc' cache: 'pnpm' @@ -347,14 +347,14 @@ jobs: runner: [ubuntu-latest, ubuntu-24.04-arm] steps: - name: Checkout code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false submodules: 'recursive' - name: Setup pnpm - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version-file: './e2e/.nvmrc' cache: 'pnpm' @@ -395,14 +395,14 @@ jobs: runner: [ubuntu-latest, ubuntu-24.04-arm] steps: - name: Checkout code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false submodules: 'recursive' - name: Setup pnpm - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version-file: './e2e/.nvmrc' cache: 'pnpm' @@ -441,7 +441,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Setup Flutter SDK @@ -466,12 +466,12 @@ jobs: run: working-directory: ./machine-learning steps: - - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Install uv - uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 - - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 + - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 # TODO: add caching when supported (https://github.com/actions/setup-python/pull/818) # with: # python-version: 3.11 @@ -503,13 +503,13 @@ jobs: working-directory: ./.github steps: - name: Checkout code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Setup pnpm - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version-file: './.github/.nvmrc' cache: 'pnpm' @@ -525,7 +525,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Run ShellCheck @@ -540,13 +540,13 @@ jobs: contents: read steps: - name: Checkout code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Setup pnpm - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version-file: './server/.nvmrc' cache: 'pnpm' @@ -581,7 +581,7 @@ jobs: contents: read services: postgres: - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3@sha256:da52bbead5d818adaa8077c8dcdaad0aaf93038c31ad8348b51f9f0ec1310a4d + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3@sha256:dbf18b3ffea4a81434c65b71e20d27203baf903a0275f4341e4c16dfd901fd67 env: POSTGRES_PASSWORD: postgres POSTGRES_USER: postgres @@ -595,13 +595,13 @@ jobs: working-directory: ./server steps: - name: Checkout code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Setup pnpm - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version-file: './server/.nvmrc' cache: 'pnpm' diff --git a/.github/workflows/weblate-lock.yml b/.github/workflows/weblate-lock.yml index 36544d4eed..d7deb244f9 100644 --- a/.github/workflows/weblate-lock.yml +++ b/.github/workflows/weblate-lock.yml @@ -25,7 +25,7 @@ jobs: steps: - name: Check what should run id: check - uses: immich-app/devtools/actions/pre-job@24820aa4ef67959b0dcf69a438cccf00d7c7042b # pre-job-action-v1.0.1 + uses: immich-app/devtools/actions/pre-job@5f91b52dfbb92b8d96ca411ab59c896cd59714ca # pre-job-action-v1.1.0 with: filters: | i18n: diff --git a/.vscode/launch.json b/.vscode/launch.json index 0cc9b256ca..9ed2bb77b8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -18,6 +18,20 @@ "name": "Immich Workers", "remoteRoot": "/usr/src/app/server", "localRoot": "${workspaceFolder}/server" + }, + { + "type": "node", + "request": "launch", + "name": "Immich CLI", + "program": "${workspaceFolder}/cli/dist/index.js", + "args": ["upload", "--help"], + "runtimeArgs": ["--enable-source-maps"], + "console": "integratedTerminal", + "resolveSourceMapLocations": ["${workspaceFolder}/cli/dist/**/*.js.map"], + "sourceMaps": true, + "outFiles": ["${workspaceFolder}/cli/dist/**/*.js"], + "skipFiles": ["/**"], + "preLaunchTask": "Build Immich CLI" } ] } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 119d6a961b..478a46b4bd 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -5,6 +5,7 @@ "label": "Fix Permissions, Install Dependencies", "type": "shell", "command": "[ -f /immich-devcontainer/container-start.sh ] && /immich-devcontainer/container-start.sh || exit 0", + "isBackground": true, "presentation": { "echo": true, "reveal": "always", @@ -25,6 +26,7 @@ "dependsOn": ["Fix Permissions, Install Dependencies"], "type": "shell", "command": "[ -f /immich-devcontainer/container-start-backend.sh ] && /immich-devcontainer/container-start-backend.sh || exit 0", + "isBackground": true, "presentation": { "echo": true, "reveal": "always", @@ -45,6 +47,7 @@ "dependsOn": ["Fix Permissions, Install Dependencies"], "type": "shell", "command": "[ -f /immich-devcontainer/container-start-frontend.sh ] && /immich-devcontainer/container-start-frontend.sh || exit 0", + "isBackground": true, "presentation": { "echo": true, "reveal": "always", @@ -67,6 +70,11 @@ "runOn": "folderOpen" }, "problemMatcher": [] + }, + { + "label": "Build Immich CLI", + "type": "shell", + "command": "pnpm --filter cli build:dev" } ] } diff --git a/Makefile b/Makefile index 34fb408c41..fc99170676 100644 --- a/Makefile +++ b/Makefile @@ -91,8 +91,6 @@ format-%: pnpm --filter $(call map-package,$*) run format:fix lint-%: pnpm --filter $(call map-package,$*) run lint:fix -lint-web: - pnpm --filter $(call map-package,$*) run lint:p check-%: pnpm --filter $(call map-package,$*) run check check-web: diff --git a/README.md b/README.md index 459cda481c..b540408475 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,8 @@ Deutsch Nederlands Türkçe - 中文 + 简体中文 + 正體中文 Українська Русский Português Brasileiro @@ -38,26 +39,25 @@ ภาษาไทย

-## Disclaimer -- ⚠️ The project is under **very active** development. -- ⚠️ Expect bugs and breaking changes. -- ⚠️ **Do not use the app as the only way to store your photos and videos.** -- ⚠️ Always follow [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) backup plan for your precious photos and videos! +> [!WARNING] +> ⚠️ Always follow [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) backup plan for your precious photos and videos! +> + > [!NOTE] > You can find the main documentation, including installation guides, at https://immich.app/. ## Links -- [Documentation](https://immich.app/docs) -- [About](https://immich.app/docs/overview/introduction) -- [Installation](https://immich.app/docs/install/requirements) +- [Documentation](https://docs.immich.app/) +- [About](https://docs.immich.app/overview/introduction) +- [Installation](https://docs.immich.app/install/requirements) - [Roadmap](https://immich.app/roadmap) - [Demo](#demo) - [Features](#features) -- [Translations](https://immich.app/docs/developer/translations) -- [Contributing](https://immich.app/docs/overview/support-the-project) +- [Translations](https://docs.immich.app/developer/translations) +- [Contributing](https://docs.immich.app/overview/support-the-project) ## Demo @@ -106,7 +106,7 @@ Access the demo [here](https://demo.immich.app). For the mobile app, you can use ## Translations -Read more about translations [here](https://immich.app/docs/developer/translations). +Read more about translations [here](https://docs.immich.app/developer/translations). Translation status diff --git a/cli/.nvmrc b/cli/.nvmrc index e2228113dd..442c7587a9 100644 --- a/cli/.nvmrc +++ b/cli/.nvmrc @@ -1 +1 @@ -22.19.0 +22.20.0 diff --git a/cli/README.md b/cli/README.md index 8fa2ace483..b9d61fce09 100644 --- a/cli/README.md +++ b/cli/README.md @@ -1,30 +1,38 @@ A command-line interface for interfacing with the self-hosted photo manager [Immich](https://immich.app/). -Please see the [Immich CLI documentation](https://immich.app/docs/features/command-line-interface). +Please see the [Immich CLI documentation](https://docs.immich.app/features/command-line-interface). # For developers Before building the CLI, you must build the immich server and the open-api client. To build the server run the following in the server folder: - $ npm install - $ npm run build + $ pnpm install + $ pnpm run build Then, to build the open-api client run the following in the open-api folder: $ ./bin/generate-open-api.sh -To run the Immich CLI from source, run the following in the cli folder: +## Run from build - $ npm install - $ npm run build - $ ts-node . +Go to the cli folder and build it: -You'll need ts-node, the easiest way to install it is to use npm: + $ pnpm install + $ pnpm run build + $ node dist/index.js - $ npm i -g ts-node +## Run and Debug from source (VSCode) + +With VScode you can run and debug the Immich CLI. Go to the launch.json file, find the Immich CLI config and change this with the command you need to debug + +`"args": ["upload", "--help"],` + +replace that for the command of your choice. + +## Install from build You can also build and install the CLI using - $ npm run build - $ npm install -g . + $ pnpm run build + $ pnpm install -g . **** diff --git a/cli/package.json b/cli/package.json index ce10484d80..f0b5bd5cd1 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@immich/cli", - "version": "2.2.92", + "version": "2.2.97", "description": "Command Line Interface (CLI) for Immich", "type": "module", "exports": "./dist/index.js", @@ -20,7 +20,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.18.1", + "@types/node": "^22.18.8", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", @@ -43,6 +43,7 @@ }, "scripts": { "build": "vite build", + "build:dev": "vite build --sourcemap true", "lint": "eslint \"src/**/*.ts\" --max-warnings 0", "lint:fix": "npm run lint -- --fix", "prepack": "npm run build", @@ -68,6 +69,6 @@ "micromatch": "^4.0.8" }, "volta": { - "node": "22.19.0" + "node": "22.20.0" } } diff --git a/deployment/.env b/deployment/.env new file mode 100644 index 0000000000..f6ce050d29 --- /dev/null +++ b/deployment/.env @@ -0,0 +1,4 @@ +export CLOUDFLARE_ACCOUNT_ID="op://tf/cloudflare/account_id" +export CLOUDFLARE_API_TOKEN="op://tf/cloudflare/api_token" +export TF_STATE_POSTGRES_CONN_STR="op://tf/tf_state/postgres_conn_str" +export TF_VAR_env=$ENVIRONMENT diff --git a/deployment/modules/cloudflare/docs-release/domain.tf b/deployment/modules/cloudflare/docs-release/domain.tf index 0602045f71..3a6f479a74 100644 --- a/deployment/modules/cloudflare/docs-release/domain.tf +++ b/deployment/modules/cloudflare/docs-release/domain.tf @@ -1,11 +1,11 @@ resource "cloudflare_pages_domain" "immich_app_release_domain" { account_id = var.cloudflare_account_id project_name = data.terraform_remote_state.cloudflare_account.outputs.immich_app_archive_pages_project_name - domain = "immich.app" + domain = "docs.immich.app" } resource "cloudflare_record" "immich_app_release_domain" { - name = "immich.app" + name = "docs.immich.app" proxied = true ttl = 1 type = "CNAME" diff --git a/deployment/modules/cloudflare/docs/domain.tf b/deployment/modules/cloudflare/docs/domain.tf index a28fb4c0f8..c5f77de6b4 100644 --- a/deployment/modules/cloudflare/docs/domain.tf +++ b/deployment/modules/cloudflare/docs/domain.tf @@ -1,11 +1,11 @@ resource "cloudflare_pages_domain" "immich_app_branch_domain" { account_id = var.cloudflare_account_id project_name = local.is_release ? data.terraform_remote_state.cloudflare_account.outputs.immich_app_archive_pages_project_name : data.terraform_remote_state.cloudflare_account.outputs.immich_app_preview_pages_project_name - domain = "${var.prefix_name}.${local.deploy_domain_prefix}.immich.app" + domain = "docs.${var.prefix_name}.${local.deploy_domain_prefix}.immich.app" } resource "cloudflare_record" "immich_app_branch_subdomain" { - name = "${var.prefix_name}.${local.deploy_domain_prefix}.immich.app" + name = "docs.${var.prefix_name}.${local.deploy_domain_prefix}.immich.app" proxied = true ttl = 1 type = "CNAME" diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index f97cf0ca0d..53d64dd0c8 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -1,5 +1,5 @@ # -# WARNING: To install Immich, follow our guide: https://immich.app/docs/install/docker-compose +# WARNING: To install Immich, follow our guide: https://docs.immich.app/install/docker-compose # # Make sure to use the docker-compose.yml of the current release: # @@ -8,8 +8,8 @@ # The compose file on main may not be compatible with the latest release. # For development see: -# - https://immich.app/docs/developer/setup -# - https://immich.app/docs/developer/troubleshooting +# - https://docs.immich.app/developer/setup +# - https://docs.immich.app/developer/troubleshooting name: immich-dev @@ -55,8 +55,8 @@ services: IMMICH_BUILD_IMAGE_URL: https://github.com/immich-app/immich/pkgs/container/immich-server IMMICH_THIRD_PARTY_SOURCE_URL: https://github.com/immich-app/immich/ IMMICH_THIRD_PARTY_BUG_FEATURE_URL: https://github.com/immich-app/immich/issues - IMMICH_THIRD_PARTY_DOCUMENTATION_URL: https://immich.app/docs - IMMICH_THIRD_PARTY_SUPPORT_URL: https://immich.app/docs/community-guides + IMMICH_THIRD_PARTY_DOCUMENTATION_URL: https://docs.immich.app + IMMICH_THIRD_PARTY_SUPPORT_URL: https://docs.immich.app/community-guides ulimits: nofile: soft: 1048576 @@ -134,13 +134,13 @@ services: redis: container_name: immich_redis - image: docker.io/valkey/valkey:8-bookworm@sha256:fea8b3e67b15729d4bb70589eb03367bab9ad1ee89c876f54327fc7c6e618571 + image: docker.io/valkey/valkey:8@sha256:81db6d39e1bba3b3ff32bd3a1b19a6d69690f94a3954ec131277b9a26b95b3aa healthcheck: test: redis-cli ping || exit 1 database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:c44be5f2871c59362966d71eab4268170eb6f5653c0e6170184e72b38ffdf107 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23 env_file: - .env environment: diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index c3fb9c7736..78ba0653ac 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -1,5 +1,5 @@ # -# WARNING: To install Immich, follow our guide: https://immich.app/docs/install/docker-compose +# WARNING: To install Immich, follow our guide: https://docs.immich.app/install/docker-compose # # Make sure to use the docker-compose.yml of the current release: # @@ -56,14 +56,14 @@ services: redis: container_name: immich_redis - image: docker.io/valkey/valkey:8-bookworm@sha256:fea8b3e67b15729d4bb70589eb03367bab9ad1ee89c876f54327fc7c6e618571 + image: docker.io/valkey/valkey:8@sha256:81db6d39e1bba3b3ff32bd3a1b19a6d69690f94a3954ec131277b9a26b95b3aa healthcheck: test: redis-cli ping || exit 1 restart: always database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:c44be5f2871c59362966d71eab4268170eb6f5653c0e6170184e72b38ffdf107 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23 env_file: - .env environment: diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 3316c17839..e4e0f964d3 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,5 +1,5 @@ # -# WARNING: To install Immich, follow our guide: https://immich.app/docs/install/docker-compose +# WARNING: To install Immich, follow our guide: https://docs.immich.app/install/docker-compose # # Make sure to use the docker-compose.yml of the current release: # @@ -36,7 +36,7 @@ services: # For hardware acceleration, add one of -[armnn, cuda, rocm, openvino, rknn] to the image tag. # Example tag: ${IMMICH_VERSION:-release}-cuda image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release} - # extends: # uncomment this section for hardware acceleration - see https://immich.app/docs/features/ml-hardware-acceleration + # extends: # uncomment this section for hardware acceleration - see https://docs.immich.app/features/ml-hardware-acceleration # file: hwaccel.ml.yml # service: cpu # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference - use the `-wsl` version for WSL2 where applicable volumes: @@ -49,14 +49,14 @@ services: redis: container_name: immich_redis - image: docker.io/valkey/valkey:8-bookworm@sha256:fea8b3e67b15729d4bb70589eb03367bab9ad1ee89c876f54327fc7c6e618571 + image: docker.io/valkey/valkey:8@sha256:81db6d39e1bba3b3ff32bd3a1b19a6d69690f94a3954ec131277b9a26b95b3aa healthcheck: test: redis-cli ping || exit 1 restart: always database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:c44be5f2871c59362966d71eab4268170eb6f5653c0e6170184e72b38ffdf107 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23 environment: POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_USER: ${DB_USERNAME} diff --git a/docker/example.env b/docker/example.env index 0450dc0805..6d6fd1e3fe 100644 --- a/docker/example.env +++ b/docker/example.env @@ -1,4 +1,4 @@ -# You can find documentation for all the supported env variables at https://immich.app/docs/install/environment-variables +# You can find documentation for all the supported env variables at https://docs.immich.app/install/environment-variables # The location where your uploaded files are stored UPLOAD_LOCATION=./library diff --git a/docker/hwaccel.ml.yml b/docker/hwaccel.ml.yml index 111202d022..c95ac7ee4c 100644 --- a/docker/hwaccel.ml.yml +++ b/docker/hwaccel.ml.yml @@ -4,7 +4,7 @@ # you can inline the config for a backend by copying its contents # into the immich-machine-learning service in the docker-compose.yml file. -# See https://immich.app/docs/features/ml-hardware-acceleration for info on usage. +# See https://docs.immich.app/features/ml-hardware-acceleration for info on usage. services: armnn: diff --git a/docker/hwaccel.transcoding.yml b/docker/hwaccel.transcoding.yml index 60ee7e8fa3..0857faf465 100644 --- a/docker/hwaccel.transcoding.yml +++ b/docker/hwaccel.transcoding.yml @@ -4,7 +4,7 @@ # you can inline the config for a backend by copying its contents # into the immich-microservices service in the docker-compose.yml file. -# See https://immich.app/docs/features/hardware-transcoding for more info on using hardware transcoding. +# See https://docs.immich.app/features/hardware-transcoding for more info on using hardware transcoding. services: cpu: {} diff --git a/docs/.nvmrc b/docs/.nvmrc index e2228113dd..442c7587a9 100644 --- a/docs/.nvmrc +++ b/docs/.nvmrc @@ -1 +1 @@ -22.19.0 +22.20.0 diff --git a/docs/blog/2022/11-10/release-1.36.mdx b/docs/blog/2022/11-10/release-1.36.mdx deleted file mode 100644 index 5f5643196c..0000000000 --- a/docs/blog/2022/11-10/release-1.36.mdx +++ /dev/null @@ -1,110 +0,0 @@ ---- -slug: release-1-36 -title: Release v1.36.0 -authors: [alextran] -tags: [release] -date: 2022-11-10 ---- - -Hello everyone, it is my pleasure to deliver the new release of Immich to you. The team has been working hard to bring you the new features and improvements. This release includes some big features that the community has been asking since the beginning of Immich. We hope you will enjoy it. - -Some notable features are: - -- OAuth integration -- LivePhoto support on iOS -- User config system - - - -## LivePhoto iOS Support 🎉 - -LivePhoto on iOS is now supported in Immich. - -The motion part will now be uploaded and can be played on the mobile app and the web. - -:::caution - -- The server and the app has to be on version **1.36.x** for the application to work correctly. -- Previous uploaded photos will not be updated automatically, you will have to remove and reupload them if you want to keep the LivePhoto functionality. - -::: - - - -## OAuth Integration 🎉 - -I want to borrow this chance to express my gratitude to [@EnricoBilla](https://github.com/EnricoBilla), who has been the trailblazer for this feature since the beginning days of Immich. His PR has sparked ideas, suggestions, and discussion among the team member on how to integrate this feature successfully into the app. Thank you so much for your work and your time. - -OAuth is now integrated into the system. Please follow the guide [here](https://immich.app/docs/usage/oauth) to set up your OAuth integration - -After setting up the correct environment variables in the `.env` file, as shown below - -| Key | Type | Default | Description | -| ------------------- | ------- | -------------------- | ------------------------------------------------------------------------- | -| OAUTH_ENABLED | boolean | false | Enable/disable OAuth2 | -| OAUTH_ISSUER_URL | URL | (required) | Required. Self-discovery URL for client | -| OAUTH_CLIENT_ID | string | (required) | Required. Client ID | -| OAUTH_CLIENT_SECRET | string | (required) | Required. Client Secret | -| OAUTH_SCOPE | string | openid email profile | Full list of scopes to send with the request (space delimited) | -| OAUTH_AUTO_REGISTER | boolean | true | When true, will automatically register a user the first time they sign in | -| OAUTH_BUTTON_TEXT | string | Login with OAuth | Text for the OAuth button on the web | - -```bash title="Authentik Example" -OAUTH_ENABLED=true -OAUTH_ISSUER_URL=http://10.1.15.216:9000/application/o/immich-test/ -OAUTH_CLIENT_ID=30596v8f78a4b6a97d5985c3076b6b4c4d12ddc33 -OAUTH_CLIENT_SECRET=50f1eafdec353b95b1c638db390db4ab67ef035a51212dbec2f56175e2eb272b5d572c099176e6fe116ecf47ffdd544bgdb9e2edc588307ee0339d25eeccd88 -OAUTH_BUTTON_TEXT=Login with Authentik -``` - -The web will have the option to sign in with OAuth. - - - -The mobile app will check if the server has OAuth enabled before displaying the OAuth -sign-in button. - - - -## Support - - - -If you find the project helpful and it helps you in some ways, you can support the project [one time](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) or [monthly](https://github.com/sponsors/alextran1502) from GitHub Sponsor - -It is a great way to let me know that you want me to continue developing and working on this project for years to come. - -## Details - -For more details, please check out the [release note](https://github.com/immich-app/immich/releases/tag/v1.36.0_55-dev) diff --git a/docs/blog/2023/06-24/update.mdx b/docs/blog/2023/06-24/update.mdx deleted file mode 100644 index 464d3e44d9..0000000000 --- a/docs/blog/2023/06-24/update.mdx +++ /dev/null @@ -1,103 +0,0 @@ ---- -title: Immich Update - June 2023 -authors: [alextran] -tags: [update] ---- - -Hello everybody, Alex here! - -I am back with another update on Immich. It has been only a month since my last update (May 18th, 2023), but it seems forever. I think the rapid releases of Immich and the amount of work make the perspective of time change in Immich’s world. We have some exciting updates that I think you will like. - -Before going into detail, on behalf of the core team, I would like to thank all of you for loving Immich and contributing to the project. Thank you for helping me make Immich an enjoyable alternative solution to Google Photos so that you have complete control of your data and privacy. I know we are still young and have a lot of work to do, but I am confident we will get there with help from the community. I appreciate all of you from the bottom of my heart! - - - -And now, to the exciting part, what is new in Immich’s world? - -- Initial support for existing gallery. -- Memory feature. -- Support XMP sidecar. -- Support more raw formats. -- Justified layout for web timeline and blurred thumbnail hash. -- Mechanism to host machine learning on a completely different machine. - -## Support for existing gallery - -I know this is the most controversial feature when it comes to Immich’s way of ingesting photos and videos. For many users, having to upload photos and videos to Immich is simply not working. We listen, discuss, and digest this feature internally more than you imagine because it is not a simple feature to tackle while keeping the performance and the user experience at the top level, which is Immich’s primary goal. - -Thankfully, we have many great contributors and developers that want to make this come true. So we came up with an initial implementation of this feature in the form of a supporting read-only gallery. - -To be concise, Immich can now read in the gallery files, register the path into the database, and then generate necessary files and put them through Immich’s machine learning pipeline so you can use all the goodness of Immich without the need to upload them. Since this is the initial implementation, some actions/behavior are not yet supported, and we aim to build toward them in future releases, namely: - -- Assets are not automatically synced and must instead be manually synced with the CLI tool. -- Only new files that are added to the gallery will be detected. -- Deleted and moved files will not be detected. - -## Memory feature - -This is considered a fun feature that the team and I wanted to build for so long, but we had to put it off because of the refactoring of the code base. The code base is now in a good enough form to circle back and add more exciting features. - -This memory feature is very much similar to GPhotos' implementation of “x years since…”. We are aiming to add more categories of memories in the future, such as “Spotlight of the day” or “Day of the Week highlights” - - - -This feature is now available on the web and will be ported to the mobile app in the near future. - -## Support XMP Sidecar - -Immich can now import/upload XMP sidecars from the CLI and use the information as the metadata of assets. - -## Support more raw formats. - -With the recent updates on the dependencies of Immich, we are now extending and hardening support for multiple raw formats. So users with DSLR or mirrorless cameras can now upload their original files to Immich and have them displayed in high-quality thumbnails on the web and mobile view. - -## Justified layout for web timeline and blurred thumbnail hash - -This is an aesthetic improvement in user experience when browsing the timeline. Photos and videos are now displayed correctly with perspective orientation, making the browsing experience more pleasurable. - -To further improve the browsing experience, we now added a blur hash to the thumbnail, so the transition is more natural with a dreamy fade in effect, similar to how our brain goes from faded to vivid memory - - - -## Hosting machine learning container on a different machine - -With more capabilities Immich is building toward, machine learning will get more powerful and therefore require more resources to run effectively. However, we understand that users might not have the best server resources where they host the Immich instance. Therefore, we changed how machine learning interacts and receives the photos and videos to run through its inference pipeline. - -The machine learning container is now a headless system that can run on any machine. As long as your Immich instance can communicate with the system running the machine learning container, it can send the files and receive the required information to make Immich powerful in terms of searching and intelligence. This helps you to utilize a more powerful machine in your home/infrastructure to perform the CPU-intensive tasks while letting Immich only handle the I/O operations for a pleasant and smooth experience. - ---- - -So, those are the highlights for the team and the community after a busy month. There are a lot more changes and improvements. I encourage you to read some release notes, starting from version [v1.57.0](https://github.com/immich-app/immich/releases/tag/v1.57.0) to now. - -Thank you, and I am asking for your support for the project. I hope to be a full-time maintainer of Immich one day to dedicate myself to the project as my life works for the community and my family. You can find the support channels below: - -- Monthly donation via [GitHub Sponsors](https://github.com/sponsors/alextran1502) -- One-time donation via [GitHub Sponsors](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) -- [Liberapay](https://liberapay.com/alex.tran1502/) -- [buymeacoffee](https://www.buymeacoffee.com/altran1502) -- Bitcoin: 3QVAb9dCHutquVejeNXitPqZX26Yg5kxb7 -- Give a project a star - the contributors love gazing at the stars and seeing their creations shining in the sky. - -Join our friendly [Discord](https://discord.immich.app) to talk and discuss Immich, tech, or anything - -Cheer! - -Until next time! - -Alex diff --git a/docs/blog/2023/07-29/images/web-shortcuts-panel.png b/docs/blog/2023/07-29/images/web-shortcuts-panel.png deleted file mode 100644 index 5a16c9f289..0000000000 Binary files a/docs/blog/2023/07-29/images/web-shortcuts-panel.png and /dev/null differ diff --git a/docs/blog/2023/07-29/update.mdx b/docs/blog/2023/07-29/update.mdx deleted file mode 100644 index 6d50ddfdc0..0000000000 --- a/docs/blog/2023/07-29/update.mdx +++ /dev/null @@ -1,151 +0,0 @@ ---- -title: Immich Update - July 2023 -authors: [alextran] -tags: [update, v1.64.0-v1.71.0] ---- - -Hello, Immich fans, another month, another milestone. We hope you are staying cool and safe in this scorching hot summer across the globe. - -Immich recently got some good recognition when getting to the front page of HackerNews, which helped to let more people know about the project's existence. The project will help more and more people find a solution to control the privacy of their most precious moments. And with the gain in popularity and recognition, we have gotten new users and more questions from the community than ever. - -I want to express my gratitude to all the contributors and the community who have been tremendously helpful to new users' questions and provided technical support. - -Below are the highlights of new features we added to the application over the past month, along with countless bug fixes and improvements across the board, from developer experience to resource optimization and UI/UX improvement. I hope you find these topics as exciting as I am. - -## Highlights - -- Memories feature. -- Facial recognition improvements. -- Improvements on multi selection behavior on the web. -- Shortcuts for common actions on the web. -- Support viewer for 360-panorama photos. - - - ---- - -### Memories feature - -We've added the memory feature on the mobile app, so you can reminisce about your past memories. - - - -### Facial recognition improvements - -Over the past few releases, we have added many UI improvements to the facial recognition feature to help you manage the recognized people better. Some of the highlights: - -#### Choose a new feature photo for a person. - - - -#### Hide and show faces. - -You can now select irrelevant faces to hide them. The hidden faces won’t be displayed in search results and the people section in the info panel. - -#### Merge faces. - -This is useful when you have multiple faces of the same person in your photos, and you want to merge them into one. - - - -We also added a nifty mechanism that when naming a face, similar names will prompt you a merge face option for the convenience. - - - -### Improvements on multi selection behavior on the web - -We have added a new multi selection behavior on the web to help you select multiple items easier. You can now select a range of photos and videos by holding the `Shift` key. - - - -### Shortcuts for common actions on the web. - -Some of us only navigate the world and the web with a keyboard (looking at you, Vim and Emacs users). So it would take away the sacred weapon of choice to require many clicks to perform repetitive actions. So we added quick shortcuts for the following action on the web. - -Dot Env Example - -### Support viewer for 360-panorama photos. - -Photos with the EXIF property of `ProjectionType` will now have a special viewer on the web to view all the angles of the panorama. - -The thumbnail of the 360 degrees panoramas will have a special icon on the top right of the thumbnail - -Dot Env Example - -Panorama in the detail view - -Dot Env Example - ---- - -Thank you, and I am asking for your support for the project. I hope to be a full-time maintainer of Immich one day to dedicate myself to the project as my life's work for the community and my family. You can find the support channels below: - -- Monthly donation via [GitHub Sponsors](https://github.com/sponsors/alextran1502) -- One-time donation via [GitHub Sponsors](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) -- [Liberapay](https://liberapay.com/alex.tran1502/) -- [buymeacoffee](https://www.buymeacoffee.com/altran1502) -- Bitcoin: 3QVAb9dCHutquVejeNXitPqZX26Yg5kxb7 -- Give a project a star - the contributors love gazing at the stars and seeing their creations shining in the sky. - -Join our friendly [Discord](https://discord.immich.app) to talk and discuss Immich, tech, or anything - -Cheer! - -Until next time! - -Alex diff --git a/docs/blog/2023/2023-recap.mdx b/docs/blog/2023/2023-recap.mdx deleted file mode 100644 index e9d93a52be..0000000000 --- a/docs/blog/2023/2023-recap.mdx +++ /dev/null @@ -1,71 +0,0 @@ ---- -title: Immich Recap 2023 -authors: [alextran] -tags: [update, recap-2023] -date: 2023-12-30T00:00 ---- - -Hi everyone, - -Alex from Immich here. - -We are entering the last few weeks of 2023, and it has been quite a year for Immich. The project has grown so much in terms of users, developers, features, maturity, and the community around it. When I started working on Immich, it was simply a challenge for myself and an opportunity to learn new technologies, crafting something fun and useful for my wife during my free time to satisfy my urge to build and create things. I never thought it would become so popular and help so many people. At the end of the day, all we have is memory. I am proud that the team and I have created something to make storing and viewing those precious memories easier without restrictions and without sacrificing our privacy. As the year closes, here’s a recap of everything the project accomplished in 2023. - -# Milestones - -- Public shared links -- Favorites page -- Immich turned 1 -- Material Design 3 on the mobile app -- Auto-link LivePhotos server-side -- iOS background backup -- Explore page -- CLIP search -- Search by metadata -- Responsive web app -- Archive page -- Asset descriptions -- 10,000 stars on GitHub -- Manage auth devices -- Map view -- Facial recognition, clustering, searching, renaming, and person management -- Partner sharing and unifying timeline between partners' users -- Custom storage label -- XMP sidecar reading -- RAW file formats -- Justified layout on the web -- Memories -- Multi-select via SHIFT -- Android Motion Photos -- 360° Photos -- Album description -- Album performance improvements (time buckets) -- Video hardware transcoding -- Slideshow mode on the web -- Configuration file -- External libraries -- Trash page -- Custom theme -- Asset Stacking -- 20,000 stars on GitHub -- Shared album activity and comments -- CLI v2 -- Down to 5 containers (from 8) - -# Fun Statistics - -- We have gone from the release version `1.41.0` to `1.90.0` at the time of writing. On average, we see a release every 7 days. -- According to GitHub's metrics, the `immich-server` container image has been pulled almost _4 million_ times. -- According to mobile app store metrics, we have 22,000 installations on Android and 6700 installation units on iOS (opt-in only). -- Immich is making around $1200/month on average from donations. (Thank you all so much!) -- We were guests on two podcasts: - - [Self-hosted](https://selfhosted.show/110) - - [The Vergecast](https://www.theverge.com/23938533/self-hosting-local-first-software-vergecast) -- There are over 4,500 members on the Discord server. -- We have over 22,000 stars on the main GitHub repository, gaining 15,000 stars since January 2023. - -Diving into the next year, the team will continue to build on the foundation we have laid out over the past year, implementing more advanced features for searching, organizing, and sharing between users. Bugs will continue to be squashed and conquered. “Shit Alex wrote'' code will continue to be replaced by beautiful, clean code from Jason, Zack, Boet, Daniel, Osorin, Mert, Fynn, Marty, Martin, and Jonathan. The team has my eternal gratitude for creating a welcoming environment for new contributors, helping, teaching, and learning from each other. I’ve realized that hardly a day has gone by where the team hasn’t been in communication about Immich related topics over the past year. - -My long-term goal is to help hone Immich into a diamond in the FOSS space, where the UI, UX, development experiences, documentation, and quality are at a high standard while remaining free for everybody to use. - -I hope you enjoy Immich and have a happy and peaceful holiday. diff --git a/docs/blog/2024/immich-core-team-goes-fulltime.mdx b/docs/blog/2024/immich-core-team-goes-fulltime.mdx deleted file mode 100644 index 0cba2b467c..0000000000 --- a/docs/blog/2024/immich-core-team-goes-fulltime.mdx +++ /dev/null @@ -1,75 +0,0 @@ ---- -title: The Immich core team goes full-time -authors: [alextran] -tags: [update, announcement, FUTO] -date: 2024-05-01T00:00 ---- - -**Immich is joining [FUTO](https://futo.org/)!** - -Since the beginning of this adventure, my goal has always been to create a better world for my children. Memories are priceless, and privacy should not be a luxury. However, building quality open source has its challenges. Over the past two years, it has taken significant dedication, time, and effort. - -Recently, a company in Austin, Texas, called FUTO contacted the team. FUTO strives to develop quality and sustainable open software. They build software alternatives that focus on giving control to users. From their mission statement: - -“Computers should belong to you, the people. We develop and fund technology to give them back.” - -FUTO loved Immich and wanted to see if we’d consider working with them to take the project to the next level. In short, FUTO offered to: - -- Pay the core team to work on Immich full-time -- Let us keep full autonomy about the project’s direction and leadership -- Continue to license Immich under AGPL -- Keep Immich’s development direction with no paywalled features -- Keep Immich “built for the people” (no ads, data mining/selling, or alternative motives) -- Provide us with financial, technical, legal, and administrative support - -After careful deliberation, the team decided that FUTO’s vision closely aligns with our own: to build a better future by providing a polished, performant, and privacy-preserving open-source software solution for photo and video management delivered in a sustainable way. - -Immich’s future has never looked brighter, and we look forward to realizing our vision for Immich as part of FUTO. - -If you have more questions, we’ll host a Q&A live stream on May 9th at 3PM UTC (10AM CST). [You can ask questions here](https://www.live-ask.com/event/01HWP2SB99A1K8EXFBDKZ5Z9CF), and the stream will be live [here on our YouTube channel](https://youtube.com/live/cwz2iZwYpgg). - -Cheers, - -The Immich Team - ---- - -## FAQs - -### What is FUTO? - -[https://futo.org/what-is-futo/](https://futo.org/what-is-futo/) - -### Will the license change? - -No. Immich will continue to be licensed under AGPL without a CLA. - -### Will Immich continue to be free? - -Yes. The Immich source code will remain freely available under the AGPL license. - -### Is Immich getting VC funding? - -No. Venture capital implies investment in a business, often with the expectation of a future payout (exit plan). Immich is neither a business that can be acquired nor comes with a money-making exit plan. - -### I am currently supporting Immich through GitHub sponsors. What will happen to my donation? - -Effective immediately, all donations to the Immich organization will be canceled. In the future, we will offer an optional, modest payment option instead. Thank you to everyone who donated to help us get this far! - -### How is funding sustainable? - -Immich and FUTO believe a sustainable future requires a model that does not rely on users-as-a-product. To this end, FUTO advocates that users pay for good, open software. In keeping with this model, we will adopt a purchase price. This means we no longer accept donations, but — _without limiting features for those who do not pay_ — we will soon allow you to purchase Immich through a modest payment. We encourage you to pay for the high-quality software you use to foster a healthy software culture where developers build great applications without hidden motives for their users. - -### When does this change take effect? - -This change takes effect immediately. - -### What will change? - -The following things will change as Immich joins FUTO: - -- The brand, logo, and other Immich trademarks will be transferred to FUTO. -- We will stop all donations to the project. -- The core team can now dedicate our full attention to Immich -- Before the end of the year, we plan to have a roadmap for what it will take to get Immich to a stable release. -- Bugs will be squashed, and features will be delivered faster. diff --git a/docs/blog/2024/immich-licensing.mdx b/docs/blog/2024/immich-licensing.mdx deleted file mode 100644 index 773abcb666..0000000000 --- a/docs/blog/2024/immich-licensing.mdx +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: Licensing announcement - Purchase a license to support Immich -authors: [alextran] -tags: [update, announcement, FUTO] -date: 2024-07-18T00:00 ---- - -Hello everybody, - -Firstly, on behalf of the Immich team, I'd like to thank everybody for your continuous support of Immich since the very first day! Your contributions, encouragement, and community engagement have helped bring Immich to its current state. The team and I are forever grateful for that. - -Since our [last announcement of the core team joining FUTO to work on Immich full-time](https://immich.app/blog/2024/immich-core-team-goes-fulltime), one of the goals of our new position is to foster a healthy relationship between the developers and the users. We believe that this enables us to create great software, establish transparent policies and build trust. - -We want to build a great software application that brings value to you and your loved ones' lives. We are not using you as a product, i.e., selling or tracking your data. We are not putting annoying ads into our software. We respect your privacy. We want to be compensated for the hard work we put in to build Immich for you. - -With those notes, we have enabled a way for you to financially support the continued development of Immich, ensuring the software can move forward and will be maintained, by offering a lifetime license of the software. We think if you like and use software, you should pay for it, but _we're never going to force anyone to pay or try to limit Immich for those who don't._ - -There are two types of license that you can choose to purchase: **Server License** and **Individual License**. - -### Server License - -This is a lifetime license costing **$99.99**. The license is applied to the whole server. You and all users that use your server are licensed. - -### Individual License - -This is a lifetime license costing **$24.99**. The license is applied to a single user, and can be used on any server they choose to connect to. - -license-social-gh - -You can purchase the license on [our page - https://buy.immich.app](https://buy.immich.app). - -Starting with release `v1.109.0` you can purchase and enter your purchased license key directly in the app. - -license-page-gh - -## Thank you - -Thank you again for your support, this will help create a strong foundation and stability for the Immich team to continue developing and maintaining the project that you love to use. - -

- -

- -
-
- -Cheers! 🎉 - -Immich team - -# FAQ - -### 1. Where can I purchase a license? - -There are several places where you can purchase the license from - -- [https://buy.immich.app](https://buy.immich.app) -- [https://pay.futo.org](https://pay.futo.org/) -- or directly from the app. - -### 2. Do I need both _Individual License_ and _Server License_? - -No, - -If you are the admin and the sole user, or your instance has less than a total of 4 users, you can buy the **Individual License** for each user. - -If your instance has more than 4 users, it is more cost-effective to buy the **Server License**, which will license all the users on your instance. - -### 3. What do I do if I don't pay? - -You can continue using Immich without any restriction. - -### 4. Will there be any paywalled features? - -No, there will never be any paywalled features. - -### 5. Where can I get support regarding payment issues? - -You can email us with your `orderId` and your email address `billing@futo.org` or on our Discord server. diff --git a/docs/blog/2024/update-july-2024.mdx b/docs/blog/2024/update-july-2024.mdx deleted file mode 100644 index cbe99177e7..0000000000 --- a/docs/blog/2024/update-july-2024.mdx +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: Immich Update - July 2024 -authors: [alextran] -date: 2024-07-01T00:00 -tags: [update, v1.106.0] ---- - -Hello everybody! Alex from Immich here and I am back with another development progress update for the project. - -Summer has returned once again, and the night sky is filled with stars, thank you for **38_000 shining stars** you have sent to our [GitHub repo](https://github.com/immich-app/immich)! Since the last announcement several core contributors have started full time. Everything is going great with development, PRs get merged with _brrrrrrr_ rate, conversation exchange between team members is on a new high, we met and are working with the great engineers at FUTO. The spirit is high and we have a lot of things brewing that we think you will like. - -Let's go over some of the updates we had since the last post. - -### Container consolidation - -Reduced the number of total containers from 5 to 4 by making the microservices thread get spawned directly in the server container. Woohoo, remember when Immich had 7 containers? - -### Email notifications - -![smtp](https://github.com/immich-app/immich/assets/27055614/949cba85-d3f1-4cd3-b246-a6f5fb5d3ae8) - -We added email notifications to the app with SMTP settings that you can configure for the following events - -- A new account is created for you. -- You are added to a shared album. -- New media is added to an album. - -### Versioned docs - -You can now jump back into the past or take a peek at the unreleased version of the documentation by selecting the version on the website. - -![version-doc](https://github.com/immich-app/immich/assets/27055614/6d22898a-5093-41ad-b416-4573d7ce6e03) - -### Similarity deduplication - -With more machine learning and CLIP magic, we now have similarity deduplication built into the application where it will search for closely similar images and let you decide what to do with them; i.e keep or trash. - -![similarity-deduplication](https://github.com/immich-app/immich/assets/27055614/3cac8478-fbf7-47ea-acb6-0146901dc67e) - -### Permanent URL for asset on the web - -The detail view for an asset now has a permanent URL so you can easily share them with your loved ones. - -### Web app translations - -We now have a public Weblate project which the community can use to translate the webapp to their native languages. We are planning to port the mobile app translation to this platform as well. If you would like to contribute, you can take a look [here](https://hosted.weblate.org/projects/immich/immich/). We're already close to 50% translations -- we really appreciate everyone contributing to that! - -![web-translation](https://github.com/immich-app/immich/assets/27055614/363df2ed-656c-4584-bd82-0708a693c5bc) - -### Read-only/Editor mode on shared album - -As the owner of the album, you can choose if the shared user can edit the album or to only view the content of the album without any modification. - -![read-only-album](https://github.com/immich-app/immich/assets/27055614/c6f66375-b869-495a-9a86-3e87b316d109) - -### Better video thumbnails - -Immich now tries to find a descriptive video thumbnail instead of simply using the first frame. No more black images for thumbnails! - -### Public Roadmap - -We now have a [public roadmap](https://immich.app/roadmap), giving you a high-level overview of things the team is working on. The first goal of this roadmap is to bring Immich to a stable release, which is expected sometime later this year. Some of the highlights include - -- Auto stacking - Auto stacking of burst photos -- Basic editor - Basic photo editing capabilities -- Workflows - Automate tasks with workflows -- Fine grained access controls - Granular access controls for users and api keys -- Better background backups - Rework background backups to be more reliable -- Private/locked photos - Private assets with extra protections - -Beyond the items in the roadmap, we have _many many_ more ideas for Immich. The team and I hope that you are enjoying the application, find it helpful in your life and we have nothing but the intention of building out great software for you all! - -Have an amazing Summer or Winter for those in the southern hemisphere! :D - -Until next time, - -Cheers! -Alex diff --git a/docs/blog/authors.yml b/docs/blog/authors.yml deleted file mode 100644 index f331efa927..0000000000 --- a/docs/blog/authors.yml +++ /dev/null @@ -1,5 +0,0 @@ -alextran: - name: Alex Tran - title: Maintainer of Immich - url: https://github.com/alextran1502 - image_url: https://github.com/alextran1502.png diff --git a/docs/docs/FAQ.mdx b/docs/docs/FAQ.mdx index b2f2e85775..e3df672d35 100644 --- a/docs/docs/FAQ.mdx +++ b/docs/docs/FAQ.mdx @@ -22,7 +22,7 @@ For organizations seeking to resell Immich, we have established the following gu - Do not misrepresent your reseller site or services as being officially affiliated with or endorsed by Immich or our development team. -- For small resellers who wish to contribute financially to Immich's development, we recommend directing your customers to purchase licenses directy from us rather than attempting to broker revenue-sharing arrangements. We ask that you refrain from misrepresenting reseller activities as directly supporting our development work. +- For small resellers who wish to contribute financially to Immich's development, we recommend directing your customers to purchase licenses directly from us rather than attempting to broker revenue-sharing arrangements. We ask that you refrain from misrepresenting reseller activities as directly supporting our development work. 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 @@ -30,11 +30,11 @@ When in doubt or if you have an edge case scenario, we encourage you to contact ### How can I reset the admin password? -The admin password can be reset by running the [reset-admin-password](/docs/administration/server-commands.md) command on the immich-server. +The admin password can be reset by running the [reset-admin-password](/administration/server-commands.md) command on the immich-server. ### How can I see a list of all users in Immich? -You can see the list of all users by running [list-users](/docs/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. --- @@ -106,20 +106,20 @@ However, Immich will delete original files that have been trashed when the trash When Storage Template is off (default) Immich saves the file names in a random string (also known as random UUIDs) to prevent duplicate file names. To retrieve the original file names, you must enable the Storage Template and then run the STORAGE TEMPLATE MIGRATION job. -It is recommended to read about [Storage Template](https://immich.app/docs/administration/storage-template) before activation. +It is recommended to read about [Storage Template](/administration/storage-template) before activation. ### Can I add my existing photo library? -Yes, with an [External Library](/docs/features/libraries.md). +Yes, with an [External Library](/features/libraries.md). -### What happens to existing files after I choose a new [Storage Template](/docs/administration/storage-template.mdx)? +### What happens to existing files after I choose a new [Storage Template](/administration/storage-template.mdx)? -Template changes will only apply to _new_ assets. To retroactively apply the template to previously uploaded assets, run the Storage Migration Job, available on the [Jobs](/docs/administration/jobs-workers/#jobs) page. +Template changes will only apply to _new_ assets. To retroactively apply the template to previously uploaded assets, run the Storage Migration Job, available on the [Jobs](/administration/jobs-workers/#jobs) page. ### Why are only photos and not videos being uploaded to Immich? This often happens when using a reverse proxy in front of Immich. -Make sure to [set your reverse proxy](/docs/administration/reverse-proxy/) to allow large requests. +Make sure to [set your reverse proxy](/administration/reverse-proxy/) to allow large requests. Also, check the disk space of your reverse proxy. In some cases, proxies cache requests to disk before passing them on, and if disk space runs out, the request fails. @@ -139,7 +139,7 @@ You can _archive_ them. ### How can I backup data from Immich? -See [Backup and Restore](/docs/administration/backup-and-restore.md). +See [Backup and Restore](/administration/backup-and-restore.md). ### Does Immich support reading existing face tag metadata? @@ -225,7 +225,7 @@ volumes: ### Can I keep my existing album structure while importing assets into Immich? -Yes, by using the [Immich CLI](/docs/features/command-line-interface) along with the `--album` flag. +Yes, by using the [Immich CLI](/features/command-line-interface) along with the `--album` flag. ### Is there a way to reorder photos within an album? @@ -266,7 +266,7 @@ Immich uses CLIP models. An ML model converts each image to an "embedding", whic ### How does facial recognition work? -See [How Facial Recognition Works](/docs/features/facial-recognition#how-facial-recognition-works) for details. +See [How Facial Recognition Works](/features/facial-recognition#how-facial-recognition-works) for details. ### How can I disable machine learning? @@ -288,7 +288,7 @@ No, this is not supported. Only models listed in the [Hugging Face][huggingface] ### I want to be able to search in other languages besides English. How can I do that? -You can change to a multilingual CLIP model. See [here](/docs/features/searching#clip-models) for instructions. +You can change to a multilingual CLIP model. See [here](/features/searching#clip-models) for instructions. ### Does Immich support Facial Recognition for videos? @@ -299,7 +299,7 @@ Scanning the entire video for faces may be implemented in the future. No. :::tip -You can use [Smart Search](/docs/features/searching.md) for this to some extent. For example, if you have a Golden Retriever and a Chihuahua, type these words in the smart search and watch the results. +You can use [Smart Search](/features/searching.md) for this to some extent. For example, if you have a Golden Retriever and a Chihuahua, type these words in the smart search and watch the results. ::: ### I'm getting a lot of "faces" that aren't faces, what can I do? @@ -329,7 +329,7 @@ ls clip/ facial-recognition/ ### Why is Immich slow on low-memory systems like the Raspberry Pi? -Immich optionally uses transcoding and machine learning for several features. However, it can be too heavy to run on a Raspberry Pi. You can [mitigate](/docs/FAQ#can-i-lower-cpu-and-ram-usage) this or host Immich's machine-learning container on a [more powerful system](/docs/guides/remote-machine-learning), or [disable](/docs/FAQ#how-can-i-disable-machine-learning) machine learning entirely. +Immich optionally uses transcoding and machine learning for several features. However, it can be too heavy to run on a Raspberry Pi. You can [mitigate](/FAQ#can-i-lower-cpu-and-ram-usage) this or host Immich's machine-learning container on a [more powerful system](/guides/remote-machine-learning), or [disable](/FAQ#how-can-i-disable-machine-learning) machine learning entirely. ### Can I lower CPU and RAM usage? @@ -339,9 +339,9 @@ The initial backup is the most intensive due to the number of jobs running. The - Under Settings > Transcoding Settings > Threads, set the number of threads to a low number like 1 or 2. - Under Settings > Machine Learning Settings > Facial Recognition > Model Name, you can change the facial recognition model to `buffalo_s` instead of `buffalo_l`. The former is a smaller and faster model, albeit not as good. - For facial recognition on new images to work properly, You must re-run the Face Detection job for all images after this. -- At the container level, you can [set resource constraints](/docs/FAQ#can-i-limit-cpu-and-ram-usage) to lower usage further. +- At the container level, you can [set resource constraints](/FAQ#can-i-limit-cpu-and-ram-usage) to lower usage further. - It's recommended to only apply these constraints _after_ taking some of the measures here for best performance. -- If these changes are not enough, see [above](/docs/FAQ#how-can-i-disable-machine-learning) for instructions on how to disable machine learning. +- If these changes are not enough, see [above](/FAQ#how-can-i-disable-machine-learning) for instructions on how to disable machine learning. ### Can I limit CPU and RAM usage? @@ -383,7 +383,7 @@ Do not exaggerate with the job concurrency because you're probably thoroughly ov ### My server shows Server Status Offline | Version Unknown. What can I do? -You need to [enable WebSockets](/docs/administration/reverse-proxy/) on your reverse proxy. +You need to [enable WebSockets](/administration/reverse-proxy/) on your reverse proxy. --- @@ -391,7 +391,7 @@ You need to [enable WebSockets](/docs/administration/reverse-proxy/) on your rev ### How can I see Immich logs? -Immich components are typically deployed using docker. To see logs for deployed docker containers, you can use the [Docker CLI](https://docs.docker.com/engine/reference/commandline/cli/), specifically the `docker logs` command. For examples, see [Docker Help](/docs/guides/docker-help.md). +Immich components are typically deployed using docker. To see logs for deployed docker containers, you can use the [Docker CLI](https://docs.docker.com/engine/reference/commandline/cli/), specifically the `docker logs` command. For examples, see [Docker Help](/guides/docker-help.md). ### How can I reduce the log verbosity of Redis? @@ -435,7 +435,7 @@ cap_drop: Data for Immich comes in two forms: 1. **Metadata** stored in a Postgres database, stored in the `DB_DATA_LOCATION` folder (previously `pg_data` Docker volume). -2. **Files** (originals, thumbs, profile, etc.), stored in the `UPLOAD_LOCATION` folder, more [info](/docs/administration/backup-and-restore#asset-types-and-storage-locations). +2. **Files** (originals, thumbs, profile, etc.), stored in the `UPLOAD_LOCATION` folder, more [info](/administration/backup-and-restore#asset-types-and-storage-locations). :::warning This will destroy your database and reset your instance, meaning that you start from scratch. @@ -473,7 +473,7 @@ If it mentions SIGILL (note the lack of a K) or error code 132, it most likely m ### Why am I getting database ownership errors? If you get database errors such as `FATAL: data directory "/var/lib/postgresql/data" has wrong ownership` upon database startup, this is likely due to an issue with your filesystem. -NTFS and ex/FAT/32 filesystems are not supported. See [here](/docs/install/requirements#special-requirements-for-windows-users) for more details. +NTFS and ex/FAT/32 filesystems are not supported. See [here](/install/requirements#special-requirements-for-windows-users) for more details. ### How can I verify the integrity of my database? diff --git a/docs/docs/administration/backup-and-restore.md b/docs/docs/administration/backup-and-restore.md index deeefa5635..fbb4cd8c23 100644 --- a/docs/docs/administration/backup-and-restore.md +++ b/docs/docs/administration/backup-and-restore.md @@ -3,7 +3,7 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -A [3-2-1 backup strategy](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) is recommended to protect your data. You should keep copies of your uploaded photos/videos as well as the Immich database for a comprehensive backup solution. This page provides an overview on how to backup the database and the location of user-uploaded pictures and videos. A template bash script that can be run as a cron job is provided [here](/docs/guides/template-backup-script.md) +A [3-2-1 backup strategy](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) is recommended to protect your data. You should keep copies of your uploaded photos/videos as well as the Immich database for a comprehensive backup solution. This page provides an overview on how to backup the database and the location of user-uploaded pictures and videos. A template bash script that can be run as a cron job is provided [here](/guides/template-backup-script.md) :::danger The instructions on this page show you how to prepare your Immich instance to be backed up, and which files to take a backup of. You still need to take care of using an actual backup tool to make a backup yourself. @@ -57,6 +57,7 @@ Then please follow the steps in the following section for restoring the database ```bash title='Backup' +# Replace with the database username - usually postgres unless you have changed it. docker exec -t immich_postgres pg_dumpall --clean --if-exists --username= | gzip > "/path/to/backup/dump.sql.gz" ``` @@ -69,16 +70,18 @@ docker compose create # Create Docker containers for Immich apps witho docker start immich_postgres # Start Postgres server sleep 10 # Wait for Postgres server to start up # Check the database user if you deviated from the default +# Replace with the database username - usually postgres unless you have changed it. gunzip --stdout "/path/to/backup/dump.sql.gz" \ | sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" \ | docker exec -i immich_postgres psql --dbname=postgres --username= # Restore Backup docker compose up -d # Start remainder of Immich apps ``` - + ```powershell title='Backup' +# Replace with the database username - usually postgres unless you have changed it. [System.IO.File]::WriteAllLines("C:\absolute\path\to\backup\dump.sql", (docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=)) ``` @@ -92,13 +95,15 @@ docker compose create # Create Docker containers for docker start immich_postgres # Start Postgres server sleep 10 # Wait for Postgres server to start up docker exec -it immich_postgres bash # Enter the Docker shell and run the following command -# Check the database user if you deviated from the default. If your backup ends in `.gz`, replace `cat` with `gunzip --stdout` +# If your backup ends in `.gz`, replace `cat` with `gunzip --stdout` +# Replace with the database username - usually postgres unless you have changed it. + cat "/dump.sql" | sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" | psql --dbname=postgres --username= exit # Exit the Docker shell docker compose up -d # Start remainder of Immich apps ``` - + Note that for the database restore to proceed properly, it requires a completely fresh install (i.e. the Immich server has never run since creating the Docker containers). If the Immich app has run, Postgres conflicts may be encountered upon database restoration (relation already exists, violated foreign key constraints, multiple primary keys, etc.), in which case you need to delete the `DB_DATA_LOCATION` folder to reset the database. @@ -160,7 +165,7 @@ for more info read the [release notes](https://github.com/immich-app/immich/rele :::danger A backup of this folder does not constitute a backup of your database! - Follow the instructions listed [here](/docs/administration/backup-and-restore#database) to learn how to perform a proper backup. + Follow the instructions listed [here](/administration/backup-and-restore#database) to learn how to perform a proper backup. ::: @@ -205,7 +210,7 @@ When you turn off the storage template engine, it will leave the assets in `UPLO :::danger A backup of this folder does not constitute a backup of your database! - Follow the instructions listed [here](/docs/administration/backup-and-restore#database) to learn how to perform a proper backup. + Follow the instructions listed [here](/administration/backup-and-restore#database) to learn how to perform a proper backup. ::: diff --git a/docs/docs/administration/email-notification.mdx b/docs/docs/administration/email-notification.mdx index 2ad4fba2be..0da132161f 100644 --- a/docs/docs/administration/email-notification.mdx +++ b/docs/docs/administration/email-notification.mdx @@ -12,7 +12,7 @@ You can access the settings panel from the web at `Administration -> Settings -> Under Email, enter the required details to connect with an SMTP server. -You can use [this guide](/docs/guides/smtp-gmail) to use Gmail's SMTP server. +You can use [this guide](/guides/smtp-gmail) to use Gmail's SMTP server. ## User's notifications settings diff --git a/docs/docs/administration/jobs-workers.md b/docs/docs/administration/jobs-workers.md index 4634151b9a..8ed3ba2694 100644 --- a/docs/docs/administration/jobs-workers.md +++ b/docs/docs/administration/jobs-workers.md @@ -11,7 +11,7 @@ The `immich-server` container contains multiple workers: ## Split workers -If you prefer to throttle or distribute the workers, you can do this using the [environment variables](/docs/install/environment-variables) to specify which container should pick up which tasks. +If you prefer to throttle or distribute the workers, you can do this using the [environment variables](/install/environment-variables) to specify which container should pick up which tasks. For example, for a simple setup with one container for the Web/API and one for all other microservices, you can do the following: @@ -53,5 +53,21 @@ Additionally, some jobs (such as memories generation) run on a schedule, which i :::note -Some jobs ([External Libraries](/docs/features/libraries) scanning, Database Dump) are configured in their own sections in System Settings. +Some jobs ([External Libraries](/features/libraries) scanning, Database Dump) are configured in their own sections in System Settings. ::: + +## Job processing order + +The below diagram shows the job run order for newly uploaded files + +```mermaid +graph TD + A[Asset Upload] --> B[Metadata Extraction] + B --> C[Storage Template Migration] + C --> D["Thumbnail Generation (Large, small, blurred and person)"] + D --> E[Smart Search] + D --> F[Face Detection] + D --> G[Video Transcoding] + E --> H[Duplicate Detection] + F --> I[Facial Recognition] +``` diff --git a/docs/docs/administration/oauth.md b/docs/docs/administration/oauth.md index 55a0ce9469..47f4a96c6a 100644 --- a/docs/docs/administration/oauth.md +++ b/docs/docs/administration/oauth.md @@ -28,7 +28,7 @@ Before enabling OAuth in Immich, a new client application needs to be configured 2. Configure Redirect URIs/Origins The **Sign-in redirect URIs** should include: - - `app.immich:///oauth-callback` - for logging in with OAuth from the [Mobile App](/docs/features/mobile-app.mdx) + - `app.immich:///oauth-callback` - for logging in with OAuth from the [Mobile App](/features/mobile-app.mdx) - `http://DOMAIN:PORT/auth/login` - for logging in with OAuth from the Web Client - `http://DOMAIN:PORT/user-settings` - for manually linking OAuth in the Web Client @@ -98,7 +98,7 @@ The redirect URI for the mobile app is `app.immich:///oauth-callback`, which is 2. Whitelist the new endpoint as a valid redirect URI with your provider. 3. Specify the new endpoint as the `Mobile Redirect URI Override`, in the OAuth settings. -With these steps in place, you should be able to use OAuth from the [Mobile App](/docs/features/mobile-app.mdx) without a custom scheme redirect URI. +With these steps in place, you should be able to use OAuth from the [Mobile App](/features/mobile-app.mdx) without a custom scheme redirect URI. :::info Immich has a route (`/api/oauth/mobile-redirect`) that is already configured to forward requests to `app.immich:///oauth-callback`, and can be used for step 1. diff --git a/docs/docs/administration/reverse-proxy.md b/docs/docs/administration/reverse-proxy.md index f13814d91f..5c9e47b010 100644 --- a/docs/docs/administration/reverse-proxy.md +++ b/docs/docs/administration/reverse-proxy.md @@ -6,6 +6,10 @@ Users can deploy a custom reverse proxy that forwards requests to Immich. This w Immich does not support being served on a sub-path such as `location /immich {`. It has to be served on the root path of a (sub)domain. ::: +:::info +If your reverse proxy uses the [Let's Encrypt](https://letsencrypt.org/) [http-01 challenge](https://letsencrypt.org/docs/challenge-types/#http-01-challenge), you may want to verify that the Immich well-known endpoint (`/.well-known/immich`) gets correctly routed to Immich, otherwise it will likely be routed elsewhere and the mobile app may run into connection issues. +::: + ### Nginx example config Below is an example config for nginx. Make sure to set `public_url` to the front-facing URL of your instance, and `backend_url` to the path of the Immich server. @@ -37,29 +41,14 @@ server { location / { proxy_pass http://:2283; } + + # useful when using Let's Encrypt http-01 challenge + # location = /.well-known/immich { + # proxy_pass http://:2283; + # } } ``` -#### Compatibility with Let's Encrypt - -In the event that your nginx configuration includes a section for Let's Encrypt, it's likely that you have a segment similar to the following: - -```nginx -location ~ /.well-known { - ... -} -``` - -This particular `location` directive can inadvertently prevent mobile clients from reaching the `/.well-known/immich` path, which is crucial for discovery. Usual error message for this case is: "Your app major version is not compatible with the server". To remedy this, you should introduce an additional location block specifically for this path, ensuring that requests are correctly proxied to the Immich server: - -```nginx -location = /.well-known/immich { - proxy_pass http://:2283; -} -``` - -By doing so, you'll maintain the functionality of Let's Encrypt while allowing mobile clients to access the necessary Immich path without obstruction. - ### Caddy example config As an alternative to nginx, you can also use [Caddy](https://caddyserver.com/) as a reverse proxy (with automatic HTTPS configuration). Below is an example config. diff --git a/docs/docs/administration/server-commands.md b/docs/docs/administration/server-commands.md index a25673abf2..3838635c24 100644 --- a/docs/docs/administration/server-commands.md +++ b/docs/docs/administration/server-commands.md @@ -16,7 +16,7 @@ The `immich-server` docker image comes preinstalled with an administrative CLI ( ## How to run a command -To run a command, [connect](/docs/guides/docker-help.md#attach-to-a-container) to the `immich_server` container and then execute the command via `immich-admin `. +To run a command, [connect](/guides/docker-help.md#attach-to-a-container) to the `immich_server` container and then execute the command via `immich-admin `. ## Examples diff --git a/docs/docs/administration/system-settings.md b/docs/docs/administration/system-settings.md index f241050136..fdfdad29ea 100644 --- a/docs/docs/administration/system-settings.md +++ b/docs/docs/administration/system-settings.md @@ -12,14 +12,14 @@ Manage password, OAuth, and other authentication settings ### OAuth Authentication -Immich supports OAuth Authentication. Read more about this feature and its configuration [here](/docs/administration/oauth). +Immich supports OAuth Authentication. Read more about this feature and its configuration [here](/administration/oauth). ### Password Authentication -The administrator can choose to disable login with username and password for the entire instance. This means that **no one**, including the system administrator, will be able to log using this method. If [OAuth Authentication](/docs/administration/oauth) is also disabled, no users will be able to login using **any** method. Changing this setting does not affect existing sessions, just new login attempts. +The administrator can choose to disable login with username and password for the entire instance. This means that **no one**, including the system administrator, will be able to log using this method. If [OAuth Authentication](/administration/oauth) is also disabled, no users will be able to login using **any** method. Changing this setting does not affect existing sessions, just new login attempts. :::tip -You can always use the [Server CLI](/docs/administration/server-commands) to re-enable password login. +You can always use the [Server CLI](/administration/server-commands) to re-enable password login. ::: ## Image Settings (thumbnails and previews) @@ -108,7 +108,7 @@ If more than one URL is provided, each server will be attempted one-at-a-time un ### Smart Search -The [smart search](/docs/features/searching) settings allow you to change the [CLIP model](https://openai.com/research/clip). Larger models will typically provide [more accurate search results](https://github.com/immich-app/immich/discussions/11862) but consume more processing power and RAM. When [changing the CLIP model](/docs/FAQ#can-i-use-a-custom-clip-model) it is mandatory to re-run the Smart Search job on all images to fully apply the change. +The [smart search](/features/searching) settings allow you to change the [CLIP model](https://openai.com/research/clip). Larger models will typically provide [more accurate search results](https://github.com/immich-app/immich/discussions/11862) but consume more processing power and RAM. When [changing the CLIP model](/FAQ#can-i-use-a-custom-clip-model) it is mandatory to re-run the Smart Search job on all images to fully apply the change. :::info Internet connection Changing models requires a connection to the Internet to download the model. @@ -132,7 +132,7 @@ Editable settings: - **Max Recognition Distance** - **Min Recognized Faces** -You can learn more about these options on the [Facial Recognition page](/docs/features/facial-recognition#how-face-detection-works) +You can learn more about these options on the [Facial Recognition page](/features/facial-recognition#how-face-detection-works) :::info When changing the values in Min Detection Score, Max Recognition Distance, and Min Recognized Faces. @@ -154,15 +154,15 @@ The map can be adjusted via [OpenMapTiles](https://openmaptiles.org/styles/) for ### Reverse Geocoding Settings -Immich supports [Reverse Geocoding](/docs/features/reverse-geocoding) using data from the [GeoNames](https://www.geonames.org/) geographical database. +Immich supports [Reverse Geocoding](/features/reverse-geocoding) using data from the [GeoNames](https://www.geonames.org/) geographical database. ## Notification Settings -SMTP server setup, for user creation notifications, new albums, etc. More information can be found [here](/docs/administration/email-notification) +SMTP server setup, for user creation notifications, new albums, etc. More information can be found [here](/administration/email-notification) ## Notification Templates -Override the default notifications text with notification templates. More information can be found [here](/docs/administration/email-notification) +Override the default notifications text with notification templates. More information can be found [here](/administration/email-notification) ## Server Settings @@ -176,7 +176,7 @@ The administrator can set a custom message on the login screen (the message will ## Storage Template -Immich supports a custom [Storage Template](/docs/administration/storage-template). Learn more about this feature and its configuration [here](/docs/administration/storage-template). +Immich supports a custom [Storage Template](/administration/storage-template). Learn more about this feature and its configuration [here](/administration/storage-template). ## Theme Settings diff --git a/docs/docs/community-guides.mdx b/docs/docs/community-guides.mdx deleted file mode 100644 index 505ec93e77..0000000000 --- a/docs/docs/community-guides.mdx +++ /dev/null @@ -1,12 +0,0 @@ -# Community Guides - -This page lists community guides that are written around Immich, but not officially supported by the development team. - -:::warning -This list comes with no guarantees about security, performance, reliability, or accuracy. Use at your own risk. -::: - -import CommunityGuides from '../src/components/community-guides.tsx'; -import React from 'react'; - - diff --git a/docs/docs/community-projects.mdx b/docs/docs/community-projects.mdx deleted file mode 100644 index eb41090cd6..0000000000 --- a/docs/docs/community-projects.mdx +++ /dev/null @@ -1,12 +0,0 @@ -# Community Projects - -This page lists community projects that are built around Immich, but not officially supported by the development team. - -:::warning -This list comes with no guarantees about security, performance, reliability, or accuracy. Use at your own risk. -::: - -import CommunityProjects from '../src/components/community-projects.tsx'; -import React from 'react'; - - diff --git a/docs/docs/developer/architecture.mdx b/docs/docs/developer/architecture.mdx index a8d38ba5c1..42d9c1b974 100644 --- a/docs/docs/developer/architecture.mdx +++ b/docs/docs/developer/architecture.mdx @@ -44,7 +44,7 @@ The web app is a [TypeScript](https://www.typescriptlang.org/) project that uses ### CLI -The Immich CLI is an [npm](https://www.npmjs.com/) package that lets users control their Immich instance from the command line. It uses the API to perform various tasks, especially uploading assets. See the [CLI documentation](/docs/features/command-line-interface.md) for more information. +The Immich CLI is an [npm](https://www.npmjs.com/) package that lets users control their Immich instance from the command line. It uses the API to perform various tasks, especially uploading assets. See the [CLI documentation](/features/command-line-interface.md) for more information. ## Server @@ -83,11 +83,11 @@ Immich uses a [worker](https://github.com/immich-app/immich/blob/main/server/src - Smart Search - Facial Recognition - Storage Template Migration -- Sidecar (see [XMP Sidecars](/docs/features/xmp-sidecars.md)) +- Sidecar (see [XMP Sidecars](/features/xmp-sidecars.md)) - Background jobs (file deletion, user deletion) :::info -This list closely matches what is available on the [Administration > Jobs](/docs/administration/jobs-workers/#jobs) page, which provides some remote queue management capabilities. +This list closely matches what is available on the [Administration > Jobs](/administration/jobs-workers/#jobs) page, which provides some remote queue management capabilities. ::: ### Machine Learning diff --git a/docs/docs/developer/devcontainers.md b/docs/docs/developer/devcontainers.md index c7c48acf2b..0a1946e6c1 100644 --- a/docs/docs/developer/devcontainers.md +++ b/docs/docs/developer/devcontainers.md @@ -431,7 +431,7 @@ While the Dev Container focuses on server and web development, you can connect m - Server URL: `http://YOUR_IP:2283/api` - Ensure firewall allows port 2283 -3. **For full mobile development**, see the [mobile development guide](/docs/developer/setup) which covers: +3. **For full mobile development**, see the [mobile development guide](/developer/setup) which covers: - Flutter setup - Running on simulators/devices - Mobile-specific debugging @@ -474,7 +474,7 @@ Recommended minimums: ## Next Steps -- Read the [architecture overview](/docs/developer/architecture) -- Learn about [database migrations](/docs/developer/database-migrations) -- Explore [API documentation](/docs/api) +- Read the [architecture overview](/developer/architecture) +- Learn about [database migrations](/developer/database-migrations) +- Explore [API documentation](https://api.immich.app/) - Join `#immich` on [Discord](https://discord.immich.app) diff --git a/docs/docs/developer/open-api.md b/docs/docs/developer/open-api.md index 2c29c7365b..f627b2c459 100644 --- a/docs/docs/developer/open-api.md +++ b/docs/docs/developer/open-api.md @@ -1,6 +1,6 @@ # OpenAPI -Immich uses the [OpenAPI](https://swagger.io/specification/) standard to generate API documentation. To view the published docs see [here](/docs/api). +Immich uses the [OpenAPI](https://swagger.io/specification/) standard to generate API documentation. To view the published docs see [here](https://api.immich.app/). ## Generator diff --git a/docs/docs/developer/pr-checklist.md b/docs/docs/developer/pr-checklist.md index ea44367742..f855e854c4 100644 --- a/docs/docs/developer/pr-checklist.md +++ b/docs/docs/developer/pr-checklist.md @@ -53,8 +53,8 @@ You can use `dart fix --apply` and `dcm fix lib` to potentially correct some iss ## OpenAPI -The OpenAPI client libraries need to be regenerated whenever there are changes to the `immich-openapi-specs.json` file. Note that you should not modify this file directly as it is auto-generated. See [OpenAPI](/docs/developer/open-api.md) for more details. +The OpenAPI client libraries need to be regenerated whenever there are changes to the `immich-openapi-specs.json` file. Note that you should not modify this file directly as it is auto-generated. See [OpenAPI](/developer/open-api.md) for more details. ## Database Migrations -A database migration needs to be generated whenever there are changes to `server/src/infra/src/entities`. See [Database Migration](/docs/developer/database-migrations.md) for more details. +A database migration needs to be generated whenever there are changes to `server/src/infra/src/entities`. See [Database Migration](/developer/database-migrations.md) for more details. diff --git a/docs/docs/features/automatic-backup.md b/docs/docs/features/automatic-backup.md index 8fcbedaa6e..30d132cef8 100644 --- a/docs/docs/features/automatic-backup.md +++ b/docs/docs/features/automatic-backup.md @@ -16,7 +16,7 @@ If foreground backup is enabled: whenever the app is opened or resumed, it will ## Background backup -This feature is intended for everyday use. For initial bulk uploading, please use the foreground upload feature. For more information on why background upload is not working as expected, please refer to the [FAQ](/docs/FAQ#why-does-foreground-backup-stop-when-i-navigate-away-from-the-app-shouldnt-it-transfer-the-job-to-background-backup). +This feature is intended for everyday use. For initial bulk uploading, please use the foreground upload feature. For more information on why background upload is not working as expected, please refer to the [FAQ](/FAQ#why-does-foreground-backup-stop-when-i-navigate-away-from-the-app-shouldnt-it-transfer-the-job-to-background-backup). If background backup is enabled. The app will periodically check if there are any new photos or videos in the selected album(s) to be uploaded to the server. If there are, it will upload them to the cloud in the background. diff --git a/docs/docs/features/casting.md b/docs/docs/features/casting.md index bca85cb28c..2a6785dc6c 100644 --- a/docs/docs/features/casting.md +++ b/docs/docs/features/casting.md @@ -4,7 +4,7 @@ Immich supports the Google's Cast protocol so that photos and videos can be cast ## Enable Google Cast Support -Google Cast support is disabled by default. The web UI uses Google-provided scripts and must retreive them from Google servers when the page loads. This is a privacy concern for some and is thus opt-in. +Google Cast support is disabled by default. The web UI uses Google-provided scripts and must retrieve them from Google servers when the page loads. This is a privacy concern for some and is thus opt-in. You can enable Google Cast support through `Account Settings > Features > Cast > Google Cast` diff --git a/docs/docs/features/command-line-interface.md b/docs/docs/features/command-line-interface.md index 436b499e50..2b35d62ce8 100644 --- a/docs/docs/features/command-line-interface.md +++ b/docs/docs/features/command-line-interface.md @@ -182,7 +182,7 @@ For example to get a list of files that would be uploaded for further processing: ```bash -immich upload --dry-run . | tail -n +4 | jq .newFiles[] +immich upload --dry-run . | tail -n +6 | jq .newFiles[] ``` ### Obtain the API Key diff --git a/docs/docs/features/facial-recognition.md b/docs/docs/features/facial-recognition.md index f0dec55484..85712ef5f6 100644 --- a/docs/docs/features/facial-recognition.md +++ b/docs/docs/features/facial-recognition.md @@ -70,7 +70,7 @@ Navigating to Administration > Settings > Machine Learning Settings > Facial Rec :::tip It's better to only tweak the parameters here than to set them to something very different unless you're ready to test a variety of options. If you do need to set a parameter to a strict setting, relaxing other settings can be a good option to compensate, and vice versa. -You can learn how the tune the result in this [Guide](/docs/guides/better-facial-clusters) +You can learn how the tune the result in this [Guide](/guides/better-facial-clusters) ::: ### Facial recognition model diff --git a/docs/docs/features/libraries.md b/docs/docs/features/libraries.md index e68bcdc272..9f1cef0bc4 100644 --- a/docs/docs/features/libraries.md +++ b/docs/docs/features/libraries.md @@ -1,5 +1,9 @@ # External Libraries +:::info +Currently an external library can only belong to a single user which is selected when the library is initially created. +::: + External libraries track assets stored in the filesystem outside of Immich. When the external library is scanned, Immich will load videos and photos from disk and create the corresponding assets. These assets will then be shown in the main timeline, and they will look and behave like any other asset, including viewing on the map, adding to albums, etc. Later, if a file is modified outside of Immich, you need to scan the library for the changes to show up. If an external asset is deleted from disk, Immich will move it to trash on rescan. To restore the asset, you need to restore the original file. After 30 days the file will be removed from trash, and any changes to metadata within Immich will be lost. @@ -103,7 +107,7 @@ The `immich-server` container will need access to the gallery. Modify your docke :::tip The `ro` flag at the end only gives read-only access to the volumes. -This will disallow the images from being deleted in the web UI, or adding metadata to the library ([XMP sidecars](/docs/features/xmp-sidecars)). +This will disallow the images from being deleted in the web UI, or adding metadata to the library ([XMP sidecars](/features/xmp-sidecars)). ::: :::info diff --git a/docs/docs/features/ml-hardware-acceleration.md b/docs/docs/features/ml-hardware-acceleration.md index a94f8c8c64..685f23932c 100644 --- a/docs/docs/features/ml-hardware-acceleration.md +++ b/docs/docs/features/ml-hardware-acceleration.md @@ -35,7 +35,7 @@ You do not need to redo any machine learning jobs after enabling hardware accele - Where and how you can get this file depends on device and vendor, but typically, the device vendor also supplies these - The `hwaccel.ml.yml` file assumes the path to it is `/usr/lib/libmali.so`, so update accordingly if it is elsewhere - The `hwaccel.ml.yml` file assumes an additional file `/lib/firmware/mali_csffw.bin`, so update accordingly if your device's driver does not require this file -- Optional: Configure your `.env` file, see [environment variables](/docs/install/environment-variables) for ARM NN specific settings +- Optional: Configure your `.env` file, see [environment variables](/install/environment-variables) for ARM NN specific settings - In particular, the `MACHINE_LEARNING_ANN_FP16_TURBO` can significantly improve performance at the cost of very slightly lower accuracy #### CUDA @@ -49,14 +49,30 @@ You do not need to redo any machine learning jobs after enabling hardware accele - The GPU must be supported by ROCm. If it isn't officially supported, you can attempt to use the `HSA_OVERRIDE_GFX_VERSION` environmental variable: `HSA_OVERRIDE_GFX_VERSION=
`. If this doesn't work, you might need to also set `HSA_USE_SVM=0`. - The ROCm image is quite large and requires at least 35GiB of free disk space. However, pulling later updates to the service through Docker will generally only amount to a few hundred megabytes as the rest will be cached. -- This backend is new and may experience some issues. For example, GPU power consumption can be higher than usual after running inference, even if the machine learning service is idle. In this case, it will only go back to normal after being idle for 5 minutes (configurable with the [MACHINE_LEARNING_MODEL_TTL](/docs/install/environment-variables) setting). +- This backend is new and may experience some issues. For example, GPU power consumption can be higher than usual after running inference, even if the machine learning service is idle. In this case, it will only go back to normal after being idle for 5 minutes (configurable with the [MACHINE_LEARNING_MODEL_TTL](/install/environment-variables) setting). #### OpenVINO - Integrated GPUs are more likely to experience issues than discrete GPUs, especially for older processors or servers with low RAM. -- Ensure the server's kernel version is new enough to use the device for hardware accceleration. +- Ensure the server's kernel version is new enough to use the device for hardware acceleration. - Expect higher RAM usage when using OpenVINO compared to CPU processing. +#### OpenVINO-WSL + +- Ensure your container can access the /dev/dri directory, you can verify this by doing `docker exec -t immich_machine_learning ls -la /dev/dri`. If this is not the case execute `getent group render` and `getent group video` on the WSL host, then add those groups to hwaccel.ml.yaml + ```yaml + openvino-wsl: + devices: + - /dev/dri:/dev/dri + - /dev/dxg:/dev/dxg + volumes: + - /dev/bus/usb:/dev/bus/usb + - /usr/lib/wsl:/usr/lib/wsl + group_add: + - 44 # Replace this number with the number you found with getent group video + - 992 # Replace this number with the number you found with getent group render + ``` + #### RKNN - You must have a supported Rockchip SoC: only RK3566, RK3568, RK3576 and RK3588 are supported at this moment. @@ -64,7 +80,7 @@ You do not need to redo any machine learning jobs after enabling hardware accele - This is usually pre-installed on the device vendor's Linux images - RKNPU driver V0.9.8 or later must be available in the host server - You may confirm this by running `cat /sys/kernel/debug/rknpu/version` to check the version -- Optional: Configure your `.env` file, see [environment variables](/docs/install/environment-variables) for RKNN specific settings +- Optional: Configure your `.env` file, see [environment variables](/install/environment-variables) for RKNN specific settings - In particular, setting `MACHINE_LEARNING_RKNN_THREADS` to 2 or 3 can _dramatically_ improve performance for RK3576 and RK3588 compared to the default of 1, at the expense of multiplying the amount of RAM each model uses by that amount. ## Setup diff --git a/docs/docs/features/mobile-app.mdx b/docs/docs/features/mobile-app.mdx index cd837741f1..2d34507a26 100644 --- a/docs/docs/features/mobile-app.mdx +++ b/docs/docs/features/mobile-app.mdx @@ -3,7 +3,6 @@ import { mdiCloudOffOutline, mdiCloudCheckOutline } from '@mdi/js'; import MobileAppDownload from '/docs/partials/_mobile-app-download.md'; import MobileAppLogin from '/docs/partials/_mobile-app-login.md'; import MobileAppBackup from '/docs/partials/_mobile-app-backup.md'; -import { cloudDonePath, cloudOffPath } from '@site/src/components/svg-paths'; # Mobile App @@ -28,7 +27,7 @@ The beta release channel allows users to test upcoming changes before they are o :::info -You can enable automatic backup on supported devices. For more information see [Automatic Backup](/docs/features/automatic-backup.md). +You can enable automatic backup on supported devices. For more information see [Automatic Backup](/features/automatic-backup.md). ::: ## Sync only selected photos @@ -75,7 +74,7 @@ You can sync or mirror an album from your phone to the Immich server on your acc - **User-Specific Sync:** Album synchronization is unique to each server user and does not sync between different users or partners. -- **Mobile-Only Feature:** Album synchronization is currently only available on mobile. For similar options on a computer, refer to [Libraries](/docs/features/libraries) for further details. +- **Mobile-Only Feature:** Album synchronization is currently only available on mobile. For similar options on a computer, refer to [Libraries](/features/libraries) for further details. ### Synchronizing albums from the past diff --git a/docs/docs/features/monitoring.md b/docs/docs/features/monitoring.md index c80f66902b..f087a3306f 100644 --- a/docs/docs/features/monitoring.md +++ b/docs/docs/features/monitoring.md @@ -28,7 +28,7 @@ The metrics in immich are grouped into API (endpoint calls and response times), Immich will not expose an endpoint for metrics by default. To enable this endpoint, you can add the `IMMICH_TELEMETRY_INCLUDE=all` environmental variable to your `.env` file. Note that only the server container currently use this variable. :::tip -`IMMICH_TELEMETRY_INCLUDE=all` enables all metrics. For a more granular configuration you can enumerate the telemetry metrics that should be included as a comma separated list (e.g. `IMMICH_TELEMETRY_INCLUDE=repo,api`). Alternatively, you can also exclude specific metrics with `IMMICH_TELEMETRY_EXCLUDE`. For more information refer to the [environment section](/docs/install/environment-variables.md#prometheus). +`IMMICH_TELEMETRY_INCLUDE=all` enables all metrics. For a more granular configuration you can enumerate the telemetry metrics that should be included as a comma separated list (e.g. `IMMICH_TELEMETRY_INCLUDE=repo,api`). Alternatively, you can also exclude specific metrics with `IMMICH_TELEMETRY_EXCLUDE`. For more information refer to the [environment section](/install/environment-variables.md#prometheus). ::: The next step is to configure a new or existing Prometheus instance to scrape this endpoint. The following steps assume that you do not have an existing Prometheus instance, but the steps will be similar either way. @@ -68,7 +68,7 @@ After bringing down the containers with `docker compose down` and back up with ` :::note To see exactly what metrics are made available, you can additionally add `8081:8081` (API metrics) and `8082:8082` (microservices metrics) to the immich_server container's ports. Visiting the `/metrics` endpoint for these services will show the same raw data that Prometheus collects. -To configure these ports see [`IMMICH_API_METRICS_PORT` & `IMMICH_MICROSERVICES_METRICS_PORT`](/docs/install/environment-variables/#general). +To configure these ports see [`IMMICH_API_METRICS_PORT` & `IMMICH_MICROSERVICES_METRICS_PORT`](/install/environment-variables/#general). ::: ### Usage diff --git a/docs/docs/features/reverse-geocoding.md b/docs/docs/features/reverse-geocoding.md index 399bdd9b48..b1aee74a99 100644 --- a/docs/docs/features/reverse-geocoding.md +++ b/docs/docs/features/reverse-geocoding.md @@ -8,7 +8,7 @@ During Exif Extraction, assets with latitudes and longitudes are reverse geocode ## Usage -Data from a reverse geocode is displayed in the image details, and used in [Smart Search](/docs/features/searching.md). +Data from a reverse geocode is displayed in the image details, and used in [Smart Search](/features/searching.md). diff --git a/docs/docs/features/sharing.md b/docs/docs/features/sharing.md index ff0a03beea..c19b4f48e1 100644 --- a/docs/docs/features/sharing.md +++ b/docs/docs/features/sharing.md @@ -24,11 +24,11 @@ After creating an album, you can access the sharing options by clicking on the s Partner sharing allows you to share your _entire_ library with other users of your choice. They can then view your library and download the assets. -You can read this guide to learn more about [partner sharing](/docs/features/partner-sharing). +You can read this guide to learn more about [partner sharing](/features/partner-sharing). ## Public sharing -You can create a public link to share a group of photos or videos, or an album, with anyone. The public link can be shared via email, social media, or any other method. There are a varierity of options to customize the public link, such as setting an expiration date, password protection, and more. Public shared link is handy when you want to share a group of photos or videos with someone who doesn't have an Immich account and allow the shared user to upload their photos or videos to your account. +You can create a public link to share a group of photos or videos, or an album, with anyone. The public link can be shared via email, social media, or any other method. There are a variety of options to customize the public link, such as setting an expiration date, password protection, and more. Public shared link is handy when you want to share a group of photos or videos with someone who doesn't have an Immich account and allow the shared user to upload their photos or videos to your account. The public shared link is generated with a random URL, which acts as as a secret to avoid the link being guessed by unwanted parties, for instance. diff --git a/docs/docs/features/tags.md b/docs/docs/features/tags.md index a5b6752c81..79a9696d9a 100644 --- a/docs/docs/features/tags.md +++ b/docs/docs/features/tags.md @@ -1,6 +1,6 @@ # Tags -Immich supports hierarchical tags, with the ability to read existing tags from the XMP `TagsList` field and IPTC `Keywords` field. Any changes to tags made through Immich are also written back to a [sidecar](/docs/features/xmp-sidecars) file. You can re-run the metadata extraction jobs for all assets to import your existing tags. +Immich supports hierarchical tags, with the ability to read existing tags from the XMP `TagsList` field and IPTC `Keywords` field. Any changes to tags made through Immich are also written back to a [sidecar](/features/xmp-sidecars) file. You can re-run the metadata extraction jobs for all assets to import your existing tags. ## Enable tags feature diff --git a/docs/docs/features/user-settings.md b/docs/docs/features/user-settings.md index a2d0308541..402105cd43 100644 --- a/docs/docs/features/user-settings.md +++ b/docs/docs/features/user-settings.md @@ -15,9 +15,9 @@ You can access the [user settings](https://my.immich.app/user-settings) by click --- :::tip Reset Password -The admin can reset a user password through the [User Management](/docs/administration/user-management.mdx) screen. +The admin can reset a user password through the [User Management](/administration/user-management.mdx) screen. ::: :::tip Reset Admin Password -The admin password can be reset using a [Server Command](/docs/administration/server-commands.md) +The admin password can be reset using a [Server Command](/administration/server-commands.md) ::: diff --git a/docs/docs/guides/better-facial-clusters.md b/docs/docs/guides/better-facial-clusters.md index f4409b441c..40796983a5 100644 --- a/docs/docs/guides/better-facial-clusters.md +++ b/docs/docs/guides/better-facial-clusters.md @@ -10,7 +10,7 @@ This guide explains how to optimize facial recognition in systems with large ima - **Best Suited For:** Large image libraries after importing a significant number of images. - **Warning:** This method deletes all previously assigned names. -- **Tip:** **Always take a [backup](/docs/administration/backup-and-restore#database) before proceeding!** +- **Tip:** **Always take a [backup](/administration/backup-and-restore#database) before proceeding!** --- diff --git a/docs/docs/guides/custom-locations.md b/docs/docs/guides/custom-locations.md index af8ca438e7..e0274d3bd9 100644 --- a/docs/docs/guides/custom-locations.md +++ b/docs/docs/guides/custom-locations.md @@ -9,7 +9,7 @@ It is important to remember to update the backup settings after following the gu In our `.env` file, we will define the paths we want to use. Note that you don't have to define all of these: UPLOAD_LOCATION will be the base folder that files are stored in by default, with the other paths acting as overrides. ```diff title=".env" -# You can find documentation for all the supported environment variables [here](/docs/install/environment-variables) +# You can find documentation for all the supported environment variables [here](/install/environment-variables) # Custom location where your uploaded, thumbnails, and transcoded video files are stored - UPLOAD_LOCATION=./library diff --git a/docs/docs/guides/database-queries.md b/docs/docs/guides/database-queries.md index 1a5c2ed193..5cdcdc04c4 100644 --- a/docs/docs/guides/database-queries.md +++ b/docs/docs/guides/database-queries.md @@ -7,7 +7,7 @@ Keep in mind that mucking around in the database might set the Moon on fire. Avo :::tip Run `docker exec -it immich_postgres psql --dbname= --username=` to connect to the database via the container directly. -(Replace `` and `` with the values from your [`.env` file](/docs/install/environment-variables#database)). +(Replace `` and `` with the values from your [`.env` file](/install/environment-variables#database)). ::: ## Assets @@ -142,7 +142,7 @@ DELETE FROM "person" WHERE "name" = 'PersonNameHere'; SELECT "key", "value" FROM "system_metadata" WHERE "key" = 'system-config'; ``` -(Only used when not using the [config file](/docs/install/config-file)) +(Only used when not using the [config file](/install/config-file)) ### File properties diff --git a/docs/docs/guides/external-library.md b/docs/docs/guides/external-library.md index 7921843297..3f366bb0d4 100644 --- a/docs/docs/guides/external-library.md +++ b/docs/docs/guides/external-library.md @@ -1,13 +1,13 @@ # External Library -This guide walks you through adding an [External Library](/docs/features/libraries). +This guide walks you through adding an [External Library](/features/libraries). This guide assumes you are running Immich in Docker and that the files you wish to access are stored in a directory on the same machine. # Mount the directory into the containers. Edit `docker-compose.yml` to add one or more new mount points in the section `immich-server:` under `volumes:`. -If you want Immich to be able to delete the images in the external library or add metadata ([XMP sidecars](/docs/features/xmp-sidecars)), remove `:ro` from the end of the mount point. +If you want Immich to be able to delete the images in the external library or add metadata ([XMP sidecars](/features/xmp-sidecars)), remove `:ro` from the end of the mount point. ```diff immich-server: @@ -21,6 +21,10 @@ Restart Immich by running `docker compose up -d`. # Create the library +:::info +External library management requires administrator access and the steps below assume you are using an admin account. +::: + In the Immich web UI: - click the **Administration** link in the upper right corner. @@ -33,7 +37,7 @@ In the Immich web UI: - In the dialog, select which user should own the new library - + - Click the three-dots menu and select **Edit Import Paths** diff --git a/docs/docs/guides/remote-access.md b/docs/docs/guides/remote-access.md index 6f401dfc5a..518b003c3a 100644 --- a/docs/docs/guides/remote-access.md +++ b/docs/docs/guides/remote-access.md @@ -46,7 +46,7 @@ You can learn how to set up Tailscale together with Immich with the [tutorial vi A reverse proxy is a service that sits between web servers and clients. A reverse proxy can either be hosted on the server itself or remotely. Clients can connect to the reverse proxy via https, and the proxy relays data to Immich. This setup makes most sense if you have your own domain and want to access your Immich instance just like any other website, from outside your LAN. You can also use a DDNS provider like DuckDNS or no-ip if you don't have a domain. This configuration allows the Immich Android and iphone apps to connect to your server without a VPN or tailscale app on the client side. -If you're hosting your own reverse proxy, [Nginx](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/) is a great option. An example configuration for Nginx is provided [here](/docs/administration/reverse-proxy.md). +If you're hosting your own reverse proxy, [Nginx](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/) is a great option. An example configuration for Nginx is provided [here](/administration/reverse-proxy.md). You'll also need your own certificate to authenticate https connections. If you're making Immich publicly accessible, [Let's Encrypt](https://letsencrypt.org/) can provide a free certificate for your domain and is the recommended option. Alternatively, a [self-signed certificate](https://en.wikipedia.org/wiki/Self-signed_certificate) allows you to encrypt your connection to Immich, but it raises a security warning on the client's browser. diff --git a/docs/docs/guides/remote-machine-learning.md b/docs/docs/guides/remote-machine-learning.md index 72ae0e3fa1..0a8ddf2577 100644 --- a/docs/docs/guides/remote-machine-learning.md +++ b/docs/docs/guides/remote-machine-learning.md @@ -1,6 +1,6 @@ # Remote Machine Learning -To alleviate [performance issues on low-memory systems](/docs/FAQ.mdx#why-is-immich-slow-on-low-memory-systems-like-the-raspberry-pi) like the Raspberry Pi, you may also host Immich's machine learning container on a more powerful system, such as your laptop or desktop computer. The server container will send requests containing the image preview to the remote machine learning container for processing. The machine learning container does not persist this data or associate it with a particular user. +To alleviate [performance issues on low-memory systems](/FAQ.mdx#why-is-immich-slow-on-low-memory-systems-like-the-raspberry-pi) like the Raspberry Pi, you may also host Immich's machine learning container on a more powerful system, such as your laptop or desktop computer. The server container will send requests containing the image preview to the remote machine learning container for processing. The machine learning container does not persist this data or associate it with a particular user. :::info Smart Search and Face Detection will use this feature, but Facial Recognition will not. This is because Facial Recognition uses the _outputs_ of these models that have already been saved to the database. As such, its processing is between the server container and the database. @@ -14,7 +14,7 @@ Image previews are sent to the remote machine learning container. Use this optio 2. Copy the following `docker-compose.yml` to the remote server :::info -If using hardware acceleration, the [hwaccel.ml.yml](https://github.com/immich-app/immich/releases/latest/download/hwaccel.ml.yml) file also needs to be added and the `docker-compose.yml` needs to be configured as described in the [hardware acceleration documentation](/docs/features/ml-hardware-acceleration) +If using hardware acceleration, the [hwaccel.ml.yml](https://github.com/immich-app/immich/releases/latest/download/hwaccel.ml.yml) file also needs to be added and the `docker-compose.yml` needs to be configured as described in the [hardware acceleration documentation](/features/ml-hardware-acceleration) ::: ```yaml diff --git a/docs/docs/guides/template-backup-script.md b/docs/docs/guides/template-backup-script.md index 34381dd0ee..19647d4ae1 100644 --- a/docs/docs/guides/template-backup-script.md +++ b/docs/docs/guides/template-backup-script.md @@ -7,7 +7,7 @@ This script assumes you have a second hard drive connected to your server for on The database is saved to your Immich upload folder in the `database-backup` subdirectory. The database is then backed up and versioned with your assets by Borg. This ensures that the database backup is in sync with your assets in every snapshot. :::info -This script makes backups of your database along with your photo/video library. This is redundant with the [automatic database backup tool](https://immich.app/docs/administration/backup-and-restore#automatic-database-backups) built into Immich. Using this script to backup your database has two advantages over the built-in backup tool: +This script makes backups of your database along with your photo/video library. This is redundant with the [automatic database backup tool](/administration/backup-and-restore#automatic-database-dumps) built into Immich. Using this script to backup your database has two advantages over the built-in backup tool: - This script uses storage more efficiently by versioning your backups instead of making multiple copies. - The database backups are performed at the same time as the library backup, ensuring that the backups of your database and the library are always in sync. diff --git a/docs/docs/install/config-file.md b/docs/docs/install/config-file.md index 54d7c61bb3..3fb0687e4a 100644 --- a/docs/docs/install/config-file.md +++ b/docs/docs/install/config-file.md @@ -209,7 +209,7 @@ So you can just grab it from there, paste it into a file and you're pretty much ### Step 2 - Specify the file location In your `.env` file, set the variable `IMMICH_CONFIG_FILE` to the path of your config. -For more information, refer to the [Environment Variables](/docs/install/environment-variables.md) section. +For more information, refer to the [Environment Variables](/install/environment-variables.md) section. :::tip YAML-formatted config files are also supported. diff --git a/docs/docs/install/docker-compose.mdx b/docs/docs/install/docker-compose.mdx index 7a0b566f5d..46b144eb4a 100644 --- a/docs/docs/install/docker-compose.mdx +++ b/docs/docs/install/docker-compose.mdx @@ -29,4 +29,4 @@ If you get an error `can't set healthcheck.start_interval as feature require Doc ## Next Steps -Read the [Post Installation](/docs/install/post-install.mdx) steps and [upgrade instructions](/docs/install/upgrading.md). +Read the [Post Installation](/install/post-install.mdx) steps and [upgrade instructions](/install/upgrading.md). diff --git a/docs/docs/install/environment-variables.md b/docs/docs/install/environment-variables.md index 4e081c8966..78a5289bf4 100644 --- a/docs/docs/install/environment-variables.md +++ b/docs/docs/install/environment-variables.md @@ -42,7 +42,7 @@ These environment variables are used by the `docker-compose.yml` file and do **N | `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices | | `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices | | `IMMICH_TRUSTED_PROXIES` | List of comma-separated IPs set as trusted proxies | | server | api | -| `IMMICH_IGNORE_MOUNT_CHECK_ERRORS` | See [System Integrity](/docs/administration/system-integrity) | | server | api, microservices | +| `IMMICH_IGNORE_MOUNT_CHECK_ERRORS` | See [System Integrity](/administration/system-integrity) | | server | api, microservices | \*1: `TZ` should be set to a `TZ identifier` from [this list][tz-list]. For example, `TZ="Etc/UTC"`. `TZ` is used by `exiftool` as a fallback in case the timezone cannot be determined from the image metadata. It is also used for logfile timestamps and cron job execution. @@ -57,7 +57,7 @@ These environment variables are used by the `docker-compose.yml` file and do **N | `IMMICH_WORKERS_EXCLUDE` | Do not run these workers. Matches against default workers, or `IMMICH_WORKERS_INCLUDE` if specified. | | server | :::info -Information on the current workers can be found [here](/docs/administration/jobs-workers). +Information on the current workers can be found [here](/administration/jobs-workers). ::: ## Ports @@ -171,6 +171,7 @@ Redis (Sentinel) URL example JSON before encoding: | `MACHINE_LEARNING_MAX_BATCH_SIZE__FACIAL_RECOGNITION` | Set the maximum number of faces that will be processed at once by the facial recognition model | None (`1` if using OpenVINO) | machine learning | | `MACHINE_LEARNING_RKNN` | Enable RKNN hardware acceleration if supported | `True` | machine learning | | `MACHINE_LEARNING_RKNN_THREADS` | How many threads of RKNN runtime should be spinned up while inferencing. | `1` | machine learning | +| `MACHINE_LEARNING_MODEL_ARENA` | Pre-allocates CPU memory to avoid memory fragmentation | true | 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. diff --git a/docs/docs/install/one-click.md b/docs/docs/install/one-click.md new file mode 100644 index 0000000000..53fcb20d21 --- /dev/null +++ b/docs/docs/install/one-click.md @@ -0,0 +1,32 @@ +--- +sidebar_position: 65 +--- + +# One-Click [Cloud Service] + +:::note +This version of Immich is provided via cloud service providers' one-click marketplaces. Hosting costs are set by the cloud service providers. +Support for these are provided by the individual cloud service providers. + +**Please report issues to the corresponding [Github Repository][github].** +::: + +## Installation + +Go to the provider's marketplace and choose Immich, then follow the provided instructions. + +## One-Click Immich marketplace providers + +### DigitalOcean + +https://marketplace.digitalocean.com/apps/immich + +### Vultr + +https://www.vultr.com/marketplace/apps/immich + +## Issues + +For issues, open an issue on the associated [GitHub Repository][github]. + +[github]: https://github.com/immich-app/immich/ diff --git a/docs/docs/install/portainer.md b/docs/docs/install/portainer.md index 916d89a0d5..07fd255292 100644 --- a/docs/docs/install/portainer.md +++ b/docs/docs/install/portainer.md @@ -45,5 +45,5 @@ alt="Dot Env Example" 11. Click on "**Deploy the stack**". :::tip -For more information on how to use the application, please refer to the [Post Installation](/docs/install/post-install.mdx) guide. +For more information on how to use the application, please refer to the [Post Installation](/install/post-install.mdx) guide. ::: diff --git a/docs/docs/install/post-install.mdx b/docs/docs/install/post-install.mdx index 636274aaea..b30e91f3cd 100644 --- a/docs/docs/install/post-install.mdx +++ b/docs/docs/install/post-install.mdx @@ -44,6 +44,6 @@ A list of common steps to take after installing Immich include: ## Setting up optional features -- [External Libraries](/docs/features/libraries.md): Adding your existing photo library to Immich -- [Hardware Transcoding](/docs/features/hardware-transcoding.md): Speeding up video transcoding -- [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md): Speeding up various machine learning tasks in Immich +- [External Libraries](/features/libraries.md): Adding your existing photo library to Immich +- [Hardware Transcoding](/features/hardware-transcoding.md): Speeding up video transcoding +- [Hardware-Accelerated Machine Learning](/features/ml-hardware-acceleration.md): Speeding up various machine learning tasks in Immich diff --git a/docs/docs/install/script.md b/docs/docs/install/script.md index 93d1fb166c..ce05dc82d9 100644 --- a/docs/docs/install/script.md +++ b/docs/docs/install/script.md @@ -5,12 +5,12 @@ sidebar_position: 20 # Install script [Experimental] :::caution -This method is experimental and not currently recommended for production use. For production, please refer to installing with [Docker Compose](/docs/install/docker-compose.mdx). +This method is experimental and not currently recommended for production use. For production, please refer to installing with [Docker Compose](/install/docker-compose.mdx). ::: ## Requirements -Follow the [requirements page](/docs/install/requirements) to get started. +Follow the [requirements page](/install/requirements) to get started. The install script only supports Linux operating systems and requires Docker to be already installed on the system. @@ -32,5 +32,5 @@ The web application and mobile app will be available at `http:// + Updating Immich using Container Manager +Check the post installation and upgrade instructions at the links above before proceeding with this section. + +## Step 1. Backup + +Ensure your photos and videos are backed up. Your `.env` settings will define where they are stored. There is no need to delete any files or folders within the `docker` folder when doing a release upgrade unless instructed in the release notes. + +## Step 2. Check release notes + +Always check the [release notes](https://github.com/immich-app/immich/releases) before proceeding with an update! + +## Step 3. Stop containers & clean up + +Open **Container Manager**. Select **Project** then your Immich app + +![Select project](../../static/img/synology-select-proj.png) + +Select **Stop** + +![Stop project](../../static/img/synology-project-stop.png) + +Select **Action** then **Clean**. This removes the containers. + +![Clean project](../../static/img/synology-action-clean.png) + +Go to **Image** and select **Remove Unused Images**. + +![Remove unused](../../static/img/synology-remove-unused.png) + +## Step 4. Build + +Go to **Project**, select **Action** then **Build**. This will download, unpack, install and start the containers. + +![Build](../../static/img/synology-build.png) + +## 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. + +Go to the **Container** section. Click on `immich_server` and scroll down on **General** to find the IP address. +![Container IP](../../static/img/synology-container-ip.png) + +Go to Synology **Control Panel**. Select **Security** and **Firewall**. + +![Firewall](../../static/img/synology-fw-rules.png) + +In this example, the IP addresses mismatch and the firewall rule needs to be edited to match above. + +![Edit IP](../../static/img/synology-fw-ipedit.png) + + diff --git a/docs/docs/install/truenas.md b/docs/docs/install/truenas.md index a13147a1be..9135b72fe6 100644 --- a/docs/docs/install/truenas.md +++ b/docs/docs/install/truenas.md @@ -60,13 +60,13 @@ For an easy setup: :::tip To improve performance, Immich recommends using SSDs for the database. If you have a pool made of SSDs, you can create the `pgData` dataset on that pool. -Thumbnails can also be stored on the SSDs for faster access. This is an advanced option and not required for Immich to run. More information on how you can use multiple datasets to manage Immich storage in a finer-grained manner can be found in the [Advanced: Multiple Datasets for Immich Storage](#advanced-multiple-datasets-for-immich-storage) section below. +Thumbnails can also be stored on the SSDs for faster access. This is an advanced option and not required for Immich to run. More information on how you can use multiple datasets to manage Immich storage in a finer-grained manner can be found in the [Additional Storage: Multiple Datasets for Immich Storage](#additional-storage-advanced-users) section below. ::: :::warning If you just created the datasets using the **Apps** preset, you can skip this warning section. -If the **data** dataset uses ACL it must have [ACL mode](https://www.truenas.com/docs/scale/scaletutorials/datasets/permissionsscale/) set to `Passthrough` if you plan on using a [storage template](/docs/administration/storage-template.mdx) and the dataset is configured for network sharing (its ACL type is set to `SMB/NFSv4`). When the template is applied and files need to be moved from **upload** to **library** (internal folder created by Immich within the **data** dataset), Immich performs `chmod` internally and must be allowed to execute the command. [More info.](https://github.com/immich-app/immich/pull/13017) +If the **data** dataset uses ACL it must have [ACL mode](https://www.truenas.com/docs/scale/scaletutorials/datasets/permissionsscale/) set to `Passthrough` if you plan on using a [storage template](/administration/storage-template.mdx) and the dataset is configured for network sharing (its ACL type is set to `SMB/NFSv4`). When the template is applied and files need to be moved from **upload** to **library** (internal folder created by Immich within the **data** dataset), Immich performs `chmod` internally and must be allowed to execute the command. [More info.](https://github.com/immich-app/immich/pull/13017) To change or verify the ACL mode, go to the **Datasets** screen, select the **library** dataset, click on the **Edit** button next to **Dataset Details**, then click on the **Advanced Options** tab, scroll down to the **ACL Mode** section, and select `Passthrough` from the dropdown menu. Click **Save** to apply the changes. If the option is greyed out, set the **ACL Type** to `SMB/NFSv4` first, then you can change the **ACL Mode** to `Passthrough`. ::: @@ -129,7 +129,7 @@ The **Timezone** is set to the system default, which usually matches your local **Enable Machine Learning** is enabled by default. It allows Immich to use machine learning features such as face recognition, image search, and smart duplicate detection. Untick this option if you do not want to use these features. -Select the **Machine Learning Image Type** based on the hardware you have. More details here: [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md) +Select the **Machine Learning Image Type** based on the hardware you have. More details here: [Hardware-Accelerated Machine Learning](/features/ml-hardware-acceleration.md) **Database Password** should be set to a custom value using only the characters `A-Za-z0-9`. This password is used to secure the Postgres database. @@ -156,7 +156,7 @@ className="border rounded-xl" /> These are used to add custom configuration options or to enable specific features. -More information on available environment variables can be found in the **[environment variables documentation](/docs/install/environment-variables/)**. +More information on available environment variables can be found in the **[environment variables documentation](/install/environment-variables/)**. :::info Some environment variables are not available for the TrueNAS Community Edition app as they can be configured through GUI options in the [Edit Immich screen](#edit-app-settings). @@ -242,7 +242,7 @@ alt="Add External Libraries with Additional Storage" className="border rounded-xl" /> -You may configure [external libraries](/docs/features/libraries) by mounting them using **Additional Storage**. +You may configure [external libraries](/features/libraries) by mounting them using **Additional Storage**. The dataset that contains your external library files must at least give **read** access to the user running Immich (Default: `apps` (UID 568), `apps` (GID 568)). If you want to be able to delete files or edit metadata in the external library using Immich, you will need to give the **modify** permission to the user running Immich. @@ -266,7 +266,7 @@ A general recommendation is to mount any external libraries to a path beginning This feature should only be used by advanced users. ::: -Immich can use multiple datasets for its storage, allowing you to manage your data more granularly, similar to the old storage configuration. This is useful if you want to separate your data into different datasets for performance or organizational reasons. There is a general guide for this [here](/docs/guides/custom-locations), but read on for the TrueNAS guide. +Immich can use multiple datasets for its storage, allowing you to manage your data more granularly, similar to the old storage configuration. This is useful if you want to separate your data into different datasets for performance or organizational reasons. There is a general guide for this [here](/guides/custom-locations), but read on for the TrueNAS guide. Each additional dataset has to give the permission **_modify_** to the user who will run Immich (Default: `apps` (UID 568), `apps` (GID 568)) As described in the [Setting up Storage Datasets](#setting-up-storage-datasets) section above, you have to create the datasets with the **Apps** preset to ensure the correct permissions are set, or you can set the permissions manually after creating the datasets. @@ -309,7 +309,7 @@ className="border rounded-xl" Both **CPU** and **Memory** are limits, not reservations. This means that Immich can use up to the specified amount of CPU threads and RAM, but it will not reserve that amount of resources at all times. The system will allocate resources as needed, and Immich will use less than the specified amount most of the time. -- Enable **GPU Configuration** options if you have a GPU or CPU with integrated graphics that you will use for [Hardware Transcoding](/docs/features/hardware-transcoding) and/or [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md). +- Enable **GPU Configuration** options if you have a GPU or CPU with integrated graphics that you will use for [Hardware Transcoding](/features/hardware-transcoding) and/or [Hardware-Accelerated Machine Learning](/features/ml-hardware-acceleration.md). The process for NVIDIA GPU passthrough requires additional steps. More details here: [GPU Passthrough Docs for TrueNAS Apps](https://apps.truenas.com/managing-apps/installing-apps/#gpu-passthrough) @@ -332,7 +332,7 @@ Click **Web Portal** on the **Application Info** widget, or go to the URL `http: After that, you can start using Immich to upload and manage your photos and videos. :::tip -For more information on how to use the application once installed, please refer to the [Post Install](/docs/install/post-install.mdx) guide. +For more information on how to use the application once installed, please refer to the [Post Install](/install/post-install.mdx) guide. ::: ## Edit App Settings @@ -347,7 +347,7 @@ For more information on how to use the application once installed, please refer ## Updating the App :::danger -Make sure to read the general [upgrade instructions](/docs/install/upgrading.md). +Make sure to read the general [upgrade instructions](/install/upgrading.md). ::: When updates become available, TrueNAS alerts and provides easy updates. @@ -387,27 +387,35 @@ To migrate from the old storage configuration to the new one, you will need to c 3. **Copy the data** from the old datasets to the new dataset. We advise using the `rsync` command to copy the data, as it will preserve the permissions and ownership of the files. The following commands are examples: ```bash -rsync -av /mnt/tank/immich/library/ /mnt/tank/immich/data/library/ -rsync -av /mnt/tank/immich/upload/ /mnt/tank/immich/data/upload/ -rsync -av /mnt/tank/immich/thumbs/ /mnt/tank/immich/data/thumbs/ -rsync -av /mnt/tank/immich/profile/ /mnt/tank/immich/data/profile/ -rsync -av /mnt/tank/immich/video/ /mnt/tank/immich/data/encoded-video/ -rsync -av /mnt/tank/immich/backups/ /mnt/tank/immich/data/backups/ +sudo rsync -av /mnt/tank/immich/library/ /mnt/tank/immich/data/library/ +sudo rsync -av /mnt/tank/immich/upload/ /mnt/tank/immich/data/upload/ +sudo rsync -av /mnt/tank/immich/thumbs/ /mnt/tank/immich/data/thumbs/ +sudo rsync -av /mnt/tank/immich/profile/ /mnt/tank/immich/data/profile/ +sudo rsync -av /mnt/tank/immich/video/ /mnt/tank/immich/data/encoded-video/ +sudo rsync -av /mnt/tank/immich/backups/ /mnt/tank/immich/data/backups/ ``` Make sure to replace `/mnt/tank/immich/` with the correct path to your old datasets and `/mnt/tank/immich/data/` with the correct path to your new dataset. :::tip -If you were using **ixVolume (dataset created automatically by the system)** for Immich data storage, the path to the data should be `/mnt/.ix-apps/app_mounts/immich/`. You have to use this path instead of `/mnt/tank/immich/` in the `rsync` command above, for example: +If you were using **ixVolume (dataset created automatically by the system)** for some of Immich data storage, the path to the data should be `/mnt/.ix-apps/app_mounts/immich/`. You have to use this path instead of `/mnt/tank/immich/` in the `rsync` command above, for example: ```bash -rsync -av /mnt/.ix-apps/app_mounts/immich/library/ /mnt/tank/immich/data/library/ +sudo rsync -av /mnt/.ix-apps/app_mounts/immich/library/ /mnt/tank/immich/data/library/ ``` +If you also were storing your files in the **ixVolume**, the **_upload_** folder is named `uploads` instead of `upload`, so the command to run should be: + +```bash +sudo rsync -av /mnt/.ix-apps/app_mounts/immich/uploads/ /mnt/tank/immich/data/upload/ +``` + +This means that depending on your old storage configuration, you might have to use a mix of paths in the `rsync` commands above. + If you were also using an ixVolume for Postgres data storage, you also should, first create the pgData dataset, as described in the [Setting up Storage Datasets](#setting-up-storage-datasets) section above, and then you can use the following command to copy the Postgres data: ```bash -rsync -av /mnt/.ix-apps/app_mounts/immich/pgData/ /mnt/tank/immich/pgData/ +sudo rsync -av /mnt/.ix-apps/app_mounts/immich/pgData/ /mnt/tank/immich/pgData/ ``` ::: @@ -416,7 +424,7 @@ rsync -av /mnt/.ix-apps/app_mounts/immich/pgData/ /mnt/tank/immich/pgData/ Make sure that for each folder, the `.immich` file is copied as well, as it contains important metadata for Immich. If for some reason the `.immich` file is not copied, you can copy it manually with the `rsync` command, for example: ```bash -rsync -av /mnt/tank/immich/library/.immich /mnt/tank/immich/data/library/ +sudo rsync -av /mnt/tank/immich/library/.immich /mnt/tank/immich/data/library/ ``` Replace `library` with the name of the folder where you are copying the file. @@ -437,38 +445,37 @@ This will recreate the Immich container with the new storage configuration and s If everything went well, you should now be able to access Immich with the new storage configuration. You can verify that the data has been copied correctly by checking the Immich web interface and ensuring that all your photos and videos are still available. You may delete the old datasets, if you no longer need them, using the TrueNAS web interface. +:::tip If you were using **ixVolume (dataset created automatically by the system)** or folders for Immich data storage, you can delete the old datasets using the following commands: ```bash -rm -r /mnt/.ix-apps/app_mounts/immich/library -rm -r /mnt/.ix-apps/app_mounts/immich/uploads -rm -r /mnt/.ix-apps/app_mounts/immich/thumbs -rm -r /mnt/.ix-apps/app_mounts/immich/profile -rm -r /mnt/.ix-apps/app_mounts/immich/video -rm -r /mnt/.ix-apps/app_mounts/immich/backups +sudo rm -r /mnt/.ix-apps/app_mounts/immich/* ``` +::: + - + To migrate from the old storage configuration to the new one without creating new datasets. + 1. **Stop the Immich app** from the TrueNAS web interface to ensure no data is being written while you are updating the app. -2. **Update the datasets permissions**: Ensure that the datasets used for Immich data storage (`library`, `upload`, `thumbs`, `profile`, `video`, `backups`) have the correct permissions set for the user who will run Immich. The user should have ***modify*** permissions on these datasets. The default user for Immich is `apps` (UID 568) and the default group is `apps` (GID 568). If you are using a different user, make sure to set the permissions accordingly. You can do this from the TrueNAS web interface by going to the **Datasets** screen, selecting each dataset, clicking on the **Edit** button next to **Permissions**, and adding the user with ***modify*** permissions. +2. **Update the datasets permissions**: Ensure that the datasets used for Immich data storage (`library`, `upload`, `thumbs`, `profile`, `video`, `backups`) have the correct permissions set for the user who will run Immich. The user should have **_modify_** permissions on these datasets. The default user for Immich is `apps` (UID 568) and the default group is `apps` (GID 568). If you are using a different user, make sure to set the permissions accordingly. You can do this from the TrueNAS web interface by going to the **Datasets** screen, selecting each dataset, clicking on the **Edit** button next to **Permissions**, and adding the user with **_modify_** permissions. 3. **Update the Immich app** to use the existing datasets: - - Go to the **Installed Applications** screen and select Immich from the list of installed applications. - - Click **Edit** on the **Application Info** widget. - - In the **Storage Configuration** section, untick the **Use Old Storage Configuration (Deprecated)** checkbox. - - For the **Data Storage**, you can keep the **ixVolume (dataset created automatically by the system)** as no data will be directly written to it. We recommend selecting **Host Path (Path that already exists on the system)** and then select a **new** dataset you created for Immich data storage, for example, `data`. - - For the **Postgres Data Storage**, keep **Host Path (Path that already exists on the system)** and then select the existing dataset you used for Postgres data storage, for example, `pgData`. - - Following the instructions in the [Multiple Datasets for Immich Storage](#additional-storage-advanced-users) section, you can add, **for each old dataset**, a new Additional Storage with the following settings: - - **Type**: `Host Path (Path that already exists on the system)` - - **Mount Path**: `/data/` (e.g. `/data/library`) - - **Host Path**: `/mnt//` (e.g. `/mnt/tank/immich/library`) - :::danger Ensure using the correct paths names - Make sure to replace `` with the actual name of the folder used by Immich: `library`, `upload`, `thumbs`, `profile`, `encoded-video`, and `backups`. Also, replace `` and `` with the actual names of your pool and dataset. - ::: - - **Read Only**: Keep it unticked as Immich needs to write to these datasets. - - Click **Update** at the bottom of the page to save changes. + - Go to the **Installed Applications** screen and select Immich from the list of installed applications. + - Click **Edit** on the **Application Info** widget. + - In the **Storage Configuration** section, untick the **Use Old Storage Configuration (Deprecated)** checkbox. + - For the **Data Storage**, you can keep the **ixVolume (dataset created automatically by the system)** as no data will be directly written to it. We recommend selecting **Host Path (Path that already exists on the system)** and then select a **new** dataset you created for Immich data storage, for example, `data`. + - For the **Postgres Data Storage**, keep **Host Path (Path that already exists on the system)** and then select the existing dataset you used for Postgres data storage, for example, `pgData`. + - Following the instructions in the [Multiple Datasets for Immich Storage](#additional-storage-advanced-users) section, you can add, **for each old dataset**, a new Additional Storage with the following settings: + - **Type**: `Host Path (Path that already exists on the system)` + - **Mount Path**: `/data/` (e.g. `/data/library`) + - **Host Path**: `/mnt//` (e.g. `/mnt/tank/immich/library`) + :::danger Ensure using the correct paths names + Make sure to replace `` with the actual name of the folder used by Immich: `library`, `upload`, `thumbs`, `profile`, `encoded-video`, and `backups`. Also, replace `` and `` with the actual names of your pool and dataset. + ::: + - **Read Only**: Keep it unticked as Immich needs to write to these datasets. + - Click **Update** at the bottom of the page to save changes. 4. **Start the Immich app** from the TrueNAS web interface. This will recreate the Immich container with the new storage configuration and start the app. If everything went well, you should now be able to access Immich with the new storage configuration. You can verify that the data is still available by checking the Immich web interface and ensuring that all your photos and videos are still accessible. diff --git a/docs/docs/install/unraid.md b/docs/docs/install/unraid.md index efb493f267..ca7263a1e8 100644 --- a/docs/docs/install/unraid.md +++ b/docs/docs/install/unraid.md @@ -125,13 +125,13 @@ alt="Go to Docker Tab and visit the address listed next to immich-web" :::tip -For more information on how to use the application once installed, please refer to the [Post Install](/docs/install/post-install.mdx) guide. +For more information on how to use the application once installed, please refer to the [Post Install](/install/post-install.mdx) guide. ::: ## Updating Steps :::danger -Make sure to read the general [upgrade instructions](/docs/install/upgrading.md). +Make sure to read the general [upgrade instructions](/install/upgrading.md). ::: Updating is extremely easy however it's important to be aware that containers managed via the Docker Compose Manager plugin do not integrate with Unraid's native dockerman UI, the label "_update ready_" will always be present on containers installed via the Docker Compose Manager. diff --git a/docs/docs/install/upgrading.md b/docs/docs/install/upgrading.md index d638a6f7d1..bf788cb680 100644 --- a/docs/docs/install/upgrading.md +++ b/docs/docs/install/upgrading.md @@ -4,9 +4,7 @@ sidebar_position: 95 # Upgrading -:::danger Read the release notes -Immich is currently under heavy development, which means you can expect [breaking changes][breaking] and bugs. You should read the release notes prior to updating and take special care when using automated tools like [Watchtower][watchtower]. - +:::tip Breaking changes You can see versions that had breaking changes [here][breaking]. ::: @@ -40,7 +38,7 @@ If you do not deploy Immich using Docker Compose and see a deprecation warning f Immich has migrated off of the deprecated pgvecto.rs database extension to its successor, [VectorChord](https://github.com/tensorchord/VectorChord), which comes with performance improvements in almost every aspect. This section will guide you on how to make this change in a Docker Compose setup. -Before making any changes, please [back up your database](/docs/administration/backup-and-restore). While every effort has been made to make this migration as smooth as possible, there’s always a chance that something can go wrong. +Before making any changes, please [back up your database](/administration/backup-and-restore). While every effort has been made to make this migration as smooth as possible, there’s always a chance that something can go wrong. After making a backup, please modify your `docker-compose.yml` file with the following information. @@ -89,7 +87,7 @@ After making a backup, please modify your `docker-compose.yml` file with the fol If you deviated from the defaults of pg14 or pgvectors0.2.0, you must adjust the pg major version and pgvecto.rs version. If you are still using the default `docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0` image, you can just follow the changes above. For example, if the previous image is `docker.io/tensorchord/pgvecto-rs:pg16-v0.3.0`, the new image should be `ghcr.io/immich-app/postgres:16-vectorchord0.3.0-pgvectors0.3.0` instead of the image specified in the diff. ::: -After making these changes, you can start Immich as normal. Immich will make some changes to the DB during startup, which can take seconds to minutes to finish, depending on hardware and library size. In particular, it’s normal for the server logs to be seemingly stuck at `Reindexing clip_index` and `Reindexing face_index`for some time if you have over 100k assets in Immich and/or Immich is on a relatively weak server. If you see these logs and there are no errors, just give it time. +After making these changes, you can start Immich as normal. Immich will make some changes to the DB during startup, which can take seconds to minutes to finish, depending on hardware and library size. In particular, it’s normal for the server logs to be seemingly stuck at `Reindexing clip_index` and `Reindexing face_index` for some time if you have over 100k assets in Immich and/or Immich is on a relatively weak server. If you see these logs and there are no errors, just give it time. :::danger After switching to VectorChord, you should not downgrade Immich below 1.133.0. @@ -101,7 +99,7 @@ Please don’t hesitate to contact us on [GitHub](https://github.com/immich-app/ #### I have a separate PostgreSQL instance shared with multiple services. How can I switch to VectorChord? -Please see the [standalone PostgreSQL documentation](/docs/administration/postgres-standalone#migrating-to-vectorchord) for migration instructions. The migration path will be different depending on whether you’re currently using pgvecto.rs or pgvector, as well as whether Immich has superuser DB permissions. +Please see the [standalone PostgreSQL documentation](/administration/postgres-standalone#migrating-to-vectorchord) for migration instructions. The migration path will be different depending on whether you’re currently using pgvecto.rs or pgvector, as well as whether Immich has superuser DB permissions. #### Why are so many lines removed from the `docker-compose.yml` file? Does this mean the health check is removed? diff --git a/docs/docs/overview/help.md b/docs/docs/overview/help.md index f38ecde168..e6523547fa 100644 --- a/docs/docs/overview/help.md +++ b/docs/docs/overview/help.md @@ -6,7 +6,7 @@ sidebar_position: 6 Running into an issue or have a question? Try the following: -1. Check the [FAQs](/docs/FAQ.mdx). +1. Check the [FAQs](/FAQ.mdx). 2. Read through the [Release Notes][github-releases]. 3. Search through existing [GitHub Issues][github-issues]. 4. Open a help ticket on [Discord][discord-link]. diff --git a/docs/docs/overview/img/social-preview-light.webp b/docs/docs/overview/img/social-preview-light.webp deleted file mode 100644 index 3d088f6522..0000000000 Binary files a/docs/docs/overview/img/social-preview-light.webp and /dev/null differ diff --git a/docs/docs/overview/quick-start.mdx b/docs/docs/overview/quick-start.mdx index 28cee15007..d80a194ad2 100644 --- a/docs/docs/overview/quick-start.mdx +++ b/docs/docs/overview/quick-start.mdx @@ -13,7 +13,7 @@ to install and use it. - A system with at least 4GB of RAM and 2 CPU cores. - [Docker](https://docs.docker.com/engine/install/) -> For a more detailed list of requirements, see the [requirements page](/docs/install/requirements). +> For a more detailed list of requirements, see the [requirements page](/install/requirements). --- @@ -61,7 +61,7 @@ import MobileAppBackup from '/docs/partials/_mobile-app-backup.md'; The backup time differs depending on how many photos are on your mobile device. Large uploads may take quite a while. -To quickly get going, you can selectively upload few photos first, by following this [guide](/docs/features/mobile-app#sync-only-selected-photos). +To quickly get going, you can selectively upload few photos first, by following this [guide](/features/mobile-app#sync-only-selected-photos). You can select the **Jobs** tab to see Immich processing your photos. @@ -72,7 +72,7 @@ You can select the **Jobs** tab to see Immich processing your photos. ## Review the database backup and restore process Immich has built-in database backups. You can refer to the -[database backup](/docs/administration/backup-and-restore) for more information. +[database backup](/administration/backup-and-restore) for more information. :::danger The database only contains metadata and user information. You must setup manual backups of the images and videos stored in `UPLOAD_LOCATION`. @@ -86,8 +86,8 @@ You may decide you'd like to install the server a different way; the Install cat You may decide you'd like to add the _rest_ of your photos from Google Photos, even those not on your mobile device, via Google Takeout. You can use [immich-go](https://github.com/simulot/immich-go) for this. -You may want to [upload photos from your own archive](/docs/features/command-line-interface). +You may want to [upload photos from your own archive](/features/command-line-interface). -You may want to incorporate a pre-existing archive of photos from an [External Library](/docs/features/libraries); there's a [guide](/docs/guides/external-library) for that. +You may want to incorporate a pre-existing archive of photos from an [External Library](/features/libraries); there's a [guide](/guides/external-library) for that. -You may want your mobile device to [back photos up to your server automatically](/docs/features/automatic-backup). +You may want your mobile device to [back photos up to your server automatically](/features/automatic-backup). diff --git a/docs/docs/overview/support-the-project.md b/docs/docs/overview/support-the-project.md index a439893a7e..ae24a3f1ce 100644 --- a/docs/docs/overview/support-the-project.md +++ b/docs/docs/overview/support-the-project.md @@ -10,11 +10,11 @@ By far the easiest way to help make Immich better it to use it and report issues ## Translations -Support the project by localizing on [Weblate](https://hosted.weblate.org/projects/immich/immich/). For more information, see the [Translations](/docs/developer/translations) section. +Support the project by localizing on [Weblate](https://hosted.weblate.org/projects/immich/immich/). For more information, see the [Translations](/developer/translations) section. ## Development -If you are a programmer or developer, take a look at Immich's [technology stack](/docs/developer/architecture.mdx) and consider fixing bugs or building new features. The team and I are always looking for new contributors. For information about how to contribute as a developer, see the [Developer](/docs/developer/architecture.mdx) section. +If you are a programmer or developer, take a look at Immich's [technology stack](/developer/architecture.mdx) and consider fixing bugs or building new features. The team and I are always looking for new contributors. For information about how to contribute as a developer, see the [Developer](/developer/architecture.mdx) section. ## Purchase Immich diff --git a/docs/docs/overview/welcome.mdx b/docs/docs/overview/welcome.mdx deleted file mode 100644 index 93ce705369..0000000000 --- a/docs/docs/overview/welcome.mdx +++ /dev/null @@ -1,27 +0,0 @@ ---- -sidebar_position: 1 ---- - -# Welcome to Immich - -Immich - Self-hosted photos and videos backup tool - -## Welcome! - -Hello, I am glad you are here. - -My name is Alex. I am an Electrical Engineer by schooling, then turned into a Software Engineer by trade and the pure love of problem solving. - -We were lying in bed with our newborn, and my wife said, "We are starting to accumulate a lot of photos and videos of our baby, and I don't want to pay for **_App-Which-Must-Not-Be-Named_** anymore. You always want to build something for me, so why don't you build me an app which can do that?" - -That was how the idea started to grow in my head. After that, I began to find existing solutions in the self-hosting space with similar backup functionality and the performance level of the **_App-Which-Must-Not-Be-Named_**. I found that the current solutions mainly focus on the gallery-type application. However, I want a simple-to-use backup tool with a native mobile app that can view photos and videos efficiently. So I set sail on this journey as a hungry engineer on the hunt. - -Another motivation that pushed me to deliver my execution of the **_App-Which-Must-Not-Be-Named_** alternative or replacement is for contributing back to the open source community that I have greatly benefited from over the years. - -I'm proud to share this creation with you, which values privacy, memories, and the joy of looking back at those moments in an easy-to-use and friendly interface. - -If you like the application or it helps you in some way, please consider [supporting](./support-the-project.md) the project. It will help me to continue to develop and maintain the application. diff --git a/docs/docs/partials/_mobile-app-download.md b/docs/docs/partials/_mobile-app-download.md index 72a3053440..31cf62bf81 100644 --- a/docs/docs/partials/_mobile-app-download.md +++ b/docs/docs/partials/_mobile-app-download.md @@ -1,5 +1,6 @@ The mobile app can be downloaded from the following places: +- Obtainium: You can get your Obtainium config link from the [Utilities page of your Immich server](https://my.immich.app/utilities). - [Google Play Store](https://play.google.com/store/apps/details?id=app.alextran.immich) - [Apple App Store](https://apps.apple.com/us/app/immich/id1613945652) - [F-Droid](https://f-droid.org/packages/app.alextran.immich) diff --git a/docs/docs/partials/_server-backup.md b/docs/docs/partials/_server-backup.md index b9479600aa..34e09670e9 100644 --- a/docs/docs/partials/_server-backup.md +++ b/docs/docs/partials/_server-backup.md @@ -1,7 +1,6 @@ Now that you have imported some pictures, you should setup server backups to preserve your memories. -You can do so by following our [backup guide](/docs/administration/backup-and-restore.md). +You can do so by following our [backup guide](/administration/backup-and-restore.md). -:::danger -Immich is still under heavy development _and_ handles very important data. -It is essential that you set up good backups, and test them. +:::info +A 3-2-1 backup strategy is still crucial. The team has the responsibility to ensure that the application doesn’t cause loss of your precious memories; however, we cannot guarantee that hard drives will not fail, or an electrical event causes unexpected shutdown of your server/system, leading to data loss. Therefore, we still encourage users to follow best practices when safeguarding their data. Keep multiple copies of your most precious data: at least two local copies and one copy offsite in cold storage. ::: diff --git a/docs/docs/partials/_storage-template.md b/docs/docs/partials/_storage-template.md index 20e9caac43..84236e0ac1 100644 --- a/docs/docs/partials/_storage-template.md +++ b/docs/docs/partials/_storage-template.md @@ -1,7 +1,7 @@ -Immich allows the admin user to set the uploaded filename pattern at the directory and filename level as well as the [storage label for a user](/docs/administration/user-management/#set-storage-label-for-user). +Immich allows the admin user to set the uploaded filename pattern at the directory and filename level as well as the [storage label for a user](/administration/user-management/#set-storage-label-for-user). :::tip -You can read more about the differences between storage template engine on and off [here](/docs/administration/backup-and-restore#asset-types-and-storage-locations) +You can read more about the differences between storage template engine on and off [here](/administration/backup-and-restore#asset-types-and-storage-locations) ::: The admin user can set the template by using the template builder in the `Administration -> Settings -> Storage Template`. Immich provides a set of variables that you can use in constructing the template, along with additional custom text. If the template produces [multiple files with the same filename, they won't be overwritten](https://github.com/immich-app/immich/discussions/3324) as a sequence number is appended to the filename. diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index d612dda253..70e0189a00 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -7,7 +7,7 @@ const prism = require('prism-react-renderer'); const config = { title: 'Immich', tagline: 'High performance self-hosted photo and video backup solution directly from your mobile phone', - url: 'https://immich.app', + url: 'https://docs.immich.app', baseUrl: '/', onBrokenLinks: 'throw', onBrokenMarkdownLinks: 'warn', @@ -42,26 +42,19 @@ const config = { ], presets: [ [ - 'docusaurus-preset-openapi', - /** @type {import('docusaurus-preset-openapi').Options} */ + 'classic', + /** @type {import('@docusaurus/preset-classic').Options} */ ({ docs: { showLastUpdateAuthor: true, showLastUpdateTime: true, + routeBasePath: '/', sidebarPath: require.resolve('./sidebars.js'), // Please change this to your repo. // Remove this to remove the "edit this page" links. editUrl: 'https://github.com/immich-app/immich/tree/main/docs/', }, - api: { - path: '../open-api/immich-openapi-specs.json', - routeBasePath: '/docs/api', - }, - // blog: { - // showReadingTime: true, - // editUrl: "https://github.com/immich-app/immich/tree/main/docs/", - // }, theme: { customCss: require.resolve('./src/css/custom.css'), }, @@ -72,11 +65,6 @@ const config = { themeConfig: /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ ({ - announcementBar: { - id: 'site_announcement_immich', - content: `⚠️ The project is under very active development. Expect bugs and changes. Do not use it as the only way to store your photos and videos!`, - isCloseable: false, - }, docs: { sidebar: { autoCollapseCategories: false, @@ -95,17 +83,17 @@ const config = { position: 'right', }, { - to: '/docs/overview/welcome', + to: '/overview/quick-start', position: 'right', label: 'Docs', }, { - to: '/roadmap', + href: 'https://immich.app/roadmap', position: 'right', label: 'Roadmap', }, { - to: '/docs/api', + href: 'https://api.immich.app/', position: 'right', label: 'API', }, @@ -139,16 +127,16 @@ const config = { title: 'Overview', items: [ { - label: 'Welcome', - to: '/docs/overview/welcome', + label: 'Quick start', + to: '/overview/quick-start', }, { label: 'Installation', - to: '/docs/install/requirements', + to: '/install/requirements', }, { label: 'Contributing', - to: '/docs/overview/support-the-project', + to: '/overview/support-the-project', }, { label: 'Privacy Policy', @@ -161,15 +149,15 @@ const config = { items: [ { label: 'Roadmap', - to: '/roadmap', + href: 'https://immich.app/roadmap', }, { label: 'API', - to: '/docs/api', + href: 'https://api.immich.app/', }, { label: 'Cursed Knowledge', - to: '/cursed-knowledge', + href: 'https://immich.app/cursed-knowledge', }, ], }, diff --git a/docs/package.json b/docs/package.json index 1a1dbcf84c..f45a4ec256 100644 --- a/docs/package.json +++ b/docs/package.json @@ -17,15 +17,14 @@ "write-heading-ids": "docusaurus write-heading-ids" }, "dependencies": { - "@docusaurus/core": "~3.8.0", - "@docusaurus/preset-classic": "~3.8.0", - "@docusaurus/theme-common": "~3.8.0", + "@docusaurus/core": "~3.9.0", + "@docusaurus/preset-classic": "~3.9.0", + "@docusaurus/theme-common": "~3.9.0", "@mdi/js": "^7.3.67", "@mdi/react": "^1.6.1", "@mdx-js/react": "^3.0.0", "autoprefixer": "^10.4.17", "docusaurus-lunr-search": "^3.3.2", - "docusaurus-preset-openapi": "^0.7.5", "lunr": "^2.3.9", "postcss": "^8.4.25", "prism-react-renderer": "^2.3.1", @@ -36,7 +35,7 @@ "url": "^0.11.0" }, "devDependencies": { - "@docusaurus/module-type-aliases": "~3.8.0", + "@docusaurus/module-type-aliases": "~3.9.0", "@docusaurus/tsconfig": "^3.7.0", "@docusaurus/types": "^3.7.0", "prettier": "^3.2.4", @@ -58,6 +57,6 @@ "node": ">=20" }, "volta": { - "node": "22.19.0" + "node": "22.20.0" } } diff --git a/docs/src/components/community-guides.tsx b/docs/src/components/community-guides.tsx deleted file mode 100644 index 08c8e096d9..0000000000 --- a/docs/src/components/community-guides.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import Link from '@docusaurus/Link'; -import React from 'react'; - -interface CommunityGuidesProps { - title: string; - description: string; - url: string; -} - -const guides: CommunityGuidesProps[] = [ - { - title: 'Cloudflare Tunnels with SSO/OAuth', - description: `Setting up Cloudflare Tunnels and a SaaS App for Immich.`, - url: 'https://github.com/immich-app/immich/discussions/8299', - }, - { - title: 'Database backup in TrueNAS', - description: `Create a database backup with pgAdmin in TrueNAS.`, - url: 'https://github.com/immich-app/immich/discussions/8809', - }, - { - title: 'Unraid backup scripts', - description: `Back up your assets in Unraid with a pre-prepared script.`, - url: 'https://github.com/immich-app/immich/discussions/8416', - }, - { - title: 'Sync folders with albums', - description: `synchronize folders in imported library with albums having the folders name.`, - url: 'https://github.com/immich-app/immich/discussions/3382', - }, - { - title: 'Immich Podman Quadlets Handbook', - description: - 'A rewrite of the original Immich Docker Compose file using Podman Quadlets, with a set of extra guides in the repository’s wiki.', - url: 'https://github.com/linux-universe/immich-podman-quadlets/blob/main/README.md', - }, - { - title: 'Podman/Quadlets Install', - description: 'Documentation for simple podman setup using quadlets.', - url: 'https://github.com/tbelway/immich-podman-quadlets/blob/main/docs/install/podman-quadlet.md', - }, - { - title: 'Google Photos import + albums', - description: 'Import your Google Photos files into Immich and add your albums.', - url: 'https://github.com/immich-app/immich/discussions/1340', - }, - { - title: 'Access Immich with custom domain', - description: 'Access your local Immich installation over the internet using your own domain.', - url: 'https://github.com/ppr88/immich-guides/blob/main/open-immich-custom-domain.md', - }, - { - title: 'Nginx caching map server', - description: 'Increase privacy by using nginx as a caching proxy in front of a map tile server.', - url: 'https://github.com/pcouy/pcouy.github.io/blob/main/_posts/2024-08-30-proxying-a-map-tile-server-for-increased-privacy.md', - }, - { - title: 'fail2ban setup instructions', - description: 'How to configure an existing fail2ban installation to block incorrect login attempts.', - url: 'https://github.com/immich-app/immich/discussions/3243#discussioncomment-6681948', - }, - { - title: 'Immich remote access with NordVPN Meshnet', - description: 'Access Immich with an end-to-end encrypted connection.', - url: 'https://meshnet.nordvpn.com/how-to/remote-files-media-access/immich-remote-access', - }, - { - title: 'Trust Self Signed Certificates with Immich - OAuth Setup', - description: - 'Set up Certificate Authority trust with Immich, and your private OAuth2/OpenID service, while using a private CA for HTTPS commication.', - url: 'https://github.com/immich-app/immich/discussions/18614', - }, -]; - -function CommunityGuide({ title, description, url }: CommunityGuidesProps): JSX.Element { - return ( -
-
-

- {title} -

- -

{description}

-

- {url} -

-
-
- - View Guide - -
-
- ); -} - -export default function CommunityGuides(): JSX.Element { - return ( -
- {guides.map((guides) => ( - - ))} -
- ); -} diff --git a/docs/src/components/community-projects.tsx b/docs/src/components/community-projects.tsx deleted file mode 100644 index efce831df0..0000000000 --- a/docs/src/components/community-projects.tsx +++ /dev/null @@ -1,158 +0,0 @@ -import Link from '@docusaurus/Link'; -import React from 'react'; - -interface CommunityProjectProps { - title: string; - description: string; - url: string; -} - -const projects: CommunityProjectProps[] = [ - { - title: 'immich-go', - description: `An alternative to the immich-CLI that doesn't depend on nodejs. It specializes in importing Google Photos Takeout archives.`, - url: 'https://github.com/simulot/immich-go', - }, - { - title: 'ImmichFrame', - description: 'Run an Immich slideshow in a photo frame.', - url: 'https://github.com/3rob3/ImmichFrame', - }, - { - title: 'API Album Sync', - description: 'A Python script to sync folders as albums.', - url: 'https://git.orenit.solutions/open/immichalbumpull', - }, - { - title: 'Remove offline files', - description: 'A simple way to remove orphaned offline assets from the Immich database', - url: 'https://github.com/Thoroslives/immich_remove_offline_files', - }, - { - title: 'Immich-Tools', - description: 'Provides scripts for handling problems on the repair page.', - url: 'https://github.com/clumsyCoder00/Immich-Tools', - }, - { - title: 'Lightroom Publisher: mi.Immich.Publisher', - description: 'Lightroom plugin to publish photos from Lightroom collections to Immich albums.', - url: 'https://github.com/midzelis/mi.Immich.Publisher', - }, - { - title: 'Lightroom Immich Plugin: lrc-immich-plugin', - description: - 'Lightroom plugin to publish, export photos from Lightroom to Immich. Import from Immich to Lightroom is also supported.', - url: 'https://blog.fokuspunk.de/lrc-immich-plugin/', - }, - { - title: 'Immich-Tiktok-Remover', - description: 'Script to search for and remove TikTok videos from your Immich library.', - url: 'https://github.com/mxc2/immich-tiktok-remover', - }, - { - title: 'Immich Android TV', - description: 'Unofficial Immich Android TV app.', - url: 'https://github.com/giejay/Immich-Android-TV', - }, - { - title: 'Create albums from folders', - description: 'A Python script to create albums based on the folder structure of an external library.', - url: 'https://github.com/Salvoxia/immich-folder-album-creator', - }, - { - title: 'Powershell Module PSImmich', - description: 'Powershell Module for the Immich API', - url: 'https://github.com/hanpq/PSImmich', - }, - { - title: 'Immich Distribution', - description: 'Snap package for easy install and zero-care auto updates of Immich. Self-hosted photo management.', - url: 'https://immich-distribution.nsg.cc', - }, - { - title: 'Immich Kiosk', - description: 'Lightweight slideshow to run on kiosk devices and browsers.', - url: 'https://github.com/damongolding/immich-kiosk', - }, - { - title: 'Immich Power Tools', - description: 'Power tools for organizing your immich library.', - url: 'https://github.com/varun-raj/immich-power-tools', - }, - { - title: 'Immich Public Proxy', - description: - 'Share your Immich photos and albums in a safe way without exposing your Immich instance to the public.', - url: 'https://github.com/alangrainger/immich-public-proxy', - }, - { - title: 'Immich Kodi', - description: 'Unofficial Kodi plugin for Immich.', - url: 'https://github.com/vladd11/immich-kodi', - }, - { - title: 'Immich Downloader', - description: 'Downloads a configurable number of random photos based on people or album ID.', - url: 'https://github.com/jon6fingrs/immich-dl', - }, - { - title: 'Immich Upload Optimizer', - description: 'Automatically optimize files uploaded to Immich in order to save storage space', - url: 'https://github.com/miguelangel-nubla/immich-upload-optimizer', - }, - { - title: 'Immich Machine Learning Load Balancer', - description: 'Speed up your machine learning by load balancing your requests to multiple computers', - url: 'https://github.com/apetersson/immich_ml_balancer', - }, - { - title: 'Immich Drop Uploader', - description: 'A tiny, zero-login web app for collecting photos/videos from anyone into your Immich server.', - url: 'https://github.com/Nasogaa/immich-drop', - }, - { - title: 'Immich Birthday Sync', - description: 'Bulk-upload and -download birthdays, with CardDAV sync support', - url: 'https://github.com/sid3windr/immich-birthday', - }, - { - title: 'Immich Stack', - description: 'Auto-stack photos with identical filenames and differing extensions (i.e. JPG+RAW)', - url: 'https://github.com/sid3windr/immich-stack', - }, -]; - -function CommunityProject({ title, description, url }: CommunityProjectProps): JSX.Element { - return ( -
-
-

- {title} -

- -

{description}

-

- {url} -

-
-
- - View Link - -
-
- ); -} - -export default function CommunityProjects(): JSX.Element { - return ( -
- {projects.map((project) => ( - - ))} -
- ); -} diff --git a/docs/src/components/svg-paths.ts b/docs/src/components/svg-paths.ts deleted file mode 100644 index 0903392307..0000000000 --- a/docs/src/components/svg-paths.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const discordPath = - 'M81.15,0c-1.2376,2.1973-2.3489,4.4704-3.3591,6.794-9.5975-1.4396-19.3718-1.4396-28.9945,0-.985-2.3236-2.1216-4.5967-3.3591-6.794-9.0166,1.5407-17.8059,4.2431-26.1405,8.0568C2.779,32.5304-1.6914,56.3725.5312,79.8863c9.6732,7.1476,20.5083,12.603,32.0505,16.0884,2.6014-3.4854,4.8998-7.1981,6.8698-11.0623-3.738-1.3891-7.3497-3.1318-10.8098-5.1523.9092-.6567,1.7932-1.3386,2.6519-1.9953,20.281,9.547,43.7696,9.547,64.0758,0,.8587.7072,1.7427,1.3891,2.6519,1.9953-3.4601,2.0457-7.0718,3.7632-10.835,5.1776,1.97,3.8642,4.2683,7.5769,6.8698,11.0623,11.5419-3.4854,22.3769-8.9156,32.0509-16.0631,2.626-27.2771-4.496-50.9172-18.817-71.8548C98.9811,4.2684,90.1918,1.5659,81.1752.0505l-.0252-.0505ZM42.2802,65.4144c-6.2383,0-11.4159-5.6575-11.4159-12.6535s4.9755-12.6788,11.3907-12.6788,11.5169,5.708,11.4159,12.6788c-.101,6.9708-5.026,12.6535-11.3907,12.6535ZM84.3576,65.4144c-6.2637,0-11.3907-5.6575-11.3907-12.6535s4.9755-12.6788,11.3907-12.6788,11.4917,5.708,11.3906,12.6788c-.101,6.9708-5.026,12.6535-11.3906,12.6535Z'; -export const discordViewBox = '0 0 126.644 96'; diff --git a/docs/src/components/version-switcher.tsx b/docs/src/components/version-switcher.tsx index 5cb23891aa..739d7bd001 100644 --- a/docs/src/components/version-switcher.tsx +++ b/docs/src/components/version-switcher.tsx @@ -11,7 +11,7 @@ export default function VersionSwitcher(): JSX.Element { useEffect(() => { async function getVersions() { try { - let baseUrl = 'https://immich.app'; + let baseUrl = 'https://docs.immich.app'; if (window.location.origin === 'http://localhost:3005') { baseUrl = window.location.origin; } @@ -21,12 +21,13 @@ export default function VersionSwitcher(): JSX.Element { const archiveVersions = await response.json(); const allVersions = [ - { label: 'Next', url: 'https://main.preview.immich.app' }, - { label: 'Latest', url: 'https://immich.app' }, + { label: 'Next', url: 'https://docs.main.preview.immich.app' }, + { label: 'Latest', url: 'https://docs.immich.app' }, ...archiveVersions, - ].map(({ label, url }) => ({ + ].map(({ label, url, rootPath }) => ({ label, url: new URL(url), + rootPath, })); setVersions(allVersions); @@ -50,12 +51,18 @@ export default function VersionSwitcher(): JSX.Element { className="version-switcher-34ab39" label={activeLabel} mobile={windowSize === 'mobile'} - items={versions.map(({ label, url }) => ({ - label, - to: new URL(location.pathname + location.search + location.hash, url).href, - target: '_self', - className: label === activeLabel ? 'dropdown__link--active menu__link--active' : '', // workaround because React Router `` only supports using URL path for checking if active: https://v5.reactrouter.com/web/api/NavLink/isactive-func - }))} + items={versions.map(({ label, url, rootPath }) => { + let path = location.pathname + location.search + location.hash; + if (rootPath && !path.startsWith(rootPath)) { + path = rootPath + path; + } + return { + label, + to: new URL(path, url).href, + target: '_self', + className: label === activeLabel ? 'dropdown__link--active menu__link--active' : '', // workaround because React Router `` only supports using URL path for checking if active: https://v5.reactrouter.com/web/api/NavLink/isactive-func + }; + })} /> ) ); diff --git a/docs/src/pages/cursed-knowledge.tsx b/docs/src/pages/cursed-knowledge.tsx deleted file mode 100644 index f3dacc2ce6..0000000000 --- a/docs/src/pages/cursed-knowledge.tsx +++ /dev/null @@ -1,273 +0,0 @@ -import { - mdiBug, - mdiCalendarToday, - mdiCrosshairsOff, - mdiCrop, - mdiDatabase, - mdiLeadPencil, - mdiLockOff, - mdiLockOutline, - mdiMicrosoftWindows, - mdiSecurity, - mdiSpeedometerSlow, - mdiTrashCan, - mdiWeb, - mdiWrap, - mdiCloudKeyOutline, - mdiRegex, - mdiCodeJson, - mdiClockOutline, - mdiAccountOutline, - mdiRestart, -} from '@mdi/js'; -import Layout from '@theme/Layout'; -import React from 'react'; -import { Timeline, Item as TimelineItem } from '../components/timeline'; - -const withLanguage = (date: Date) => (language: string) => date.toLocaleDateString(language); - -type Item = Omit & { date: Date }; - -const items: Item[] = [ - { - icon: mdiClockOutline, - iconColor: 'gray', - title: 'setTimeout is cursed', - description: - 'The setTimeout method in JavaScript is cursed when used with small values because the implementation may or may not actually wait the specified time.', - link: { - url: 'https://github.com/immich-app/immich/pull/20655', - text: '#20655', - }, - date: new Date(2025, 7, 4), - }, - { - icon: mdiAccountOutline, - iconColor: '#DAB1DA', - title: 'PostgreSQL USER is cursed', - description: - 'The USER keyword in PostgreSQL is cursed because you can select from it like a table, which leads to confusion if you have a table name user as well.', - link: { - url: 'https://github.com/immich-app/immich/pull/19891', - text: '#19891', - }, - date: new Date(2025, 7, 4), - }, - { - icon: mdiRestart, - iconColor: '#8395e3', - title: 'PostgreSQL RESET is cursed', - description: - 'PostgreSQL RESET is cursed because it is impossible to RESET a PostgreSQL extension parameter if the extension has been uninstalled.', - link: { - url: 'https://github.com/immich-app/immich/pull/19363', - text: '#19363', - }, - date: new Date(2025, 5, 20), - }, - { - icon: mdiRegex, - iconColor: 'purple', - title: 'Zitadel Actions are cursed', - description: - "Zitadel is cursed because its custom scripting feature is executed with a JS engine that doesn't support regex named capture groups.", - link: { - url: 'https://github.com/dop251/goja', - text: 'Go JS engine', - }, - date: new Date(2025, 5, 4), - }, - { - icon: mdiCloudKeyOutline, - iconColor: '#0078d4', - title: 'Entra is cursed', - description: - "Microsoft Entra supports PKCE, but doesn't include it in its OpenID discovery document. This leads to clients thinking PKCE isn't available.", - link: { - url: 'https://github.com/immich-app/immich/pull/18725', - text: '#18725', - }, - date: new Date(2025, 4, 30), - }, - { - icon: mdiCrop, - iconColor: 'tomato', - title: 'Image dimensions in EXIF metadata are cursed', - description: - 'The dimensions in EXIF metadata can be different from the actual dimensions of the image, causing issues with cropping and resizing.', - link: { - url: 'https://github.com/immich-app/immich/pull/17974', - text: '#17974', - }, - date: new Date(2025, 4, 5), - }, - { - icon: mdiCodeJson, - iconColor: 'yellow', - title: 'YAML whitespace is cursed', - description: 'YAML whitespaces are often handled in unintuitive ways.', - link: { - url: 'https://github.com/immich-app/immich/pull/17309', - text: '#17309', - }, - date: new Date(2025, 3, 1), - }, - { - icon: mdiMicrosoftWindows, - iconColor: '#357EC7', - title: 'Hidden files in Windows are cursed', - description: - 'Hidden files in Windows cannot be opened with the "w" flag. That, combined with SMB option "hide dot files" leads to a lot of confusion.', - link: { - url: 'https://github.com/immich-app/immich/pull/12812', - text: '#12812', - }, - date: new Date(2024, 8, 20), - }, - { - icon: mdiWrap, - iconColor: 'gray', - title: 'Carriage returns in bash scripts are cursed', - description: 'Git can be configured to automatically convert LF to CRLF on checkout and CRLF breaks bash scripts.', - link: { - url: 'https://github.com/immich-app/immich/pull/11613', - text: '#11613', - }, - date: new Date(2024, 7, 7), - }, - { - icon: mdiLockOff, - iconColor: 'red', - title: 'Fetch inside Cloudflare Workers is cursed', - description: - 'Fetch requests in Cloudflare Workers use http by default, even if you explicitly specify https, which can often cause redirect loops.', - link: { - url: 'https://community.cloudflare.com/t/does-cloudflare-worker-allow-secure-https-connection-to-fetch-even-on-flexible-ssl/68051/5', - text: 'Cloudflare', - }, - date: new Date(2024, 7, 7), - }, - { - icon: mdiCrosshairsOff, - iconColor: 'gray', - title: 'GPS sharing on mobile is cursed', - description: - 'Some phones will silently strip GPS data from images when apps without location permission try to access them.', - link: { - url: 'https://github.com/immich-app/immich/discussions/11268', - text: '#11268', - }, - date: new Date(2024, 6, 21), - }, - { - icon: mdiLeadPencil, - iconColor: 'gold', - title: 'PostgreSQL NOTIFY is cursed', - description: - 'PostgreSQL does everything in a transaction, including NOTIFY. This means using the socket.io postgres-adapter writes to WAL every 5 seconds.', - link: { url: 'https://github.com/immich-app/immich/pull/10801', text: '#10801' }, - date: new Date(2024, 6, 3), - }, - { - icon: mdiWeb, - iconColor: 'lightskyblue', - title: 'npm scripts are cursed', - description: - 'npm scripts make a http call to the npm registry each time they run, which means they are a terrible way to execute a health check.', - link: { url: 'https://github.com/immich-app/immich/issues/10796', text: '#10796' }, - date: new Date(2024, 6, 3), - }, - { - icon: mdiSpeedometerSlow, - iconColor: 'brown', - title: '50 extra packages are cursed', - description: - 'There is a user in the JavaScript community who goes around adding "backwards compatibility" to projects. They do this by adding 50 extra package dependencies to your project, which are maintained by them.', - link: { url: 'https://github.com/immich-app/immich/pull/10690', text: '#10690' }, - date: new Date(2024, 5, 28), - }, - { - icon: mdiLockOutline, - iconColor: 'gold', - title: 'Long passwords are cursed', - description: - 'The bcrypt implementation only uses the first 72 bytes of a string. Any characters after that are ignored.', - // link: GHSA-4p64-9f7h-3432 - date: new Date(2024, 5, 25), - }, - { - icon: mdiCalendarToday, - iconColor: 'greenyellow', - title: 'JavaScript Date objects are cursed', - description: 'JavaScript date objects are 1 indexed for years and days, but 0 indexed for months.', - link: { url: 'https://github.com/immich-app/immich/pull/6787', text: '#6787' }, - date: new Date(2024, 0, 31), - }, - { - icon: mdiBug, - iconColor: 'green', - title: 'ESM imports are cursed', - description: - 'Prior to Node.js v20.8 using --experimental-vm-modules in a CommonJS project that imported an ES module that imported a CommonJS modules would create a segfault and crash Node.js', - link: { - url: 'https://github.com/immich-app/immich/pull/6719', - text: '#6179', - }, - date: new Date(2024, 0, 9), - }, - { - icon: mdiDatabase, - iconColor: 'gray', - title: 'PostgreSQL parameters are cursed', - description: `PostgresSQL has a limit of ${Number(65535).toLocaleString()} parameters, so bulk inserts can fail with large datasets.`, - link: { - url: 'https://github.com/immich-app/immich/pull/6034', - text: '#6034', - }, - date: new Date(2023, 11, 28), - }, - { - icon: mdiSecurity, - iconColor: 'gold', - title: 'Secure contexts are cursed', - description: `Some web features like the clipboard API only work in "secure contexts" (ie. https or localhost)`, - link: { - url: 'https://github.com/immich-app/immich/issues/2981', - text: '#2981', - }, - date: new Date(2023, 5, 26), - }, - { - icon: mdiTrashCan, - iconColor: 'gray', - title: 'TypeORM deletes are cursed', - description: `The remove implementation in TypeORM mutates the input, deleting the id property from the original object.`, - link: { - url: 'https://github.com/typeorm/typeorm/issues/7024#issuecomment-948519328', - text: 'typeorm#6034', - }, - date: new Date(2023, 1, 23), - }, -]; - -export default function CursedKnowledgePage(): JSX.Element { - return ( - -
-

- Cursed Knowledge -

-

- Cursed knowledge we have learned as a result of building Immich that we wish we never knew. -

-
- b.date.getTime() - a.date.getTime()) - .map((item) => ({ ...item, getDateLabel: withLanguage(item.date) }))} - /> -
-
-
- ); -} diff --git a/docs/src/pages/index.tsx b/docs/src/pages/index.tsx index 277a1d0b46..37455cde16 100644 --- a/docs/src/pages/index.tsx +++ b/docs/src/pages/index.tsx @@ -1,123 +1,5 @@ -import React from 'react'; -import Link from '@docusaurus/Link'; -import Layout from '@theme/Layout'; -import { discordPath, discordViewBox } from '@site/src/components/svg-paths'; -import ThemedImage from '@theme/ThemedImage'; -import Icon from '@mdi/react'; - -function HomepageHeader() { - return ( -
-
- Immich logo -
-
-
- - - - -
-

- Self-hosted{' '} - - photo and - video management{' '} - - solution -

- -

- Easily back up, organize, and manage your photos on your own server. Immich helps you - browse, search and organize your photos and videos with ease, without - sacrificing your privacy. -

-
-
- - Get Started - - - - Open Demo - -
- -
- - Join our Discord -
- -
-
-
- -
-

Download the mobile app

-

- Download the Immich app and start backing up your photos and videos securely to your own server -

-
-
-
- - Get it on Google Play - -
- -
- - Download on the App Store - -
- -
- - Download APK - -
-
- -
-
- ); -} +import { Redirect } from '@docusaurus/router'; export default function Home(): JSX.Element { - return ( - - -
-

This project is available under GNU AGPL v3 license.

-

Privacy should not be a luxury

-
-
- ); + return ; } diff --git a/docs/src/pages/roadmap.tsx b/docs/src/pages/roadmap.tsx deleted file mode 100644 index e002c4d032..0000000000 --- a/docs/src/pages/roadmap.tsx +++ /dev/null @@ -1,944 +0,0 @@ -import { - mdiAccountGroup, - mdiAccountGroupOutline, - mdiAndroid, - mdiAppleIos, - mdiArchiveOutline, - mdiBash, - mdiBookSearchOutline, - mdiBookmark, - mdiCakeVariant, - mdiCameraBurst, - mdiChartBoxMultipleOutline, - mdiCheckAll, - mdiCheckboxMarked, - mdiCloudUploadOutline, - mdiCollage, - mdiContentDuplicate, - mdiCrop, - mdiDevices, - mdiEmailOutline, - mdiExpansionCard, - mdiEyeOutline, - mdiEyeRefreshOutline, - mdiFaceMan, - mdiFaceManOutline, - mdiFile, - mdiFileSearch, - mdiFlash, - mdiFolder, - mdiFolderMultiple, - mdiForum, - mdiHandshakeOutline, - mdiHeart, - mdiHistory, - mdiImage, - mdiImageAlbum, - mdiImageEdit, - mdiImageMultipleOutline, - mdiImageSearch, - mdiKeyboardSettingsOutline, - mdiLicense, - mdiLockOutline, - mdiMagnify, - mdiMagnifyScan, - mdiMap, - mdiMaterialDesign, - mdiMatrix, - mdiMerge, - mdiMonitor, - mdiMotionPlayOutline, - mdiPalette, - mdiPanVertical, - mdiPartyPopper, - mdiPencil, - mdiRaw, - mdiRocketLaunch, - mdiRotate360, - mdiScaleBalance, - mdiSecurity, - mdiServer, - mdiShare, - mdiShareAll, - mdiShareCircle, - mdiStar, - mdiStarOutline, - mdiTableKey, - mdiTag, - mdiTagMultiple, - mdiText, - mdiThemeLightDark, - mdiTrashCanOutline, - mdiVectorCombine, - mdiFolderSync, - mdiFaceRecognition, - mdiVideo, - mdiWeb, - mdiDatabaseOutline, - mdiLinkEdit, - mdiTagFaces, - mdiMovieOpenPlayOutline, - mdiCast, -} from '@mdi/js'; -import Layout from '@theme/Layout'; -import React from 'react'; -import { Item, Timeline } from '../components/timeline'; - -const releases = { - 'v1.135.0': new Date(2025, 5, 18), - 'v1.133.0': new Date(2025, 4, 21), - 'v1.130.0': new Date(2025, 2, 25), - 'v1.127.0': new Date(2025, 1, 26), - 'v1.122.0': new Date(2024, 11, 5), - 'v1.120.0': new Date(2024, 10, 6), - 'v1.114.0': new Date(2024, 8, 6), - 'v1.113.0': new Date(2024, 7, 30), - 'v1.112.0': new Date(2024, 7, 14), - 'v1.111.0': new Date(2024, 6, 26), - 'v1.110.0': new Date(2024, 5, 11), - 'v1.109.0': new Date(2024, 6, 18), - 'v1.106.1': new Date(2024, 5, 11), - 'v1.104.0': new Date(2024, 4, 13), - 'v1.103.0': new Date(2024, 3, 29), - 'v1.102.0': new Date(2024, 3, 15), - 'v1.99.0': new Date(2024, 2, 20), - 'v1.98.0': new Date(2024, 2, 7), - 'v1.95.0': new Date(2024, 1, 20), - 'v1.94.0': new Date(2024, 0, 31), - 'v1.93.0': new Date(2024, 0, 19), - 'v1.91.0': new Date(2023, 11, 15), - 'v1.90.0': new Date(2023, 11, 7), - 'v1.88.0': new Date(2023, 10, 20), - 'v1.84.0': new Date(2023, 10, 1), - 'v1.83.0': new Date(2023, 9, 28), - 'v1.82.0': new Date(2023, 9, 17), - 'v1.79.0': new Date(2023, 8, 21), - 'v1.76.0': new Date(2023, 7, 29), - 'v1.75.0': new Date(2023, 7, 26), - 'v1.72.0': new Date(2023, 7, 6), - 'v1.71.0': new Date(2023, 6, 29), - 'v1.69.0': new Date(2023, 6, 23), - 'v1.68.0': new Date(2023, 6, 20), - 'v1.67.0': new Date(2023, 6, 14), - 'v1.66.0': new Date(2023, 6, 4), - 'v1.65.0': new Date(2023, 5, 30), - 'v1.63.0': new Date(2023, 5, 24), - 'v1.61.0': new Date(2023, 5, 16), - 'v1.58.0': new Date(2023, 4, 28), - 'v1.57.0': new Date(2023, 4, 23), - 'v1.56.0': new Date(2023, 4, 18), - 'v1.55.0': new Date(2023, 4, 9), - 'v1.54.0': new Date(2023, 3, 18), - 'v1.52.0': new Date(2023, 2, 29), - 'v1.51.0': new Date(2023, 2, 20), - 'v1.48.0': new Date(2023, 1, 21), - 'v1.47.0': new Date(2023, 1, 13), - 'v1.46.0': new Date(2023, 1, 9), - 'v1.43.0': new Date(2023, 1, 3), - 'v1.41.0': new Date(2023, 0, 10), - 'v1.39.0': new Date(2022, 11, 19), - 'v1.36.0': new Date(2022, 10, 20), - 'v1.33.1': new Date(2022, 9, 26), - 'v1.32.0': new Date(2022, 9, 14), - 'v1.27.0': new Date(2022, 8, 6), - 'v1.24.0': new Date(2022, 7, 19), - 'v1.10.0': new Date(2022, 4, 29), - 'v1.7.0': new Date(2022, 3, 24), - 'v1.3.0': new Date(2022, 2, 22), - 'v1.2.0': new Date(2022, 1, 8), -} as const; - -const weirdTags = { - 'v1.41.0': 'v1.41.1_64-dev', - 'v1.39.0': 'v1.39.0_61-dev', - 'v1.36.0': 'v1.36.0_55-dev', - 'v1.33.1': 'v1.33.0_52-dev', - 'v1.32.0': 'v1.32.0_50-dev', - 'v1.27.0': 'v1.27.0_37-dev', - 'v1.24.0': 'v1.24.0_34-dev', - 'v1.10.0': 'v1.10.0_15-dev', - 'v1.7.0': 'v1.7.0_11-dev ', - 'v1.3.0': 'v1.3.0-dev ', - 'v1.2.0': 'v0.2-dev ', -}; - -const title = 'Roadmap'; -const description = 'A list of future plans and goals, as well as past achievements and milestones.'; - -const withLanguage = (date: Date) => (language: string) => date.toLocaleDateString(language); - -type Base = { icon: string; iconColor?: React.CSSProperties['color']; title: string; description: string }; -const withRelease = ({ - icon, - iconColor, - title, - description, - release: version, -}: Base & { release: keyof typeof releases }) => { - return { - icon, - iconColor: iconColor ?? 'gray', - title, - description, - link: { - url: `https://github.com/immich-app/immich/releases/tag/${weirdTags[version] ?? version}`, - text: version, - }, - getDateLabel: withLanguage(releases[version]), - }; -}; - -const roadmap: Item[] = [ - { - done: false, - icon: mdiFlash, - iconColor: 'gold', - title: 'Workflows', - description: 'Automate tasks with workflows', - getDateLabel: () => 'Planned for 2025', - }, - { - done: false, - icon: mdiImageEdit, - iconColor: 'rebeccapurple', - title: 'Basic editor', - description: 'Basic photo editing capabilities', - getDateLabel: () => 'Planned for 2025', - }, - { - done: false, - icon: mdiRocketLaunch, - iconColor: 'indianred', - title: 'Stable release', - description: 'Immich goes stable', - getDateLabel: () => 'Planned for 2025', - }, - { - done: false, - icon: mdiCloudUploadOutline, - iconColor: 'cornflowerblue', - title: 'Better background backups', - description: 'Rework background backups to be more reliable', - getDateLabel: () => 'Planned for 2025', - }, - { - done: false, - icon: mdiCameraBurst, - iconColor: 'rebeccapurple', - title: 'Auto stacking', - description: 'Auto stack burst photos', - getDateLabel: () => 'Planned for 2025', - }, -]; - -const milestones: Item[] = [ - { - icon: mdiStar, - iconColor: 'gold', - title: '70,000 Stars', - description: 'Reached 70K Stars on GitHub!', - getDateLabel: withLanguage(new Date(2025, 6, 9)), - }, - withRelease({ - icon: mdiTableKey, - iconColor: 'gray', - title: 'Fine grained access controls', - description: 'Granular access controls for api keys', - release: 'v1.135.0', - }), - withRelease({ - icon: mdiCast, - iconColor: 'aqua', - title: 'Google Cast (web and mobile)', - description: 'Cast assets to Google Cast/Chromecast compatible devices', - release: 'v1.135.0', - }), - withRelease({ - icon: mdiLockOutline, - iconColor: 'sandybrown', - title: 'Private/locked photos', - description: 'Private assets with extra protections', - release: 'v1.133.0', - }), - withRelease({ - icon: mdiFolderMultiple, - iconColor: 'brown', - title: 'Folders view in the mobile app', - description: 'Browse your photos and videos in their folder structure inside the mobile app', - release: 'v1.130.0', - }), - { - icon: mdiStar, - iconColor: 'gold', - title: '60,000 Stars', - description: 'Reached 60K Stars on GitHub!', - getDateLabel: withLanguage(new Date(2025, 2, 4)), - }, - withRelease({ - icon: mdiTagFaces, - iconColor: 'teal', - title: 'Manual face tagging', - description: - 'Manually tag or remove faces in photos and videos, even when automatic detection misses or misidentifies them.', - release: 'v1.127.0', - }), - withRelease({ - icon: mdiLinkEdit, - iconColor: 'crimson', - title: 'Automatic URL switching', - description: 'The mobile app now supports automatic switching between different server URLs', - release: 'v1.122.0', - }), - withRelease({ - icon: mdiMovieOpenPlayOutline, - iconColor: 'darksalmon', - title: 'Native video player', - description: 'HDR videos are now fully supported using the Immich native video player', - release: 'v1.122.0', - }), - withRelease({ - icon: mdiDatabaseOutline, - iconColor: 'brown', - title: 'Automatic database dumps', - description: 'Database dumps are now integrated into the Immich server', - release: 'v1.120.0', - }), - { - icon: mdiStar, - iconColor: 'gold', - title: '50,000 Stars', - description: 'Reached 50K Stars on GitHub!', - getDateLabel: withLanguage(new Date(2024, 10, 1)), - }, - withRelease({ - icon: mdiFaceRecognition, - title: 'Metadata Face Import', - description: 'Read face metadata in Digikam format during import', - release: 'v1.114.0', - }), - withRelease({ - icon: mdiTagMultiple, - iconColor: 'orange', - title: 'Tags', - description: 'Tag your photos and videos', - release: 'v1.113.0', - }), - withRelease({ - icon: mdiFolderSync, - iconColor: 'green', - title: 'Album sync (mobile)', - description: 'Sync or mirror an album from your phone to the Immich server', - release: 'v1.113.0', - }), - withRelease({ - icon: mdiFolderMultiple, - iconColor: 'brown', - title: 'Folders view', - description: 'Browse your photos and videos in their folder structure', - release: 'v1.113.0', - }), - withRelease({ - icon: mdiPalette, - title: 'Theming (mobile)', - description: 'Pick a primary color for the mobile app', - release: 'v1.112.0', - }), - withRelease({ - icon: mdiStarOutline, - iconColor: 'gold', - title: 'Star rating', - description: 'Rate your photos and videos', - release: 'v1.112.0', - }), - withRelease({ - icon: mdiCrop, - iconColor: 'royalblue', - title: 'Editor (mobile)', - description: 'Crop and rotate on mobile', - release: 'v1.111.0', - }), - withRelease({ - icon: mdiMap, - iconColor: 'green', - title: 'Deploy tiles.immich.cloud', - description: 'Dedicated tile server for Immich', - release: 'v1.111.0', - }), - { - icon: mdiStar, - iconColor: 'gold', - title: '40,000 Stars', - description: 'Reached 40K Stars on GitHub!', - getDateLabel: withLanguage(new Date(2024, 6, 21)), - }, - withRelease({ - icon: mdiShare, - title: 'Deploy my.immich.app', - description: 'Url router for immich links', - release: 'v1.109.0', - }), - withRelease({ - icon: mdiLicense, - iconColor: 'gold', - title: 'Supporter Badge', - description: 'The option to buy Immich to support its development!', - release: 'v1.109.0', - }), - withRelease({ - icon: mdiHistory, - title: 'Versioned documentation', - description: 'View documentation as it was at the time of past releases', - release: 'v1.106.1', - }), - withRelease({ - icon: mdiWeb, - iconColor: 'royalblue', - title: 'Web translations', - description: 'Translate the web application to multiple languages', - release: 'v1.106.1', - }), - withRelease({ - icon: mdiContentDuplicate, - title: 'Similar image detection', - description: "Detect duplicate assets that aren't exactly identical", - release: 'v1.106.1', - }), - withRelease({ - icon: mdiVectorCombine, - title: 'Container consolidation', - description: - 'The microservices container can be run as a worker within the server image, allowing us to remove it from the default stack.', - release: 'v1.106.1', - }), - withRelease({ - icon: mdiPencil, - iconColor: 'saddlebrown', - title: 'Read-write external libraries', - description: 'Edit, update, and delete files in external libraries', - release: 'v1.104.0', - }), - withRelease({ - icon: mdiEmailOutline, - iconColor: 'crimson', - title: 'Email notifications', - description: 'Send emails for important events', - release: 'v1.104.0', - }), - { - icon: mdiHandshakeOutline, - iconColor: 'magenta', - title: 'Immich joins FUTO!', - description: 'Joined Futo and Immich core team goes full-time', - getDateLabel: withLanguage(new Date(2024, 4, 1)), - }, - withRelease({ - icon: mdiEyeOutline, - iconColor: 'darkslategray', - title: 'Read-only albums', - description: 'Share albums with other users as read-only', - release: 'v1.103.0', - }), - withRelease({ - icon: mdiBookmark, - iconColor: 'orangered', - title: 'Permanent URLs (Web)', - description: 'Assets on the web now have permanent URLs', - release: 'v1.103.0', - }), - withRelease({ - icon: mdiStar, - iconColor: 'gold', - title: '30,000 Stars', - description: 'Reached 30K Stars on GitHub!', - release: 'v1.102.0', - }), - withRelease({ - icon: mdiChartBoxMultipleOutline, - iconColor: 'mediumvioletred', - title: 'OpenTelemetry metrics', - description: 'OpenTelemetry metrics for local evaluation and advanced debugging', - release: 'v1.99.0', - }), - withRelease({ - icon: 'immich', - title: 'New logo', - description: 'Immich got its new logo', - release: 'v1.98.0', - }), - withRelease({ - icon: mdiMagnifyScan, - title: 'Search enhancement with advanced filters', - description: 'Advanced search with filters by date, location and more', - release: 'v1.95.0', - }), - withRelease({ - icon: mdiScaleBalance, - iconColor: 'gold', - title: 'AGPL License', - description: 'Immich switches to AGPLv3 license', - release: 'v1.95.0', - }), - withRelease({ - icon: mdiEyeRefreshOutline, - title: 'Library watching', - description: 'Automatically import files in external libraries when the operating system detects changes.', - release: 'v1.94.0', - }), - withRelease({ - icon: mdiExpansionCard, - iconColor: 'green', - title: 'GPU acceleration for machine-learning', - description: 'Hardware acceleration support for Nvidia and Intel devices through CUDA and OpenVINO.', - release: 'v1.94.0', - }), - withRelease({ - icon: mdiAccountGroupOutline, - iconColor: 'gray', - title: '250 unique contributors', - description: '250 amazing people contributed to Immich', - release: 'v1.93.0', - }), - withRelease({ - icon: mdiMatrix, - title: 'Search improvement with pgvecto.rs', - description: 'Moved the search from typesense to pgvecto.rs', - release: 'v1.91.0', - }), - withRelease({ - icon: mdiPencil, - iconColor: 'saddlebrown', - title: 'Edit metadata', - description: "Edit a photo or video's date, time, hours, timezone, and GPS information", - release: 'v1.90.0', - }), - withRelease({ - icon: mdiVectorCombine, - title: 'Container consolidation', - description: - 'The serving of the web app is merged into the server image, allowing us to remove two containers from the stack.', - release: 'v1.88.0', - }), - withRelease({ - icon: mdiBash, - iconColor: 'gray', - title: 'CLI v2', - description: 'Version 2 of the Immich CLI is released, replacing the legacy v1 CLI.', - release: 'v1.88.0', - }), - withRelease({ - icon: mdiForum, - iconColor: 'dodgerblue', - title: 'Activity', - description: 'Comment a photo or a video in a shared album', - release: 'v1.84.0', - }), - withRelease({ - icon: mdiStar, - iconColor: 'gold', - title: '20,000 Stars', - description: 'Reached 20K Stars on GitHub!', - release: 'v1.83.0', - }), - withRelease({ - icon: mdiCameraBurst, - iconColor: 'rebeccapurple', - title: 'Stack assets', - description: 'Manual asset stacking for grouping and hiding related assets in the main timeline.', - release: 'v1.83.0', - }), - withRelease({ - icon: mdiPalette, - iconColor: 'magenta', - title: 'Custom theme', - description: 'Apply your custom CSS for modifying fonts, colors, and styles in the web application.', - release: 'v1.83.0', - }), - withRelease({ - icon: mdiTrashCanOutline, - iconColor: 'brown', - title: 'Trash feature', - description: 'Trash, restore from trash, and automatically empty the recycle bin after 30 days.', - release: 'v1.82.0', - }), - withRelease({ - icon: mdiBookSearchOutline, - title: 'External libraries', - description: 'Automatically import media into Immich based on imports paths and ignore patterns.', - release: 'v1.79.0', - }), - withRelease({ - icon: mdiMap, - iconColor: 'darksalmon', - title: 'Map view (mobile)', - description: 'Heat map implementation in the mobile app.', - release: 'v1.76.0', - }), - withRelease({ - icon: mdiFile, - iconColor: 'lightblue', - title: 'Configuration file', - description: 'Auto-configure an Immich installation via a configuration file.', - release: 'v1.75.0', - }), - withRelease({ - icon: mdiMonitor, - iconColor: 'darkcyan', - title: 'Slideshow mode (web)', - description: 'Start a full-screen slideshow from an Album on the web.', - release: 'v1.75.0', - }), - withRelease({ - icon: mdiServer, - iconColor: 'lightskyblue', - title: 'Hardware transcoding', - description: 'Support hardware acceleration (QuickSync, VAAPI, and Nvidia) for video transcoding.', - release: 'v1.72.0', - }), - withRelease({ - icon: mdiImageAlbum, - iconColor: 'olivedrab', - title: 'View albums via time buckets', - description: 'Upgrade albums to use time buckets, an optimized virtual viewport.', - release: 'v1.72.0', - }), - withRelease({ - icon: mdiImageAlbum, - iconColor: 'olivedrab', - title: 'Album description', - description: 'Save an album description.', - release: 'v1.72.0', - }), - withRelease({ - icon: mdiRotate360, - title: '360° Photos (web)', - description: 'View 360° Photos on the web.', - release: 'v1.71.0', - }), - withRelease({ - icon: mdiMotionPlayOutline, - title: 'Android motion photos', - description: 'Add support for Android Motion Photos.', - release: 'v1.69.0', - }), - withRelease({ - icon: mdiFaceManOutline, - iconColor: 'mistyrose', - title: 'Show/hide faces', - description: 'Add the options to show or hide faces.', - release: 'v1.68.0', - }), - withRelease({ - icon: mdiMerge, - iconColor: 'forestgreen', - title: 'Merge faces', - description: 'Add the ability to merge multiple faces together.', - release: 'v1.67.0', - }), - withRelease({ - icon: mdiImage, - iconColor: 'rebeccapurple', - title: 'Feature photo', - description: 'Add the option to change the feature photo for a person.', - release: 'v1.66.0', - }), - withRelease({ - icon: mdiKeyboardSettingsOutline, - iconColor: 'darkslategray', - title: 'Multi-select via SHIFT', - description: 'Add the option to multi-select while holding SHIFT.', - release: 'v1.66.0', - }), - withRelease({ - icon: mdiImageMultipleOutline, - iconColor: 'rebeccapurple', - title: 'Memories (mobile)', - description: 'View "On this day..." memories in the mobile app.', - release: 'v1.65.0', - }), - withRelease({ - icon: mdiFaceMan, - iconColor: 'mistyrose', - title: 'Facial recognition (mobile)', - description: 'View detected faces in the mobile app.', - release: 'v1.63.0', - }), - withRelease({ - icon: mdiImageMultipleOutline, - iconColor: 'rebeccapurple', - title: 'Memories (web)', - description: 'View pictures taken in past years on this day on the web.', - release: 'v1.61.0', - }), - withRelease({ - icon: mdiCollage, - iconColor: 'deeppink', - title: 'Justified layout (web)', - description: 'Implement justified layout (collage) on the web.', - release: 'v1.61.0', - }), - withRelease({ - icon: mdiRaw, - title: 'RAW file formats', - description: 'Support for RAW file formats.', - release: 'v1.61.0', - }), - withRelease({ - icon: mdiShareAll, - iconColor: 'darkturquoise', - title: 'Partner sharing (mobile)', - description: 'View shared partner photos in the mobile app.', - release: 'v1.58.0', - }), - withRelease({ - icon: mdiFile, - iconColor: 'lightblue', - title: 'XMP sidecar', - description: 'Attach XMP sidecar files to assets.', - release: 'v1.58.0', - }), - withRelease({ - icon: mdiFolder, - iconColor: 'brown', - title: 'Custom storage label', - description: 'Replace the user UUID in the storage template with a custom label.', - release: 'v1.57.0', - }), - withRelease({ - icon: mdiShareCircle, - title: 'Partner sharing', - description: 'Share your entire collection with another user.', - release: 'v1.56.0', - }), - withRelease({ - icon: mdiFaceMan, - iconColor: 'mistyrose', - title: 'Facial recognition', - description: 'Detect faces in pictures and cluster them together as people, which can be named.', - release: 'v1.56.0', - }), - withRelease({ - icon: mdiMap, - iconColor: 'darksalmon', - title: 'Map view (web)', - description: 'View a global map, with clusters of photos based on corresponding GPS data.', - release: 'v1.55.0', - }), - withRelease({ - icon: mdiDevices, - iconColor: 'slategray', - title: 'Manage auth devices', - description: 'Manage logged-in devices and revoke access from User Settings.', - release: 'v1.55.0', - }), - withRelease({ - icon: mdiStar, - iconColor: 'gold', - title: '10,000 Stars', - description: 'Reached 10K stars on GitHub!', - release: 'v1.54.0', - }), - withRelease({ - icon: mdiText, - title: 'Asset descriptions', - description: 'Save an asset description', - release: 'v1.54.0', - }), - withRelease({ - icon: mdiArchiveOutline, - title: 'Archiving', - description: 'Remove assets from the main timeline by archiving them.', - release: 'v1.54.0', - }), - withRelease({ - icon: mdiDevices, - iconColor: 'slategray', - title: 'Responsive web app', - description: 'Optimize the web app for small screen.', - release: 'v1.54.0', - }), - withRelease({ - icon: mdiFileSearch, - iconColor: 'brown', - title: 'Search by metadata', - description: 'Search images by filename, description, tagged people, make, model, and other metadata.', - release: 'v1.52.0', - }), - withRelease({ - icon: mdiImageSearch, - iconColor: 'rebeccapurple', - title: 'CLIP search', - description: 'Search images with free-form text like "Sunset at the beach".', - release: 'v1.51.0', - }), - withRelease({ - icon: mdiMagnify, - iconColor: 'lightblue', - title: 'Explore page', - description: 'View tagged places, object, and people.', - release: 'v1.51.0', - }), - withRelease({ - icon: mdiAppleIos, - title: 'iOS background uploads', - description: 'Automatically backup pictures in the background on iOS.', - release: 'v1.48.0', - }), - withRelease({ - icon: mdiMotionPlayOutline, - title: 'Auto-Link live photos', - description: 'Automatically link live photos, even when uploaded as separate files.', - release: 'v1.48.0', - }), - withRelease({ - icon: mdiMaterialDesign, - iconColor: 'blue', - title: 'Material design 3 (mobile)', - description: 'Upgrade the mobile app to Material Design 3.', - release: 'v1.47.0', - }), - withRelease({ - icon: mdiHeart, - iconColor: 'red', - title: 'Favorites (mobile)', - description: 'Show favorites on the mobile app.', - release: 'v1.46.0', - }), - withRelease({ - icon: mdiCakeVariant, - iconColor: 'deeppink', - title: 'Immich turns 1', - description: 'Immich is officially one year old.', - release: 'v1.43.0', - }), - withRelease({ - icon: mdiHeart, - iconColor: 'red', - title: 'Favorites page (web)', - description: 'Favorite and view favorites on the web.', - release: 'v1.43.0', - }), - withRelease({ - icon: mdiShareCircle, - title: 'Public share links', - description: 'Share photos and albums publicly via a shared link.', - release: 'v1.41.0', - }), - withRelease({ - icon: mdiFolder, - iconColor: 'lightblue', - title: 'User-defined storage structure', - description: 'Support custom storage structures.', - release: 'v1.39.0', - }), - withRelease({ - icon: mdiMotionPlayOutline, - title: 'iOS live photos', - description: 'Backup and display iOS Live Photos.', - release: 'v1.36.0', - }), - withRelease({ - icon: mdiSecurity, - iconColor: 'green', - title: 'OAuth integration', - description: 'Support OAuth2 and OIDC capable identity providers.', - release: 'v1.36.0', - }), - withRelease({ - icon: mdiWeb, - iconColor: 'royalblue', - title: 'Documentation site', - description: 'Release an official documentation website.', - release: 'v1.33.1', - }), - withRelease({ - icon: mdiThemeLightDark, - iconColor: 'slategray', - title: 'Dark mode (web)', - description: 'Dark mode on the web.', - release: 'v1.32.0', - }), - withRelease({ - icon: mdiPanVertical, - title: 'Virtual scrollbar (web)', - description: 'View the main timeline with a virtual scrollbar, allowing to jump to any point in time, instantly.', - release: 'v1.27.0', - }), - withRelease({ - icon: mdiCheckAll, - iconColor: 'green', - title: 'Checksum duplication check', - description: 'Enforce per user sha1 checksum uniqueness.', - release: 'v1.27.0', - }), - withRelease({ - icon: mdiAndroid, - iconColor: 'greenyellow', - title: 'Android background backup', - description: 'Automatic backup in the background on Android.', - release: 'v1.24.0', - }), - withRelease({ - icon: mdiAccountGroup, - iconColor: 'gray', - title: 'Admin portal', - description: 'Manage users and admin settings from the web.', - release: 'v1.10.0', - }), - withRelease({ - icon: mdiShareCircle, - title: 'Album sharing', - description: 'Share albums with other users.', - release: 'v1.7.0', - }), - withRelease({ - icon: mdiTag, - iconColor: 'coral', - title: 'Image tagging', - description: 'Tag images with custom values.', - release: 'v1.7.0', - }), - withRelease({ - icon: mdiImage, - iconColor: 'rebeccapurple', - title: 'View exif', - description: 'View metadata about assets.', - release: 'v1.3.0', - }), - withRelease({ - icon: mdiCheckboxMarked, - iconColor: 'green', - title: 'Multi select', - description: 'Select and execute actions on multiple assets at the same time.', - release: 'v1.2.0', - }), - withRelease({ - icon: mdiVideo, - iconColor: 'slategray', - title: 'Video player', - description: 'Play videos in the web and on mobile.', - release: 'v1.2.0', - }), - { - icon: mdiPartyPopper, - iconColor: 'deeppink', - title: 'First commit', - description: 'First commit on GitHub, Immich is born.', - getDateLabel: withLanguage(new Date(2022, 1, 3)), - }, -]; - -export default function MilestonePage(): JSX.Element { - return ( - -
-

- {title} -

-

{description}

-
- -
-
-
- ); -} diff --git a/docs/static/.well-known/security.txt b/docs/static/.well-known/security.txt deleted file mode 100644 index 5a8414c3e2..0000000000 --- a/docs/static/.well-known/security.txt +++ /dev/null @@ -1,5 +0,0 @@ -Policy: https://github.com/immich-app/immich/blob/main/SECURITY.md -Contact: mailto:security@immich.app -Preferred-Languages: en -Expires: 2026-05-01T23:59:00.000Z -Canonical: https://immich.app/.well-known/security.txt diff --git a/docs/static/_redirects b/docs/static/_redirects index 7b01d1e3bb..ce4b670246 100644 --- a/docs/static/_redirects +++ b/docs/static/_redirects @@ -1,34 +1,36 @@ -/docs /docs/overview/welcome 307 -/docs/ /docs/overview/welcome 307 -/docs/mobile-app-beta-program /docs/features/mobile-app 307 -/docs/contribution-guidelines /docs/overview/support-the-project#contributing 307 -/docs/install /docs/install/docker-compose 307 -/docs/installation/one-step-installation /docs/install/script 307 -/docs/installation/portainer-installation /docs/install/portainer 307 -/docs/installation/recommended-installation /docs/install/docker-compose 307 -/docs/installation/unraid /docs/install/unraid 307 -/docs/installation/requirements /docs/install/requirements 307 -/docs/overview/logo-meaning /docs/overview/logo 307 -/docs/overview/technology-stack /docs/developer/architecture 307 -/docs/usage/automatic-backup /docs/features/automatic-backup 307 -/docs/usage/bulk-upload /docs/features/command-line-interface 307 -/docs/features/bulk-upload /docs/features/command-line-interface 307 -/docs/usage/oauth /docs/administration/oauth 307 -/docs/usage/post-installation /docs/install/post-install 307 -/docs/usage/update /docs/install/docker-compose#step-4---upgrading 307 -/docs/usage/server-commands /docs/administration/server-commands 307 -/docs/features/jobs /docs/administration/jobs 307 -/docs/features/oauth /docs/administration/oauth 307 -/docs/features/password-login /docs/administration/password-login 307 -/docs/features/server-commands /docs/administration/server-commands 307 -/docs/features/storage-template /docs/administration/storage-template 307 -/docs/features/user-management /docs/administration/user-management 307 -/docs/developer/contributing /docs/developer/pr-checklist 307 -/docs/guides/machine-learning /docs/guides/remote-machine-learning 307 -/docs/administration/password-login /docs/administration/system-settings 307 -/docs/features/search /docs/features/searching 307 -/docs/features/smart-search /docs/features/searching 307 -/docs/guides/api-album-sync /docs/community-projects 307 -/docs/guides/remove-offline-files /docs/community-projects 307 -/milestones /roadmap 307 -/docs/overview/introduction /docs/overview/welcome 307 +/ /overview/quick-start 307 +/mobile-app-beta-program /features/mobile-app 307 +/contribution-guidelines /overview/support-the-project#contributing 307 +/install /install/docker-compose 307 +/installation/one-step-installation /install/script 307 +/installation/portainer-installation /install/portainer 307 +/installation/recommended-installation /install/docker-compose 307 +/installation/unraid /install/unraid 307 +/installation/requirements /install/requirements 307 +/overview/logo-meaning /overview/logo 307 +/overview/technology-stack /developer/architecture 307 +/usage/automatic-backup /features/automatic-backup 307 +/usage/bulk-upload /features/command-line-interface 307 +/features/bulk-upload /features/command-line-interface 307 +/usage/oauth /administration/oauth 307 +/usage/post-installation /install/post-install 307 +/usage/update /install/docker-compose#step-4---upgrading 307 +/usage/server-commands /administration/server-commands 307 +/features/jobs /administration/jobs 307 +/features/oauth /administration/oauth 307 +/features/password-login /administration/password-login 307 +/features/server-commands /administration/server-commands 307 +/features/storage-template /administration/storage-template 307 +/features/user-management /administration/user-management 307 +/developer/contributing /developer/pr-checklist 307 +/guides/machine-learning /guides/remote-machine-learning 307 +/administration/password-login /administration/system-settings 307 +/features/search /features/searching 307 +/features/smart-search /features/searching 307 +/guides/api-album-sync https://awesome.immich.app/ 307 +/guides/remove-offline-files https://awesome.immich.app/ 307 +/community-guides https://awesome.immich.app/ 307 +/community-projects https://awesome.immich.app/ 307 +/overview/introduction /overview/quick-start 307 +/overview/welcome /overview/quick-start 307 +/docs/* /:splat 307 diff --git a/docs/static/archived-versions.json b/docs/static/archived-versions.json index 4c678b7faa..58f3d67cd2 100644 --- a/docs/static/archived-versions.json +++ b/docs/static/archived-versions.json @@ -1,234 +1,241 @@ [ { - "label": "v1.143.1", - "url": "https://v1.143.1.archive.immich.app" + "label": "v2.1.0", + "url": "https://docs.v2.1.0.archive.immich.app" }, { - "label": "v1.143.0", - "url": "https://v1.143.0.archive.immich.app" + "label": "v2.0.1", + "url": "https://docs.v2.0.1.archive.immich.app" + }, + { + "label": "v2.0.0", + "url": "https://docs.v2.0.0.archive.immich.app" + }, + { + "label": "v1.144.1", + "url": "https://docs.v1.144.1.archive.immich.app" + }, + { + "label": "v1.144.0", + "url": "https://docs.v1.144.0.archive.immich.app" + }, + { + "label": "v1.143.1", + "url": "https://docs.v1.143.1.archive.immich.app" }, { "label": "v1.142.1", - "url": "https://v1.142.1.archive.immich.app" - }, - { - "label": "v1.142.0", - "url": "https://v1.142.0.archive.immich.app" + "url": "https://v1.142.1.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.141.1", - "url": "https://v1.141.1.archive.immich.app" - }, - { - "label": "v1.141.0", - "url": "https://v1.141.0.archive.immich.app" + "url": "https://v1.141.1.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.140.1", - "url": "https://v1.140.1.archive.immich.app" - }, - { - "label": "v1.140.0", - "url": "https://v1.140.0.archive.immich.app" + "url": "https://v1.140.1.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.139.4", - "url": "https://v1.139.4.archive.immich.app" - }, - { - "label": "v1.139.3", - "url": "https://v1.139.3.archive.immich.app" - }, - { - "label": "v1.139.2", - "url": "https://v1.139.2.archive.immich.app" + "url": "https://v1.139.4.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.138.1", - "url": "https://v1.138.1.archive.immich.app" - }, - { - "label": "v1.138.0", - "url": "https://v1.138.0.archive.immich.app" + "url": "https://v1.138.1.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.137.3", - "url": "https://v1.137.3.archive.immich.app" - }, - { - "label": "v1.137.2", - "url": "https://v1.137.2.archive.immich.app" - }, - { - "label": "v1.137.1", - "url": "https://v1.137.1.archive.immich.app" - }, - { - "label": "v1.137.0", - "url": "https://v1.137.0.archive.immich.app" + "url": "https://v1.137.3.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.136.0", - "url": "https://v1.136.0.archive.immich.app" + "url": "https://v1.136.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.135.3", - "url": "https://v1.135.3.archive.immich.app" - }, - { - "label": "v1.135.2", - "url": "https://v1.135.2.archive.immich.app" - }, - { - "label": "v1.135.1", - "url": "https://v1.135.1.archive.immich.app" - }, - { - "label": "v1.135.0", - "url": "https://v1.135.0.archive.immich.app" + "url": "https://v1.135.3.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.134.0", - "url": "https://v1.134.0.archive.immich.app" + "url": "https://v1.134.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.133.1", - "url": "https://v1.133.1.archive.immich.app" - }, - { - "label": "v1.133.0", - "url": "https://v1.133.0.archive.immich.app" + "url": "https://v1.133.1.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.132.3", - "url": "https://v1.132.3.archive.immich.app" + "url": "https://v1.132.3.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.131.3", - "url": "https://v1.131.3.archive.immich.app" + "url": "https://v1.131.3.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.130.3", - "url": "https://v1.130.3.archive.immich.app" + "url": "https://v1.130.3.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.129.0", - "url": "https://v1.129.0.archive.immich.app" + "url": "https://v1.129.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.128.0", - "url": "https://v1.128.0.archive.immich.app" + "url": "https://v1.128.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.127.0", - "url": "https://v1.127.0.archive.immich.app" + "url": "https://v1.127.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.126.1", - "url": "https://v1.126.1.archive.immich.app" + "url": "https://v1.126.1.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.125.7", - "url": "https://v1.125.7.archive.immich.app" + "url": "https://v1.125.7.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.124.2", - "url": "https://v1.124.2.archive.immich.app" + "url": "https://v1.124.2.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.123.0", - "url": "https://v1.123.0.archive.immich.app" + "url": "https://v1.123.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.122.3", - "url": "https://v1.122.3.archive.immich.app" + "url": "https://v1.122.3.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.121.0", - "url": "https://v1.121.0.archive.immich.app" + "url": "https://v1.121.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.120.2", - "url": "https://v1.120.2.archive.immich.app" + "url": "https://v1.120.2.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.119.1", - "url": "https://v1.119.1.archive.immich.app" + "url": "https://v1.119.1.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.118.2", - "url": "https://v1.118.2.archive.immich.app" + "url": "https://v1.118.2.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.117.0", - "url": "https://v1.117.0.archive.immich.app" + "url": "https://v1.117.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.116.2", - "url": "https://v1.116.2.archive.immich.app" + "url": "https://v1.116.2.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.115.0", - "url": "https://v1.115.0.archive.immich.app" + "url": "https://v1.115.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.114.0", - "url": "https://v1.114.0.archive.immich.app" + "url": "https://v1.114.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.113.1", - "url": "https://v1.113.1.archive.immich.app" + "url": "https://v1.113.1.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.112.1", - "url": "https://v1.112.1.archive.immich.app" + "url": "https://v1.112.1.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.111.0", - "url": "https://v1.111.0.archive.immich.app" + "url": "https://v1.111.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.110.0", - "url": "https://v1.110.0.archive.immich.app" + "url": "https://v1.110.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.109.2", - "url": "https://v1.109.2.archive.immich.app" + "url": "https://v1.109.2.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.108.0", - "url": "https://v1.108.0.archive.immich.app" + "url": "https://v1.108.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.107.2", - "url": "https://v1.107.2.archive.immich.app" + "url": "https://v1.107.2.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.106.4", - "url": "https://v1.106.4.archive.immich.app" + "url": "https://v1.106.4.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.105.1", - "url": "https://v1.105.1.archive.immich.app" + "url": "https://v1.105.1.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.104.0", - "url": "https://v1.104.0.archive.immich.app" + "url": "https://v1.104.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.103.1", - "url": "https://v1.103.1.archive.immich.app" + "url": "https://v1.103.1.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.102.3", - "url": "https://v1.102.3.archive.immich.app" + "url": "https://v1.102.3.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.101.0", - "url": "https://v1.101.0.archive.immich.app" + "url": "https://v1.101.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.100.0", - "url": "https://v1.100.0.archive.immich.app" + "url": "https://v1.100.0.archive.immich.app", + "rootPath": "/docs" } ] diff --git a/docs/static/img/synology-action-clean.png b/docs/static/img/synology-action-clean.png new file mode 100644 index 0000000000..4d168b0bd8 Binary files /dev/null and b/docs/static/img/synology-action-clean.png differ diff --git a/docs/static/img/synology-build.png b/docs/static/img/synology-build.png new file mode 100644 index 0000000000..50c4d0fc98 Binary files /dev/null and b/docs/static/img/synology-build.png differ diff --git a/docs/static/img/synology-container-ip.png b/docs/static/img/synology-container-ip.png new file mode 100644 index 0000000000..21617d8c72 Binary files /dev/null and b/docs/static/img/synology-container-ip.png differ diff --git a/docs/static/img/synology-container-manager-customize-docker-compose.png b/docs/static/img/synology-container-manager-customize-docker-compose.png index 558557487a..2c0a40def0 100644 Binary files a/docs/static/img/synology-container-manager-customize-docker-compose.png and b/docs/static/img/synology-container-manager-customize-docker-compose.png differ diff --git a/docs/static/img/synology-custom-port-firewall-rule.png b/docs/static/img/synology-custom-port-firewall-rule.png new file mode 100644 index 0000000000..26ee17785c Binary files /dev/null and b/docs/static/img/synology-custom-port-firewall-rule.png differ diff --git a/docs/static/img/synology-fw-ipedit.png b/docs/static/img/synology-fw-ipedit.png new file mode 100644 index 0000000000..7f4e561395 Binary files /dev/null and b/docs/static/img/synology-fw-ipedit.png differ diff --git a/docs/static/img/synology-fw-rules.png b/docs/static/img/synology-fw-rules.png new file mode 100644 index 0000000000..2ec43a682f Binary files /dev/null and b/docs/static/img/synology-fw-rules.png differ diff --git a/docs/static/img/synology-ipaddress-firewall-rule.png b/docs/static/img/synology-ipaddress-firewall-rule.png new file mode 100644 index 0000000000..d1982b053d Binary files /dev/null and b/docs/static/img/synology-ipaddress-firewall-rule.png differ diff --git a/docs/static/img/synology-project-stop.png b/docs/static/img/synology-project-stop.png new file mode 100644 index 0000000000..8a77446dc2 Binary files /dev/null and b/docs/static/img/synology-project-stop.png differ diff --git a/docs/static/img/synology-remove-unused.png b/docs/static/img/synology-remove-unused.png new file mode 100644 index 0000000000..9b1a217902 Binary files /dev/null and b/docs/static/img/synology-remove-unused.png differ diff --git a/docs/static/img/synology-select-proj.png b/docs/static/img/synology-select-proj.png new file mode 100644 index 0000000000..21642d8713 Binary files /dev/null and b/docs/static/img/synology-select-proj.png differ diff --git a/e2e/.nvmrc b/e2e/.nvmrc index e2228113dd..442c7587a9 100644 --- a/e2e/.nvmrc +++ b/e2e/.nvmrc @@ -1 +1 @@ -22.19.0 +22.20.0 diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index 6aba8ff72a..9aef2288f6 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -35,10 +35,10 @@ services: - 2285:2285 redis: - image: redis:6.2-alpine@sha256:7fe72c486b910f6b1a9769c937dad5d63648ddee82e056f47417542dd40825bb + image: redis:6.2-alpine@sha256:77697a75da9f94e9357b61fcaf8345f69e3d9d32e9d15032c8415c21263977dc database: - image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:11ced39d65a92a54d12890ced6a26cc2003f92697d6f0d4d944b98459dba7138 + image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:6f3e9d2c2177af16c2988ff71425d79d89ca630ec2f9c8db03209ab716542338 command: -c fsync=off -c shared_preload_libraries=vchord.so -c config_file=/var/lib/postgresql/data/postgresql.conf environment: POSTGRES_PASSWORD: postgres diff --git a/e2e/package.json b/e2e/package.json index 8362046578..67aeb00c1b 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,6 +1,6 @@ { "name": "immich-e2e", - "version": "1.143.1", + "version": "2.1.0", "description": "", "main": "index.js", "type": "module", @@ -25,7 +25,7 @@ "@playwright/test": "^1.44.1", "@socket.io/component-emitter": "^3.1.2", "@types/luxon": "^3.4.2", - "@types/node": "^22.18.1", + "@types/node": "^22.18.8", "@types/oidc-provider": "^9.0.0", "@types/pg": "^8.15.1", "@types/pngjs": "^6.0.4", @@ -43,7 +43,7 @@ "pngjs": "^7.0.0", "prettier": "^3.2.5", "prettier-plugin-organize-imports": "^4.0.0", - "sharp": "^0.34.3", + "sharp": "^0.34.4", "socket.io-client": "^4.7.4", "supertest": "^7.0.0", "typescript": "^5.3.3", @@ -52,6 +52,6 @@ "vitest": "^3.0.0" }, "volta": { - "node": "22.19.0" + "node": "22.20.0" } } diff --git a/e2e/src/api/specs/album.e2e-spec.ts b/e2e/src/api/specs/album.e2e-spec.ts index 5615a312f2..c4f06edd93 100644 --- a/e2e/src/api/specs/album.e2e-spec.ts +++ b/e2e/src/api/specs/album.e2e-spec.ts @@ -136,6 +136,7 @@ describe('/albums', () => { expect(body).toEqual({ ...user1Albums[0], assets: [expect.objectContaining({ isFavorite: false })], + contributorCounts: [{ userId: user1.userId, assetCount: 1 }], lastModifiedAssetTimestamp: expect.any(String), startDate: expect.any(String), endDate: expect.any(String), @@ -310,6 +311,7 @@ describe('/albums', () => { expect(body).toEqual({ ...user1Albums[0], assets: [expect.objectContaining({ id: user1Albums[0].assets[0].id })], + contributorCounts: [{ userId: user1.userId, assetCount: 1 }], lastModifiedAssetTimestamp: expect.any(String), startDate: expect.any(String), endDate: expect.any(String), @@ -345,6 +347,7 @@ describe('/albums', () => { expect(body).toEqual({ ...user1Albums[0], assets: [expect.objectContaining({ id: user1Albums[0].assets[0].id })], + contributorCounts: [{ userId: user1.userId, assetCount: 1 }], lastModifiedAssetTimestamp: expect.any(String), startDate: expect.any(String), endDate: expect.any(String), @@ -362,6 +365,7 @@ describe('/albums', () => { expect(body).toEqual({ ...user1Albums[0], assets: [], + contributorCounts: [{ userId: user1.userId, assetCount: 1 }], assetCount: 1, lastModifiedAssetTimestamp: expect.any(String), endDate: expect.any(String), @@ -382,6 +386,7 @@ describe('/albums', () => { expect(body).toEqual({ ...user2Albums[0], assets: [], + contributorCounts: [{ userId: user1.userId, assetCount: 1 }], assetCount: 1, lastModifiedAssetTimestamp: expect.any(String), endDate: expect.any(String), diff --git a/e2e/src/web/specs/auth.e2e-spec.ts b/e2e/src/web/specs/auth.e2e-spec.ts index 173131ec5e..a14a177917 100644 --- a/e2e/src/web/specs/auth.e2e-spec.ts +++ b/e2e/src/web/specs/auth.e2e-spec.ts @@ -38,6 +38,7 @@ test.describe('Registration', () => { await page.getByRole('button', { name: 'User Privacy' }).click(); await page.getByRole('button', { name: 'Storage Template' }).click(); await page.getByRole('button', { name: 'Backups' }).click(); + await page.getByRole('button', { name: 'Mobile App' }).click(); await page.getByRole('button', { name: 'Done' }).click(); // success @@ -85,6 +86,7 @@ test.describe('Registration', () => { await page.getByRole('button', { name: 'Theme' }).click(); await page.getByRole('button', { name: 'Language' }).click(); await page.getByRole('button', { name: 'User Privacy' }).click(); + await page.getByRole('button', { name: 'Mobile App' }).click(); await page.getByRole('button', { name: 'Done' }).click(); // success diff --git a/i18n/ar.json b/i18n/ar.json index 3a2e9204f8..5d94846c52 100644 --- a/i18n/ar.json +++ b/i18n/ar.json @@ -28,10 +28,12 @@ "add_to_album": "إضافة إلى ألبوم", "add_to_album_bottom_sheet_added": "تمت الاضافة الى {album}", "add_to_album_bottom_sheet_already_exists": "موجود مسبقا في {album}", + "add_to_album_bottom_sheet_some_local_assets": "لا يمكن إضافة بعض الأصول المحلية إلى الألبوم", "add_to_album_toggle": "تبديل التحديد لـ{album}", "add_to_albums": "إضافة الى البومات", "add_to_albums_count": "إضافه إلى البومات ({count})", "add_to_shared_album": "إضافة إلى ألبوم مشارك", + "add_upload_to_stack": "اضف رفع الى حزمة", "add_url": "إضافة رابط", "added_to_archive": "أُضيفت للأرشيف", "added_to_favorites": "أُضيفت للمفضلات", @@ -124,6 +126,12 @@ "logging_level_description": "عند التفعيل، أي مستوى تسجيل سيستخدم.", "logging_settings": "تسجيل الاحداث", "machine_learning_availability_checks": "تحقق من التوفر", + "machine_learning_availability_checks_description": "تحديد خوادم التعلم الآلي المتاحة تلقائيًا وإعطاءها الأولوية", + "machine_learning_availability_checks_enabled": "تفعيل عمليات فحص التوفر", + "machine_learning_availability_checks_interval": "فترة التحقق", + "machine_learning_availability_checks_interval_description": "الفترة الزمنية بالمللي ثانية بين عمليات فحص التوفر", + "machine_learning_availability_checks_timeout": "انتهت مدة انتظار الطلب", + "machine_learning_availability_checks_timeout_description": "مدة انتظار (بالمللي ثانية) لاختبارات توفر الخدمة", "machine_learning_clip_model": "نموذج CLIP", "machine_learning_clip_model_description": "اسم نموذج CLIP مدرجٌ هنا. يرجى ملاحظة أنه يجب إعادة تشغيل وظيفة \"البحث الذكي\" لجميع الصور بعد تغيير النموذج.", "machine_learning_duplicate_detection": "كشف التكرار", @@ -395,6 +403,7 @@ "advanced_settings_prefer_remote_title": "تفضل الصور البعيدة", "advanced_settings_proxy_headers_subtitle": "عرف عناوين الوكيل التي يستخدمها Immich لارسال كل طلب شبكي", "advanced_settings_proxy_headers_title": "عناوين الوكيل", + "advanced_settings_readonly_mode_subtitle": "تتيح هذه الميزة وضع العرض فقط، حيث يمكن للمستخدم معاينة الصور فقط، بينما يتم تعطيل جميع الخيارات الأخرى مثل تحديد عدة صور، أو مشاركتها، أو بثها، أو حذفها. يمكن تفعيل/تعطيل وضع العرض فقط من خلال صورة المستخدم في الشاشة الرئيسية", "advanced_settings_readonly_mode_title": "وضع القراءة فقط", "advanced_settings_self_signed_ssl_subtitle": "تخطي التحقق من شهادة SSL لخادم النقطة النهائي. مكلوب للشهادات الموقعة ذاتيا.", "advanced_settings_self_signed_ssl_title": "السماح بشهادات SSL الموقعة ذاتيًا", @@ -423,6 +432,7 @@ "album_remove_user_confirmation": "هل أنت متأكد أنك تريد إزالة {user}؟", "album_search_not_found": "لم يتم ايجاد البوم مطابق لبحثك", "album_share_no_users": "يبدو أنك قمت بمشاركة هذا الألبوم مع جميع المستخدمين أو ليس لديك أي مستخدم للمشاركة معه.", + "album_summary": "ملخص الألبوم", "album_updated": "تم تحديث الألبوم", "album_updated_setting_description": "تلقي إشعارًا عبر البريد الإلكتروني عندما يحتوي الألبوم المشترك على محتويات جديدة", "album_user_left": "تم ترك {album}", @@ -461,6 +471,7 @@ "app_bar_signout_dialog_title": "خروج", "app_settings": "إعدادات التطبيق", "appears_in": "يظهر في", + "apply_count": "تطبيق ({count, number})", "archive": "الأرشيف", "archive_action_prompt": "{count} اضيف إلى الارشيف", "archive_or_unarchive_photo": "أرشفة الصورة أو إلغاء أرشفتها", @@ -493,6 +504,8 @@ "asset_restored_successfully": "تم استعادة الاصل بنجاح", "asset_skipped": "تم تخطيه", "asset_skipped_in_trash": "في سلة المهملات", + "asset_trashed": "اصول محذوفة", + "asset_troubleshoot": "استكشاف مشاكل الأصول", "asset_uploaded": "تم الرفع", "asset_uploading": "جارٍ الرفع…", "asset_viewer_settings_subtitle": "إدارة إعدادات عارض المعرض الخاص بك", @@ -500,7 +513,9 @@ "assets": "المحتويات", "assets_added_count": "تمت إضافة {count, plural, one {# محتوى} other {# محتويات}}", "assets_added_to_album_count": "تمت إضافة {count, plural, one {# الأصل} other {# الأصول}} إلى الألبوم", + "assets_added_to_albums_count": "تمت اضافة {assetTotal, plural, one {# اصل} other {# اصول}} to {albumTotal, plural, one {# البوم} other {# البومات}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} لايمكن اضافته الى الالبوم", + "assets_cannot_be_added_to_albums": "{count, plural, one {اصل} other {اصول}} لا يمكن إضافته إلى أي من الألبومات", "assets_count": "{count, plural, one {# محتوى} other {# محتويات}}", "assets_deleted_permanently": "{count} الاص(و)ل المحذوف(ه) بشكل دائم", "assets_deleted_permanently_from_server": "{count} الاص(و)ل المحذوف(ه) بشكل دائمي من خادم Immich", @@ -517,14 +532,17 @@ "assets_trashed_count": "تم إرسال {count, plural, one {# محتوى} other {# محتويات}} إلى سلة المهملات", "assets_trashed_from_server": "{count} الاص(و)ل المنقولة الى سلة المهملات من خادم Immich", "assets_were_part_of_album_count": "{count, plural, one {هذا المحتوى} other {هذه المحتويات}} في الألبوم بالفعل", + "assets_were_part_of_albums_count": "{count, plural, one {اصل هو} other {اصول هي}}بالفعل جزء من الألبومات", "authorized_devices": "الأجهزه المخولة", "automatic_endpoint_switching_subtitle": "اتصل محليا من خلال شبكه Wi-Fi عند توفرها و استخدم اتصالات بديله في الاماكن الاخرى", "automatic_endpoint_switching_title": "تبديل URL تلقائي", "autoplay_slideshow": "تشغيل تلقائي لعرض الشرائح", "back": "خلف", "back_close_deselect": "الرجوع أو الإغلاق أو إلغاء التحديد", + "background_backup_running_error": "يتم تشغيل النسخ الاحتياطي في الخلفية حاليًا، ولا يمكن بدء النسخ الاحتياطي اليدوي", "background_location_permission": "اذن الوصول للموقع في الخلفية", "background_location_permission_content": "للتمكن من تبديل الشبكه بالخلفية، Immich يحتاج*دائما* للحصول على موقع دقيق ليتمكن التطبيق من قرائة اسم شبكة الWi-Fi", + "background_options": "خيارات الخلفية", "backup": "نسخ احتياطي", "backup_album_selection_page_albums_device": "الالبومات على الجهاز ({count})", "backup_album_selection_page_albums_tap": "انقر للتضمين، وانقر نقرًا مزدوجًا للاستثناء", @@ -532,6 +550,7 @@ "backup_album_selection_page_select_albums": "حدد الألبومات", "backup_album_selection_page_selection_info": "معلومات الاختيار", "backup_album_selection_page_total_assets": "إجمالي الأصول الفريدة", + "backup_albums_sync": "مزامنة ألبومات النسخ الاحتياطي", "backup_all": "الجميع", "backup_background_service_backup_failed_message": "فشل في النسخ الاحتياطي للأصول. جارٍ إعادة المحاولة…", "backup_background_service_connection_failed_message": "فشل في الاتصال بالخادم. جارٍ إعادة المحاولة…", @@ -581,6 +600,7 @@ "backup_controller_page_turn_on": "قم بتشغيل النسخ الاحتياطي المقدمة", "backup_controller_page_uploading_file_info": "تحميل معلومات الملف", "backup_err_only_album": "لا يمكن إزالة الألبوم الوحيد", + "backup_error_sync_failed": "فشل المزامنة. لا يمكن معالجة النسخ الاحتياطي.", "backup_info_card_assets": "أصول", "backup_manual_cancelled": "ملغي", "backup_manual_in_progress": "قيد التحميل حاول مره اخرى", @@ -648,6 +668,8 @@ "change_pin_code": "تغيير رمز PIN", "change_your_password": "غير كلمة المرور الخاصة بك", "changed_visibility_successfully": "تم تغيير الرؤية بنجاح", + "charging": "الشحن", + "charging_requirement_mobile_backup": "يتطلب النسخ الاحتياطي في الخلفية أن يكون الجهاز قيد الشحن", "check_corrupt_asset_backup": "التحقق من وجود نسخ احتياطية فاسدة للاصول", "check_corrupt_asset_backup_button": "اجراء فحص", "check_corrupt_asset_backup_description": "قم بإجراء هذا الفحص فقط عبر شبكة Wi-Fi وبعد نسخ جميع الأصول احتياطيًا. قد يستغرق الإجراء بضع دقائق.", @@ -734,6 +756,7 @@ "create_user": "إنشاء مستخدم", "created": "تم الإنشاء", "created_at": "مخلوق", + "creating_linked_albums": "جاري إنشاء الألبومات المرتبطة...", "crop": "قص", "curated_object_page_title": "أشياء", "current_device": "الجهاز الحالي", @@ -883,7 +906,9 @@ "error": "خطأ", "error_change_sort_album": "فشل في تغيير ترتيب الألبوم", "error_delete_face": "حدث خطأ في حذف الوجه من الأصول", + "error_getting_places": "خطأ أثناء استرجاع بيانات المواقع", "error_loading_image": "حدث خطأ أثناء تحميل الصورة", + "error_loading_partners": "خطأ بتحميل بيانات الشركاء: {error}", "error_saving_image": "خطأ: {error}", "error_tag_face_bounding_box": "خطأ في وضع علامة على الوجه - لا يمكن الحصول على إحداثيات المربع المحيط", "error_title": "خطأ - حدث خللٌ ما", @@ -1048,6 +1073,7 @@ "favorites_page_no_favorites": "لم يتم العثور على الأصول المفضلة", "feature_photo_updated": "تم تحديث الصورة المميزة", "features": "الميزات", + "features_in_development": "الميزات قيد التطوير", "features_setting_description": "إدارة ميزات التطبيق", "file_name": "إسم الملف", "file_name_or_extension": "اسم الملف أو امتداده", @@ -1068,12 +1094,15 @@ "gcast_enabled": "كوكل كاست", "gcast_enabled_description": "تقوم هذه الميزة بتحميل الموارد الخارجية من Google حتى تعمل.", "general": "عام", + "geolocation_instruction_location": "انقر على الاصل الذي يحتوي على إحداثيات نظام تحديد المواقع لاستخدام موقعه، أو اختر الموقع مباشرة من الخريطة", "get_help": "الحصول على المساعدة", "get_wifiname_error": "تعذر الحصول على اسم شبكة Wi-Fi. تأكد من منح الأذونات اللازمة واتصالك بشبكة Wi-Fi", "getting_started": "البدء", "go_back": "الرجوع للخلف", "go_to_folder": "اذهب إلى المجلد", "go_to_search": "اذهب إلى البحث", + "gps": "نظام تحديد المواقع", + "gps_missing": "لا يوجد نظام تحديد المواقع", "grant_permission": "منح الاذن", "group_albums_by": "تجميع الألبومات حسب...", "group_country": "مجموعة البلد", @@ -1209,6 +1238,7 @@ "local": "محلّي", "local_asset_cast_failed": "غير قادر على بث أصل لم يتم تحميله إلى الخادم", "local_assets": "أُصول (ملفات) محلية", + "local_media_summary": "ملخص الملفات المحلية", "local_network": "شبكة محلية", "local_network_sheet_info": "سيتصل التطبيق بالخادم من خلال عنوان URL هذا عند استخدام شبكة Wi-Fi المحددة", "location_permission": "اذن الموقع", @@ -1220,6 +1250,7 @@ "location_picker_longitude_hint": "أدخل خط الطول هنا", "lock": "قفل", "locked_folder": "مجلد مقفول", + "log_detail_title": "تفاصيل السجل", "log_out": "تسجيل خروج", "log_out_all_devices": "تسجيل الخروج من كافة الأجهزة", "logged_in_as": "تم تسجيل الدخول باسم {user}", @@ -1250,6 +1281,7 @@ "login_password_changed_success": "تم تحديث كلمة السر بنجاح", "logout_all_device_confirmation": "هل أنت متأكد أنك تريد تسجيل الخروج من جميع الأجهزة؟", "logout_this_device_confirmation": "هل أنت متأكد أنك تريد تسجيل الخروج من هذا الجهاز؟", + "logs": "السجلات", "longitude": "خط الطول", "look": "الشكل", "loop_videos": "تكرار مقاطع الفيديو", @@ -1257,6 +1289,7 @@ "main_branch_warning": "أنت تستخدم إصداراً قيد التطوير؛ ونحن نوصي بشدة باستخدام إصدار النشر!", "main_menu": "القائمة الرئيسية", "make": "صنع", + "manage_geolocation": "إدارة الموقع", "manage_shared_links": "إدارة الروابط المشتركة", "manage_sharing_with_partners": "إدارة المشاركة مع الشركاء", "manage_the_app_settings": "إدارة إعدادات التطبيق", @@ -1291,6 +1324,7 @@ "mark_as_read": "تحديد كمقروء", "marked_all_as_read": "تم تحديد الكل كمقروء", "matches": "تطابقات", + "matching_assets": "‏الاصول المطابقة", "media_type": "نوع الوسائط", "memories": "الذكريات", "memories_all_caught_up": "كل شيء محدث", @@ -1331,6 +1365,7 @@ "name_or_nickname": "الاسم أو اللقب", "network_requirement_photos_upload": "استخدام بيانات الهاتف المحمول لعمل نسخة احتياطية للصور", "network_requirement_videos_upload": "استخدام بيانات الهاتف المحمول لعمل نسخة احتياطية لمقاطع الفيديو", + "network_requirements": "متطلبات الشبكة", "network_requirements_updated": "تم تغيير متطلبات الشبكة، يتم إعادة تعيين قائمة انتظار النسخ الاحتياطي", "networking_settings": "الشبكات", "networking_subtitle": "إدارة إعدادات نقطة الخادم النهائية", @@ -1341,6 +1376,7 @@ "new_person": "شخص جديد", "new_pin_code": "رمز PIN الجديد", "new_pin_code_subtitle": "هذه أول مرة تدخل فيها إلى المجلد المقفل. أنشئ رمزًا PIN للوصول بامان إلى هذه الصفحة", + "new_timeline": "الخط الزمني الجديد", "new_user_created": "تم إنشاء مستخدم جديد", "new_version_available": "إصدار جديد متاح", "newest_first": "الأحدث أولاً", @@ -1354,20 +1390,25 @@ "no_assets_message": "انقر لتحميل صورتك الأولى", "no_assets_to_show": "لا توجد أصول لعرضها", "no_cast_devices_found": "لم يتم ايجاد جهاز بث", + "no_checksum_local": "لا توجد بيانات تحقق متاحة - يتعذر تحميل الاصول المحلية", + "no_checksum_remote": "لا يوجد رمز تحقق متاح - يتعذر تحميل الاصل من الموقع البعيد", "no_duplicates_found": "لم يتم العثور على أي تكرارات.", "no_exif_info_available": "لا تتوفر معلومات exif", "no_explore_results_message": "قم برفع المزيد من الصور لاستكشاف مجموعتك.", "no_favorites_message": "أضف المفضلة للعثور بسرعة على أفضل الصور ومقاطع الفيديو", "no_libraries_message": "إنشاء مكتبة خارجية لعرض الصور ومقاطع الفيديو الخاصة بك", + "no_local_assets_found": "لم يتم العثور على أي اصول محلية تتطابق مع قيمة التحقق هذه", "no_locked_photos_message": "الصور والفديوهات في المجلد المقفل مخفية ولن تصهر في التصفح او البحث في مكتبتك.", "no_name": "لا اسم", "no_notifications": "لا توجد تنبيهات", "no_people_found": "لم يتم العثور على اشخاص مطابقين", "no_places": "لا أماكن", + "no_remote_assets_found": "لم يتم العثور على أي اصول بعيدة تتطابق مع رمز التحقق هذل", "no_results": "لا يوجد نتائج", "no_results_description": "جرب كلمة رئيسية مرادفة أو أكثر عمومية", "no_shared_albums_message": "قم بإنشاء ألبوم لمشاركة الصور ومقاطع الفيديو مع الأشخاص في شبكتك", "no_uploads_in_progress": "لا يوجد اي ملفات قيد الرفع", + "not_available": "غير متاح", "not_in_any_album": "ليست في أي ألبوم", "not_selected": "لم يختار", "note_apply_storage_label_to_previously_uploaded assets": "ملاحظة: لتطبيق سمة التخزين على المحتويات التي تم رفعها مسبقًا، قم بتشغيل", @@ -1402,6 +1443,8 @@ "open_the_search_filters": "افتح مرشحات البحث", "options": "خيارات", "or": "أو", + "organize_into_albums": "ترتيب في ألبومات", + "organize_into_albums_description": "أضف الصور الموجودة إلى الألبومات باستخدام إعدادات النسخ المتزامن الحالية", "organize_your_library": "تنظيم مكتبتك", "original": "أصلي", "other": "أخرى", @@ -1487,6 +1530,7 @@ "port": "المنفذ", "preferences_settings_subtitle": "ادارة تفضيلات التطبيق", "preferences_settings_title": "التفضيلات", + "preparing": "قيد التحضير", "preset": "الإعداد المسبق", "preview": "معاينة", "previous": "السابق", @@ -1503,6 +1547,7 @@ "profile_drawer_client_out_of_date_minor": "تطبيق الهاتف المحمول قديم.يرجى التحديث إلى أحدث إصدار صغير.", "profile_drawer_client_server_up_to_date": "العميل والخادم محدثان", "profile_drawer_github": "Github", + "profile_drawer_readonly_mode": "تم تفعيل وضع القراءة فقط. اضغط مطولا على رمز صورة المستخدم للخروج.", "profile_drawer_server_out_of_date_major": "الخادم قديم.يرجى التحديث إلى أحدث إصدار رئيسي.", "profile_drawer_server_out_of_date_minor": "الخادم قديم.يرجى التحديث إلى أحدث إصدار صغير.", "profile_image_of_user": "صورة الملف الشخصي لـ {user}", @@ -1541,6 +1586,7 @@ "purchase_server_description_2": "حالة الداعم", "purchase_server_title": "الخادم", "purchase_settings_server_activated": "يتم إدارة مفتاح منتج الخادم من قبل مدير النظام", + "query_asset_id": "استعلام عن معرف الأصل", "queue_status": "يتم الاضافة الى قائمة انتظار النسخ الاحتياطي {count}/{total}", "rating": "تقييم نجمي", "rating_clear": "مسح التقييم", @@ -1548,6 +1594,9 @@ "rating_description": "‫‌اعرض تقييم EXIF في لوحة المعلومات", "reaction_options": "خيارات رد الفعل", "read_changelog": "قراءة سجل التغيير", + "readonly_mode_disabled": "تم تعطيل وضع القراءة فقط", + "readonly_mode_enabled": "تم تفعيل وضع القراءة فقط", + "ready_for_upload": "جاهز للرفع", "reassign": "إعادة التعيين", "reassigned_assets_to_existing_person": "تمت إعادة تعيين {count, plural, one {# الأصل} other {# الاصول}} إلى {name, select, null {شخص موجود } other {{name}}}", "reassigned_assets_to_new_person": "تمت إعادة تعيين {count, plural, one {# المحتوى} other {# المحتويات}} إلى شخص جديد", @@ -1572,6 +1621,7 @@ "regenerating_thumbnails": "جارٍ تجديد الصور المصغرة", "remote": "بعيد", "remote_assets": "الأُصول البعيدة", + "remote_media_summary": "ملخص الملفات البعيدة", "remove": "إزالة", "remove_assets_album_confirmation": "هل أنت متأكد أنك تريد إزالة {count, plural, one {# المحتوى} other {# المحتويات}} من الألبوم ؟", "remove_assets_shared_link_confirmation": "هل أنت متأكد أنك تريد إزالة {count, plural, one {# المحتوى} other {# المحتويات}} من رابط المشاركة هذا؟", @@ -1624,6 +1674,7 @@ "restore_user": "استعادة المستخدم", "restored_asset": "المحتويات المستعادة", "resume": "استئناف", + "resume_paused_jobs": "استكمال {count, plural, one {# وظيفة معلقة} other {# وظائف معلقة}}", "retry_upload": "أعد محاولة الرفع", "review_duplicates": "مراجعة التكرارات", "review_large_files": "مراجعة الملفات الكبيرة", @@ -1717,6 +1768,7 @@ "select_user_for_sharing_page_err_album": "فشل في إنشاء ألبوم", "selected": "التحديد", "selected_count": "{count, plural, other {# محددة }}", + "selected_gps_coordinates": "إحداثيات نظام تحديد المواقع المختارة", "send_message": "‏إرسال رسالة", "send_welcome_email": "إرسال بريدًا إلكترونيًا ترحيبيًا", "server_endpoint": "نقطة نهاية الخادم", @@ -1845,6 +1897,7 @@ "show_slideshow_transition": "إظهار انتقال عرض الشرائح", "show_supporter_badge": "شارة المؤيد", "show_supporter_badge_description": "إظهار شارة المؤيد", + "show_text_search_menu": "عرض قائمة خيارات البحث في النص", "shuffle": "خلط", "sidebar": "الشريط الجانبي", "sidebar_display_description": "عرض رابط للعرض في الشريط الجانبي", @@ -1875,6 +1928,7 @@ "stacktrace": "تتّبُع التكديس", "start": "ابدأ", "start_date": "تاريخ البدء", + "start_date_before_end_date": "يجب أن يكون تاريخ بدء الفترة قبل تاريخ نهايتها", "state": "الولاية", "status": "الحالة", "stop_casting": "ايقاف البث", @@ -1899,6 +1953,8 @@ "sync_albums_manual_subtitle": "مزامنة جميع الفديوهات والصور المرفوعة الى البومات الخزن الاحتياطي المختارة", "sync_local": "مزامنة الملفات المحلية", "sync_remote": "مزامنة الملفات البعيدة", + "sync_status": "حالة النسخ المتزامن", + "sync_status_subtitle": "عرض وإدارة نظام النسخ المتزامن", "sync_upload_album_setting_subtitle": "انشئ و ارفع صورك و فديوهاتك الالبومات المختارة في Immich", "tag": "العلامة", "tag_assets": "أصول العلامة", @@ -1936,7 +1992,9 @@ "to_change_password": "تغيير كلمة المرور", "to_favorite": "تفضيل", "to_login": "تسجيل الدخول", + "to_multi_select": "للتحديد المتعدد", "to_parent": "انتقل إلى الوالد", + "to_select": "للتحديد", "to_trash": "حذف", "toggle_settings": "الإعدادات", "total": "الإجمالي", @@ -1956,6 +2014,7 @@ "trash_page_select_assets_btn": "اختر الأصول", "trash_page_title": "سلة المهملات ({count})", "trashed_items_will_be_permanently_deleted_after": "سيتم حذفُ العناصر المحذوفة نِهائيًا بعد {days, plural, one {# يوم} other {# أيام }}.", + "troubleshoot": "استكشاف المشاكل", "type": "النوع", "unable_to_change_pin_code": "تفيير رمز PIN غير ممكن", "unable_to_setup_pin_code": "انشاء رمز PIN غير ممكن", @@ -1986,6 +2045,7 @@ "unstacked_assets_count": "تم إخراج {count, plural, one {# الأصل} other {# الأصول}} من التكديس", "untagged": "غير مُعَلَّم", "up_next": "التالي", + "update_location_action_prompt": "تحديث موقع {count} عناصر محددة على النحو التالي:", "updated_at": "تم التحديث", "updated_password": "تم تحديث كلمة المرور", "upload": "رفع", @@ -2052,6 +2112,7 @@ "view_next_asset": "عرض المحتوى التالي", "view_previous_asset": "عرض المحتوى السابق", "view_qr_code": "­عرض رمز الاستجابة السريعة", + "view_similar_photos": "عرض صور مشابهة", "view_stack": "عرض التكديس", "view_user": "عرض المستخدم", "viewer_remove_from_stack": "حذف من الكومه أو المجموعة", @@ -2070,5 +2131,6 @@ "yes": "نعم", "you_dont_have_any_shared_links": "ليس لديك أي روابط مشتركة", "your_wifi_name": "اسم شبكة Wi-Fi الخاص بك", - "zoom_image": "تكبير الصورة" + "zoom_image": "تكبير الصورة", + "zoom_to_bounds": "تكبير حتى حدود المنطقة" } diff --git a/i18n/az.json b/i18n/az.json index 0449289735..53e7f55db6 100644 --- a/i18n/az.json +++ b/i18n/az.json @@ -1,7 +1,7 @@ { "about": "Haqqında", "account": "Hesab", - "account_settings": "Hesab parametrləri", + "account_settings": "Hesab Parametrləri", "acknowledge": "Təsdiq et", "action": "Əməliyyat", "action_common_update": "Yenilə", @@ -16,8 +16,8 @@ "add_a_title": "Başlıq əlavə et", "add_birthday": "Doğum günü əlavə et", "add_endpoint": "Son nöqtə əlavə et", - "add_exclusion_pattern": "İstisna nümunəsi əlavə et", - "add_import_path": "Import yolunu əlavə et", + "add_exclusion_pattern": "Çıxarma nümunəsi əlavə et", + "add_import_path": "İdxal yolu əlavə et", "add_location": "Məkan əlavə et", "add_more_users": "Daha çox istifadəçi əlavə et", "add_partner": "Partnyor əlavə et", @@ -25,20 +25,21 @@ "add_photos": "Şəkillər əlavə et", "add_tag": "Etiket əlavə et", "add_to": "Bura əlavə et…", - "add_to_album": "Albom əlavə et", + "add_to_album": "Alboma əlavə et", "add_to_album_bottom_sheet_added": "{album} albomuna əlavə edildi", "add_to_album_bottom_sheet_already_exists": "Artıq {album} albomunda var", + "add_to_album_bottom_sheet_some_local_assets": "Bəzi lokal resurslar alboma əlavə edilə bilmədi", "add_to_album_toggle": "{album} üçün seçimi dəyişin", "add_to_albums": "Albomlara əlavə et", - "add_to_albums_count": "Albomlara əlavə et ({count})", + "add_to_albums_count": "({count}) albomlarına əlavə et", "add_to_shared_album": "Paylaşılan alboma əlavə et", "add_url": "URL əlavə et", "added_to_archive": "Arxivə əlavə edildi", "added_to_favorites": "Sevimlilələrə əlavə edildi", "added_to_favorites_count": "{count, number} şəkil sevimlilələrə əlavə edildi", "admin": { - "add_exclusion_pattern_description": "İstisna şablonlarını əlavə edin. *, ** və ? ilə Globbing dəstəklənir. Məs.: \"Raw\" adlanan hər hansısa bir qovluqda bütün faylları saymamaq üçün \"**/Raw/**\"-dan istifadə edin. \".tif\" ilə bitən bütün faylları saymamaq üçün \"**/*.tif\"-dən istifadə edin. Faylı mütləq yoldan istifadə etməklə saymamaq istəyirsinizsə \"/path/to/ignore/**\"-dan istifadə edin.", - "admin_user": "Admin İstifadəçi", + "add_exclusion_pattern_description": "Çıxarma nümunələri əlavə et. *, ** və ? istifadə edilərək globbing dəstəklənir. Hər hansı bir \"Raw\" adlı qovluqdakı bütün faylları görməməzlikdən gəlmək üçün **/Raw/** istifadə et. “.tif” ilə bitən bütün faylları görməməzlikdən gəlmək üçün **/*.tif istifadə et. Tam yolu görməməzlikdən gəlmək üçün /path/to/ignore/** istifadə et.", + "admin_user": "İdarəçi İstifadəçi", "asset_offline_description": "Bu xarici kitabxana varlığı diskdə artıq tapılmadı və zibil qutusuna köçürüldü. Əgər fayl kitabxana içərisində köçürülübsə, zaman şkalanızı yeni uyğun gələn varlıq üçün yoxlayın. Varlığı yenidən qaytarmaq üçün aşağıda verilmiş fayl yolunun Immich tərəfindən əlçatan olduğundan əmin olduqdan sonra kitabxananı skan edin.", "authentication_settings": "Səlahiyyətləndirmə parametrləri", "authentication_settings_description": "Şifrə, OAuth və digər səlahiyyətləndirmə parametrləri", @@ -48,8 +49,15 @@ "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_keep_last_amount": "Tutulması gərəkən nüsxələrin sayı", - "backup_settings": "Ehtiyat Nüsxə Parametrləri", + "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_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_footer": "Immich-in ehtiyat nüsxəsini çıxarmaq haqqında ətraflı məlumat üçün sənədlərə müraciət edin.", + "backup_onboarding_parts_title": "3-2-1 ehtiyat nüsxəsinə aşağıdakılar daxildir:", + "backup_onboarding_title": "Ehtiyat surətlər", + "backup_settings": "Bazanın Dump Parametrləri", "backup_settings_description": "Verilənlər bazasının ehtiyat nüsxə parametrlərini idarə et", + "cleared_jobs": "{job} üçün tapşırıqlar silindi", "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_email_below": "Təsdiqləmək üçün aşağıya {email} yazın", @@ -69,7 +77,7 @@ "image_thumbnail_title": "Önizləmə parametrləri", "job_concurrency": "{job}paralellik", "job_created": "Tapşırıq yaradıldı", - "job_not_concurrency_safe": "Bu tapşırıq parallel fəaliyyət üçün uyğun deyil", + "job_not_concurrency_safe": "Bu iş eyni vaxtda icra üçün təhlükəsiz deyil.", "job_settings": "Tapşırıq parametrləri", "job_settings_description": "Parallel şəkildə fəaliyyət göstərən tapşırıqları idarə et", "job_status": "Tapşırıq statusu", diff --git a/i18n/be.json b/i18n/be.json index f98609b84b..98c02cae53 100644 --- a/i18n/be.json +++ b/i18n/be.json @@ -28,6 +28,8 @@ "add_to_album": "Дадаць у альбом", "add_to_album_bottom_sheet_added": "Дададзена да {album}", "add_to_album_bottom_sheet_already_exists": "Ужо знаходзіцца ў {album}", + "add_to_album_bottom_sheet_some_local_assets": "Некаторыя лакальныя актывы не могуць быць дададзены ў альбом", + "add_to_album_toggle": "Пераключыць выбар для {album}", "add_to_albums": "Дадаць у альбомы", "add_to_albums_count": "Дадаць у альбомы ({count})", "add_to_shared_album": "Дадаць у агульны альбом", @@ -49,6 +51,9 @@ "backup_keep_last_amount": "Колькасць папярэдніх рэзервовых копій для захавання", "backup_onboarding_1_description": "зняшняя копія ў воблаку або ў іншым фізічным месцы.", "backup_onboarding_2_description": "лакальныя копіі на іншых прыладах. Гэта ўключае ў сябе асноўныя файлы і лакальную рэзервовую копію гэтых файлаў.", + "backup_onboarding_3_description": "поўная колькасць копій вашых данных, у тым ліку зыходных файлаў. Гэта ўключае 1 пазаштатную копію і 2 лакальныя копіі.", + "backup_onboarding_description": " стратэгія рэзервавання 3-2-1 рэкамендавана для аховы вашых данных. Вы павінны захоўваць копіі вашых загружаных фота / відэа гэтак жа добра, як базу данных Immich для вычарпальна поўнага рэзервовага капіявання.", + "backup_onboarding_footer": "Каб атрымаць дадатковую інфармацыю пра рэзервовае капіраванне Immich, звярніцеся да дакументацыі.", "backup_onboarding_parts_title": "Рэзервовая копія «3-2-1» уключае ў сябе:", "backup_onboarding_title": "Рэзервовыя копіі", "backup_settings": "Налады рэзервовага капіявання", @@ -91,6 +96,8 @@ "image_resolution": "Раздзяляльнасць", "image_settings": "Налады відарыса", "image_settings_description": "Кіруйце якасцю і раздзяляльнасцю сгенерыраваных відарысаў", + "image_thumbnail_description": "Маленькая мініяцюра з выдаленымі метададзенымі, якая выкарыстоўваецца пры праглядзе груп фатаграфій, такіх як асноўная хроніка", + "image_thumbnail_quality_description": "Якасць мініяцюр ад 1 да 100. Чым вышэй якасць, тым лепш, але пры гэтым ствараюцца файлы большага памеру і можа знізіцца хуткасць водгуку прыкладання.", "image_thumbnail_title": "Налады мініяцюр", "job_concurrency": "{job} канкурэнтнасць", "job_created": "Заданне створана", @@ -98,6 +105,8 @@ "job_settings": "Налады заданняў", "job_settings_description": "Кіраваць наладамі адначасовага (паралельнага) выканання задання", "job_status": "Становішча задання", + "jobs_delayed": "{jobCount, plural, other {# адкладзена}}", + "jobs_failed": "{jobCount, plural, other {# не выканалася}}", "library_created": "Створана бібліятэка: {library}", "library_deleted": "Бібліятэка выдалена", "library_scanning": "Сканаванне па раскладзе", @@ -154,6 +163,9 @@ "trash_settings": "Налады сметніцы", "trash_settings_description": "Кіраванне наладамі сметніцы", "user_cleanup_job": "Ачыстка карыстальніка", + "user_delete_delay": "Уліковы запіс {user} і актывы будуць запланаваны для канчатковага выдалення праз {delay, plural, one {# дзень} few {# дні} many {# дзён} other {# дзён}}.", + "user_delete_delay_settings_description": "Колькасць дзён пасля выдалення, па заканчэнні якіх уліковы запіс карыстальніка і яго актывы будуць выдаленыя незваротна. Заданне на выдаленне карыстальніка запускаецца апоўначы для праверкі гатоўнасці карыстальнікаў да выдалення. Змены ў гэтым параметры будуць улічаныя пры наступным выкананні.", + "user_delete_immediately": "Уліковы запіс {user} і актывы будуць неадкладна змешчаны ў чаргу на канчатковае выдаленне.", "user_management": "Кіраванне карыстальнікамі", "user_password_has_been_reset": "Пароль карыстальніка быў скінуты:", "user_password_reset_description": "Задайце карыстальніку часовы пароль і паведаміце яму, што пры наступным уваходзе ў сістэму яму трэба будзе змяніць пароль.", diff --git a/i18n/bg.json b/i18n/bg.json index 9debd3b10f..a0ab4d0a80 100644 --- a/i18n/bg.json +++ b/i18n/bg.json @@ -28,6 +28,7 @@ "add_to_album": "Добави към албум", "add_to_album_bottom_sheet_added": "Добавено в {album}", "add_to_album_bottom_sheet_already_exists": "Вече е в {album}", + "add_to_album_bottom_sheet_some_local_assets": "Някои локални файлове не успяха да се добавят към албума", "add_to_album_toggle": "Сменете избора за {album}", "add_to_albums": "Добавяне в албуми", "add_to_albums_count": "Добавяне в албуми ({count})", @@ -123,6 +124,13 @@ "logging_enable_description": "Включване на запис (логове)", "logging_level_description": "Когато е включено, какво ниво на записване да се използва.", "logging_settings": "Записване", + "machine_learning_availability_checks": "Проверки за наличност", + "machine_learning_availability_checks_description": "Автоматично откриване и предпочитане на налични сървъри за машинно обучение", + "machine_learning_availability_checks_enabled": "Активиране на проверки за наличност", + "machine_learning_availability_checks_interval": "Интервал на проверяване", + "machine_learning_availability_checks_interval_description": "Време в милисекунди между проверките за наличност", + "machine_learning_availability_checks_timeout": "Време за изчакване на отговор", + "machine_learning_availability_checks_timeout_description": "Време за изчакване на отговор в милисекунди при проверка на наличност", "machine_learning_clip_model": "CLIP модел", "machine_learning_clip_model_description": "Името на CLIP модела, посочен тук. Имайте предвид, че при промяна на модела трябва да стартирате отново задачата \"Интелигентно Търсене\" за всички изображения.", "machine_learning_duplicate_detection": "Откриване на дубликати", @@ -591,6 +599,7 @@ "backup_controller_page_turn_on": "Включи архивиране в активен режим", "backup_controller_page_uploading_file_info": "Инфо за архивирания файл", "backup_err_only_album": "Не може да се премахне единствения албум", + "backup_error_sync_failed": "Синхронизацията е неуспешна. Резервното копие не може да се обработи.", "backup_info_card_assets": "обекта", "backup_manual_cancelled": "Отменено", "backup_manual_in_progress": "Върви архивиране. Опитай след малко", @@ -1228,7 +1237,7 @@ "local": "Локално", "local_asset_cast_failed": "Не може да се предава обект, който още не е качен на сървъра", "local_assets": "Локални обекти", - "local_media_summary": "Обобщение на локалните медийни файлове", + "local_media_summary": "Обобщение на локалните файлове", "local_network": "Локална мрежа", "local_network_sheet_info": "Приложението ще се свърже със сървъра на този URL, когато устройството е свързано към зададената Wi-Fi мрежа", "location_permission": "Разрешение за местоположение", @@ -1337,7 +1346,7 @@ "missing": "Липсващи", "model": "Модел", "month": "Месец", - "monthly_title_text_date_format": "MMMM y", + "monthly_title_text_date_format": "MMMM г", "more": "Още", "move": "Премести", "move_off_locked_folder": "Извади от заключената папка", @@ -1520,6 +1529,7 @@ "port": "Порт", "preferences_settings_subtitle": "Управление на предпочитанията на приложението", "preferences_settings_title": "Предпочитания", + "preparing": "Подготовка", "preset": "Шаблон", "preview": "Прегледи", "previous": "Предишно", @@ -1532,13 +1542,13 @@ "privacy": "Поверителност", "profile": "Профил", "profile_drawer_app_logs": "Дневник", - "profile_drawer_client_out_of_date_major": "Мобилното приложение е остаряло. Моля, актуализирай до най-новата основна версия.", - "profile_drawer_client_out_of_date_minor": "Мобилното приложение е остаряло. Моля, актуализирай до най-новата версия.", + "profile_drawer_client_out_of_date_major": "Мобилното приложение е остаряло. Моля, актуализирайте до най-новата основна версия.", + "profile_drawer_client_out_of_date_minor": "Мобилното приложение е остаряло. Моля, актуализирайте до най-новата версия.", "profile_drawer_client_server_up_to_date": "Клиента и сървъра са обновени", "profile_drawer_github": "GitHub", "profile_drawer_readonly_mode": "Режима само за четене е активиран. С дълго натискане върху картиката-аватар на потребителя ще деактивирате само за четене.", - "profile_drawer_server_out_of_date_major": "Версията на сървъра е остаряла. Моля, актуализирай поне до последната главна версия.", - "profile_drawer_server_out_of_date_minor": "Версията на сървъра е остаряла. Моля, актуализирай до последната версия.", + "profile_drawer_server_out_of_date_major": "Версията на сървъра е остаряла. Моля, актуализирайте поне до последната главна версия.", + "profile_drawer_server_out_of_date_minor": "Версията на сървъра е остаряла. Моля, актуализирайте до последната версия.", "profile_image_of_user": "Профилна снимка на {user}", "profile_picture_set": "Профилната снимка е сложена.", "public_album": "Публичен албум", @@ -1585,6 +1595,7 @@ "read_changelog": "Прочети промените", "readonly_mode_disabled": "Режима само за четене е деактивиран", "readonly_mode_enabled": "Режима само за четене е активиран", + "ready_for_upload": "Готово за качване", "reassign": "Преназначаване", "reassigned_assets_to_existing_person": "Преназначени {count, plural, one {# елемент} other {# елемента}} на {name, select, null {съществуващ човек} other {{name}}}", "reassigned_assets_to_new_person": "Преназначени {count, plural, one {# елемент} other {# елемента}} на нов човек", @@ -1916,6 +1927,7 @@ "stacktrace": "Следа на събраните", "start": "Старт", "start_date": "Начална дата", + "start_date_before_end_date": "Началната дата трябва да бъде преди крайната дата", "state": "Щат", "status": "Статус", "stop_casting": "Спри предаването", diff --git a/i18n/bn.json b/i18n/bn.json index 004b584d3c..813aadea7e 100644 --- a/i18n/bn.json +++ b/i18n/bn.json @@ -28,6 +28,7 @@ "add_to_album": "এলবাম এ যোগ করুন", "add_to_album_bottom_sheet_added": "{album} এ যোগ করা হয়েছে", "add_to_album_bottom_sheet_already_exists": "{album} এ আগে থেকেই আছে", + "add_to_album_bottom_sheet_some_local_assets": "কিছু স্থানীয় ছবি বা ভিডিও অ্যালবামে যোগ করা যায়নি", "add_to_album_toggle": "{album} - এর নির্বাচন পরিবর্তন করুন", "add_to_albums": "অ্যালবামে যোগ করুন", "add_to_albums_count": "অ্যালবামে যোগ করুন ({count})", @@ -123,6 +124,11 @@ "logging_enable_description": "লগিং এনাবল/সক্ষম করুন", "logging_level_description": "সক্রিয় থাকাকালীন, কোন লগ স্তর ব্যবহার করতে হবে।", "logging_settings": "লগিং", + "machine_learning_availability_checks": "প্রাপ্যতা পরীক্ষা", + "machine_learning_availability_checks_description": "স্বয়ংক্রিয়ভাবে উপলব্ধ মেশিন লার্নিং সার্ভারগুলি সনাক্ত করুন এবং পছন্দ করুন", + "machine_learning_availability_checks_enabled": "প্রাপ্যতা পরীক্ষা সক্ষম করুন", + "machine_learning_availability_checks_interval": "চেক ব্যবধান", + "machine_learning_availability_checks_interval_description": "প্রাপ্যতা পরীক্ষাগুলির মধ্যে ব্যবধান মিলিসেকেন্ডে", "machine_learning_clip_model": "CLIP মডেল", "machine_learning_clip_model_description": "এখানে তালিকাভুক্ত একটি CLIP মডেলের নাম। মনে রাখবেন, মডেল পরিবর্তনের পর সব ছবির জন্য অবশ্যই ‘Smart Search’ কাজটি আবার চালাতে হবে।", "machine_learning_duplicate_detection": "পুনরাবৃত্তি সনাক্তকরণ", diff --git a/i18n/ca.json b/i18n/ca.json index 2eb795a0d4..cd55641f64 100644 --- a/i18n/ca.json +++ b/i18n/ca.json @@ -28,12 +28,14 @@ "add_to_album": "Afegir a un l'àlbum", "add_to_album_bottom_sheet_added": "Afegit a {album}", "add_to_album_bottom_sheet_already_exists": "Ja està a {album}", + "add_to_album_bottom_sheet_some_local_assets": "Alguns recursos locals no s'han pogut afegir a l'àlbum", "add_to_album_toggle": "Commutar selecció de {album}", "add_to_albums": "Afegir als àlbums", "add_to_albums_count": "Afegir als àlbums ({count})", "add_to_shared_album": "Afegir a un àlbum compartit", + "add_upload_to_stack": "Afegeix la càrrega a la pila", "add_url": "Afegir URL", - "added_to_archive": "Afegit als arxivats", + "added_to_archive": "Afegir a l'arxiu", "added_to_favorites": "Afegit als preferits", "added_to_favorites_count": "{count, number} afegits als preferits", "admin": { @@ -548,6 +550,7 @@ "backup_album_selection_page_select_albums": "Selecciona àlbums", "backup_album_selection_page_selection_info": "Informació de la selecció", "backup_album_selection_page_total_assets": "Total d'elements únics", + "backup_albums_sync": "Sincronització d'àlbums de còpia de seguretat", "backup_all": "Tots", "backup_background_service_backup_failed_message": "No s'ha pogut copiar els elements. Tornant a intentar…", "backup_background_service_connection_failed_message": "No s'ha pogut connectar al servidor. Tornant a intentar…", @@ -597,6 +600,7 @@ "backup_controller_page_turn_on": "Activa la còpia de seguretat", "backup_controller_page_uploading_file_info": "S'està pujant la informació del fitxer", "backup_err_only_album": "No es pot eliminar l'únic àlbum", + "backup_error_sync_failed": "Sincronització malament, No es pot fer backup.", "backup_info_card_assets": "elements", "backup_manual_cancelled": "Cancel·lat", "backup_manual_in_progress": "La pujada ja està en curs. Torneu-ho a provar més tard", @@ -665,6 +669,7 @@ "change_your_password": "Canvia la teva contrasenya", "changed_visibility_successfully": "Visibilitat canviada amb èxit", "charging": "Carregant", + "charging_requirement_mobile_backup": "La còpia de seguretat en segon pla requereix que el dispositiu estigui carregant", "check_corrupt_asset_backup": "Comprovar les còpies de seguretat corruptes", "check_corrupt_asset_backup_button": "Realitzar comprovació", "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.", @@ -903,6 +908,7 @@ "error_delete_face": "Error esborrant cara de les cares reconegudes", "error_getting_places": "S'ha produït un error en obtenir els llocs", "error_loading_image": "Error carregant la imatge", + "error_loading_partners": "No s'han pogut carregar les parelles: {error}", "error_saving_image": "Error: {error}", "error_tag_face_bounding_box": "Error a l'etiquetar la cara - no s'han pogut obtenir les coordenades de l'àrea", "error_title": "Error - Quelcom ha anat malament", @@ -1109,7 +1115,7 @@ "has_quota": "Quota", "hash_asset": "Hash del recurs", "hashed_assets": "Recursos amb hash", - "hashing": "Hashing", + "hashing": "Generant el hash", "header_settings_add_header_tip": "Afegeix Capçalera", "header_settings_field_validator_msg": "El valor no pot estar buit", "header_settings_header_name_input": "Nom de la capçalera", @@ -1232,6 +1238,7 @@ "local": "Local", "local_asset_cast_failed": "No es pot convertir un actiu que no s'ha penjat al servidor", "local_assets": "Recursos Locals", + "local_media_summary": "Resum de Mitjans Locals", "local_network": "Xarxa local", "local_network_sheet_info": "L'aplicació es connectarà al servidor mitjançant aquest URL quan utilitzeu la xarxa Wi-Fi especificada", "location_permission": "Permís d'ubicació", @@ -1243,6 +1250,7 @@ "location_picker_longitude_hint": "Introdueix aquí la longitud", "lock": "Bloqueja", "locked_folder": "Carpeta bloquejada", + "log_detail_title": "Detall de Registres", "log_out": "Tanca la sessió", "log_out_all_devices": "Tanqueu la sessió de tots els dispositius", "logged_in_as": "Sessió iniciada com a {user}", @@ -1273,6 +1281,7 @@ "login_password_changed_success": "La contrasenya s'ha canviat correctament", "logout_all_device_confirmation": "Esteu segur que voleu tancar la sessió de tots els dispositius?", "logout_this_device_confirmation": "Esteu segur que voleu tancar la sessió d'aquest dispositiu?", + "logs": "Registres", "longitude": "Longitud", "look": "Aspecte", "loop_videos": "Vídeos en bucle", @@ -1315,6 +1324,7 @@ "mark_as_read": "Marcar com ha llegit", "marked_all_as_read": "Marcat tot com a llegit", "matches": "Coincidències", + "matching_assets": "Recursos Coincidents", "media_type": "Tipus de mitjà", "memories": "Records", "memories_all_caught_up": "Posat al dia", @@ -1355,6 +1365,7 @@ "name_or_nickname": "Nom o sobrenom", "network_requirement_photos_upload": "Fes servir dades mòbils per a còpies de seguretat de fotos", "network_requirement_videos_upload": "Fes servir dades mòbils per a còpies de seguretat de videos", + "network_requirements": "Requeriments de Xarxa", "network_requirements_updated": "Han canviat els requeriments de xarxa, reiniciant la cua", "networking_settings": "Xarxes", "networking_subtitle": "Gestiona la configuració del endpoint del servidor", @@ -1365,6 +1376,7 @@ "new_person": "Persona nova", "new_pin_code": "Nou codi PIN", "new_pin_code_subtitle": "Aquesta és la primera vegada que accedeixes a la carpeta bloquejada. Crea una codi PIN i accedeix de manera segura a aquesta pàgina", + "new_timeline": "Nova Línia de Temps", "new_user_created": "Nou usuari creat", "new_version_available": "NOVA VERSIÓ DISPONIBLE", "newest_first": "El més nou primer", @@ -1378,20 +1390,25 @@ "no_assets_message": "FEU CLIC PER PUJAR LA VOSTRA PRIMERA FOTO", "no_assets_to_show": "No hi ha elements per mostrar", "no_cast_devices_found": "No s'han trobat dispositius per transmetre", + "no_checksum_local": "Cap checksum disponible - no s'han pogut carregar els recursos locals", + "no_checksum_remote": "Cap checksum disponible - no s'ha pogut obtenir el recurs remot", "no_duplicates_found": "No s'han trobat duplicats.", "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_favorites_message": "Afegiu preferits per trobar les millors fotos i vídeos a l'instant", "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_locked_photos_message": "Les fotos i vídeos d'aquesta carpeta estan ocultes, i no es mostraran a mesura que navegues o cerques a la teva biblioteca.", "no_name": "Sense nom", "no_notifications": "No hi ha notificacions", "no_people_found": "No s'han trobat coincidències de persones", "no_places": "No hi ha llocs", + "no_remote_assets_found": "No s'ha trobat cap recurs remot amb aquest checksum", "no_results": "Sense resultats", "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_uploads_in_progress": "Cap pujada en progrés", + "not_available": "N/A", "not_in_any_album": "En cap àlbum", "not_selected": "No seleccionat", "note_apply_storage_label_to_previously_uploaded assets": "Nota: per aplicar l'etiqueta d'emmagatzematge als actius penjats anteriorment, executeu el", @@ -1513,6 +1530,7 @@ "port": "Port", "preferences_settings_subtitle": "Gestiona les preferències de l'aplicació", "preferences_settings_title": "Preferències", + "preparing": "Preparant", "preset": "Preestablert", "preview": "Previsualització", "previous": "Anterior", @@ -1578,6 +1596,7 @@ "read_changelog": "Llegeix el registre de canvis", "readonly_mode_disabled": "Mode de només lectura desactivat", "readonly_mode_enabled": "Mode de només lectura activat", + "ready_for_upload": "Llest per a pujar", "reassign": "Reassignar", "reassigned_assets_to_existing_person": "{count, plural, one {S'ha reassignat # recurs} other {S'han reassignat # recursos}} a {name, select, null {una persona existent} other {{name}}}", "reassigned_assets_to_new_person": "{count, plural, one {S'ha reassignat # recurs} other {S'han reassignat # recursos}} a una persona nova", @@ -1602,6 +1621,7 @@ "regenerating_thumbnails": "Regenerant les miniatures", "remote": "Remot", "remote_assets": "Recursos Remots", + "remote_media_summary": "Resum de Mitjans Remots", "remove": "Eliminar", "remove_assets_album_confirmation": "Confirmes que vols eliminar {count, plural, one {# recurs} other {# recursos}} de l'àlbum?", "remove_assets_shared_link_confirmation": "Esteu segur que voleu eliminar {count, plural, one {# recurs} other {# recursos}} d'aquest enllaç compartit?", @@ -1654,6 +1674,7 @@ "restore_user": "Restaurar l'usuari", "restored_asset": "Element restaurat", "resume": "Reprendre", + "resume_paused_jobs": "Reprèn {count, plural, one {# treball pausat} other {# treballs pausats}}", "retry_upload": "Torna a provar de pujar", "review_duplicates": "Revisar duplicats", "review_large_files": "Revisar fitxers grans", @@ -1876,6 +1897,7 @@ "show_slideshow_transition": "Mostra la transició de la presentació de diapositives", "show_supporter_badge": "Insígnia de contribuent", "show_supporter_badge_description": "Mostra una insígnia de contributor", + "show_text_search_menu": "Mostra el menú de cerca amb text", "shuffle": "Mescla", "sidebar": "Barra lateral", "sidebar_display_description": "Mostra un enllaç a la vista a la barra lateral", @@ -1906,6 +1928,7 @@ "stacktrace": "Traça de pila", "start": "Inicia", "start_date": "Data inicial", + "start_date_before_end_date": "La data d'inici ha de ser abans de la data de fi", "state": "Regió", "status": "Estat", "stop_casting": "Atura la transmisió", @@ -1930,6 +1953,8 @@ "sync_albums_manual_subtitle": "Sincronitza tots els vídeos i fotos penjats amb els àlbums de còpia de seguretat seleccionats", "sync_local": "Sincronitza Local", "sync_remote": "Sincronitza Remot", + "sync_status": "Estat 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", "tag": "Etiqueta", "tag_assets": "Etiquetar actius", @@ -1989,6 +2014,7 @@ "trash_page_select_assets_btn": "Selecciona elements", "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}}.", + "troubleshoot": "Solució de problemes", "type": "Tipus", "unable_to_change_pin_code": "No es pot canviar el codi PIN", "unable_to_setup_pin_code": "No s'ha pogut configurar el codi PIN", @@ -2105,5 +2131,6 @@ "yes": "Sí", "you_dont_have_any_shared_links": "No tens cap enllaç compartit", "your_wifi_name": "Nom del teu Wi-Fi", - "zoom_image": "Ampliar Imatge" + "zoom_image": "Ampliar Imatge", + "zoom_to_bounds": "Amplia als límits" } diff --git a/i18n/cs.json b/i18n/cs.json index 4c42d2d3f1..e6fe1a3589 100644 --- a/i18n/cs.json +++ b/i18n/cs.json @@ -28,10 +28,12 @@ "add_to_album": "Přidat do alba", "add_to_album_bottom_sheet_added": "Přidáno do {album}", "add_to_album_bottom_sheet_already_exists": "Je již v {album}", + "add_to_album_bottom_sheet_some_local_assets": "Některé místní položky nebylo možné přidat do alba", "add_to_album_toggle": "Přepnout výběr pro {album}", "add_to_albums": "Přidat do alb", "add_to_albums_count": "Přidat do alb ({count})", "add_to_shared_album": "Přidat do sdíleného alba", + "add_upload_to_stack": "Přidat nahrané do zásobníku", "add_url": "Přidat URL", "added_to_archive": "Přidáno do archivu", "added_to_favorites": "Přidáno do oblíbených", @@ -128,8 +130,8 @@ "machine_learning_availability_checks_enabled": "Povolit kontroly dostupnosti", "machine_learning_availability_checks_interval": "Interval kontrol", "machine_learning_availability_checks_interval_description": "Interval v milisekundách mezi kontrolami dostupnosti", - "machine_learning_availability_checks_timeout": "Vypršení požadavku", - "machine_learning_availability_checks_timeout_description": "Časové vypršení požadavku v milisekundách u kontrol dostupnosti", + "machine_learning_availability_checks_timeout": "Časový limit požadavku", + "machine_learning_availability_checks_timeout_description": "Časový limit v milisekundách pro kontrolu dostupnosti", "machine_learning_clip_model": "Model CLIP", "machine_learning_clip_model_description": "Název CLIP modelu je uvedený zde. Pamatujte, že při změně modelu je nutné znovu spustit úlohu 'Chytré vyhledávání' pro všechny obrázky.", "machine_learning_duplicate_detection": "Kontrola duplicit", @@ -598,6 +600,7 @@ "backup_controller_page_turn_on": "Povolit zálohování na popředí", "backup_controller_page_uploading_file_info": "Informace o nahraném souboru", "backup_err_only_album": "Nelze odstranit jediné vybrané album", + "backup_error_sync_failed": "Synchronizace selhala. Nelze zpracovat zálohu.", "backup_info_card_assets": "položek", "backup_manual_cancelled": "Zrušeno", "backup_manual_in_progress": "Nahrávání již probíhá. Zkuste to znovu později", diff --git a/i18n/da.json b/i18n/da.json index 61891e7ef8..24dd99d45f 100644 --- a/i18n/da.json +++ b/i18n/da.json @@ -28,6 +28,7 @@ "add_to_album": "Tilføj til album", "add_to_album_bottom_sheet_added": "Tilføjet til {album}", "add_to_album_bottom_sheet_already_exists": "Allerede i {album}", + "add_to_album_bottom_sheet_some_local_assets": "Nogle lokale elementer kunne ikke føjes til albummet", "add_to_album_toggle": "Skift selektion for {album}", "add_to_albums": "Tilføj til albummer", "add_to_albums_count": "Tilføj til albummer({count})", @@ -598,6 +599,7 @@ "backup_controller_page_turn_on": "Slå sikkerhedskopiering til", "backup_controller_page_uploading_file_info": "Uploader filinformation", "backup_err_only_album": "Kan ikke slette det eneste album", + "backup_error_sync_failed": "Synkroniseringen mislykkedes. Sikkerhedskopieringen kunne ikke behandles.", "backup_info_card_assets": "objekter", "backup_manual_cancelled": "Annulleret", "backup_manual_in_progress": "Upload er allerede undervejs. Prøv igen efter noget tid", @@ -649,7 +651,7 @@ "cast": "Caste", "cast_description": "Konfigurer tilgængelige cast destinationer", "change_date": "Ændr dato", - "change_description": "Beskrivelse af ændringer", + "change_description": "Ændr beskrivelse", "change_display_order": "Ændrer visningsrækkefølge", "change_expiration_time": "Ændr udløbstidspunkt", "change_location": "Ændr sted", @@ -1383,7 +1385,7 @@ "no_albums_message": "Opret et album for at organisere dine billeder og videoer", "no_albums_with_name_yet": "Det ser ud til, at du ikke har noget album med dette navn endnu.", "no_albums_yet": "Det ser ud til, at du ikke har nogen album endnu.", - "no_archived_assets_message": "Arkivér billeder og videoer for at gemme dem væk fra din Billede oversigt", + "no_archived_assets_message": "Arkivér billeder og videoer for at gemme dem væk fra din billedoversigt", "no_assets_message": "KLIK FOR AT UPLOADE DIT FØRSTE BILLEDE", "no_assets_to_show": "Ingen elementer at vise", "no_cast_devices_found": "Ingen Cast-enheder fundet", diff --git a/i18n/de.json b/i18n/de.json index a9deba04fa..7f81950c29 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -28,10 +28,12 @@ "add_to_album": "Zu Album hinzufügen", "add_to_album_bottom_sheet_added": "Zu {album} hinzugefügt", "add_to_album_bottom_sheet_already_exists": "Bereits in {album}", + "add_to_album_bottom_sheet_some_local_assets": "Einige lokale Dateien konnten nicht zum Album hinzugefügt werden", "add_to_album_toggle": "Auswahl umschalten für {album}", "add_to_albums": "Zu Alben hinzufügen", "add_to_albums_count": "Zu Alben hinzufügen ({count})", "add_to_shared_album": "Zu geteiltem Album hinzufügen", + "add_upload_to_stack": "Upload zum Stapel hinzufügen", "add_url": "URL hinzufügen", "added_to_archive": "Zum Archiv hinzugefügt", "added_to_favorites": "Zu Favoriten hinzugefügt", @@ -47,14 +49,14 @@ "background_task_job": "Hintergrundaufgaben", "backup_database": "Datenbanksicherung erstellen", "backup_database_enable_description": "Datenbank regelmäßig sichern", - "backup_keep_last_amount": "Anzahl der aufzubewahrenden früheren Backups", + "backup_keep_last_amount": "Anzahl der aufzubewahrenden früheren Sicherungen", "backup_onboarding_1_description": "Offsite-Kopie in der Cloud oder an einem anderen physischen Ort.", - "backup_onboarding_2_description": "Lokale Kopien auf verschiedenen Geräten. Dazu gehören die Hauptdateien und eine lokale Sicherung dieser Dateien.", - "backup_onboarding_3_description": "3 komplette Kopien deiner Daten, inkl. der Originaldateien. Dies umfasst 1 Kopie an einem anderen Ort und 2 lokale Kopie.", - "backup_onboarding_description": "Eine 3-2-1 Backup-Strategie wird empfohlen, um deine Daten zu schützen. Du solltest sowohl Kopien deiner hochgeladenen Fotos/Videos als auch der Immich-Datenbank aufbewahren, um eine umfassende Backup-Lösung zu haben.", + "backup_onboarding_2_description": "lokale Kopien auf verschiedenen Geräten. Dazu gehören die Hauptdateien und eine lokale Sicherung dieser Dateien.", + "backup_onboarding_3_description": "Kopien deiner Daten inklusive Originaldateien. Dies umfasst 1 Kopie an einem anderen Ort und 2 lokale Kopien.", + "backup_onboarding_description": "Eine 3-2-1 Sicherungssrategie wird empfohlen, um deine Daten zu schützen. Du solltest sowohl Kopien deiner hochgeladenen Fotos/Videos als auch der Immich-Datenbank aufbewahren, um eine umfassende Sicherungslösung zu haben.", "backup_onboarding_footer": "Weitere Informationen zum Sichern von Immich findest du in der Dokumentation.", "backup_onboarding_parts_title": "Eine 3-2-1-Sicherung umfasst:", - "backup_onboarding_title": "Backups", + "backup_onboarding_title": "Sicherungen", "backup_settings": "Einstellungen für Datenbanksicherung", "backup_settings_description": "Einstellungen zur regelmäßigen Sicherung der Datenbank. Hinweis: Diese Jobs werden nicht überwacht und du wirst nicht über Fehler informiert.", "cleared_jobs": "Folgende Aufgaben zurückgesetzt: {job}", @@ -66,9 +68,9 @@ "confirm_user_password_reset": "Bist du sicher, dass du das Passwort für {user} zurücksetzen möchtest?", "confirm_user_pin_code_reset": "Bist du sicher, dass du den PIN Code von {user} zurücksetzen möchtest?", "create_job": "Aufgabe erstellen", - "cron_expression": "Cron Zeitangabe", - "cron_expression_description": "Setze ein Intervall für die Sicherung mittels cron. Hilfe mit dem Format bietet dir dabei z. B. der Crontab Guru", - "cron_expression_presets": "Nützliche Zeitangaben für Cron", + "cron_expression": "Cron-Zeitangabe", + "cron_expression_description": "Setze das Scanintervall im Cron-Format. Hilfe mit dem Format bietet dir dabei z. B. der Crontab Guru", + "cron_expression_presets": "Vorlagen für Cron-Zeitangabe", "disable_login": "Login deaktivieren", "duplicate_detection_job_description": "Diese Aufgabe führt das maschinelle Lernen für jede Datei aus, um Duplikate zu finden. Diese Aufgabe beruht auf der intelligenten Suche", "exclusion_pattern_description": "Mit Ausschlussmustern können Dateien und Ordner beim Scannen Ihrer Bibliothek ignoriert werden. Dies ist nützlich, wenn du Ordner hast, die Dateien enthalten, die du nicht importieren möchtest, wie z. B. RAW-Dateien.", @@ -285,13 +287,13 @@ "storage_template_user_label": "{label} is die Speicherpfadbezeichnung des Benutzers", "system_settings": "Systemeinstellungen", "tag_cleanup_job": "Tags aufräumen", - "template_email_available_tags": "In deiner Vorlage kannst du die folgenden Variablen verwenden: {tags}", - "template_email_if_empty": "Wenn die Vorlage leer ist, wird die Standard-E-Mail verwendet.", - "template_email_invite_album": "E-Mail-Vorlage: Einladung zu Album", + "template_email_available_tags": "Du kannst die folgenden Variablen in deiner Vorlage verwenden: {tags}", + "template_email_if_empty": "Wenn die Vorlage leer ist, wird die Standard-E-Mail-Vorlage verwendet.", + "template_email_invite_album": "Einladung zu Album", "template_email_preview": "Vorschau", "template_email_settings": "E-Mail-Vorlagen", - "template_email_update_album": "Album-Vorlage aktualisieren", - "template_email_welcome": "Willkommen bei den E-Mail-Vorlagen", + "template_email_update_album": "Aktualisiertes Album", + "template_email_welcome": "Willkommens-E-Mail", "template_settings": "Benachrichtigungsvorlagen", "template_settings_description": "Benutzerdefinierte Vorlagen für Benachrichtigungen verwalten", "theme_custom_css_settings": "Benutzerdefiniertes CSS", @@ -537,18 +539,18 @@ "autoplay_slideshow": "Automatische Diashow", "back": "Zurück", "back_close_deselect": "Zurück, Schließen oder Abwählen", - "background_backup_running_error": "Hintergrund Sicherung läuft, kann manuelle Sicherung nicht starten", + "background_backup_running_error": "Sicherung läuft im Hintergrund. Manuelle Sicherung kann nicht gestartet werden", "background_location_permission": "Hintergrund Standortfreigabe", "background_location_permission_content": "Um im Hintergrund zwischen den Netzwerken wechseln zu können, muss Immich *immer* Zugriff auf den genauen Standort haben, damit die App den Namen des WLAN-Netzwerks ermitteln kann", "background_options": "Hintergrund Optionen", "backup": "Sicherung", "backup_album_selection_page_albums_device": "Alben auf dem Gerät ({count})", - "backup_album_selection_page_albums_tap": "Einmalig das Album antippen um es zu sichern, doppelt antippen um es nicht mehr zu sichern", + "backup_album_selection_page_albums_tap": "Antippen zum sichern, erneut antippen zum Ausschließen", "backup_album_selection_page_assets_scatter": "Elemente (Fotos / Videos) können sich über mehrere Alben verteilen. Daher können diese vor der Sicherung eingeschlossen oder ausgeschlossen werden.", "backup_album_selection_page_select_albums": "Alben auswählen", - "backup_album_selection_page_selection_info": "Information", - "backup_album_selection_page_total_assets": "Elemente", - "backup_albums_sync": "Synchronisation von Alben beim Backup", + "backup_album_selection_page_selection_info": "Auswahlinformation", + "backup_album_selection_page_total_assets": "Elemente gesamt", + "backup_albums_sync": "Synchronisation der Sicherungsalben", "backup_all": "Alle", "backup_background_service_backup_failed_message": "Es trat ein Fehler bei der Sicherung auf. Erneuter Versuch…", "backup_background_service_connection_failed_message": "Es konnte keine Verbindung zum Server hergestellt werden. Erneuter Versuch…", @@ -598,6 +600,7 @@ "backup_controller_page_turn_on": "Sicherung im Vordergrund einschalten", "backup_controller_page_uploading_file_info": "Informationen", "backup_err_only_album": "Das einzige Album kann nicht entfernt werden", + "backup_error_sync_failed": "Synchronisierung fehlgeschlagen. Sicherung kann nicht verarbeitet werden.", "backup_info_card_assets": "Elemente", "backup_manual_cancelled": "Abgebrochen", "backup_manual_in_progress": "Sicherung läuft bereits. Bitte versuche es später erneut", @@ -1036,6 +1039,7 @@ "exif_bottom_sheet_description_error": "Fehler bei der Aktualisierung der Beschreibung", "exif_bottom_sheet_details": "DETAILS", "exif_bottom_sheet_location": "STANDORT", + "exif_bottom_sheet_no_description": "Keine Beschreibung", "exif_bottom_sheet_people": "PERSONEN", "exif_bottom_sheet_person_add_person": "Namen hinzufügen", "exit_slideshow": "Diashow beenden", @@ -1952,7 +1956,7 @@ "sync_remote": "mit Server synchronisieren", "sync_status": "Synchronisierungstatus", "sync_status_subtitle": "Synchronisierungssystem anzeigen und bearbeiten", - "sync_upload_album_setting_subtitle": "Erstelle deine ausgewählten Alben in Immich und lade die Fotos und Videos dort hoch", + "sync_upload_album_setting_subtitle": "Erstelle und lade deine ausgewählten Fotos und Videos in die ausgewählten Alben auf Immich hoch", "tag": "Tag", "tag_assets": "Dateien taggen", "tag_created": "Tag erstellt: {tag}", diff --git a/i18n/el.json b/i18n/el.json index cb6708d711..6f6835b441 100644 --- a/i18n/el.json +++ b/i18n/el.json @@ -28,10 +28,12 @@ "add_to_album": "Προσθήκη σε άλμπουμ", "add_to_album_bottom_sheet_added": "Προστέθηκε στο {album}", "add_to_album_bottom_sheet_already_exists": "Ήδη στο {album}", + "add_to_album_bottom_sheet_some_local_assets": "Ορισμένοι τοπικά στοιχεία δεν μπόρεσαν να προστεθούν στο άλμπουμ", "add_to_album_toggle": "Εναλλαγή επιλογής για το {album}", "add_to_albums": "Προσθήκη στα άλμπουμ", "add_to_albums_count": "Προσθήκη στα άλμπουμ ({count})", "add_to_shared_album": "Προσθήκη σε κοινόχρηστο άλμπουμ", + "add_upload_to_stack": "Προσθήκη αρχείου στην ουρά", "add_url": "Προσθήκη Συνδέσμου", "added_to_archive": "Προστέθηκε στο αρχείο", "added_to_favorites": "Προστέθηκε στα αγαπημένα", @@ -401,6 +403,8 @@ "advanced_settings_prefer_remote_title": "Προτίμηση απομακρυσμένων εικόνων", "advanced_settings_proxy_headers_subtitle": "Καθορισμός κεφαλίδων διακομιστή μεσολάβησης που το Immich πρέπει να στέλνει με κάθε αίτημα δικτύου", "advanced_settings_proxy_headers_title": "Κεφαλίδες διακομιστή μεσολάβησης", + "advanced_settings_readonly_mode_subtitle": "Ενεργοποιεί τη λειτουργία μόνο-για-ανάγνωση, όπου οι φωτογραφίες μπορούν μόνο να προβληθούν. Ενέργειες όπως επιλογή πολλών εικόνων, κοινή χρήση, αποστολή (casting) και διαγραφή είναι απενεργοποιημένες. Η ενεργοποίηση/απενεργοποίηση της λειτουργίας μόνο-για-ανάγνωση γίνεται μέσω της εικόνας του χρήστη από την κεντρική οθόνη", + "advanced_settings_readonly_mode_title": "Λειτουργία μόνο-για-ανάγνωση", "advanced_settings_self_signed_ssl_subtitle": "Παρακάμπτει τον έλεγχο πιστοποιητικού SSL του διακομιστή. Απαραίτητο για αυτο-υπογεγραμμένα πιστοποιητικά.", "advanced_settings_self_signed_ssl_title": "Να επιτρέπονται αυτο-υπογεγραμμένα πιστοποιητικά SSL", "advanced_settings_sync_remote_deletions_subtitle": "Αυτόματη διαγραφή ή επαναφορά ενός περιουσιακού στοιχείου σε αυτή τη συσκευή, όταν η ενέργεια αυτή πραγματοποιείται στο διαδίκτυο", @@ -428,6 +432,7 @@ "album_remove_user_confirmation": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε τον/την {user};", "album_search_not_found": "Δε βρέθηκαν άλμπουμ που να ταιριάζουν με την αναζήτησή σας", "album_share_no_users": "Φαίνεται ότι έχετε κοινοποιήσει αυτό το άλμπουμ σε όλους τους χρήστες ή δεν έχετε χρήστες για να το κοινοποιήσετε.", + "album_summary": "Περίληψη άλμπουμ", "album_updated": "Το άλμπουμ, ενημερώθηκε", "album_updated_setting_description": "Λάβετε ειδοποίηση μέσω email όταν ένα κοινόχρηστο άλμπουμ έχει νέα αρχεία", "album_user_left": "Αποχωρήσατε από το {album}", @@ -466,6 +471,7 @@ "app_bar_signout_dialog_title": "Αποσύνδεση", "app_settings": "Ρυθμίσεις εφαρμογής", "appears_in": "Εμφανίζεται σε", + "apply_count": "Εφαρμογή ({count, number})", "archive": "Αρχείο", "archive_action_prompt": "Προστέθηκαν {count} στο Αρχείο", "archive_or_unarchive_photo": "Αρχειοθέτηση ή αποαρχειοθέτηση φωτογραφίας", @@ -498,6 +504,8 @@ "asset_restored_successfully": "Το στοιχείο αποκαταστάθηκε με επιτυχία", "asset_skipped": "Παραλείφθηκε", "asset_skipped_in_trash": "Στον κάδο απορριμμάτων", + "asset_trashed": "Το στοιχείο διαγράφηκε", + "asset_troubleshoot": "Αντιμετώπιση προβλήματος στοιχείου", "asset_uploaded": "Ανεβάστηκε", "asset_uploading": "Ανεβάζεται…", "asset_viewer_settings_subtitle": "Διαχείριση ρυθμίσεων προβολής συλλογής", @@ -531,8 +539,10 @@ "autoplay_slideshow": "Αυτόματη αναπαραγωγή παρουσίασης", "back": "Πίσω", "back_close_deselect": "Πίσω, κλείσιμο ή αποεπιλογή", + "background_backup_running_error": "Η δημιουργία αντιγράφων ασφάλειας στο παρασκήνιο εκτελείται ήδη, δεν μπορεί να ξεκινήσετε χειροκίνητο αντίγραφο ασφάλειας", "background_location_permission": "Άδεια τοποθεσίας στο παρασκήνιο", "background_location_permission_content": "Το Immich για να μπορεί να αλλάζει δίκτυα όταν τρέχει στο παρασκήνιο, πρέπει *πάντα* να έχει πρόσβαση στην ακριβή τοποθεσία ώστε η εφαρμογή να μπορεί να διαβάζει το όνομα του δικτύου Wi-Fi", + "background_options": "Επιλογές παρασκηνίου", "backup": "Αντίγραφο ασφαλείας", "backup_album_selection_page_albums_device": "Άλμπουμ στη συσκευή ({count})", "backup_album_selection_page_albums_tap": "Πάτημα για συμπερίληψη, διπλό πάτημα για εξαίρεση", @@ -540,6 +550,7 @@ "backup_album_selection_page_select_albums": "Επιλογή άλμπουμ", "backup_album_selection_page_selection_info": "Πληροφορίες επιλογής", "backup_album_selection_page_total_assets": "Συνολικά μοναδικά στοιχεία", + "backup_albums_sync": "Συγχρονισμός αντιγράφων ασφαλείας άλμπουμ", "backup_all": "Όλα", "backup_background_service_backup_failed_message": "Αποτυχία δημιουργίας αντιγράφων ασφαλείας. Επανάληψη…", "backup_background_service_connection_failed_message": "Αποτυχία σύνδεσης με το διακομιστή. Επανάληψη…", @@ -589,6 +600,7 @@ "backup_controller_page_turn_on": "Ενεργοποίηση δημιουργίας αντιγράφου ασφαλείας στο προσκήνιο", "backup_controller_page_uploading_file_info": "Μεταφόρτωση πληροφοριών αρχείου", "backup_err_only_album": "Δεν είναι δυνατή η αφαίρεση του μοναδικού άλμπουμ", + "backup_error_sync_failed": "Ο συγχρονισμός απέτυχε. Δεν είναι δυνατή η επεξεργασία του αντιγράφου ασφαλείας.", "backup_info_card_assets": "στοιχεία", "backup_manual_cancelled": "Ακυρώθηκε", "backup_manual_in_progress": "Μεταφόρτωση σε εξέλιξη. Δοκιμάστε αργότερα", @@ -656,6 +668,8 @@ "change_pin_code": "Αλλαγή κωδικού PIN", "change_your_password": "Αλλάξτε τον κωδικό σας", "changed_visibility_successfully": "Η προβολή, άλλαξε με επιτυχία", + "charging": "Φόρτιση", + "charging_requirement_mobile_backup": "Η δημιουργία αντιγράφων ασφάλειας στο παρασκήνιο απαιτεί η συσκευή να φορτίζει", "check_corrupt_asset_backup": "Έλεγχος για κατεστραμμένα αντίγραφα ασφαλείας στοιχείων", "check_corrupt_asset_backup_button": "Εκτέλεση ελέγχου", "check_corrupt_asset_backup_description": "Εκτέλεσε αυτόν τον έλεγχο μόνο μέσω Wi-Fi και αφού έχουν αποθηκευτεί όλα τα αντίγραφα ασφαλείας των στοιχείων. Η διαδικασία μπορεί να διαρκέσει μερικά λεπτά.", @@ -742,6 +756,7 @@ "create_user": "Δημιουργία χρήστη", "created": "Δημιουργήθηκε", "created_at": "Δημιουργήθηκε", + "creating_linked_albums": "Δημιουργία συνδεδεμένων άλμπουμ...", "crop": "Αποκοπή", "curated_object_page_title": "Πράγματα", "current_device": "Τρέχουσα συσκευή", @@ -891,7 +906,9 @@ "error": "Σφάλμα", "error_change_sort_album": "Απέτυχε η αλλαγή σειράς του άλμπουμ", "error_delete_face": "Σφάλμα διαγραφής προσώπου από το στοιχείο", + "error_getting_places": "Σφάλμα κατά την ανάκτηση τοποθεσιών", "error_loading_image": "Σφάλμα κατά τη φόρτωση της εικόνας", + "error_loading_partners": "Σφάλμα κατά τη φόρτωση συνεργατών: {error}", "error_saving_image": "Σφάλμα: {error}", "error_tag_face_bounding_box": "Σφάλμα επισήμανσης προσώπου - δεν μπορούν να ληφθούν οι συντεταγμένες του πλαισίου οριοθέτησης", "error_title": "Σφάλμα - Κάτι πήγε στραβά", @@ -1056,6 +1073,7 @@ "favorites_page_no_favorites": "Δεν βρέθηκαν αγαπημένα στοιχεία", "feature_photo_updated": "Η φωτογραφία προβολής ενημερώθηκε", "features": "Χαρακτηριστικά", + "features_in_development": "Λειτουργίες υπό Ανάπτυξη", "features_setting_description": "Διαχειριστείτε τα χαρακτηριστικά της εφαρμογής", "file_name": "Όνομα αρχείου", "file_name_or_extension": "Όνομα αρχείου ή επέκταση", @@ -1076,12 +1094,15 @@ "gcast_enabled": "Μετάδοση περιεχομένου Google Cast", "gcast_enabled_description": "Αυτό το χαρακτηριστικό φορτώνει εξωτερικούς πόρους από τη Google για να λειτουργήσει.", "general": "Γενικά", + "geolocation_instruction_location": "Κάνε κλικ σε ένα στοιχείο με συντεταγμένες GPS για να χρησιμοποιήσεις την τοποθεσία του, ή επίλεξε απευθείας μια τοποθεσία από τον χάρτη", "get_help": "Ζητήστε βοήθεια", "get_wifiname_error": "Δεν ήταν δυνατή η λήψη του ονόματος Wi-Fi. Βεβαιωθείτε ότι έχετε δώσει τις απαραίτητες άδειες και ότι είστε συνδεδεμένοι σε δίκτυο Wi-Fi", "getting_started": "Ξεκινώντας", "go_back": "Πηγαίνετε πίσω", "go_to_folder": "Μετάβαση στο φάκελο", "go_to_search": "Πηγαίνετε στην αναζήτηση", + "gps": "GPS", + "gps_missing": "Χωρίς GPS", "grant_permission": "Επιτρέψτε την άδεια", "group_albums_by": "Ομαδοποίηση άλμπουμ κατά...", "group_country": "Ομαδοποίηση κατά χώρα", @@ -1217,6 +1238,7 @@ "local": "Τοπικά", "local_asset_cast_failed": "Αδυναμία μετάδοσης στοιχείου που δεν έχει ανέβει στον διακομιστή", "local_assets": "Τοπικά στοιχεία", + "local_media_summary": "Περίληψη τοπικών πολυμέσων", "local_network": "Τοπικό δίκτυο", "local_network_sheet_info": "Η εφαρμογή θα συνδεθεί με τον διακομιστή μέσω αυτού του URL όταν χρησιμοποιείται το καθορισμένο δίκτυο Wi-Fi", "location_permission": "Άδεια τοποθεσίας", @@ -1228,6 +1250,7 @@ "location_picker_longitude_hint": "Εισαγάγετε εδώ το γεωγραφικό σας μήκος", "lock": "Κλείδωμα", "locked_folder": "Κλειδωμένος φάκελος", + "log_detail_title": "Λεπτομέρεια καταγραφής", "log_out": "Αποσύνδεση", "log_out_all_devices": "Αποσύνδεση από Όλες τις Συσκευές", "logged_in_as": "Συνδεδεμένος ως {user}", @@ -1258,6 +1281,7 @@ "login_password_changed_success": "Ο κωδικός πρόσβασης ενημερώθηκε με επιτυχία", "logout_all_device_confirmation": "Είστε βέβαιοι ότι θέλετε να αποσυνδεθείτε από όλες τις συσκευές;", "logout_this_device_confirmation": "Είστε βέβαιοι ότι θέλετε να αποσυνδεθείτε από αυτήν τη συσκευή;", + "logs": "Καταγραφές", "longitude": "Γεωγραφικό μήκος", "look": "Εμφάνιση", "loop_videos": "Επανάληψη βίντεο", @@ -1265,6 +1289,7 @@ "main_branch_warning": "Χρησιμοποιείτε μια έκδοση σε ανάπτυξη· συνιστούμε ανεπιφύλακτα τη χρήση μιας τελικής έκδοσης!", "main_menu": "Κύριο μενού", "make": "Κατασκευαστής", + "manage_geolocation": "Διαχείριση τοποθεσίας", "manage_shared_links": "Διαχείριση κοινόχρηστων συνδέσμων", "manage_sharing_with_partners": "Διαχειριστείτε την κοινή χρήση με συνεργάτες", "manage_the_app_settings": "Διαχειριστείτε τις ρυθμίσεις της εφαρμογής", @@ -1299,6 +1324,7 @@ "mark_as_read": "Επισήμανση ως αναγνωσμένο", "marked_all_as_read": "Όλα επισημάνθηκαν ως αναγνωσμένα", "matches": "Αντιστοιχίες", + "matching_assets": "Αντιστοιχία στοιχείων", "media_type": "Τύπος πολυμέσου", "memories": "Αναμνήσεις", "memories_all_caught_up": "Συγχρονισμένα", @@ -1339,6 +1365,7 @@ "name_or_nickname": "Όνομα ή ψευδώνυμο", "network_requirement_photos_upload": "Χρήση δεδομένων κινητής τηλεφωνίας για τη δημιουργία αντιγράφων ασφαλείας των φωτογραφιών", "network_requirement_videos_upload": "Χρήση δεδομένων κινητής τηλεφωνίας για τη δημιουργία αντιγράφων ασφαλείας των βίντεο", + "network_requirements": "Απαιτήσεις Δυκτίου", "network_requirements_updated": "Οι απαιτήσεις δικτύου άλλαξαν, γίνεται επαναφορά της ουράς αντιγράφων ασφαλείας", "networking_settings": "Δικτύωση", "networking_subtitle": "Διαχείριση ρυθμίσεων τελικών σημείων διακομιστή", @@ -1349,6 +1376,7 @@ "new_person": "Νέο άτομο", "new_pin_code": "Νέος κωδικός PIN", "new_pin_code_subtitle": "Αυτή είναι η πρώτη φορά που αποκτάτε πρόσβαση στον κλειδωμένο φάκελο. Δημιουργήστε έναν κωδικό PIN για ασφαλή πρόσβαση σε αυτή τη σελίδα", + "new_timeline": "Νέο Χρονολόγιο", "new_user_created": "Ο νέος χρήστης δημιουργήθηκε", "new_version_available": "ΔΙΑΘΕΣΙΜΗ ΝΕΑ ΕΚΔΟΣΗ", "newest_first": "Τα νεότερα πρώτα", @@ -1362,20 +1390,25 @@ "no_assets_message": "ΚΑΝΤΕ ΚΛΙΚ ΓΙΑ ΝΑ ΑΝΕΒΑΣΕΤΕ ΤΗΝ ΠΡΩΤΗ ΣΑΣ ΦΩΤΟΓΡΑΦΙΑ", "no_assets_to_show": "Δεν υπάρχουν στοιχεία προς εμφάνιση", "no_cast_devices_found": "Δε βρέθηκαν συσκευές μετάδοσης", + "no_checksum_local": "Δεν υπάρχει διαθέσιμο checksum για έλεγχο ακεραιότητας – δεν μπορούν να ανακτηθούν τα τοπικά στοιχεία", + "no_checksum_remote": "Δεν υπάρχει διαθέσιμο checksum για έλεγχο ακεραιότητας – δεν μπορούν να ανακτηθούν τα απομακρυσμένα στοιχεία", "no_duplicates_found": "Δεν βρέθηκαν διπλότυπα.", "no_exif_info_available": "Καμία πληροφορία exif διαθέσιμη", "no_explore_results_message": "Ανεβάστε περισσότερες φωτογραφίες για να περιηγηθείτε στη συλλογή σας.", "no_favorites_message": "Προσθέστε αγαπημένα για να βρείτε γρήγορα τις καλύτερες φωτογραφίες και τα βίντεό σας", "no_libraries_message": "Δημιουργήστε μια εξωτερική βιβλιοθήκη για να προβάλετε τις φωτογραφίες και τα βίντεό σας", + "no_local_assets_found": "Δεν βρέθηκαν τοπικά στοιχεία με αυτό το checksum", "no_locked_photos_message": "Οι φωτογραφίες και τα βίντεο στον κλειδωμένο φάκελο, είναι κρυμμένες και δεν θα εμφανίζονται κατά την περιήγηση ή την αναζήτηση στη βιβλιοθήκη σας.", "no_name": "Χωρίς Όνομα", "no_notifications": "Καμία ειδοποίηση", "no_people_found": "Δεν βρέθηκαν άτομα που να ταιριάζουν", "no_places": "Καμία τοποθεσία", + "no_remote_assets_found": "Δεν βρέθηκαν απομακρυσμένα στοιχεία με αυτό το checksum", "no_results": "Κανένα αποτέλεσμα", "no_results_description": "Δοκιμάστε ένα συνώνυμο ή πιο γενική λέξη-κλειδί", "no_shared_albums_message": "Δημιουργήστε ένα άλμπουμ για να μοιράζεστε φωτογραφίες και βίντεο με άτομα στο δίκτυό σας", "no_uploads_in_progress": "Καμία μεταφόρτωση σε εξέλιξη", + "not_available": "Μ/Δ (Μη Διαθέσιμο)", "not_in_any_album": "Σε κανένα άλμπουμ", "not_selected": "Δεν επιλέχθηκε", "note_apply_storage_label_to_previously_uploaded assets": "Σημείωση: Για να εφαρμόσετε την Ετικέτα Αποθήκευσης σε στοιχεία που έχουν μεταφορτωθεί προηγουμένως, εκτελέστε το", @@ -1410,6 +1443,8 @@ "open_the_search_filters": "Ανοίξτε τα φίλτρα αναζήτησης", "options": "Επιλογές", "or": "ή", + "organize_into_albums": "Οργάνωση σε άλμπουμ", + "organize_into_albums_description": "Τοποθετείστε τις υπάρχουσες φωτογραφίες σε άλμπουμ χρησιμοποιώντας τις τρέχουσες ρυθμίσεις συγχρονισμού", "organize_your_library": "Οργανώστε τη βιβλιοθήκη σας", "original": "πρωτότυπο", "other": "Άλλες", @@ -1495,6 +1530,7 @@ "port": "Θύρα", "preferences_settings_subtitle": "Διαχειριστείτε τις προτιμήσεις της εφαρμογής", "preferences_settings_title": "Προτιμήσεις", + "preparing": "Προετοιμασία", "preset": "Προκαθορισμένη ρύθμιση", "preview": "Προεπισκόπηση", "previous": "Προηγούμενο", @@ -1511,6 +1547,7 @@ "profile_drawer_client_out_of_date_minor": "Παρακαλώ ενημερώστε την εφαρμογή στην πιο πρόσφατη δευτερεύουσα έκδοση.", "profile_drawer_client_server_up_to_date": "Ο πελάτης και ο διακομιστής είναι ενημερωμένοι", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Η λειτουργία μόνο-για-ανάγνωση ενεργοποιήθηκε. Κρατήστε πατημένο το εικονίδιο του χρήστη για απενεργοποίηση.", "profile_drawer_server_out_of_date_major": "Παρακαλώ ενημερώστε τον διακομιστή στην πιο πρόσφατη κύρια έκδοση.", "profile_drawer_server_out_of_date_minor": "Παρακαλώ ενημερώστε τον διακομιστή στην πιο πρόσφατη δευτερεύουσα έκδοση.", "profile_image_of_user": "Εικόνα προφίλ του χρήστη {user}", @@ -1549,6 +1586,7 @@ "purchase_server_description_2": "Κατάσταση υποστηρικτή", "purchase_server_title": "Διακομιστής", "purchase_settings_server_activated": "Η διαχείριση του κλειδιού προϊόντος του διακομιστή γίνεται από τον διαχειριστή", + "query_asset_id": "Αναζήτηση ID Στοιχείου", "queue_status": "Τοποθέτηση στη ουρά {count} από {total}", "rating": "Αξιολόγηση με αστέρια", "rating_clear": "Εκκαθάριση αξιολόγησης", @@ -1556,6 +1594,9 @@ "rating_description": "Εμφάνιση της αξιολόγησης EXIF στον πίνακα πληροφοριών", "reaction_options": "Επιλογές αντίδρασης", "read_changelog": "Διαβάστε το Αρχείο Καταγραφής Αλλαγών", + "readonly_mode_disabled": "Η λειτουργία μόνο-για-ανάγνωση απενεργοποιήθηκε", + "readonly_mode_enabled": "Η λειτουργία μόνο-για-ανάγνωση ενεργοποιήθηκε", + "ready_for_upload": "Έτοιμο για μεταφόρτωση", "reassign": "Ανάθεση", "reassigned_assets_to_existing_person": "Η ανάθεση {count, plural, one {# αρχείου} other {# αρχείων}} στον/στην {name, select, null {έναν/μία υπάρχοντα/ουσα χρήστη} other {{name}}}", "reassigned_assets_to_new_person": "Η ανάθεση {count, plural, one {# αρχείου} other {# αρχείων}} σε νέο άτομο", @@ -1580,6 +1621,7 @@ "regenerating_thumbnails": "Οι μικρογραφίες αναγεννώνται", "remote": "Απομακρυσμένος", "remote_assets": "Απομακρυσμένα στοιχεία", + "remote_media_summary": "Περίληψη απομακρυσμένων πολυμέσων", "remove": "Αφαίρεση", "remove_assets_album_confirmation": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε {count, plural, one {# στοιχείο} other {# στοιχεία}} από το άλμπουμ;", "remove_assets_shared_link_confirmation": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε {count, plural, one {# στοιχείο} other {# στοιχεία}} από αυτόν τον κοινόχρηστο σύνδεσμο;", @@ -1632,6 +1674,7 @@ "restore_user": "Επαναφορά χρήστη", "restored_asset": "Ανακτήθηκε το αρχείο", "resume": "Συνέχιση", + "resume_paused_jobs": "Συνέχιση {count, plural, one {# σε παύση εργασία} other {# σε παύση εργασίες}}", "retry_upload": "Επανάληψη ανεβάσματος", "review_duplicates": "Προβολή διπλότυπων", "review_large_files": "Επισκόπηση μεγάλων αρχείων", @@ -1725,6 +1768,7 @@ "select_user_for_sharing_page_err_album": "Αποτυχία δημιουργίας άλπουμ", "selected": "Επιλεγμένοι", "selected_count": "{count, plural, other {# επιλεγμένοι}}", + "selected_gps_coordinates": "Επιλεγμένες συντεταγμένες GPS", "send_message": "Αποστολή μηνύματος", "send_welcome_email": "Αποστολή email καλωσορίσματος", "server_endpoint": "Τελικό σημείο Διακομιστή", @@ -1853,6 +1897,7 @@ "show_slideshow_transition": "Εμφάνιση μετάβασης παρουσίασης", "show_supporter_badge": "Σήμα υποστηρικτή", "show_supporter_badge_description": "Εμφάνιση σήματος υποστηρικτή", + "show_text_search_menu": "Εμφάνιση μενού αναζήτησης κειμένου", "shuffle": "Ανάμειξη", "sidebar": "Πλαϊνή μπάρα", "sidebar_display_description": "Εμφάνιση συνδέσμου για προβολή στην πλαϊνή μπάρα", @@ -1883,6 +1928,7 @@ "stacktrace": "Καταγραφή στοίβας", "start": "Έναρξη", "start_date": "Από", + "start_date_before_end_date": "Η ημερομηνία έναρξης πρέπει να είναι πριν από την ημερομηνία λήξης", "state": "Νομός", "status": "Κατάσταση", "stop_casting": "Διακοπή μετάδοσης", @@ -1907,6 +1953,8 @@ "sync_albums_manual_subtitle": "Συγχρονίστε όλα τα μεταφορτωμένα βίντεο και φωτογραφίες με τα επιλεγμένα εφεδρικά άλμπουμ", "sync_local": "Τοπικός Συγχρονισμός", "sync_remote": "Απομακρυσμένος Συγχρονισμός", + "sync_status": "Κατάσταση συγχρονισμού", + "sync_status_subtitle": "Προβολή και διαχείριση του συστήματος συγχρονισμού", "sync_upload_album_setting_subtitle": "Δημιουργήστε και ανεβάστε τις φωτογραφίες και τα βίντεό σας στα επιλεγμένα άλμπουμ στο Immich", "tag": "Ετικέτα", "tag_assets": "Ετικετοποίηση στοιχείων", @@ -1944,7 +1992,9 @@ "to_change_password": "Αλλαγή κωδικού πρόσβασης", "to_favorite": "Αγαπημένο", "to_login": "Είσοδος", + "to_multi_select": "για πολλαπλή επιλογή", "to_parent": "Μεταβείτε στο γονικό φάκελο", + "to_select": "για επιλογή", "to_trash": "Κάδος απορριμμάτων", "toggle_settings": "Εναλλαγή ρυθμίσεων", "total": "Σύνολο", @@ -1964,6 +2014,7 @@ "trash_page_select_assets_btn": "Επιλέξτε στοιχεία", "trash_page_title": "Κάδος Απορριμμάτων ({count})", "trashed_items_will_be_permanently_deleted_after": "Τα στοιχεία που βρίσκονται στον κάδο απορριμμάτων θα διαγραφούν οριστικά μετά από {days, plural, one {# ημέρα} other {# ημέρες}}.", + "troubleshoot": "Επίλυση προβλημάτων", "type": "Τύπος", "unable_to_change_pin_code": "Αδυναμία αλλαγής κωδικού PIN", "unable_to_setup_pin_code": "Αδυναμία ρύθμισης κωδικού PIN", @@ -1994,6 +2045,7 @@ "unstacked_assets_count": "Αποστοιβάξατε {count, plural, one {# στοιχείο} other {# στοιχεία}}", "untagged": "Χωρίς ετικέτα", "up_next": "Ακολουθεί", + "update_location_action_prompt": "Ενημέρωση τοποθεσίας για {count} επιλεγμένα στοιχεία με:", "updated_at": "Ενημερωμένο", "updated_password": "Ο κωδικός πρόσβασης ενημερώθηκε", "upload": "Μεταφόρτωση", @@ -2060,6 +2112,7 @@ "view_next_asset": "Προβολή επόμενου στοιχείου", "view_previous_asset": "Προβολή προηγούμενου στοιχείου", "view_qr_code": "Προβολή κωδικού QR", + "view_similar_photos": "Προβολή παρόμοιων φωτογραφιών", "view_stack": "Προβολή της στοίβας", "view_user": "Προβολή Χρήστη", "viewer_remove_from_stack": "Κατάργηση από τη Στοίβα", @@ -2078,5 +2131,6 @@ "yes": "Ναι", "you_dont_have_any_shared_links": "Δεν έχετε κοινόχρηστους συνδέσμους", "your_wifi_name": "Το όνομα του Wi-Fi σας", - "zoom_image": "Ζουμ Εικόνας" + "zoom_image": "Ζουμ Εικόνας", + "zoom_to_bounds": "Εστίαση στα όρια" } diff --git a/i18n/en.json b/i18n/en.json index 23e1071a2d..d265c9b9d8 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -28,10 +28,12 @@ "add_to_album": "Add to album", "add_to_album_bottom_sheet_added": "Added to {album}", "add_to_album_bottom_sheet_already_exists": "Already in {album}", + "add_to_album_bottom_sheet_some_local_assets": "Some local assets could not be added to album", "add_to_album_toggle": "Toggle selection for {album}", "add_to_albums": "Add to albums", "add_to_albums_count": "Add to albums ({count})", "add_to_shared_album": "Add to shared album", + "add_upload_to_stack": "Add upload to stack", "add_url": "Add URL", "added_to_archive": "Added to archive", "added_to_favorites": "Added to favorites", @@ -209,6 +211,8 @@ "notification_email_ignore_certificate_errors_description": "Ignore TLS certificate validation errors (not recommended)", "notification_email_password_description": "Password to use when authenticating with the email server", "notification_email_port_description": "Port of the email server (e.g 25, 465, or 587)", + "notification_email_secure": "SMTPS", + "notification_email_secure_description": "Use SMTPS (SMTP over TLS)", "notification_email_sent_test_email_button": "Send test email and save", "notification_email_setting_description": "Settings for sending email notifications", "notification_email_test_email": "Send test email", @@ -331,7 +335,7 @@ "transcoding_max_b_frames": "Maximum B-frames", "transcoding_max_b_frames_description": "Higher values improve compression efficiency, but slow down encoding. May not be compatible with hardware acceleration on older devices. 0 disables B-frames, while -1 sets this value automatically.", "transcoding_max_bitrate": "Maximum bitrate", - "transcoding_max_bitrate_description": "Setting a max bitrate can make file sizes more predictable at a minor cost to quality. At 720p, typical values are 2600 kbit/s for VP9 or HEVC, or 4500 kbit/s for H.264. Disabled if set to 0.", + "transcoding_max_bitrate_description": "Setting a max bitrate can make file sizes more predictable at a minor cost to quality. At 720p, typical values are 2600 kbit/s for VP9 or HEVC, or 4500 kbit/s for H.264. Disabled if set to 0. When no unit is specified, k (for kbit/s) is assumed; therefore 5000, 5000k, and 5M (for Mbit/s) are equivalent.", "transcoding_max_keyframe_interval": "Maximum keyframe interval", "transcoding_max_keyframe_interval_description": "Sets the maximum frame distance between keyframes. Lower values worsen compression efficiency, but improve seek times and may improve quality in scenes with fast movement. 0 sets this value automatically.", "transcoding_optimal_description": "Videos higher than target resolution or not in an accepted format", @@ -349,7 +353,7 @@ "transcoding_target_resolution": "Target resolution", "transcoding_target_resolution_description": "Higher resolutions can preserve more detail but take longer to encode, have larger file sizes, and can reduce app responsiveness.", "transcoding_temporal_aq": "Temporal AQ", - "transcoding_temporal_aq_description": "Applies only to NVENC. Increases quality of high-detail, low-motion scenes. May not be compatible with older devices.", + "transcoding_temporal_aq_description": "Applies only to NVENC. Temporal Adaptive Quantization increases quality of high-detail, low-motion scenes. May not be compatible with older devices.", "transcoding_threads": "Threads", "transcoding_threads_description": "Higher values lead to faster encoding, but leave less room for the server to process other tasks while active. This value should not be more than the number of CPU cores. Maximizes utilization if set to 0.", "transcoding_tone_mapping": "Tone-mapping", @@ -464,9 +468,11 @@ "api_key_description": "This value will only be shown once. Please be sure to copy it before closing the window.", "api_key_empty": "Your API Key name shouldn't be empty", "api_keys": "API Keys", + "app_architecture_variant": "Variant (Architecture)", "app_bar_signout_dialog_content": "Are you sure you want to sign out?", "app_bar_signout_dialog_ok": "Yes", "app_bar_signout_dialog_title": "Sign out", + "app_download_links": "App Download Links", "app_settings": "App Settings", "appears_in": "Appears in", "apply_count": "Apply ({count, number})", @@ -598,6 +604,7 @@ "backup_controller_page_turn_on": "Turn on foreground backup", "backup_controller_page_uploading_file_info": "Uploading file info", "backup_err_only_album": "Cannot remove the only album", + "backup_error_sync_failed": "Sync failed. Cannot process backup.", "backup_info_card_assets": "assets", "backup_manual_cancelled": "Cancelled", "backup_manual_in_progress": "Upload already in progress. Try after sometime", @@ -1036,6 +1043,7 @@ "exif_bottom_sheet_description_error": "Error updating description", "exif_bottom_sheet_details": "DETAILS", "exif_bottom_sheet_location": "LOCATION", + "exif_bottom_sheet_no_description": "No description", "exif_bottom_sheet_people": "PEOPLE", "exif_bottom_sheet_person_add_person": "Add name", "exit_slideshow": "Exit Slideshow", @@ -1342,6 +1350,8 @@ "minute": "Minute", "minutes": "Minutes", "missing": "Missing", + "mobile_app": "Mobile App", + "mobile_app_download_onboarding_note": "You can access these options again from the Utilities page.", "model": "Model", "month": "Month", "monthly_title_text_date_format": "MMMM y", @@ -1360,6 +1370,8 @@ "my_albums": "My albums", "name": "Name", "name_or_nickname": "Name or nickname", + "navigate": "Navigate", + "navigate_to_time": "Navigate to Time", "network_requirement_photos_upload": "Use cellular data to backup photos", "network_requirement_videos_upload": "Use cellular data to backup videos", "network_requirements": "Network Requirements", @@ -1369,6 +1381,7 @@ "never": "Never", "new_album": "New Album", "new_api_key": "New API Key", + "new_date_range": "New date range", "new_password": "New password", "new_person": "New person", "new_pin_code": "New PIN code", @@ -1419,6 +1432,8 @@ "notifications": "Notifications", "notifications_setting_description": "Manage notifications", "oauth": "OAuth", + "obtainium_configurator": "Obtainium Configurator", + "obtainium_configurator_instructions": "Please create an API key and select a variant to create your Obtainium configuration link.", "official_immich_resources": "Official Immich Resources", "offline": "Offline", "offset": "Offset", @@ -1803,6 +1818,8 @@ "setting_notifications_subtitle": "Adjust your notification preferences", "setting_notifications_total_progress_subtitle": "Overall upload progress (done/total assets)", "setting_notifications_total_progress_title": "Show background backup total progress", + "setting_video_viewer_auto_play_subtitle": "Automatically start playing videos when they are opened", + "setting_video_viewer_auto_play_title": "Auto play videos", "setting_video_viewer_looping_title": "Looping", "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", "setting_video_viewer_original_video_title": "Force original video", diff --git a/i18n/es.json b/i18n/es.json index 40ea723ded..c08f93da08 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -28,10 +28,12 @@ "add_to_album": "Incluir en álbum", "add_to_album_bottom_sheet_added": "Agregado a {album}", "add_to_album_bottom_sheet_already_exists": "Ya se encuentra en {album}", + "add_to_album_bottom_sheet_some_local_assets": "Algunos recursos locales no se pudieron añadir al álbum", "add_to_album_toggle": "Alternar selección para el {album}", "add_to_albums": "Incluir en álbumes", "add_to_albums_count": "Incluir en {count} álbumes", "add_to_shared_album": "Incluir en álbum compartido", + "add_upload_to_stack": "Añadir archivo y apilar", "add_url": "Agregar URL", "added_to_archive": "Agregado al Archivado", "added_to_favorites": "Agregado a favoritos", @@ -598,6 +600,7 @@ "backup_controller_page_turn_on": "Activar la copia de seguridad", "backup_controller_page_uploading_file_info": "Subiendo información del archivo", "backup_err_only_album": "No se puede eliminar el único álbum", + "backup_error_sync_failed": "La sincronización falló. No es posible procesar la copia de seguridad.", "backup_info_card_assets": "elementos", "backup_manual_cancelled": "Cancelado", "backup_manual_in_progress": "Subida ya en progreso. Vuelve a intentarlo más tarde", @@ -1087,7 +1090,7 @@ "folders": "Carpetas", "folders_feature_description": "Explorar la vista de carpetas para las fotos y los videos en el sistema de archivos", "forgot_pin_code_question": "¿Olvidaste tu código PIN?", - "forward": "Reenviar", + "forward": "Avanzar", "gcast_enabled": "Google Cast", "gcast_enabled_description": "Esta funcionalidad carga recursos externos desde Google para poder funcionar.", "general": "General", @@ -1904,7 +1907,7 @@ "skip_to_content": "Saltar al contenido", "skip_to_folders": "Ir a las carpetas", "skip_to_tags": "Ir a las etiquetas", - "slideshow": "Diapositivas", + "slideshow": "Pase de diapositivas", "slideshow_settings": "Ajustes de diapositivas", "sort_albums_by": "Ordenar álbumes por…", "sort_created": "Fecha de creación", diff --git a/i18n/et.json b/i18n/et.json index 3683f142f3..bfd083ad4e 100644 --- a/i18n/et.json +++ b/i18n/et.json @@ -28,10 +28,12 @@ "add_to_album": "Lisa albumisse", "add_to_album_bottom_sheet_added": "Lisatud albumisse {album}", "add_to_album_bottom_sheet_already_exists": "On juba albumis {album}", + "add_to_album_bottom_sheet_some_local_assets": "Kõiki lokaalseid üksuseid ei õnnestunud albumisse lisada", "add_to_album_toggle": "Muuda albumi {album} valikut", "add_to_albums": "Lisa albumitesse", "add_to_albums_count": "Lisa albumitesse ({count})", "add_to_shared_album": "Lisa jagatud albumisse", + "add_upload_to_stack": "Virnasta üleslaaditud üksus", "add_url": "Lisa URL", "added_to_archive": "Lisatud arhiivi", "added_to_favorites": "Lisatud lemmikutesse", @@ -598,6 +600,7 @@ "backup_controller_page_turn_on": "Lülita esiplaanil varundus sisse", "backup_controller_page_uploading_file_info": "Faili info üleslaadimine", "backup_err_only_album": "Ei saa ainsat albumit eemaldada", + "backup_error_sync_failed": "Sünkroonimine ebaõnnestus. Varundust ei saa töödelda.", "backup_info_card_assets": "üksused", "backup_manual_cancelled": "Tühistatud", "backup_manual_in_progress": "Üleslaadimine juba käib. Proovi hiljem uuesti", @@ -1036,6 +1039,7 @@ "exif_bottom_sheet_description_error": "Viga kirjelduse muutmisel", "exif_bottom_sheet_details": "ÜKSIKASJAD", "exif_bottom_sheet_location": "ASUKOHT", + "exif_bottom_sheet_no_description": "Kirjeldus puudub", "exif_bottom_sheet_people": "ISIKUD", "exif_bottom_sheet_person_add_person": "Lisa nimi", "exit_slideshow": "Sulge slaidiesitlus", @@ -1322,7 +1326,7 @@ "marked_all_as_read": "Kõik märgiti loetuks", "matches": "Ühtivad failid", "matching_assets": "Ühtivad üksused", - "media_type": "Meediumi tüüp", + "media_type": "Üksuse tüüp", "memories": "Mälestused", "memories_all_caught_up": "Ongi kõik", "memories_check_back_tomorrow": "Vaata homme juba uusi mälestusi", @@ -1405,6 +1409,7 @@ "no_results_description": "Proovi sünonüümi või üldisemat märksõna", "no_shared_albums_message": "Lisa album, et fotosid ja videosid teistega jagada", "no_uploads_in_progress": "Üleslaadimisi käimas ei ole", + "not_available": "Pole saadaval", "not_in_any_album": "Pole üheski albumis", "not_selected": "Ei ole valitud", "note_apply_storage_label_to_previously_uploaded assets": "Märkus: Et rakendada talletussilt varem üleslaaditud üksustele, käivita", @@ -1582,6 +1587,7 @@ "purchase_server_description_2": "Toetaja staatus", "purchase_server_title": "Server", "purchase_settings_server_activated": "Serveri tootevõtit haldab administraator", + "query_asset_id": "Päringu üksuse ID", "queue_status": "Järjekorras {count}/{total}", "rating": "Hinnang", "rating_clear": "Tühjenda hinnang", @@ -1709,8 +1715,8 @@ "search_filter_filename": "Otsi failinime alusel", "search_filter_location": "Asukoht", "search_filter_location_title": "Vali asukoht", - "search_filter_media_type": "Meediumi tüüp", - "search_filter_media_type_title": "Vali meediumi tüüp", + "search_filter_media_type": "Üksuse tüüp", + "search_filter_media_type_title": "Vali üksuse tüüp", "search_filter_people_title": "Vali isikud", "search_for": "Otsi", "search_for_existing_person": "Otsi olemasolevat isikut", diff --git a/i18n/fa.json b/i18n/fa.json index 3b0be9a9b1..76f8d956fc 100644 --- a/i18n/fa.json +++ b/i18n/fa.json @@ -13,6 +13,7 @@ "add_a_location": "افزودن یک مکان", "add_a_name": "افزودن نام", "add_a_title": "افزودن عنوان", + "add_birthday": "افزودن تاریخ تولد", "add_exclusion_pattern": "افزودن الگوی استثنا", "add_import_path": "افزودن مسیر ورودی", "add_location": "افزودن مکان", @@ -22,10 +23,13 @@ "add_photos": "افزودن عکس ها", "add_to": "افزودن به …", "add_to_album": "افزودن به آلبوم", + "add_to_album_bottom_sheet_added": "به آلبوم {album} اضافه شد", + "add_to_album_bottom_sheet_already_exists": "قبلا در آلبوم {album} موجود است", + "add_to_album_bottom_sheet_some_local_assets": "برخی از محتواهای محلی را نشد به آلبوم اضافه کرد", "add_to_shared_album": "افزودن به آلبوم اشتراکی", "added_to_archive": "به آرشیو اضافه شد", "added_to_favorites": "به علاقه مندی ها اضافه شد", - "added_to_favorites_count": "{count} تا اضافه شد به علاقه مندی ها", + "added_to_favorites_count": "{count, number} تا به علاقه مندی ها اضافه شد", "admin": { "add_exclusion_pattern_description": "الگوهای استثنا را اضافه کنید. پشتیبانی از گلابینگ با استفاده از *, ** و ? وجود دارد. برای نادیده گرفتن تمام فایل‌ها در هر دایرکتوری با نام \"Raw\"، از \"**/Raw/**\" استفاده کنید. برای نادیده گرفتن تمام فایل‌هایی که با \".tif\" پایان می‌یابند، از \"**/*.tif\" استفاده کنید. برای نادیده گرفتن یک مسیر مطلق، از \"/path/to/ignore/**\" استفاده کنید.", "authentication_settings": "تنظیمات احراز هویت", diff --git a/i18n/fi.json b/i18n/fi.json index d2b36cf4d3..a940493d95 100644 --- a/i18n/fi.json +++ b/i18n/fi.json @@ -28,6 +28,7 @@ "add_to_album": "Lisää albumiin", "add_to_album_bottom_sheet_added": "Lisätty albumiin {album}", "add_to_album_bottom_sheet_already_exists": "Kohde on jo albumissa {album}", + "add_to_album_bottom_sheet_some_local_assets": "Joitakin osia paikallisesta sisällöstä ei pystytty lisämään albumiin", "add_to_album_toggle": "Vaihda albumin {album} valintaa", "add_to_albums": "Lisää albumeihin", "add_to_albums_count": "Lisää albumeihin ({count})", @@ -39,7 +40,7 @@ "admin": { "add_exclusion_pattern_description": "Lisää mallit, jonka mukaan jätetään tiedostoja pois. Jokerimerkit *, ** ja ? ovat tuettuna. Jättääksesi pois kaikki tiedostot mistä tahansa löytyvästä kansiosta \"Raw\" käytä \"**/Raw/**\". Jättääksesi pois kaikki \". tif\" päätteiset tiedot, käytä \"**/*.tif\". Jättääksesi pois tarkan tiedostopolun, käytä \"/path/to/ignore/**\".", "admin_user": "Ylläpitäjä", - "asset_offline_description": "Ulkoista kirjaston resurssia ei enää löydy levyltä, ja se on siirretty roskakoriin. Jos tiedosto siirrettiin kirjaston sisällä, tarkista aikajanaltasi uusi vastaava resurssi. Palautaaksesi tämän resurssin, varmista, että alla oleva tiedostopolku on Immichin käytettävissä ja skannaa kirjasto uudelleen.", + "asset_offline_description": "Ulkoista kirjaston resurssia ei enää löydy levyltä, ja se on siirretty roskakoriin. Jos tiedosto siirrettiin kirjaston sisällä, tarkista aikajanaltasi uusi vastaava resurssi. Palauttaaksesi tämän resurssin, varmista, että alla oleva tiedostopolku on Immichin käytettävissä ja skannaa kirjasto uudelleen.", "authentication_settings": "Autentikointiasetukset", "authentication_settings_description": "Hallitse salasana-, OAuth- ja muut autentikoinnin asetukset", "authentication_settings_disable_all": "Haluatko varmasti poistaa kaikki kirjautumistavat käytöstä? Kirjautuminen on tämän jälkeen mahdotonta.", @@ -123,7 +124,13 @@ "logging_enable_description": "Ota lokikirjaus käyttöön", "logging_level_description": "Kun käytössä, mitä lokituksen tasoa käytetään.", "logging_settings": "Lokit", - "machine_learning_availability_checks": "Saatavyystarkastukset", + "machine_learning_availability_checks": "Saatavuustarkastukset", + "machine_learning_availability_checks_description": "Automaattisesti havaitse ja suosi vapaita koneoppimisen palvelimia", + "machine_learning_availability_checks_enabled": "Laita päälle saatavuus tarkistukset", + "machine_learning_availability_checks_interval": "Tarkastusväli", + "machine_learning_availability_checks_interval_description": "Aikaväli millisekunneissa saavutettavuus tarkistuksille", + "machine_learning_availability_checks_timeout": "Pyynnön aikakatkaisu", + "machine_learning_availability_checks_timeout_description": "Aikakatkaisu aika millisekunneissa saatavuus tarkistuksille", "machine_learning_clip_model": "CLIP-malli", "machine_learning_clip_model_description": "Käytettävän CLIP-mallin nimi toimivien mallien listasta. Huomaa että sinun täytyy suorittaa \"Älykäs etsintä\"-työ uudelleen vaihdettuasi käytettävää mallia.", "machine_learning_duplicate_detection": "Kaksoiskappaleiden tunnistus", @@ -143,7 +150,7 @@ "machine_learning_max_recognition_distance": "Suurin kasvojen eroavaisuus", "machine_learning_max_recognition_distance_description": "Kahden kasvon suurin eroavaisuus, milloin ne vielä mielletään samaksi henkilöksi, välillä 0-2. Arvoa alentamalla voidaan ehkäistä kahden saman näköisen henkilön mieltäminen samaksi henkilöksi, kun taas korottamalla voidaan ehkäistä saman henkilön mieltäminen kahdeksi erilliseksi henkilöksi. Huomaa että on helpompaa yhdistää kaksi, kuin erottaa, joten suosi mahdollisimman matalaa arvoa.", "machine_learning_min_detection_score": "Tunnistuksen vähimmäistulos", - "machine_learning_min_detection_score_description": "Pienin kasvojen tunnistamisessa saatu vahvuusarvo välillä 0-1. Matalammalla arvolla havaitaan enemmän kascoja, mutta voi lisätä virhearvioiden määrää.", + "machine_learning_min_detection_score_description": "Pienin kasvojen tunnistamisessa saatu vahvuusarvo välillä 0-1. Matalammalla arvolla havaitaan enemmän kasvoja, mutta voi lisätä virhearvioiden määrää.", "machine_learning_min_recognized_faces": "Tunnistettujen kasvojen vähimmäismäärä", "machine_learning_min_recognized_faces_description": "Luotavan käyttäjän kasvojen vähimmäismäärä. Arvoa nostamalla kasvojentunnistuksen tarkkuus paranee, mutta todennäköisyys sille, että kasvoja ei osata yhdistää henkilöön kasvaa.", "machine_learning_settings": "Koneoppimisen asetukset", @@ -177,7 +184,7 @@ "metadata_settings": "Metatietoasetukset", "metadata_settings_description": "Hallitse metatietoja", "migration_job": "Migraatio", - "migration_job_description": "Migroi aineiston pikkukuvat ja kasvot uusimpaan kansiorakenteeseen", + "migration_job_description": "Migratoi aineiston pikkukuvat ja kasvot uusimpaan kansiorakenteeseen", "nightly_tasks_cluster_faces_setting_description": "Aja kasvojen tunnistus uusiin tunnistettuihin kasvoihin", "nightly_tasks_cluster_new_faces_setting": "Kokoa uudet kasvot", "nightly_tasks_database_cleanup_setting": "Tietokannan puhdistuksen tehtävät", @@ -197,7 +204,7 @@ "note_apply_storage_label_previous_assets": "Huom: Asettaaksesi nimikkeen aiemmin ladatulle aineistolle, aja", "note_cannot_be_changed_later": "Huom: Tätä ei voi enää myöhemmin vaihtaa!", "notification_email_from_address": "Lähettäjän osoite", - "notification_email_from_address_description": "Lähettäjän sähköpostiosoite. Esimerkiksi \"Immich-kuvapalvelin \". Varmista, että käytetystä osoiteesta on lupa lähettää sähköposteja.", + "notification_email_from_address_description": "Lähettäjän sähköpostiosoite. Esimerkiksi \"Immich-kuvapalvelin \". Varmista, että käytetystä osoitteesta on lupa lähettää sähköposteja.", "notification_email_host_description": "Sähköpostipalvelin (esim. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Älä huomioi varmennevirheitä", "notification_email_ignore_certificate_errors_description": "Älä huomioi TLS-varmenteiden validointivirheitä (ei suositeltu)", @@ -219,9 +226,9 @@ "oauth_button_text": "Painikkeen teksti", "oauth_client_secret_description": "Vaaditaan, jos OAuth-palveluntarjoaja ei tue PKCE:tä (Proof Key for Code Exchange)", "oauth_enable_description": "Kirjaudu käyttäen OAuthia", - "oauth_mobile_redirect_uri": "Mobiilin uudellenohjaus-URI", + "oauth_mobile_redirect_uri": "Mobiilin uudelleenohjaus-URI", "oauth_mobile_redirect_uri_override": "Ohita mobiilin uudelleenohjaus-URI", - "oauth_mobile_redirect_uri_override_description": "Ota käyttöön kun OAuth tarjoaja ei salli mobiili URI:a, kuten ''{callback}''", + "oauth_mobile_redirect_uri_override_description": "Ota käyttöön kun OAuth-tarjoaja ei salli mobiili-URI:a, kuten ''{callback}''", "oauth_role_claim": "Roolin vaatimus", "oauth_role_claim_description": "Salli pääkäyttäjän pääsyoikeus automaattisesti tämän vaatimuksen perusteella. Vaatimus voi sisältää, joko 'käyttäjän' tai 'pääkäyttäjän'.", "oauth_settings": "OAuth", @@ -272,7 +279,7 @@ "storage_template_migration_info": "Tallennusmalli muuntaa kaikki tiedostopäätteet pieniksi kirjaimiksi. Mallipohjan muutokset koskevat vain uusia resursseja. Jos haluat käyttää mallipohjaa takautuvasti aiemmin ladattuihin resursseihin, suorita {job}.", "storage_template_migration_job": "Tallennustilan mallin muutostyö", "storage_template_more_details": "Saadaksesi lisätietoa tästä ominaisuudesta, katso Tallennustilan Mallit sekä mihin se vaikuttaa", - "storage_template_onboarding_description_v2": "Päälle kytkettynä, toiminto järjestestelee tiedostot automaattisesti käyttäjän määrittämän mallin mukaisesti. Lisätietoja dokumentaatiosta..", + "storage_template_onboarding_description_v2": "Päälle kytkettynä toiminto järjestelee tiedostot automaattisesti käyttäjän määrittämän mallin mukaisesti. Lisätietoja dokumentaatiosta..", "storage_template_path_length": "Arvioitu tiedostopolun pituusrajoitus: {length, number}/{limit, number}", "storage_template_settings": "Tallennustilan malli", "storage_template_settings_description": "Hallitse palvelimelle ladatun aineiston kansiorakennetta ja tiedostonimiä", @@ -295,17 +302,17 @@ "thumbnail_generation_job": "Luo pikkukuvat", "thumbnail_generation_job_description": "Generoi isot, pienet sekä sumeat pikkukuvat jokaisesta aineistosta, kuten myös henkilöistä", "transcoding_acceleration_api": "Kiihdytysrajapinta", - "transcoding_acceleration_api_description": "Rajapinta, jolla keskustellaan laittesi kanssa nopeuttaaksemme koodausta. Tämä asetus on paras mahdollinen: Mikäli ongelmia ilmenee, palataan käyttämään ohjelmistopohjaista koodausta. VP9 voi toimia tai ei, riippuen laitteistosi kokoonpanosta.", + "transcoding_acceleration_api_description": "Rajapinta, jolla keskustellaan laitteesi kanssa nopeuttaaksemme koodausta. Tämä asetus on paras mahdollinen: Mikäli ongelmia ilmenee, palataan käyttämään ohjelmistopohjaista koodausta. VP9 voi toimia tai ei, riippuen laitteistosi kokoonpanosta.", "transcoding_acceleration_nvenc": "NVENC (vaatii NVIDIA:n grafiikkasuorittimen)", "transcoding_acceleration_qsv": "Quick Sync (Vaatii vähintään gen7 Intel prosessorin)", "transcoding_acceleration_rkmpp": "RKMPP (vain Rockchip SOCt)", "transcoding_acceleration_vaapi": "VAAPI", "transcoding_accepted_audio_codecs": "Sallitut äänikoodekit", - "transcoding_accepted_audio_codecs_description": "Valitse mitä äänikoodekkeja ei tarvitse muuntaa. Käytetään vain tiettyjen koodauskäytäntöjen kanssa.", - "transcoding_accepted_containers": "Hyväksytyt kontit", - "transcoding_accepted_containers_description": "Valitse, mitä formaatteja ei tarvitse kääntää MP4- muotoon. Käytössä vain tietyille muunnos säännöille.", + "transcoding_accepted_audio_codecs_description": "Valitse, mitä äänikoodekkeja ei tarvitse muuntaa. Käytetään vain tiettyjen koodauskäytäntöjen kanssa.", + "transcoding_accepted_containers": "Sallitut säiliömuodot", + "transcoding_accepted_containers_description": "Valitse, mitä säiliömuotoja ei tarvitse muuntaa MP4-muotoon. Käytetään vain tiettyjen koodauskäytäntöjen kanssa.", "transcoding_accepted_video_codecs": "Sallitut videokoodekit", - "transcoding_accepted_video_codecs_description": "Valitse mitä videokoodekkeja ei tarvitse muuntaa. Käytetään vain tiettyjen koodauskäytäntöjen kanssa.", + "transcoding_accepted_video_codecs_description": "Valitse, mitä videokoodekkeja ei tarvitse muuntaa. Käytetään vain tiettyjen koodauskäytäntöjen kanssa.", "transcoding_advanced_options_description": "Asetukset, joita useimpien käyttäjien ei tulisi muuttaa", "transcoding_audio_codec": "Äänikoodekki", "transcoding_audio_codec_description": "Opus on paras laadultaan, mutta ei välttämättä ole yhteensopiva vanhempien laitteiden tai sovellusten kanssa.", @@ -332,7 +339,7 @@ "transcoding_policy": "Transkoodauskäytäntö", "transcoding_policy_description": "Aseta milloin video transkoodataan", "transcoding_preferred_hardware_device": "Ensisijainen laite", - "transcoding_preferred_hardware_device_description": "On voimassa vain VAAPI ja QSV -määritteille. Asettaa laitteistokoodauksessa käytetyn DRI noodin.", + "transcoding_preferred_hardware_device_description": "On voimassa vain VAAPI- ja QSV-määritteille. Asettaa laitteistokoodauksessa käytetyn DRI-noodin.", "transcoding_preset_preset": "Esiasetus (-asetus)", "transcoding_preset_preset_description": "Pakkausnopeus. Hitaampi tuottaa pienempiä tiedostoja ja parantaa laatua, kun kohdistetaan tiettyyn bittinopeuteen. VP9 ei huomioi korkeampaa kuin 'faster'.", "transcoding_reference_frames": "Kehysviitteet", @@ -349,7 +356,7 @@ "transcoding_tone_mapping": "Sävykartoitus", "transcoding_tone_mapping_description": "Pyrkii säilömään HDR-kuvien ulkonäön, kun muunnetaan peruskuvaksi. Jokaisella algoritmilla on omat heikkoutensa värien, yksityiskohtien tai kirkkauksien kesken. Hable säilöö yksityiskohdat, Mobius värit ja Reinhard kirkkaudet.", "transcoding_transcode_policy": "Transkoodauskäytäntö", - "transcoding_transcode_policy_description": "Käytäntö miten video tulisi transkoodata. HDR videot transkoodataan aina, paitsi jos transkoodaus on poistettu käytöstä.", + "transcoding_transcode_policy_description": "Käytäntö, miten video tulisi transkoodata. HDR-videot transkoodataan aina, paitsi jos transkoodaus on poistettu käytöstä.", "transcoding_two_pass_encoding": "Two-pass enkoodaus", "transcoding_two_pass_encoding_setting_description": "Transkoodaa kahdessa vaiheessa tuottaaksesi paremmin koodattuja videoita. Kun maksimibittinopeus on käytössä (vaaditaan H.264- ja HEVC-koodaukselle), tämä tila käyttää bittinopeusaluetta, joka perustuu maksimibittinopeuteen ja ohittaa CRF. VP9 osalta CRF:ää voidaan käyttää, jos maksimibittinopeus on poistettu käytöstä.", "transcoding_video_codec": "Videokoodekki", @@ -424,11 +431,12 @@ "album_remove_user_confirmation": "Oletko varma että haluat poistaa {user}?", "album_search_not_found": "Haullasi ei löytynyt yhtään albumia", "album_share_no_users": "Näyttää että olet jakanut tämän albumin kaikkien kanssa, tai sinulla ei ole käyttäjiä joille jakaa.", + "album_summary": "Albumi tiivistelmä", "album_updated": "Albumi päivitetty", "album_updated_setting_description": "Saa sähköpostia kun jaetussa albumissa on uutta sisältöä", "album_user_left": "Poistuttiin albumista {album}", "album_user_removed": "{user} poistettu", - "album_viewer_appbar_delete_confirm": "Haluatko varmast poistaa tämän albumin tililtäsi?", + "album_viewer_appbar_delete_confirm": "Haluatko varmasti poistaa tämän albumin tililtäsi?", "album_viewer_appbar_share_err_delete": "Albumin poistaminen epäonnistui", "album_viewer_appbar_share_err_leave": "Albumista poistuminen epäonnistui", "album_viewer_appbar_share_err_remove": "Ongelmia kohteiden poistamisessa albumista", @@ -474,7 +482,7 @@ "archived_count": "{count, plural, other {Arkistoitu #}}", "are_these_the_same_person": "Ovatko he sama henkilö?", "are_you_sure_to_do_this": "Haluatko varmasti tehdä tämän?", - "asset_action_delete_err_read_only": "Vain luku-tilassa olevia kohteita ei voitu poistaa, ohitetaan", + "asset_action_delete_err_read_only": "Vain luku -tilassa olevia kohteita ei voitu poistaa, ohitetaan", "asset_action_share_err_offline": "Verkottomassa tilassa olevia kohteita ei voitu noutaa, ohitetaan", "asset_added_to_album": "Lisätty albumiin", "asset_adding_to_album": "Lisätään albumiin…", @@ -495,6 +503,8 @@ "asset_restored_successfully": "Kohde palautettu onnistuneesti", "asset_skipped": "Ohitettu", "asset_skipped_in_trash": "Roskakorissa", + "asset_trashed": "Kohde poistettu", + "asset_troubleshoot": "Sisällön vian paikannus", "asset_uploaded": "Lähetetty", "asset_uploading": "Ladataan…", "asset_viewer_settings_subtitle": "Galleriakatseluohjelman asetusten hallinta", @@ -502,7 +512,7 @@ "assets": "Kohteet", "assets_added_count": "Lisätty {count, plural, one {# kohde} other {# kohdetta}}", "assets_added_to_album_count": "Albumiin lisätty {count, plural, one {# kohde} other {# kohdetta}}", - "assets_added_to_albums_count": "Lisätty {assetTotal, plural, one {# aineisto} other {# aaineistoa}} {albumTotal, plural, one {# albumiin} other {# albumeihin}}", + "assets_added_to_albums_count": "Lisätty {assetTotal, plural, one {# kohde} other {# kohdetta}} {albumTotal, plural, one {# albumiin} other {# albumiin}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Kohdetta} other {Kohdetta}} ei voida lisätä albumiin", "assets_cannot_be_added_to_albums": "{count, plural, one {Aineisto} other {Aineistoa}} ei voi lisätä mihinkään albumiin", "assets_count": "{count, plural, one {# media} other {# mediaa}}", @@ -528,8 +538,10 @@ "autoplay_slideshow": "Toista diaesitys automaattisesti", "back": "Takaisin", "back_close_deselect": "Palaa, sulje tai poista valinnat", + "background_backup_running_error": "Tausta varmuuskopiointi on aktiivinen, ei voida aloittaa manuaalista varmuuskopiointia", "background_location_permission": "Taustasijainnin käyttöoikeus", "background_location_permission_content": "Jotta sovellus voi vaihtaa verkkoa taustalla toimiessaan, Immichillä on *aina* oltava pääsy tarkkaan sijaintiin, jotta se voi lukea Wi-Fi-verkon nimen", + "background_options": "Tausta valinnat", "backup": "Varmuuskopiointi", "backup_album_selection_page_albums_device": "Laitteen albumit ({count})", "backup_album_selection_page_albums_tap": "Napauta sisällyttääksesi, kaksoisnapauta jättääksesi pois", @@ -537,6 +549,7 @@ "backup_album_selection_page_select_albums": "Valitse albumit", "backup_album_selection_page_selection_info": "Valintatiedot", "backup_album_selection_page_total_assets": "Ainulaatuisia kohteita yhteensä", + "backup_albums_sync": "Varmuuskopioitujen albumeiden synkronointi", "backup_all": "Kaikki", "backup_background_service_backup_failed_message": "Kohteiden varmuuskopiointi epäonnistui. Yritetään uudelleen…", "backup_background_service_connection_failed_message": "Palvelimeen ei saatu yhteyttä. Yritetään uudelleen…", @@ -586,7 +599,8 @@ "backup_controller_page_turn_on": "Varmuuskopiointi päälle", "backup_controller_page_uploading_file_info": "Tiedostojen lähetystiedot", "backup_err_only_album": "Vähintään yhden albumin tulee olla valittuna", - "backup_info_card_assets": "kohteet", + "backup_error_sync_failed": "Synkronointi epäonnistui. Varmuuskopion käsittely ei onnistu.", + "backup_info_card_assets": "kohdetta", "backup_manual_cancelled": "Peruutettu", "backup_manual_in_progress": "Lähetys palvelimelle on jo käynnissä. Kokeile myöhemmin uudelleen", "backup_manual_success": "Onnistui", @@ -621,7 +635,7 @@ "cache_settings_statistics_thumbnail": "Esikatselukuvat", "cache_settings_statistics_title": "Välimuistin käyttö", "cache_settings_subtitle": "Hallitse Immich-mobiilisovelluksen välimuistin käyttöä", - "cache_settings_tile_subtitle": "Hallitse paikallista tallenustilaa", + "cache_settings_tile_subtitle": "Hallitse paikallista tallennustilaa", "cache_settings_tile_title": "Paikallinen tallennustila", "cache_settings_title": "Välimuistin asetukset", "camera": "Kamera", @@ -653,6 +667,8 @@ "change_pin_code": "Vaihda PIN-koodi", "change_your_password": "Vaihda salasanasi", "changed_visibility_successfully": "Näkyvyys vaihdettu", + "charging": "Ladataan laitetta", + "charging_requirement_mobile_backup": "Varmuuskopiointi taustalla vaatii laitteen latautumista", "check_corrupt_asset_backup": "Vioittuneiden varmuuskopioiden tarkistaminen", "check_corrupt_asset_backup_button": "Suorita tarkistus", "check_corrupt_asset_backup_description": "Suorita tämä tarkistus vain Wi-Fi-yhteyden kautta ja vasta, kun kaikki kohteet on varmuuskopioitu. Toimenpide voi kestää muutamia minuutteja.", @@ -739,6 +755,7 @@ "create_user": "Luo käyttäjä", "created": "Luotu", "created_at": "Luotu", + "creating_linked_albums": "Luodaan linkattuja albumeita...", "crop": "Rajaa", "curated_object_page_title": "Asiat", "current_device": "Nykyinen laite", @@ -763,7 +780,7 @@ "deduplication_criteria_1": "Kuvan koko tavuina", "deduplication_criteria_2": "EXIF-datan määrä", "deduplication_info": "Deduplikaatiotieto", - "deduplication_info_description": "Jotta voimme automaattisesti esivalita aineistot ja poistaa duplikaatit suurina erinä, tarkastelemme:", + "deduplication_info_description": "Jotta voimme automaattisesti esivalita aineistot ja poistaa kaksoiskappaleet suurina erinä, tarkastelemme:", "default_locale": "Oletuskieliasetus", "default_locale_description": "Muotoile päivämäärät ja numerot selaimesi kielen mukaan", "delete": "Poista", @@ -838,7 +855,7 @@ "downloading_media": "Median lataaminen", "drop_files_to_upload": "Pudota tiedostot mihin tahansa ladataksesi ne", "duplicates": "Kaksoiskappaleet", - "duplicates_description": "Selvitä jokaisen kohdalla mitkä (jos yksikään) ovat kaksoiskappaleita", + "duplicates_description": "Selvitä jokaisen kohdalla mitkä (jos mitkään) ovat kaksoiskappaleita", "duration": "Kesto", "edit": "Muokkaa", "edit_album": "Muokkaa albumia", @@ -888,7 +905,9 @@ "error": "Virhe", "error_change_sort_album": "Albumin lajittelujärjestyksen muuttaminen epäonnistui", "error_delete_face": "Virhe kasvojen poistamisessa kohteesta", + "error_getting_places": "Ongelma paikkojen haussa", "error_loading_image": "Kuvan lataus ei onnistunut", + "error_loading_partners": "Ongelma partnerin haussa: {error}", "error_saving_image": "Virhe: {error}", "error_tag_face_bounding_box": "Kasvojen merkitseminen epäonnistui – rajausruudun koordinaatteja ei löydy", "error_title": "Virhe - Jotain meni pieleen", @@ -1048,11 +1067,12 @@ "failed_to_load_folder": "Kansion lataaminen epäonnistui", "favorite": "Suosikki", "favorite_action_prompt": "{count} lisätty suosikkeihin", - "favorite_or_unfavorite_photo": "Suosikki- tai ei-suosikkikuva", + "favorite_or_unfavorite_photo": "Lisää tai poista kuva suosikeista", "favorites": "Suosikit", "favorites_page_no_favorites": "Suosikkikohteita ei löytynyt", "feature_photo_updated": "Kansikuva ladattu", "features": "Ominaisuudet", + "features_in_development": "Kehityksessä olevat ominaisuudet", "features_setting_description": "Hallitse sovelluksen ominaisuuksia", "file_name": "Tiedoston nimi", "file_name_or_extension": "Tiedostonimi tai tiedostopääte", @@ -1080,6 +1100,8 @@ "go_back": "Palaa", "go_to_folder": "Mene kansioon", "go_to_search": "Siirry hakuun", + "gps": "GPS", + "gps_missing": "Ei GPS:ää", "grant_permission": "Myönnä lupa", "group_albums_by": "Ryhmitä albumi...", "group_country": "Ryhmitä maan mukaan", @@ -1109,18 +1131,18 @@ "home_page_add_to_album_conflicts": "Lisätty {added} kohdetta albumiin {album}. {failed} kohdetta on jo albumissa.", "home_page_add_to_album_err_local": "Paikallisten kohteiden lisääminen albumeihin ei ole mahdollista, ohitetaan", "home_page_add_to_album_success": "Lisätty {added} kohdetta albumiin {album}.", - "home_page_album_err_partner": "Kumppanin kohteita ei voi vielä lisätä albumiin. Hypätään yli", + "home_page_album_err_partner": "Kumppanin kohteita ei voi vielä lisätä albumiin, ohitetaan", "home_page_archive_err_local": "Paikallisten kohteiden arkistointi ei ole mahdollista, ohitetaan", - "home_page_archive_err_partner": "Kumppanin kohteita ei voi arkistoida. Hypätään yli", + "home_page_archive_err_partner": "Kumppanin kohteita ei voi arkistoida, ohitetaan", "home_page_building_timeline": "Rakennetaan aikajanaa", - "home_page_delete_err_partner": "Kumppanin kohteita ei voi poistaa.Hypätään yli", + "home_page_delete_err_partner": "Kumppanin kohteita ei voi poistaa, ohitetaan", "home_page_delete_remote_err_local": "Paikallisia kohteita etäkohdevalintojen joukossa, ohitetaan", "home_page_favorite_err_local": "Paikallisten kohteiden lisääminen suosikkeihin ei ole mahdollista, ohitetaan", - "home_page_favorite_err_partner": "Kumppanin kohteita ei voi vielä merkitä suosikiksi. Hypätään yli", + "home_page_favorite_err_partner": "Kumppanin kohteita ei voi vielä merkitä suosikiksi, ohitetaan", "home_page_first_time_notice": "Jos käytät sovellusta ensimmäistä kertaa, muista valita varmuuskopioitavat albumi(t), jotta aikajanalla voi olla kuvia ja videoita", "home_page_locked_error_local": "Paikallisten kohteiden siirto lukittuun kansioon ei onnistu, ohitetaan", "home_page_locked_error_partner": "Kumppanin kohteita ei voi siirtää lukittuun kansioon, ohitetaan", - "home_page_share_err_local": "Paikallisia kohteita ei voitu jakaa linkkien avulla. Hypätään yli", + "home_page_share_err_local": "Paikallisia kohteita ei voitu jakaa linkkien avulla, ohitetaan", "home_page_upload_err_limit": "Voit lähettää palvelimelle enintään 30 kohdetta kerrallaan, ohitetaan", "host": "Isäntä", "hour": "Tunti", @@ -1148,7 +1170,7 @@ "immich_web_interface": "Immich-verkkokäyttöliittymä", "import_from_json": "Tuo JSON-tiedostosta", "import_path": "Tuontipolku", - "in_albums": "{count, plural, one {# Albumissa} other {# albumissa}}", + "in_albums": "{count, plural, one {# albumissa} other {# albumissa}}", "in_archive": "Arkistossa", "include_archived": "Sisällytä arkistoidut", "include_shared_albums": "Sisällytä jaetut albumit", @@ -1157,10 +1179,10 @@ "individual_shares": "Yksittäiset jaot", "info": "Lisätietoja", "interval": { - "day_at_onepm": "Joka päivä klo 13:00", + "day_at_onepm": "Joka päivä klo 13.00", "hours": "Joka {hours, plural, one {tunti} other {{hours, number} tuntia}}", "night_at_midnight": "Joka yö keskiyöllä", - "night_at_twoam": "Joka yö klo 02:00" + "night_at_twoam": "Joka yö klo 2.00" }, "invalid_date": "Virheellinen päivämäärä", "invalid_date_format": "Virheellinen päivämäärämuoto", @@ -1215,6 +1237,7 @@ "local": "Paikallinen", "local_asset_cast_failed": "Kohdetta, joka ei ole ladattuna palvelimelle, ei voida striimata", "local_assets": "Paikalliset kohteet", + "local_media_summary": "Paikallisen median yhteenveto", "local_network": "Lähiverkko", "local_network_sheet_info": "Sovellus muodostaa yhteyden palvelimeen tämän URL-osoitteen kautta, kun käytetään määritettyä Wi-Fi-verkkoa", "location_permission": "Sijainnin käyttöoikeus", @@ -1226,6 +1249,7 @@ "location_picker_longitude_hint": "Syötä pituusaste", "lock": "Lukitse", "locked_folder": "Lukittu kansio", + "log_detail_title": "Lokin yksityiskohtaisuus", "log_out": "Kirjaudu ulos", "log_out_all_devices": "Kirjaudu ulos kaikilta laitteilta", "logged_in_as": "Kirjautunut käyttäjänä {user}", @@ -1256,6 +1280,7 @@ "login_password_changed_success": "Salasan päivitetty onnistuneesti", "logout_all_device_confirmation": "Haluatko varmasti kirjautua ulos kaikilta laitteilta?", "logout_this_device_confirmation": "Haluatko varmasti kirjautua ulos näiltä laitteilta?", + "logs": "Loki", "longitude": "Pituusaste", "look": "Tyyli", "loop_videos": "Toista videot uudelleen", @@ -1263,6 +1288,7 @@ "main_branch_warning": "Käytät kehitysversiota; suosittelemme vahvasti käyttämään julkaisuversiota!", "main_menu": "Päävalikko", "make": "Valmistaja", + "manage_geolocation": "Muokkaa sijaintia", "manage_shared_links": "Hallitse jaettuja linkkejä", "manage_sharing_with_partners": "Hallitse jakamista kumppaneille", "manage_the_app_settings": "Hallitse sovelluksen asetuksia", @@ -1297,6 +1323,7 @@ "mark_as_read": "Merkitse luetuksi", "marked_all_as_read": "Merkitty kaikki luetuiksi", "matches": "Osumia", + "matching_assets": "Vastaava sisältö", "media_type": "Median tyyppi", "memories": "Muistoja", "memories_all_caught_up": "Kaikki ajan tasalla", @@ -1330,13 +1357,14 @@ "moved_to_library": "Siirretty {count, plural, one {# kohde} other {# kohdetta}} kirjastoon", "moved_to_trash": "Siirretty roskakoriin", "multiselect_grid_edit_date_time_err_read_only": "Vain luku -tilassa olevien kohteiden päivämäärää ei voitu muokata, ohitetaan", - "multiselect_grid_edit_gps_err_read_only": "Vain luku-tilassa olevien kohteiden sijantitietoja ei voitu muokata, ohitetaan", + "multiselect_grid_edit_gps_err_read_only": "Vain luku -tilassa olevien kohteiden sijantitietoja ei voitu muokata, ohitetaan", "mute_memories": "Mykistä muistot", "my_albums": "Omat albumit", "name": "Nimi", "name_or_nickname": "Nimi tai lempinimi", "network_requirement_photos_upload": "Käytä mobiiliverkkoa kuvien varmuuskopioimiseksi", "network_requirement_videos_upload": "Käytä mobiiliverkkoa videoiden varmuuskopioimiseksi", + "network_requirements": "Verkkovaatimukset", "network_requirements_updated": "Verkkovaatimukset muuttuivat, nollataan varmuuskopiointijono", "networking_settings": "Verkko", "networking_subtitle": "Hallitse palvelinasetuksia", @@ -1347,6 +1375,7 @@ "new_person": "Uusi henkilö", "new_pin_code": "Uusi PIN-koodi", "new_pin_code_subtitle": "Tämä on ensimmäinen kerta, kun käytät lukittua kansiota. Luo PIN-koodi päästäksesi tähän sisältöön turvallisesti", + "new_timeline": "Uusi aikajana", "new_user_created": "Uusi käyttäjä lisätty", "new_version_available": "UUSI VERSIO SAATAVILLA", "newest_first": "Uusin ensin", @@ -1357,23 +1386,28 @@ "no_albums_with_name_yet": "Näyttää siltä, ettei sinulla ole yhtään tämän nimistä albumia.", "no_albums_yet": "Näyttää siltä, ettei sinulla ole vielä yhtään albumia.", "no_archived_assets_message": "Arkistoi kuvia ja videoita piilottaaksesi ne kuvat näkymästä", - "no_assets_message": "NAPAUTA LATAAKSESI ENSIMMÄISEN KUVASI", + "no_assets_message": "NAPAUTA LADATAKSESI ENSIMMÄINEN KUVASI", "no_assets_to_show": "Ei näytettäviä kohteita", "no_cast_devices_found": "Cast-laitteita ei löytynyt", + "no_checksum_local": "Ei tarkistussummaa - paikallista sisältöä ei voida hakea", + "no_checksum_remote": "Ei tarkistussummaa - etänä olevaa sisältöä ei voida hakea", "no_duplicates_found": "Kaksoiskappaleita ei löytynyt.", "no_exif_info_available": "EXIF-tietoa ei saatavilla", "no_explore_results_message": "Lataa lisää kuvia tutkiaksesi kokoelmaasi.", "no_favorites_message": "Lisää suosikkeja löytääksesi nopeasti parhaat kuvasi ja videosi", "no_libraries_message": "Luo ulkoinen kirjasto nähdäksesi valokuvasi ja videot", + "no_local_assets_found": "Paikallista sisältöä ei löytynyt tällä tarkistussummalla", "no_locked_photos_message": "Kuvat ja videot lukitussa kansiossa ovat piilotettuja, eivätkä ne näy selatessasi tai etsiessäsi kirjastoasi.", "no_name": "Ei nimeä", "no_notifications": "Ei ilmoituksia", "no_people_found": "Ei vastaavia henkilöitä", "no_places": "Ei paikkoja", + "no_remote_assets_found": "Etänä olevaa sisältöä ei löytynyt tällä tarkistussummalla", "no_results": "Ei tuloksia", "no_results_description": "Kokeile synonyymiä tai yleisempää avainsanaa", "no_shared_albums_message": "Luo albumi, jotta voit jakaa kuvia ja videoita toisille", "no_uploads_in_progress": "Ei käynnissä olevia latauksia", + "not_available": "N/A", "not_in_any_album": "Ei yhdessäkään albumissa", "not_selected": "Ei valittu", "note_apply_storage_label_to_previously_uploaded assets": "Huom: Jotta voit soveltaa tallennustunnistetta aiemmin ladattuihin kohteisiin, suorita", @@ -1408,6 +1442,8 @@ "open_the_search_filters": "Avaa hakusuodattimet", "options": "Vaihtoehdot", "or": "tai", + "organize_into_albums": "Järjestä albumeihin", + "organize_into_albums_description": "Siirrä olemassa olevat kuvat albumeihin käyttäen nykyisiä synkronointiasetuksia", "organize_your_library": "Järjestele kirjastosi", "original": "alkuperäinen", "other": "Muut", @@ -1453,7 +1489,7 @@ "permanent_deletion_warning_setting_description": "Näytä varoitus, kun poistat kohteita pysyvästi", "permanently_delete": "Poista pysyvästi", "permanently_delete_assets_count": "Poista pysyvästi {count, plural, one {kohde} other {kohteita}}", - "permanently_delete_assets_prompt": "Haluatko varmasti poistaa pysyvästi {count, plural, one {tämän kohteen?} other {nämä # kohteet?}} Tämä poistaa myös {count, plural, one {sen} other {ne}} kaikista albumeista.", + "permanently_delete_assets_prompt": "Haluatko varmasti poistaa pysyvästi {count, plural, one {tämän kohteen?} other {nämä # kohteet?}} Tämä poistaa {count, plural, one {sen} other {ne}} myös kaikista albumeista.", "permanently_deleted_asset": "Media poistettu pysyvästi", "permanently_deleted_assets_count": "{count, plural, one {# media} other {# mediaa}} poistettu pysyvästi", "permission": "Käyttöoikeus", @@ -1493,6 +1529,7 @@ "port": "Portti", "preferences_settings_subtitle": "Hallitse sovelluksen asetuksia", "preferences_settings_title": "Asetukset", + "preparing": "Valmistellaan", "preset": "Asetus", "preview": "Esikatselu", "previous": "Edellinen", @@ -1509,6 +1546,7 @@ "profile_drawer_client_out_of_date_minor": "Sovelluksen mobiiliversio on vanhentunut. Päivitä viimeisimpään versioon.", "profile_drawer_client_server_up_to_date": "Asiakasohjelma ja palvelin ovat ajan tasalla", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Muokkaus on estetty. Paina käyttäjäkuvaketta pitkään palataksesi muokkaustilaan.", "profile_drawer_server_out_of_date_major": "Palvelimen ohjelmistoversio on vanhentunut. Päivitä viimeisimpään merkittävään versioon.", "profile_drawer_server_out_of_date_minor": "Palvelimen ohjelmistoversio on vanhentunut. Päivitä viimeisimpään versioon.", "profile_image_of_user": "Käyttäjän {user} profiilikuva", @@ -1547,6 +1585,7 @@ "purchase_server_description_2": "Tukijan tila", "purchase_server_title": "Palvelin", "purchase_settings_server_activated": "Palvelimen tuoteavainta hallinnoi ylläpitäjä", + "query_asset_id": "Kysy sisällön ID:tä", "queue_status": "Jonossa {count}/{total}", "rating": "Tähtiarvostelu", "rating_clear": "Tyhjennä arvostelu", @@ -1554,6 +1593,9 @@ "rating_description": "Näytä EXIF-arvosana lisätietopaneelissa", "reaction_options": "Reaktioasetukset", "read_changelog": "Lue muutosloki", + "readonly_mode_disabled": "Muokkaustila päällä", + "readonly_mode_enabled": "Muokkaustila pois päältä", + "ready_for_upload": "Valmis lähetystä varten", "reassign": "Määritä uudelleen", "reassigned_assets_to_existing_person": "Uudelleen määritetty {count, plural, one {# kohde} other {# kohdetta}} {name, select, null {olemassa olevalle henkilölle} other {{name}}}", "reassigned_assets_to_new_person": "Määritetty {count, plural, one {# media} other {# mediaa}} uudelle henkilölle", @@ -1578,6 +1620,7 @@ "regenerating_thumbnails": "Regeneroidaan pikkukuvia", "remote": "Etä", "remote_assets": "Etäkohteet", + "remote_media_summary": "Yhteenveto etänä olevasta mediasta", "remove": "Poista", "remove_assets_album_confirmation": "Haluatko varmasti poistaa {count, plural, one {# median} other {# mediaa}} albumista?", "remove_assets_shared_link_confirmation": "Haluatko varmasti poistaa {count, plural, one {# median} other {# mediaa}} tästä jakolinkistä?", @@ -1630,6 +1673,7 @@ "restore_user": "Palauta käyttäjä", "restored_asset": "Palautettu media", "resume": "Jatka", + "resume_paused_jobs": "Jatka {count, plural, one {# paused job} other {# paused jobs}}", "retry_upload": "Yritä latausta uudelleen", "review_duplicates": "Tarkastele kaksoiskappaleita", "review_large_files": "Tarkista suuret tiedostot", @@ -1694,7 +1738,7 @@ "search_places": "Etsi paikkoja", "search_rating": "Hae luokituksen mukaan...", "search_result_page_new_search_hint": "Uusi haku", - "search_settings": "Hakuasetukset", + "search_settings": "Etsi asetuksia", "search_state": "Etsi maakuntaa...", "search_suggestion_list_smart_search_hint_1": "Älykäs haku on oletuksena käytössä. Käytä metatietojen etsimiseen syntaksia ", "search_suggestion_list_smart_search_hint_2": "m:hakusana", @@ -1706,7 +1750,7 @@ "second": "Toinen", "see_all_people": "Näytä kaikki henkilöt", "select": "Valitse", - "select_album_cover": "Valitse albmin kansi", + "select_album_cover": "Valitse albumin kansi", "select_all": "Valitse kaikki", "select_all_duplicates": "Valitse kaikki kaksoiskappaleet", "select_all_in": "Valitse kaikki {group}", @@ -1723,6 +1767,7 @@ "select_user_for_sharing_page_err_album": "Albumin luonti epäonnistui", "selected": "Valittu", "selected_count": "{count, plural, other {# valittu}}", + "selected_gps_coordinates": "Valitut GPS-koordinaatit", "send_message": "Lähetä viesti", "send_welcome_email": "Lähetä tervetuloviesti", "server_endpoint": "Palvelinosoite", @@ -1764,7 +1809,7 @@ "setting_video_viewer_original_video_subtitle": "Kun toistat videota palvelimelta, toista alkuperäinen, vaikka transkoodattu versio olisi saatavilla. Tämä voi johtaa puskurointiin. Paikalliset videot toistetaan aina alkuperäislaadulla.", "setting_video_viewer_original_video_title": "Pakota alkuperäinen video", "settings": "Asetukset", - "settings_require_restart": "Käynnistä Immich uudelleen ottaaksesti tämän asetuksen käyttöön", + "settings_require_restart": "Käynnistä Immich uudelleen ottaaksesi tämä asetus käyttöön", "settings_saved": "Asetukset tallennettu", "setup_pin_code": "Määritä PIN-koodi", "share": "Jaa", @@ -1830,7 +1875,7 @@ "sharing_sidebar_description": "Näytä jakamislinkki sivupalkissa", "sharing_silver_appbar_create_shared_album": "Luo jaettu albumi", "sharing_silver_appbar_share_partner": "Jaa kumppanille", - "shift_to_permanent_delete": "Paina ⇧ poistaaksesi median pysyvästi", + "shift_to_permanent_delete": "Paina ⇧ poistaaksesi media pysyvästi", "show_album_options": "Näytä albumin asetukset", "show_albums": "Näytä albumit", "show_all_people": "Näytä kaikki henkilöt", @@ -1851,6 +1896,7 @@ "show_slideshow_transition": "Näytä diaesitys siirtymä", "show_supporter_badge": "Kannattajan merkki", "show_supporter_badge_description": "Näytä kannattajan merkki", + "show_text_search_menu": "Näytä tekstihakuvalikko", "shuffle": "Sekoita", "sidebar": "Sivupalkki", "sidebar_display_description": "Näytä linkki näkymään sivupalkissa", @@ -1881,6 +1927,7 @@ "stacktrace": "Vianetsintätiedot", "start": "Aloita", "start_date": "Alkupäivä", + "start_date_before_end_date": "Aloituspäivämäärän pitää olla ennen lopetuspäivämäärää", "state": "Maakunta", "status": "Tila", "stop_casting": "Lopeta suoratoisto", @@ -1905,6 +1952,8 @@ "sync_albums_manual_subtitle": "Synkronoi kaikki ladatut videot ja valokuvat valittuihin varmuuskopioalbumeihin", "sync_local": "Synkronoi paikallinen", "sync_remote": "Synkronoi etä", + "sync_status": "Synkronoinnin status", + "sync_status_subtitle": "Näytä ja hallinnoi synkronointijärjestelmää", "sync_upload_album_setting_subtitle": "Luo ja lataa valokuvasi ja videosi valittuihin albumeihin Immichissä", "tag": "Tunniste", "tag_assets": "Lisää tunnisteita", @@ -1942,7 +1991,9 @@ "to_change_password": "Vaihda salasana", "to_favorite": "Aseta suosikiksi", "to_login": "Kirjaudu sisään", + "to_multi_select": "usean valitsemiseksi", "to_parent": "Siirry vanhempaan", + "to_select": "valitsemiseksi", "to_trash": "Roskakoriin", "toggle_settings": "Määritä asetukset", "total": "Yhteensä", @@ -1950,7 +2001,7 @@ "trash": "Roskakori", "trash_action_prompt": "{count} siirretty roskakoriin", "trash_all": "Vie kaikki roskakoriin", - "trash_count": "Roskakori {count, number}", + "trash_count": "Vie {count, number} roskakoriin", "trash_delete_asset": "Poista / vie roskakoriin", "trash_emptied": "Roskakori tyhjennetty", "trash_no_results_message": "Roskakorissa olevat kuvat ja videot näytetään täällä.", @@ -1962,6 +2013,7 @@ "trash_page_select_assets_btn": "Valitse kohteet", "trash_page_title": "Roskakori ({count})", "trashed_items_will_be_permanently_deleted_after": "Roskakorin kohteet poistetaan pysyvästi {days, plural, one {# päivän} other {# päivän}} päästä.", + "troubleshoot": "Vianetsintä", "type": "Tyyppi", "unable_to_change_pin_code": "PIN-koodin vaihtaminen epäonnistui", "unable_to_setup_pin_code": "PIN-koodin määrittäminen epäonnistui", @@ -1992,6 +2044,7 @@ "unstacked_assets_count": "Poistettu pinosta {count, plural, one {# kohde} other {# kohdetta}}", "untagged": "Ilman tunnistetta", "up_next": "Seuraavaksi", + "update_location_action_prompt": "Päivitä {count} kohteen sijaintia:", "updated_at": "Päivitetty", "updated_password": "Salasana päivitetty", "upload": "Siirrä palvelimelle", @@ -2058,6 +2111,7 @@ "view_next_asset": "Näytä seuraava", "view_previous_asset": "Näytä edellinen", "view_qr_code": "Näytä QR-koodi", + "view_similar_photos": "Näytä samankaltaiset kuvat", "view_stack": "Näytä pinona", "view_user": "Näytä käyttäjä", "viewer_remove_from_stack": "Poista pinosta", @@ -2076,5 +2130,6 @@ "yes": "Kyllä", "you_dont_have_any_shared_links": "Sinulla ei ole jaettuja linkkejä", "your_wifi_name": "Wi-Fi-verkkosi nimi", - "zoom_image": "Zoomaa kuvaa" + "zoom_image": "Zoomaa kuvaa", + "zoom_to_bounds": "Zoomaa reunoihin" } diff --git a/i18n/fr.json b/i18n/fr.json index c0a76c69fe..0c92c52764 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -28,10 +28,12 @@ "add_to_album": "Ajouter à l'album", "add_to_album_bottom_sheet_added": "Ajouté à {album}", "add_to_album_bottom_sheet_already_exists": "Déjà dans {album}", + "add_to_album_bottom_sheet_some_local_assets": "Certains médias n'ont pas pu être ajoutés à l'album", "add_to_album_toggle": "Basculer la sélection pour {album}", "add_to_albums": "Ajouter aux albums", "add_to_albums_count": "Ajouter aux albums ({count})", "add_to_shared_album": "Ajouter à l'album partagé", + "add_upload_to_stack": "Ajouter les éléments téléversés à la pile", "add_url": "Ajouter l'URL", "added_to_archive": "Ajouté à l'archive", "added_to_favorites": "Ajouté aux favoris", @@ -598,7 +600,8 @@ "backup_controller_page_turn_on": "Activer la sauvegarde au premier plan", "backup_controller_page_uploading_file_info": "Envoi des informations du fichier", "backup_err_only_album": "Impossible de retirer le seul album", - "backup_info_card_assets": "éléments", + "backup_error_sync_failed": "Échec de la synchronisation. Impossible d'exécuter la sauvegarde.", + "backup_info_card_assets": "médias", "backup_manual_cancelled": "Annulé", "backup_manual_in_progress": "Envoi déjà en cours. Réessayez plus tard", "backup_manual_success": "Succès", diff --git a/i18n/gl.json b/i18n/gl.json index b6a59fadc7..8a38ef2c86 100644 --- a/i18n/gl.json +++ b/i18n/gl.json @@ -11,14 +11,14 @@ "activity_changed": "A actividade está {enabled, select, true {activada} other {desactivada}}", "add": "Engadir", "add_a_description": "Engadir unha descrición", - "add_a_location": "Engadir unha ubicación", + "add_a_location": "Engadir unha localización", "add_a_name": "Engadir un nome", "add_a_title": "Engadir un título", - "add_birthday": "Engadir cumpleanos", - "add_endpoint": "Engadir endpoint", + "add_birthday": "Engadir cumpreanos", + "add_endpoint": "Engadir punto final", "add_exclusion_pattern": "Engadir patrón de exclusión", "add_import_path": "Engadir ruta de importación", - "add_location": "Engadir ubicación", + "add_location": "Engadir localización", "add_more_users": "Engadir máis usuarios", "add_partner": "Engadir compañeiro/a", "add_path": "Engadir ruta", @@ -28,69 +28,70 @@ "add_to_album": "Engadir ao álbum", "add_to_album_bottom_sheet_added": "Engadido a {album}", "add_to_album_bottom_sheet_already_exists": "Xa está en {album}", - "add_to_album_toggle": "Alternar selección para o {album}", - "add_to_albums": "Engadir en álbums", + "add_to_album_bottom_sheet_some_local_assets": "Algunhas imaxes ou ficheiros locais non se puideron engadir ao álbum", + "add_to_album_toggle": "Alternar selección para {album}", + "add_to_albums": "Engadir a álbums", "add_to_albums_count": "Engadir a {count} álbums", "add_to_shared_album": "Engadir ao álbum compartido", "add_url": "Engadir URL", "added_to_archive": "Engadido ao arquivo", "added_to_favorites": "Engadido a favoritos", - "added_to_favorites_count": "Engadido {count, number} a favoritos", + "added_to_favorites_count": "Engadíronse {count, number} a favoritos", "admin": { - "add_exclusion_pattern_description": "Engadir patróns de exclusión. Admítense caracteres comodín usando *, ** e ?. Para ignorar todos os ficheiros en calquera directorio chamado \"Raw\", emprega \"**/Raw/**\". Para ignorar todos os ficheiros que rematen en \".tif\", usa \"**/*.tif\". Para ignorar unha ruta absoluta, emprega \"/ruta/a/ignorar/**\".", + "add_exclusion_pattern_description": "Engadir patróns de exclusión. Admítense caracteres comodín usando *, ** e ?. Para ignorar todos os ficheiros en calquera directorio chamado \"Raw\", empregue \"**/Raw/**\". Para ignorar todos os ficheiros que rematen en \".tif\", use \"**/*.tif\". Para ignorar unha ruta absoluta, empregue \"/ruta/a/ignorar/**\".", "admin_user": "Usuario administrador", - "asset_offline_description": "Este activo da biblioteca externa xa non se atopa no disco e moveuse ao lixo. Se o ficheiro se moveu dentro da biblioteca, comproba a túa liña de tempo para o novo activo correspondente. Para restaurar este activo, asegúrate de que Immich poida acceder á ruta do ficheiro a continuación e escanee a biblioteca.", + "asset_offline_description": "Este activo da biblioteca externa xa non se atopa no disco e moveuse ao lixo. Se o ficheiro se moveu dentro da biblioteca, comprobe a súa liña de tempo para o novo activo correspondente. Para restaurar este activo, asegúrese de que Immich poida acceder á ruta do ficheiro a continuación e escanee a biblioteca.", "authentication_settings": "Configuración de autenticación", "authentication_settings_description": "Xestionar contrasinal, OAuth e outras configuracións de autenticación", - "authentication_settings_disable_all": "Estás seguro de que queres desactivar todos os métodos de inicio de sesión? O inicio de sesión desactivarase completamente.", + "authentication_settings_disable_all": "Está seguro de que quere desactivar todos os métodos de inicio de sesión? O inicio de sesión desactivarase completamente.", "authentication_settings_reenable": "Para reactivalo, use un Comando de servidor.", "background_task_job": "Tarefas en segundo plano", - "backup_database": "Crear un vertedoiro de base de datos", - "backup_database_enable_description": "Activar o vertedoiro de copias de seguridade da base de datos", - "backup_keep_last_amount": "Cantidade de copias de seguridade anteriores a conservar", - "backup_onboarding_1_description": "Copia no exterior na nube ou noutra localización física.", - "backup_onboarding_2_description": "Copias locais en diferentes dispositivos. Isto inclue os arquivos principais e as copias de esos arquivos localmente.", - "backup_onboarding_3_description": "copias totais da tua información, incluindo os arquivos orixinais. Isto inclue 1 copia externa e 2 copias locais.", - "backup_onboarding_description": "Unha estratexia de copia 3-2-1 é recomendada para protexer os teus datos. Deberías gardar copias das túas fotos/videos subidas así como da base de datos de Immich como unha solución de seguridade.", - "backup_onboarding_footer": "Pra máis información sobre copias de seguridade de Immich, por favor use a seguinte ligazón de documentación.", - "backup_onboarding_parts_title": "Unha copia de seguridade 3-2-1 inclue:", + "backup_database": "Crear unha copia da base de datos", + "backup_database_enable_description": "Activar a copia de seguridade da base de datos", + "backup_keep_last_amount": "Número de copias anteriores a conservar", + "backup_onboarding_1_description": "Copia externa na nube ou noutra localización física.", + "backup_onboarding_2_description": "Copias locais en diferentes dispositivos. Isto inclúe os arquivos principais e as copias deses arquivos localmente.", + "backup_onboarding_3_description": "Copias totais da súa información, incluíndo os arquivos orixinais. Isto inclúe 1 copia externa e 2 copias locais.", + "backup_onboarding_description": "Unha estratexia de copia de seguridade 3-2-1 é recomendada para protexer os seus datos. Debería gardar copias das súas fotos/vídeos subidos así como da base de datos de Immich como unha solución de seguridade.", + "backup_onboarding_footer": "Para máis información sobre copias de seguridade de Immich, por favor use a seguinte ligazón á documentación.", + "backup_onboarding_parts_title": "Unha copia de seguridade 3-2-1 inclúe:", "backup_onboarding_title": "Copia de seguridade", - "backup_settings": "Configuración da copia de seguridade", - "backup_settings_description": "Xestionar a configuración do volcado da base de datos", + "backup_settings": "Configuración da copia da base de datos", + "backup_settings_description": "Xestionar a configuración da copia da base de datos", "cleared_jobs": "Traballos borrados para: {job}", "config_set_by_file": "A configuración establécese actualmente mediante un ficheiro de configuración", - "confirm_delete_library": "Estás seguro de que queres eliminar a biblioteca {library}?", - "confirm_delete_library_assets": "Estás seguro de que queres eliminar esta biblioteca? Isto eliminará {count, plural, one {# activo contido} other {todos os # activos contidos}} de Immich e non se pode desfacer. Os ficheiros permanecerán no disco.", + "confirm_delete_library": "Está seguro de que quere eliminar a biblioteca {library}?", + "confirm_delete_library_assets": "Está seguro de que quere eliminar esta biblioteca? Isto eliminará {count, plural, one {# activo contido} other {todos os # activos contidos}} de Immich e non se pode desfacer. Os ficheiros permanecerán no disco.", "confirm_email_below": "Para confirmar, escriba \"{email}\" a continuación", - "confirm_reprocess_all_faces": "Estás seguro de que queres reprocesar todas as caras? Isto tamén borrará as persoas nomeadas.", - "confirm_user_password_reset": "Estás seguro de que queres restablecer o contrasinal de {user}?", - "confirm_user_pin_code_reset": "Estás seguro de que queres restablecer o PIN de {user}?", + "confirm_reprocess_all_faces": "Está seguro de que quere reprocesar todas as caras? Isto tamén borrará as persoas nomeadas.", + "confirm_user_password_reset": "Está seguro de que quere restablecer o contrasinal de {user}?", + "confirm_user_pin_code_reset": "Está seguro de que quere restablecer o PIN de {user}?", "create_job": "Crear traballo", "cron_expression": "Expresión Cron", "cron_expression_description": "Estableza o intervalo de escaneo usando o formato cron. Para obter máis información, consulte por exemplo Crontab Guru", "cron_expression_presets": "Preaxustes de expresión Cron", "disable_login": "Desactivar inicio de sesión", "duplicate_detection_job_description": "Executar aprendizaxe automática nos activos para detectar imaxes similares. Depende da Busca Intelixente", - "exclusion_pattern_description": "Os patróns de exclusión permítenche ignorar ficheiros e cartafoles ao escanear a túa biblioteca. Isto é útil se tes cartafoles que conteñen ficheiros que non queres importar, como ficheiros RAW.", + "exclusion_pattern_description": "Os patróns de exclusión permítenlle ignorar ficheiros e cartafoles ao escanear a súa biblioteca. Isto é útil se ten cartafoles que conteñen ficheiros que non quere importar, como ficheiros RAW.", "external_library_management": "Xestión da biblioteca externa", "face_detection": "Detección de caras", "face_detection_description": "Detectar as caras nos activos usando aprendizaxe automática. Para vídeos, só se considera a miniatura. \"Actualizar\" (re)procesa todos os activos. \"Restablecer\" ademais borra todos os datos de caras actuais. \"Faltantes\" pon en cola os activos que aínda non foron procesados. As caras detectadas poranse en cola para o Recoñecemento Facial despois de completar a Detección de Caras, agrupándoas en persoas existentes ou novas.", "facial_recognition_job_description": "Agrupar caras detectadas en persoas. Este paso execútase despois de completar a Detección de Caras. \"Restablecer\" (re)agrupa todas as caras. \"Faltantes\" pon en cola as caras que non teñen unha persoa asignada.", "failed_job_command": "O comando {command} fallou para o traballo: {job}", - "force_delete_user_warning": "AVISO: Isto eliminará inmediatamente o usuario e todos os activos. Isto non se pode desfacer e os ficheiros non se poden recuperar.", + "force_delete_user_warning": "AVISO: Isto eliminará inmediatamente o usuario e todos os seus activos. Esta acción non se pode desfacer e os ficheiros non se poderán recuperar.", "image_format": "Formato", "image_format_description": "WebP produce ficheiros máis pequenos que JPEG, pero é máis lento de codificar.", "image_fullsize_description": "Imaxe a tamaño completo con metadatos eliminados, usada ao facer zoom", "image_fullsize_enabled": "Activar a xeración de imaxes a tamaño completo", "image_fullsize_enabled_description": "Xerar imaxe a tamaño completo para formatos non compatibles coa web. Cando \"Preferir vista previa incrustada\" está activado, as vistas previas incrustadas utilízanse directamente sen conversión. Non afecta a formatos compatibles coa web como JPEG.", - "image_fullsize_quality_description": "Calidade da imaxe a tamaño completo de 1 a 100. Máis alto é mellor, pero produce ficheiros máis grandes.", + "image_fullsize_quality_description": "Calidade da imaxe a tamaño completo de 1 a 100. Canto máis alto, mellor, pero produce ficheiros máis grandes.", "image_fullsize_title": "Configuración da imaxe a tamaño completo", "image_prefer_embedded_preview": "Preferir vista previa incrustada", - "image_prefer_embedded_preview_setting_description": "Usar vistas previas incrustadas en fotos RAW como entrada para o procesamento de imaxes e cando estean dispoñibles. Isto pode producir cores máis precisas para algunhas imaxes, pero a calidade da vista previa depende da cámara e a imaxe pode ter máis artefactos de compresión.", - "image_prefer_wide_gamut": "Preferir gama ampla", + "image_prefer_embedded_preview_setting_description": "Usar vistas previas incrustadas en fotos RAW como entrada para o procesamento de imaxes cando estean dispoñibles. Isto pode producir cores máis precisas para algunhas imaxes, pero a calidade da vista previa depende da cámara e a imaxe pode ter máis artefactos de compresión.", + "image_prefer_wide_gamut": "Preferir gama de cores ampla", "image_prefer_wide_gamut_setting_description": "Usar Display P3 para as miniaturas. Isto preserva mellor a viveza das imaxes con espazos de cor amplos, pero as imaxes poden aparecer de forma diferente en dispositivos antigos cunha versión de navegador antiga. As imaxes sRGB mantéñense como sRGB para evitar cambios de cor.", "image_preview_description": "Imaxe de tamaño medio con metadatos eliminados, usada ao ver un único activo e para aprendizaxe automática", - "image_preview_quality_description": "Calidade da vista previa de 1 a 100. Máis alto é mellor, pero produce ficheiros máis grandes e pode reducir a capacidade de resposta da aplicación. Establecer un valor baixo pode afectar á calidade da aprendizaxe automática.", + "image_preview_quality_description": "Calidade da vista previa de 1 a 100. Canto máis alto, mellor, pero produce ficheiros máis grandes e pode reducir a capacidade de resposta da aplicación. Establecer un valor baixo pode afectar á calidade da aprendizaxe automática.", "image_preview_title": "Configuración da vista previa", "image_quality": "Calidade", "image_resolution": "Resolución", @@ -98,11 +99,11 @@ "image_settings": "Configuración da imaxe", "image_settings_description": "Xestionar a calidade e resolución das imaxes xeradas", "image_thumbnail_description": "Miniatura pequena con metadatos eliminados, usada ao ver grupos de fotos como a liña de tempo principal", - "image_thumbnail_quality_description": "Calidade da miniatura de 1 a 100. Máis alto é mellor, pero produce ficheiros máis grandes e pode reducir a capacidade de resposta da aplicación.", + "image_thumbnail_quality_description": "Calidade da miniatura de 1 a 100. Canto máis alto, mellor, pero produce ficheiros máis grandes e pode reducir a capacidade de resposta da aplicación.", "image_thumbnail_title": "Configuración da miniatura", "job_concurrency": "concorrencia de {job}", "job_created": "Traballo creado", - "job_not_concurrency_safe": "Este traballo non é seguro para concorrencia.", + "job_not_concurrency_safe": "Este traballo non é seguro para execución concorrente.", "job_settings": "Configuración de traballos", "job_settings_description": "Xestionar a concorrencia de traballos", "job_status": "Estado do traballo", @@ -117,31 +118,31 @@ "library_settings": "Biblioteca externa", "library_settings_description": "Xestionar a configuración da biblioteca externa", "library_tasks_description": "Escanear bibliotecas externas en busca de activos novos e/ou modificados", - "library_watching_enable_description": "Vixiar bibliotecas externas para cambios nos ficheiros", + "library_watching_enable_description": "Vixiar bibliotecas externas para detectar cambios nos ficheiros", "library_watching_settings": "Vixilancia da biblioteca (EXPERIMENTAL)", "library_watching_settings_description": "Vixiar automaticamente os ficheiros modificados", "logging_enable_description": "Activar rexistro", "logging_level_description": "Cando estea activado, que nivel de rexistro usar.", "logging_settings": "Rexistro", "machine_learning_availability_checks": "Comprobacións de dispoñibilidade", - "machine_learning_availability_checks_description": "Detectar automáticamente e preferir servidores de aprendizaxe profunda dispoñibles", + "machine_learning_availability_checks_description": "Detectar automaticamente e preferir servidores de aprendizaxe automática dispoñibles", "machine_learning_availability_checks_enabled": "Activar comprobacións de dispoñibilidade", "machine_learning_availability_checks_interval": "Intervalo de comprobación", "machine_learning_availability_checks_interval_description": "Intervalo en milisegundos entre comprobacións de dispoñibilidade", "machine_learning_availability_checks_timeout": "Tempo de espera da solicitude", - "machine_learning_availability_checks_timeout_description": "Tempo de espera en milisegundos para as comprobación de dispoñibilidade", + "machine_learning_availability_checks_timeout_description": "Tempo de espera en milisegundos para as comprobacións de dispoñibilidade", "machine_learning_clip_model": "Modelo CLIP", - "machine_learning_clip_model_description": "O nome dun modelo CLIP listado aquí. Ten en conta que debe volver executar o traballo 'Busca Intelixente' para todas as imaxes ao cambiar un modelo.", + "machine_learning_clip_model_description": "O nome dun modelo CLIP listado aquí. Teña en conta que debe volver executar o traballo 'Busca Intelixente' para todas as imaxes ao cambiar un modelo.", "machine_learning_duplicate_detection": "Detección de duplicados", "machine_learning_duplicate_detection_enabled": "Activar detección de duplicados", - "machine_learning_duplicate_detection_enabled_description": "Se está desactivado, os activos exactamente idénticos aínda se eliminarán duplicados.", + "machine_learning_duplicate_detection_enabled_description": "Se está desactivado, os activos exactamente idénticos aínda se eliminarán.", "machine_learning_duplicate_detection_setting_description": "Usar incrustacións CLIP para atopar posibles duplicados", "machine_learning_enabled": "Activar aprendizaxe automática", - "machine_learning_enabled_description": "Se está desactivado, todas as funcións de ML desactivaranse independentemente da configuración a continuación.", + "machine_learning_enabled_description": "Se está desactivado, todas as funcións de aprendizaxe automática desactivaranse independentemente da configuración a continuación.", "machine_learning_facial_recognition": "Recoñecemento facial", "machine_learning_facial_recognition_description": "Detectar, recoñecer e agrupar caras en imaxes", "machine_learning_facial_recognition_model": "Modelo de recoñecemento facial", - "machine_learning_facial_recognition_model_description": "Os modelos están listados en orde descendente de tamaño. Os modelos máis grandes son máis lentos e usan máis memoria, pero producen mellores resultados. Teña en conta que debes volver executar o traballo de Detección de Caras para todas as imaxes ao cambiar un modelo.", + "machine_learning_facial_recognition_model_description": "Os modelos están listados en orde descendente de tamaño. Os modelos máis grandes son máis lentos e usan máis memoria, pero producen mellores resultados. Teña en conta que debe volver executar o traballo de Detección de Caras para todas as imaxes ao cambiar un modelo.", "machine_learning_facial_recognition_setting": "Activar recoñecemento facial", "machine_learning_facial_recognition_setting_description": "Se está desactivado, as imaxes non se codificarán para o recoñecemento facial e non encherán a sección Persoas na páxina Explorar.", "machine_learning_max_detection_distance": "Distancia máxima de detección", @@ -188,21 +189,22 @@ "nightly_tasks_cluster_new_faces_setting": "Agrupar novas caras", "nightly_tasks_database_cleanup_setting": "Tarefas de limpeza da base de datos", "nightly_tasks_database_cleanup_setting_description": "Limpar información vella e obsoleta da base de datos", - "nightly_tasks_generate_memories_setting": "Xerar memorias", - "nightly_tasks_generate_memories_setting_description": "Crear novas memorias dende os recursos", + "nightly_tasks_generate_memories_setting": "Xerar recordos", + "nightly_tasks_generate_memories_setting_description": "Crear novos recordos a partir dos activos", "nightly_tasks_missing_thumbnails_setting": "Xerar as miniaturas que faltan", - "nightly_tasks_missing_thumbnails_setting_description": "Encolar arquivos sin miniaturas para a xeración das miniaturas", + "nightly_tasks_missing_thumbnails_setting_description": "Poñer en cola os arquivos sen miniaturas para a súa xeración", "nightly_tasks_settings": "Configuración das tarefas nocturnas", "nightly_tasks_settings_description": "Administrar as tarefas nocturnas", - "nightly_tasks_start_time_setting": "Tempo de inicio", - "nightly_tasks_start_time_setting_description": "O tempo no que o servidor comeza a executar as tarefas nocturnas", - "nightly_tasks_sync_quota_usage_setting": "Sincronizar uso de cuota", + "nightly_tasks_start_time_setting": "Hora de inicio", + "nightly_tasks_start_time_setting_description": "A hora na que o servidor comeza a executar as tarefas nocturnas", + "nightly_tasks_sync_quota_usage_setting": "Sincronizar uso de cota", + "nightly_tasks_sync_quota_usage_setting_description": "Actualizar a cota de almacenamento do usuario, en base ao uso actual", "no_paths_added": "Non se engadiron rutas", - "no_pattern_added": "Non se engadiu ningún padrón", + "no_pattern_added": "Non se engadiu ningún patrón", "note_apply_storage_label_previous_assets": "Nota: Para aplicar a Etiqueta de Almacenamento a activos cargados previamente, execute o", "note_cannot_be_changed_later": "NOTA: Isto non se pode cambiar máis tarde!", "notification_email_from_address": "Enderezo do remitente", - "notification_email_from_address_description": "Enderezo de correo electrónico do remitente, por exemplo: \"Servidor de Fotos Immich \"", + "notification_email_from_address_description": "Enderezo de correo do remitente, por exemplo: \"Servidor de Fotos Immich noreply@exemplo.com\". Asegúrese de empregar un enderezo dende o que teña permiso para enviar correos.", "notification_email_host_description": "Host do servidor de correo electrónico (p. ex. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ignorar erros de certificado", "notification_email_ignore_certificate_errors_description": "Ignorar erros de validación do certificado TLS (non recomendado)", @@ -211,8 +213,8 @@ "notification_email_sent_test_email_button": "Enviar correo de proba e gardar", "notification_email_setting_description": "Configuración para enviar notificacións por correo electrónico", "notification_email_test_email": "Enviar correo de proba", - "notification_email_test_email_failed": "Erro ao enviar correo de proba, comproba os teus valores", - "notification_email_test_email_sent": "Enviouse un correo electrónico de proba a {email}. Por favor, comproba a túa caixa de entrada.", + "notification_email_test_email_failed": "Erro ao enviar correo de proba, comprobe os seus valores", + "notification_email_test_email_sent": "Enviouse un correo electrónico de proba a {email}. Por favor, comprobe a súa caixa de entrada.", "notification_email_username_description": "Nome de usuario a usar ao autenticarse co servidor de correo electrónico", "notification_enable_email_notifications": "Activar notificacións por correo electrónico", "notification_settings": "Configuración de Notificacións", @@ -222,10 +224,13 @@ "oauth_auto_register": "Rexistro automático", "oauth_auto_register_description": "Rexistrar automaticamente novos usuarios despois de iniciar sesión con OAuth", "oauth_button_text": "Texto do botón", + "oauth_client_secret_description": "Requirido se o provedor OAuth non admite PKCE (Proof Key for Code Exchange)", "oauth_enable_description": "Iniciar sesión con OAuth", "oauth_mobile_redirect_uri": "URI de redirección móbil", "oauth_mobile_redirect_uri_override": "Substitución de URI de redirección móbil", "oauth_mobile_redirect_uri_override_description": "Activar cando o provedor OAuth non permite un URI móbil, como ''{callback}''", + "oauth_role_claim": "Declaración de rol", + "oauth_role_claim_description": "Conceder acceso de administrador automaticamente segundo a presenza desta declaración. A declaración pode ter os valores 'user' ou 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Xestionar a configuración de inicio de sesión OAuth", "oauth_settings_more_details": "Para máis detalles sobre esta función, consulte a documentación.", @@ -234,7 +239,9 @@ "oauth_storage_quota_claim": "Declaración de cota de almacenamento", "oauth_storage_quota_claim_description": "Establecer automaticamente a cota de almacenamento do usuario ao valor desta declaración.", "oauth_storage_quota_default": "Cota de almacenamento predeterminada (GiB)", - "oauth_storage_quota_default_description": "Cota en GiB a usar cando non se proporciona ningunha declaración (Introduza 0 para cota ilimitada).", + "oauth_storage_quota_default_description": "Cota en GiB a empregar cando non se proporciona ningunha declaración.", + "oauth_timeout": "Tempo máximo de espera da solicitude", + "oauth_timeout_description": "Tempo máximo de espera para as solicitudes en milisegundos", "password_enable_description": "Iniciar sesión con correo electrónico e contrasinal", "password_settings": "Inicio de sesión con contrasinal", "password_settings_description": "Xestionar a configuración de inicio de sesión con contrasinal", @@ -243,17 +250,17 @@ "quota_size_gib": "Tamaño da cota (GiB)", "refreshing_all_libraries": "Actualizando todas as bibliotecas", "registration": "Rexistro do administrador", - "registration_description": "Dado que ti es o primeiro usuario no sistema, asignarásete como Administrador e serás responsable das tarefas administrativas, e os usuarios adicionais serán creados por ti.", + "registration_description": "Dado que vostede é o primeiro usuario no sistema, asignaráselle como Administrador e será responsable das tarefas administrativas. Os usuarios adicionais serán creados por vostede.", "require_password_change_on_login": "Requirir que o usuario cambie o contrasinal no primeiro inicio de sesión", "reset_settings_to_default": "Restablecer a configuración aos valores predeterminados", - "reset_settings_to_recent_saved": "Restablecer a configuración á configuración gardada recentemente", + "reset_settings_to_recent_saved": "Restablecer á configuración gardada recentemente", "scanning_library": "Escaneando biblioteca", "search_jobs": "Buscar traballos…", "send_welcome_email": "Enviar correo electrónico de benvida", "server_external_domain_settings": "Dominio externo", "server_external_domain_settings_description": "Dominio para ligazóns públicas compartidas, incluíndo http(s)://", "server_public_users": "Usuarios públicos", - "server_public_users_description": "Todos os usuarios (nome e correo electrónico) listanse ao engadir un usuario a álbums compartidos. Cando está desactivado, a lista de usuarios só estará dispoñible para os usuarios administradores.", + "server_public_users_description": "Todos os usuarios (nome e correo electrónico) lístanse ao engadir un usuario a álbums compartidos. Cando está desactivado, a lista de usuarios só estará dispoñible para os usuarios administradores.", "server_settings": "Configuración do servidor", "server_settings_description": "Xestionar a configuración do servidor", "server_welcome_message": "Mensaxe de benvida", @@ -266,19 +273,20 @@ "storage_template_date_time_sample": "Tempo de mostra {date}", "storage_template_enable_description": "Activar o motor de modelos de almacenamento", "storage_template_hash_verification_enabled": "Verificación de hash activada", - "storage_template_hash_verification_enabled_description": "Activa a verificación de hash, non desactives isto a menos que esteas seguro das implicacións", + "storage_template_hash_verification_enabled_description": "Activa a verificación de hash. Non desactive isto a menos que estea seguro das implicacións.", "storage_template_migration": "Migración do modelo de almacenamento", "storage_template_migration_description": "Aplicar o {template} actual aos activos cargados previamente", "storage_template_migration_info": "O modelo de almacenamento converterá todas as extensións a minúsculas. Os cambios no modelo só se aplicarán aos activos novos. Para aplicar retroactivamente o modelo aos activos cargados previamente, execute o {job}.", "storage_template_migration_job": "Traballo de Migración do Modelo de Almacenamento", "storage_template_more_details": "Para máis detalles sobre esta función, consulte o Modelo de Almacenamento e as súas implicacións", + "storage_template_onboarding_description_v2": "Cando está activada, esta función organizará automaticamente os ficheiros segundo un modelo definido polo usuario. Para máis información, consulte a documentación.", "storage_template_path_length": "Límite aproximado da lonxitude da ruta: {length, number}/{limit, number}", "storage_template_settings": "Modelo de Almacenamento", "storage_template_settings_description": "Xestionar a estrutura de cartafoles e o nome de ficheiro do activo cargado", "storage_template_user_label": "{label} é a Etiqueta de Almacenamento do usuario", "system_settings": "Configuración do Sistema", "tag_cleanup_job": "Limpeza de etiquetas", - "template_email_available_tags": "Podes usar as seguintes variables no teu modelo: {tags}", + "template_email_available_tags": "Pode usar as seguintes variables no seu modelo: {tags}", "template_email_if_empty": "Se o modelo está baleiro, usarase o correo electrónico predeterminado.", "template_email_invite_album": "Modelo de Invitación a Álbum", "template_email_preview": "Vista previa", @@ -286,15 +294,15 @@ "template_email_update_album": "Modelo de Actualización de Álbum", "template_email_welcome": "Modelo de correo electrónico de benvida", "template_settings": "Modelos de Notificación", - "template_settings_description": "Xestionar modelos personalizados para notificacións.", + "template_settings_description": "Xestionar modelos personalizados para notificacións", "theme_custom_css_settings": "CSS Personalizado", - "theme_custom_css_settings_description": "As Follas de Estilo en Cascada permiten personalizar o deseño de Immich.", + "theme_custom_css_settings_description": "As Follas de Estilo en Cascada (CSS) permiten personalizar o deseño de Immich.", "theme_settings": "Configuración do Tema", "theme_settings_description": "Xestionar a personalización da interface web de Immich", "thumbnail_generation_job": "Xerar Miniaturas", "thumbnail_generation_job_description": "Xerar miniaturas grandes, pequenas e borrosas para cada activo, así como miniaturas para cada persoa", "transcoding_acceleration_api": "API de aceleración", - "transcoding_acceleration_api_description": "A API que interactuará co teu dispositivo para acelerar a transcodificación. Esta configuración é de 'mellor esforzo': recurrirá á transcodificación por software en caso de fallo. VP9 pode funcionar ou non dependendo do teu hardware.", + "transcoding_acceleration_api_description": "A API que interactuará co seu dispositivo para acelerar a transcodificación. Esta configuración é de 'mellor esforzo': recurrirá á transcodificación por software en caso de fallo. VP9 pode funcionar ou non dependendo do seu hardware.", "transcoding_acceleration_nvenc": "NVENC (require GPU NVIDIA)", "transcoding_acceleration_qsv": "Quick Sync (require CPU Intel de 7ª xeración ou posterior)", "transcoding_acceleration_rkmpp": "RKMPP (só en SOCs Rockchip)", @@ -309,16 +317,16 @@ "transcoding_audio_codec": "Códec de audio", "transcoding_audio_codec_description": "Opus é a opción de maior calidade, pero ten menor compatibilidade con dispositivos ou software antigos.", "transcoding_bitrate_description": "Vídeos cun bitrate superior ao máximo ou que non estean nun formato aceptado", - "transcoding_codecs_learn_more": "Para saber máis sobre a terminoloxía usada aquí, consulte a documentación de FFmpeg para códec H.264, códec HEVC e códec VP9.", + "transcoding_codecs_learn_more": "Para saber máis sobre a terminoloxía usada aquí, consulte a documentación de FFmpeg para o códec H.264, o códec HEVC e o códec VP9.", "transcoding_constant_quality_mode": "Modo de calidade constante", "transcoding_constant_quality_mode_description": "ICQ é mellor que CQP, pero algúns dispositivos de aceleración por hardware non admiten este modo. Establecer esta opción preferirá o modo especificado ao usar codificación baseada na calidade. Ignorado por NVENC xa que non admite ICQ.", "transcoding_constant_rate_factor": "Factor de taxa constante (-crf)", - "transcoding_constant_rate_factor_description": "Nivel de calidade do vídeo. Valores típicos son 23 para H.264, 28 para HEVC, 31 para VP9 e 35 para AV1. Máis baixo é mellor, pero produce ficheiros máis grandes.", - "transcoding_disabled_description": "Non transcodificar ningún vídeo, pode romper a reprodución nalgúns clientes", + "transcoding_constant_rate_factor_description": "Nivel de calidade do vídeo. Valores típicos son 23 para H.264, 28 para HEVC, 31 para VP9 e 35 para AV1. Canto máis baixo, mellor, pero produce ficheiros máis grandes.", + "transcoding_disabled_description": "Non transcodificar ningún vídeo; pode romper a reprodución nalgúns clientes", "transcoding_encoding_options": "Opcións de Codificación", "transcoding_encoding_options_description": "Establecer códecs, resolución, calidade e outras opcións para os vídeos codificados", "transcoding_hardware_acceleration": "Aceleración por Hardware", - "transcoding_hardware_acceleration_description": "Experimental; moito máis rápido, pero terá menor calidade co mesmo bitrate", + "transcoding_hardware_acceleration_description": "Experimental: transcodificación máis rápida, pero pode reducir a calidade ao mesmo bitrate", "transcoding_hardware_decoding": "Decodificación por hardware", "transcoding_hardware_decoding_setting_description": "Activa a aceleración de extremo a extremo en lugar de só acelerar a codificación. Pode non funcionar en todos os vídeos.", "transcoding_max_b_frames": "Máximo de B-frames", @@ -350,7 +358,7 @@ "transcoding_transcode_policy": "Política de transcodificación", "transcoding_transcode_policy_description": "Política para cando un vídeo debe ser transcodificado. Os vídeos HDR sempre serán transcodificados (excepto se a transcodificación está desactivada).", "transcoding_two_pass_encoding": "Codificación en dous pasos", - "transcoding_two_pass_encoding_setting_description": "Transcodificar en dous pasos para producir vídeos codificados mellor. Cando o bitrate máximo está activado (requirido para que funcione con H.264 e HEVC), este modo usa un rango de bitrate baseado no bitrate máximo e ignora CRF. Para VP9, pódese usar CRF se o bitrate máximo está desactivado.", + "transcoding_two_pass_encoding_setting_description": "Transcodificar en dous pasos para producir vídeos codificados de mellor calidade. Cando o bitrate máximo está activado (requirido para que funcione con H.264 e HEVC), este modo usa un rango de bitrate baseado no bitrate máximo e ignora CRF. Para VP9, pódese usar CRF se o bitrate máximo está desactivado.", "transcoding_video_codec": "Códec de vídeo", "transcoding_video_codec_description": "VP9 ten alta eficiencia e compatibilidade web, pero tarda máis en transcodificarse. HEVC ten un rendemento similar, pero ten menor compatibilidade web. H.264 é amplamente compatible e rápido de transcodificar, pero produce ficheiros moito máis grandes. AV1 é o códec máis eficiente pero carece de soporte en dispositivos máis antigos.", "trash_enabled_description": "Activar funcións do Lixo", @@ -358,6 +366,9 @@ "trash_number_of_days_description": "Número de días para manter os activos no lixo antes de eliminalos permanentemente", "trash_settings": "Configuración do Lixo", "trash_settings_description": "Xestionar a configuración do lixo", + "unlink_all_oauth_accounts": "Desvincular todas as contas OAuth", + "unlink_all_oauth_accounts_description": "Lembre desvincular todas as contas OAuth antes de migrar a un novo provedor.", + "unlink_all_oauth_accounts_prompt": "Está seguro de que quere desvincular todas as contas OAuth? Isto reiniciará o ID de OAuth de cada usuario e non se poderá desfacer.", "user_cleanup_job": "Limpeza de usuarios", "user_delete_delay": "A conta e os activos de {user} programaranse para a súa eliminación permanente en {delay, plural, one {# día} other {# días}}.", "user_delete_delay_settings": "Atraso na eliminación", @@ -384,13 +395,15 @@ "admin_password": "Contrasinal do administrador", "administration": "Administración", "advanced": "Avanzado", - "advanced_settings_enable_alternate_media_filter_subtitle": "Usa esta opción para filtrar medios durante a sincronización baseándose en criterios alternativos. Só proba isto se tes problemas coa aplicación detectando todos os álbums.", + "advanced_settings_enable_alternate_media_filter_subtitle": "Use esta opción para filtrar medios durante a sincronización baseándose en criterios alternativos. Só probe isto se ten problemas coa aplicación para detectar todos os álbums.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Usar filtro alternativo de sincronización de álbums do dispositivo", "advanced_settings_log_level_title": "Nivel de rexistro: {level}", - "advanced_settings_prefer_remote_subtitle": "Algúns dispositivos son extremadamente lentos para cargar miniaturas de activos no dispositivo. Active esta configuración para cargar imaxes remotas no seu lugar.", + "advanced_settings_prefer_remote_subtitle": "Algúns dispositivos tardan moito en cargar as miniaturas de ficheiros locais. Active esta opción para cargar imaxes remotas no seu lugar.", "advanced_settings_prefer_remote_title": "Preferir imaxes remotas", "advanced_settings_proxy_headers_subtitle": "Definir cabeceiras de proxy que Immich debería enviar con cada solicitude de rede", "advanced_settings_proxy_headers_title": "Cabeceiras de Proxy", + "advanced_settings_readonly_mode_subtitle": "Activa o modo de só lectura, no que as fotos só se poden visualizar; opcións como seleccionar varias imaxes, compartir, enviar a outros dispositivos ou eliminar están deshabilitadas. Active/desactive o modo de só lectura a través do avatar do usuario na pantalla principal", + "advanced_settings_readonly_mode_title": "Modo de só lectura", "advanced_settings_self_signed_ssl_subtitle": "Omite a verificación do certificado SSL para o punto final do servidor. Requirido para certificados autofirmados.", "advanced_settings_self_signed_ssl_title": "Permitir certificados SSL autofirmados", "advanced_settings_sync_remote_deletions_subtitle": "Eliminar ou restaurar automaticamente un activo neste dispositivo cando esa acción se realiza na web", @@ -398,32 +411,35 @@ "advanced_settings_tile_subtitle": "Configuración de usuario avanzado", "advanced_settings_troubleshooting_subtitle": "Activar funcións adicionais para a resolución de problemas", "advanced_settings_troubleshooting_title": "Resolución de problemas", - "age_months": "Idade {months, plural, one {# mes} other {# meses}}", - "age_year_months": "Idade 1 ano, {months, plural, one {# mes} other {# meses}}", - "age_years": "{years, plural, other {Idade #}}", + "age_months": "Idade: {months, plural, one {# mes} other {# meses}}", + "age_year_months": "Idade: 1 ano e {months, plural, one {# mes} other {# meses}}", + "age_years": "Idade: {years, plural, one {# ano} other {# anos}}", "album_added": "Álbum engadido", - "album_added_notification_setting_description": "Recibir unha notificación por correo electrónico cando sexas engadido a un álbum compartido", + "album_added_notification_setting_description": "Recibir unha notificación por correo electrónico cando sexa engadido a un álbum compartido", "album_cover_updated": "Portada do álbum actualizada", - "album_delete_confirmation": "Estás seguro de que queres eliminar o álbum {album}?", + "album_delete_confirmation": "Está seguro de que quere eliminar o álbum {album}?", "album_delete_confirmation_description": "Se este álbum está compartido, outros usuarios non poderán acceder a el.", + "album_deleted": "Álbum eliminado", "album_info_card_backup_album_excluded": "EXCLUÍDO", "album_info_card_backup_album_included": "INCLUÍDO", "album_info_updated": "Información do álbum actualizada", "album_leave": "Saír do álbum?", - "album_leave_confirmation": "Estás seguro de que queres saír de {album}?", + "album_leave_confirmation": "Está seguro de que quere saír de {album}?", "album_name": "Nome do Álbum", "album_options": "Opcións do álbum", "album_remove_user": "Eliminar usuario?", - "album_remove_user_confirmation": "Estás seguro de que queres eliminar a {user}?", - "album_share_no_users": "Parece que compartiches este álbum con todos os usuarios ou non tes ningún usuario co que compartir.", + "album_remove_user_confirmation": "Está seguro de que quere eliminar a {user}?", + "album_search_not_found": "Non se atoparon álbums que coincidan coa súa busca", + "album_share_no_users": "Parece que compartiu este álbum con todos os usuarios ou non ten ningún usuario co que compartir.", + "album_summary": "Resumo do álbum", "album_updated": "Álbum actualizado", "album_updated_setting_description": "Recibir unha notificación por correo electrónico cando un álbum compartido teña novos activos", "album_user_left": "Saíu de {album}", - "album_user_removed": "Eliminado {user}", - "album_viewer_appbar_delete_confirm": "Estás seguro de que queres eliminar este álbum da túa conta?", + "album_user_removed": "Eliminouse a {user}", + "album_viewer_appbar_delete_confirm": "Está seguro de que quere eliminar este álbum da súa conta?", "album_viewer_appbar_share_err_delete": "Erro ao eliminar o álbum", "album_viewer_appbar_share_err_leave": "Erro ao saír do álbum", - "album_viewer_appbar_share_err_remove": "Hai problemas ao eliminar activos do álbum", + "album_viewer_appbar_share_err_remove": "Houbo problemas ao eliminar activos do álbum", "album_viewer_appbar_share_err_title": "Erro ao cambiar o título do álbum", "album_viewer_appbar_share_leave": "Saír do álbum", "album_viewer_appbar_share_to": "Compartir con", @@ -431,6 +447,10 @@ "album_with_link_access": "Permitir que calquera persoa coa ligazón vexa fotos e persoas neste álbum.", "albums": "Álbums", "albums_count": "{count, plural, one {{count, number} Álbum} other {{count, number} Álbums}}", + "albums_default_sort_order": "Orde de clasificación predeterminada do álbum", + "albums_default_sort_order_description": "Orde inicial dos ficheiros ao crear novos álbums.", + "albums_feature_description": "Coleccións de ficheiros que se poden compartir con outros usuarios.", + "albums_on_device_count": "Álbums no dispositivo ({count})", "all": "Todo", "all_albums": "Todos os álbums", "all_people": "Todas as persoas", @@ -445,12 +465,14 @@ "api_key_description": "Este valor só se mostrará unha vez. Asegúrese de copialo antes de pechar a xanela.", "api_key_empty": "O nome da súa chave API non pode estar baleiro", "api_keys": "Chaves API", - "app_bar_signout_dialog_content": "Estás seguro de que queres pechar sesión?", + "app_bar_signout_dialog_content": "Está seguro de que quere pechar sesión?", "app_bar_signout_dialog_ok": "Si", "app_bar_signout_dialog_title": "Pechar sesión", "app_settings": "Configuración da Aplicación", "appears_in": "Aparece en", + "apply_count": "Aplicar ({count, number})", "archive": "Arquivo", + "archive_action_prompt": "{count} engadido(s) ao Arquivo", "archive_or_unarchive_photo": "Arquivar ou desarquivar foto", "archive_page_no_archived_assets": "Non se atoparon activos arquivados", "archive_page_title": "Arquivo ({count})", @@ -459,14 +481,14 @@ "archived": "Arquivado", "archived_count": "{count, plural, other {Arquivados #}}", "are_these_the_same_person": "Son estas a mesma persoa?", - "are_you_sure_to_do_this": "Estás seguro de que queres facer isto?", + "are_you_sure_to_do_this": "Está seguro de que quere facer isto?", "asset_action_delete_err_read_only": "Non se poden eliminar activo(s) de só lectura, omitindo", "asset_action_share_err_offline": "Non se poden obter activo(s) fóra de liña, omitindo", "asset_added_to_album": "Engadido ao álbum", "asset_adding_to_album": "Engadindo ao álbum…", "asset_description_updated": "A descrición do activo actualizouse", "asset_filename_is_offline": "O activo {filename} está fóra de liña", - "asset_has_unassigned_faces": "O activo ten caras non asignadas", + "asset_has_unassigned_faces": "O activo ten caras sen asignar", "asset_hashing": "Calculando hash…", "asset_list_group_by_sub_title": "Agrupar por", "asset_list_layout_settings_dynamic_layout_title": "Deseño dinámico", @@ -477,59 +499,71 @@ "asset_list_settings_subtitle": "Configuración do deseño da grella de fotos", "asset_list_settings_title": "Grella de Fotos", "asset_offline": "Activo Fóra de Liña", - "asset_offline_description": "Este activo externo xa non se atopa no disco. Por favor, contacta co teu administrador de Immich para obter axuda.", + "asset_offline_description": "Este activo externo xa non se atopa no disco. Por favor, contacte co seu administrador de Immich para obter axuda.", "asset_restored_successfully": "Activo restaurado correctamente", "asset_skipped": "Omitido", "asset_skipped_in_trash": "No lixo", + "asset_trashed": "Ficheiro enviado ao lixo", + "asset_troubleshoot": "Solución de problemas do ficheiro", "asset_uploaded": "Subido", "asset_uploading": "Subindo…", - "asset_viewer_settings_subtitle": "Xestionar a túa configuración do visor da galería", + "asset_viewer_settings_subtitle": "Xestionar a súa configuración do visor da galería", "asset_viewer_settings_title": "Visor de Activos", "assets": "Activos", "assets_added_count": "Engadido {count, plural, one {# activo} other {# activos}}", "assets_added_to_album_count": "Engadido {count, plural, one {# activo} other {# activos}} ao álbum", + "assets_added_to_albums_count": "Engadido {assetTotal, plural, one {# ficheiro} other {# ficheiros}} a {albumTotal, plural, one {# álbum} other {# álbums}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {O ficheiro non se pode engadir} other {Os ficheiros non se poden engadir}} ao álbum", + "assets_cannot_be_added_to_albums": "{count, plural, one {O ficheiro non se pode engadir} other {Os ficheiros non se poden engadir}} a ningún dos álbums", "assets_count": "{count, plural, one {# activo} other {# activos}}", "assets_deleted_permanently": "{count} activo(s) eliminado(s) permanentemente", "assets_deleted_permanently_from_server": "{count} activo(s) eliminado(s) permanentemente do servidor Immich", + "assets_downloaded_failed": "{count, plural, one {Descargouse # ficheiro - fallou 1 ficheiro} other {Descargáronse # ficheiros - fallaron {error} ficheiros}}", + "assets_downloaded_successfully": "{count, plural, one {Descargouse # ficheiro correctamente} other {Descargáronse # ficheiros correctamente}}", "assets_moved_to_trash_count": "Movido {count, plural, one {# activo} other {# activos}} ao lixo", "assets_permanently_deleted_count": "Eliminados permanentemente {count, plural, one {# activo} other {# activos}}", "assets_removed_count": "Eliminados {count, plural, one {# activo} other {# activos}}", - "assets_removed_permanently_from_device": "{count} activo(s) eliminado(s) permanentemente do teu dispositivo", - "assets_restore_confirmation": "Estás seguro de que queres restaurar todos os seus activos no lixo? Non podes desfacer esta acción! Ten en conta que calquera activo fóra de liña non pode ser restaurado desta maneira.", + "assets_removed_permanently_from_device": "{count} activo(s) eliminado(s) permanentemente do seu dispositivo", + "assets_restore_confirmation": "Está seguro de que quere restaurar todos os activos no lixo? Non pode desfacer esta acción! Teña en conta que calquera activo fóra de liña non pode ser restaurado desta maneira.", "assets_restored_count": "Restaurados {count, plural, one {# activo} other {# activos}}", "assets_restored_successfully": "{count} activo(s) restaurado(s) correctamente", "assets_trashed": "{count} activo(s) movido(s) ao lixo", "assets_trashed_count": "Movido {count, plural, one {# activo} other {# activos}} ao lixo", "assets_trashed_from_server": "{count} activo(s) movido(s) ao lixo desde o servidor Immich", "assets_were_part_of_album_count": "{count, plural, one {O activo xa era} other {Os activos xa eran}} parte do álbum", + "assets_were_part_of_albums_count": "{count, plural, one {O ficheiro xa estaba} other {Os ficheiros xa estaban}} neses álbums", "authorized_devices": "Dispositivos Autorizados", "automatic_endpoint_switching_subtitle": "Conectar localmente a través da wifi designada cando estea dispoñible e usar conexións alternativas noutros lugares", "automatic_endpoint_switching_title": "Cambio automático de URL", + "autoplay_slideshow": "Reprodución automática da presentación", "back": "Atrás", "back_close_deselect": "Atrás, pechar ou deseleccionar", - "background_location_permission": "Permiso de ubicación en segundo plano", - "background_location_permission_content": "Para cambiar de rede cando se executa en segundo plano, Immich debe ter *sempre* acceso á ubicación precisa para que a aplicación poida ler o nome da rede wifi", + "background_backup_running_error": "A copia de seguridade en segundo plano está en curso, non se pode iniciar unha copia manual", + "background_location_permission": "Permiso de localización en segundo plano", + "background_location_permission_content": "Para cambiar de rede cando se executa en segundo plano, Immich debe ter *sempre* acceso á localización precisa para que a aplicación poida ler o nome da rede wifi", + "background_options": "Opcións en segundo plano", "backup": "Copia de Seguridade", "backup_album_selection_page_albums_device": "Álbums no dispositivo ({count})", "backup_album_selection_page_albums_tap": "Tocar para incluír, dobre toque para excluír", - "backup_album_selection_page_assets_scatter": "Os activos poden dispersarse por varios álbums. Polo tanto, os álbums poden incluírse ou excluírse durante o proceso de copia de seguridade.", + "backup_album_selection_page_assets_scatter": "Os activos poden estar dispersos por varios álbums. Polo tanto, os álbums poden incluírse ou excluírse durante o proceso de copia de seguridade.", "backup_album_selection_page_select_albums": "Seleccionar álbums", "backup_album_selection_page_selection_info": "Información da selección", "backup_album_selection_page_total_assets": "Total de activos únicos", + "backup_albums_sync": "Sincronización de álbums da copia de seguridade", "backup_all": "Todo", "backup_background_service_backup_failed_message": "Erro ao facer copia de seguridade dos activos. Reintentando…", "backup_background_service_connection_failed_message": "Erro ao conectar co servidor. Reintentando…", "backup_background_service_current_upload_notification": "Subindo {filename}", "backup_background_service_default_notification": "Comprobando novos activos…", "backup_background_service_error_title": "Erro na copia de seguridade", - "backup_background_service_in_progress_notification": "Facendo copia de seguridade dos teus activos…", + "backup_background_service_in_progress_notification": "Facendo copia de seguridade dos seus activos…", "backup_background_service_upload_failure_notification": "Erro ao subir {filename}", "backup_controller_page_albums": "Álbums da Copia de Seguridade", "backup_controller_page_background_app_refresh_disabled_content": "Active a actualización de aplicacións en segundo plano en Axustes > Xeral > Actualización en segundo plano para usar a copia de seguridade en segundo plano.", "backup_controller_page_background_app_refresh_disabled_title": "Actualización de aplicacións en segundo plano desactivada", "backup_controller_page_background_app_refresh_enable_button_text": "Ir a axustes", - "backup_controller_page_background_battery_info_link": "Móstrame como", - "backup_controller_page_background_battery_info_message": "Para a mellor experiencia de copia de seguridade en segundo plano, desactiva calquera optimización de batería que restrinxa a actividade en segundo plano para Immich.\n\nDado que isto é específico do dispositivo, busque a información requirida para o fabricante do teu dispositivo.", + "backup_controller_page_background_battery_info_link": "Móstreme como", + "backup_controller_page_background_battery_info_message": "Para a mellor experiencia de copia de seguridade en segundo plano, desactive calquera optimización de batería que restrinxa a actividade en segundo plano para Immich.\n\nDado que isto é específico do dispositivo, busque a información requirida para o fabricante do seu dispositivo.", "backup_controller_page_background_battery_info_ok": "Aceptar", "backup_controller_page_background_battery_info_title": "Optimizacións da batería", "backup_controller_page_background_charging": "Só mentres se carga", @@ -565,29 +599,35 @@ "backup_controller_page_turn_on": "Activar copia de seguridade en primeiro plano", "backup_controller_page_uploading_file_info": "Subindo información do ficheiro", "backup_err_only_album": "Non se pode eliminar o único álbum", + "backup_error_sync_failed": "A sincronización fallou. Non se pode procesar a copia de seguridade.", "backup_info_card_assets": "activos", "backup_manual_cancelled": "Cancelado", - "backup_manual_in_progress": "Subida xa en progreso. Intenta despois dun tempo", + "backup_manual_in_progress": "Subida xa en progreso. Inténteo despois dun tempo", "backup_manual_success": "Éxito", "backup_manual_title": "Estado da subida", + "backup_options": "Opcións de copia de seguridade", "backup_options_page_title": "Opcións da copia de seguridade", "backup_setting_subtitle": "Xestionar a configuración de carga en segundo plano e primeiro plano", + "backup_settings_subtitle": "Xestionar configuración de subidas", "backward": "Atrás", "biometric_auth_enabled": "Autenticación biométrica activada", + "biometric_locked_out": "Está bloqueado da autenticación biométrica", + "biometric_no_options": "Non hai opcións biométricas dispoñibles", + "biometric_not_available": "A autenticación biométrica non está dispoñible neste dispositivo", "birthdate_saved": "Data de nacemento gardada correctamente", "birthdate_set_description": "A data de nacemento úsase para calcular a idade desta persoa no momento dunha foto.", "blurred_background": "Fondo borroso", "bugs_and_feature_requests": "Erros e Solicitudes de Funcións", "build": "Compilación", "build_image": "Construír Imaxe", - "bulk_delete_duplicates_confirmation": "Estás seguro de que queres eliminar masivamente {count, plural, one {# activo duplicado} other {# activos duplicados}}? Isto conservará o activo máis grande de cada grupo e eliminará permanentemente todos os demais duplicados. Non pode desfacer esta acción!", - "bulk_keep_duplicates_confirmation": "Estás seguro de que queres conservar {count, plural, one {# activo duplicado} other {# activos duplicados}}? Isto resolverá todos os grupos duplicados sen eliminar nada.", - "bulk_trash_duplicates_confirmation": "Estás seguro de que queres mover masivamente ao lixo {count, plural, one {# activo duplicado} other {# activos duplicados}}? Isto conservará o activo máis grande de cada grupo e moverá ao lixo todos os demais duplicados.", - "buy": "Comprar Immich", + "bulk_delete_duplicates_confirmation": "Está seguro de que quere eliminar masivamente {count, plural, one {# activo duplicado} other {# activos duplicados}}? Isto conservará o activo máis grande de cada grupo e eliminará permanentemente todos os demais duplicados. Non pode desfacer esta acción!", + "bulk_keep_duplicates_confirmation": "Está seguro de que quere conservar {count, plural, one {# activo duplicado} other {# activos duplicados}}? Isto resolverá todos os grupos duplicados sen eliminar nada.", + "bulk_trash_duplicates_confirmation": "Está seguro de que quere mover masivamente ao lixo {count, plural, one {# activo duplicado} other {# activos duplicados}}? Isto conservará o activo máis grande de cada grupo e moverá ao lixo todos os demais duplicados.", + "buy": "Apoiar Immich", "cache_settings_clear_cache_button": "Borrar caché", "cache_settings_clear_cache_button_title": "Borra a caché da aplicación. Isto afectará significativamente o rendemento da aplicación ata que a caché se reconstruíu.", "cache_settings_duplicated_assets_clear_button": "BORRAR", - "cache_settings_duplicated_assets_subtitle": "Fotos e vídeos que están na lista negra da aplicación", + "cache_settings_duplicated_assets_subtitle": "Fotos e vídeos que a aplicación ten na lista de ignorados", "cache_settings_duplicated_assets_title": "Activos Duplicados ({count})", "cache_settings_statistics_album": "Miniaturas da biblioteca", "cache_settings_statistics_full": "Imaxes completas", @@ -604,24 +644,31 @@ "cancel": "Cancelar", "cancel_search": "Cancelar busca", "canceled": "Cancelado", + "canceling": "Cancelando", "cannot_merge_people": "Non se poden fusionar persoas", "cannot_undo_this_action": "Non pode desfacer esta acción!", "cannot_update_the_description": "Non se pode actualizar a descrición", + "cast": "Enviar a dispositivo", + "cast_description": "Configurar destinos dispoñibles para enviar a dispositivo", "change_date": "Cambiar data", + "change_description": "Cambiar descrición", "change_display_order": "Cambiar orde de visualización", "change_expiration_time": "Cambiar hora de caducidade", - "change_location": "Cambiar ubicación", + "change_location": "Cambiar localización", "change_name": "Cambiar nome", "change_name_successfully": "Nome cambiado correctamente", "change_password": "Cambiar Contrasinal", - "change_password_description": "Esta é a primeira vez que inicias sesión no sistema ou solicitouse un cambio do teu contrasinal. Introduza o novo contrasinal a continuación.", + "change_password_description": "Esta é a primeira vez que inicia sesión no sistema ou solicitouse un cambio do seu contrasinal. Introduza o novo contrasinal a continuación.", "change_password_form_confirm_password": "Confirmar Contrasinal", - "change_password_form_description": "Ola {name},\n\nEsta é a primeira vez que inicias sesión no sistema ou solicitouse un cambio do teu contrasinal. Introduza o novo contrasinal a continuación.", + "change_password_form_description": "Ola {name},\n\nEsta é a primeira vez que inicia sesión no sistema ou solicitouse un cambio do seu contrasinal. Introduza o novo contrasinal a continuación.", "change_password_form_new_password": "Novo Contrasinal", "change_password_form_password_mismatch": "Os contrasinais non coinciden", "change_password_form_reenter_new_password": "Reintroducir Novo Contrasinal", - "change_your_password": "Cambiar o teu contrasinal", + "change_pin_code": "Cambiar código PIN", + "change_your_password": "Cambiar o seu contrasinal", "changed_visibility_successfully": "Visibilidade cambiada correctamente", + "charging": "Cargando", + "charging_requirement_mobile_backup": "A copia de seguridade en segundo plano require que o dispositivo estea cargando", "check_corrupt_asset_backup": "Comprobar copias de seguridade de activos corruptos", "check_corrupt_asset_backup_button": "Realizar comprobación", "check_corrupt_asset_backup_description": "Execute esta comprobación só a través da wifi e unha vez que todos os activos teñan copia de seguridade. O procedemento pode tardar uns minutos.", @@ -631,6 +678,7 @@ "clear": "Limpar", "clear_all": "Limpar todo", "clear_all_recent_searches": "Limpar todas as buscas recentes", + "clear_file_cache": "Limpar caché de ficheiros", "clear_message": "Limpar mensaxe", "clear_value": "Limpar valor", "client_cert_dialog_msg_confirm": "Aceptar", @@ -652,21 +700,26 @@ "comments_and_likes": "Comentarios e Gústames", "comments_are_disabled": "Os comentarios están desactivados", "common_create_new_album": "Crear novo álbum", - "common_server_error": "Por favor, comprobe a túa conexión de rede, asegúrache de que o servidor sexa accesible e que as versións da aplicación/servidor sexan compatibles.", + "common_server_error": "Por favor, comprobe a súa conexión de rede, asegúrese de que o servidor sexa accesible e que as versións da aplicación/servidor sexan compatibles.", "completed": "Completado", "confirm": "Confirmar", "confirm_admin_password": "Confirmar Contrasinal do Administrador", - "confirm_delete_face": "Estás seguro de que queres eliminar a cara de {name} do activo?", - "confirm_delete_shared_link": "Estás seguro de que queres eliminar esta ligazón compartida?", - "confirm_keep_this_delete_others": "Todos os demais activos na pila eliminaranse excepto este activo. Estás seguro de que queres continuar?", + "confirm_delete_face": "Está seguro de que quere eliminar a cara de {name} do activo?", + "confirm_delete_shared_link": "Está seguro de que quere eliminar esta ligazón compartida?", + "confirm_keep_this_delete_others": "Todos os demais activos na pila eliminaranse excepto este activo. Está seguro de que quere continuar?", + "confirm_new_pin_code": "Confirmar novo código PIN", "confirm_password": "Confirmar contrasinal", + "confirm_tag_face": "Quere etiquetar esta cara como {name}?", + "confirm_tag_face_unnamed": "Quere etiquetar esta cara?", + "connected_device": "Dispositivo conectado", + "connected_to": "Conectado a", "contain": "Conter", "context": "Contexto", "continue": "Continuar", "control_bottom_app_bar_create_new_album": "Crear novo álbum", "control_bottom_app_bar_delete_from_immich": "Eliminar de Immich", "control_bottom_app_bar_delete_from_local": "Eliminar do dispositivo", - "control_bottom_app_bar_edit_location": "Editar ubicación", + "control_bottom_app_bar_edit_location": "Editar localización", "control_bottom_app_bar_edit_time": "Editar Data e Hora", "control_bottom_app_bar_share_link": "Compartir Ligazón", "control_bottom_app_bar_share_to": "Compartir Con", @@ -696,19 +749,25 @@ "create_new_user": "Crear novo usuario", "create_shared_album_page_share_add_assets": "ENGADIR ACTIVOS", "create_shared_album_page_share_select_photos": "Seleccionar Fotos", + "create_shared_link": "Crear ligazón compartida", "create_tag": "Crear etiqueta", "create_tag_description": "Crear unha nova etiqueta. Para etiquetas aniñadas, introduza a ruta completa da etiqueta incluíndo barras inclinadas.", "create_user": "Crear usuario", "created": "Creado", + "created_at": "Creado", + "creating_linked_albums": "Creando álbums vinculados...", "crop": "Recortar", "curated_object_page_title": "Cousas", "current_device": "Dispositivo actual", + "current_pin_code": "Código PIN actual", "current_server_address": "Enderezo do servidor actual", "custom_locale": "Configuración Rexional Personalizada", "custom_locale_description": "Formatar datas e números baseándose na lingua e a rexión", + "custom_url": "URL personalizada", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Escuro", + "dark_theme": "Alternar tema escuro", "date_after": "Data posterior a", "date_and_time": "Data e Hora", "date_before": "Data anterior a", @@ -716,45 +775,54 @@ "date_of_birth_saved": "Data de nacemento gardada correctamente", "date_range": "Rango de datas", "day": "Día", - "deduplicate_all": "Eliminar Duplicados Todos", + "days": "Días", + "deduplicate_all": "Eliminar todos os duplicados", "deduplication_criteria_1": "Tamaño da imaxe en bytes", "deduplication_criteria_2": "Reconto de datos EXIF", "deduplication_info": "Información de Deduplicación", "deduplication_info_description": "Para preseleccionar automaticamente activos e eliminar duplicados masivamente, miramos:", "default_locale": "Configuración Rexional Predeterminada", - "default_locale_description": "Formatar datas e números baseándose na configuración rexional do teu navegador", + "default_locale_description": "Formatar datas e números baseándose na configuración rexional do seu navegador", "delete": "Eliminar", + "delete_action_confirmation_message": "Está seguro de que quere eliminar este ficheiro? Esta acción moverá o ficheiro ao lixo do servidor e preguntaralle se tamén quere eliminalo localmente", + "delete_action_prompt": "{count} eliminado(s)", "delete_album": "Eliminar álbum", - "delete_api_key_prompt": "Estás seguro de que queres eliminar esta chave API?", - "delete_dialog_alert": "Estes elementos eliminaranse permanentemente de Immich e do teu dispositivo", - "delete_dialog_alert_local": "Estes elementos eliminaranse permanentemente do teu dispositivo pero aínda estarán dispoñibles no servidor Immich", - "delete_dialog_alert_local_non_backed_up": "Algúns dos elementos non teñen copia de seguridade en Immich e eliminaranse permanentemente do teu dispositivo", + "delete_api_key_prompt": "Está seguro de que quere eliminar esta chave API?", + "delete_dialog_alert": "Estes elementos eliminaranse permanentemente de Immich e do seu dispositivo", + "delete_dialog_alert_local": "Estes elementos eliminaranse permanentemente do seu dispositivo pero aínda estarán dispoñibles no servidor Immich", + "delete_dialog_alert_local_non_backed_up": "Algúns dos elementos non teñen copia de seguridade en Immich e eliminaranse permanentemente do seu dispositivo", "delete_dialog_alert_remote": "Estes elementos eliminaranse permanentemente do servidor Immich", "delete_dialog_ok_force": "Eliminar Igualmente", "delete_dialog_title": "Eliminar Permanentemente", - "delete_duplicates_confirmation": "Estás seguro de que queres eliminar permanentemente estes duplicados?", + "delete_duplicates_confirmation": "Está seguro de que quere eliminar permanentemente estes duplicados?", "delete_face": "Eliminar cara", "delete_key": "Eliminar chave", "delete_library": "Eliminar Biblioteca", "delete_link": "Eliminar ligazón", + "delete_local_action_prompt": "{count} eliminado(s) localmente", "delete_local_dialog_ok_backed_up_only": "Eliminar Só con Copia de Seguridade", "delete_local_dialog_ok_force": "Eliminar Igualmente", "delete_others": "Eliminar outros", + "delete_permanently": "Eliminar permanentemente", + "delete_permanently_action_prompt": "{count} eliminado(s) permanentemente", "delete_shared_link": "Eliminar ligazón compartida", "delete_shared_link_dialog_title": "Eliminar Ligazón Compartida", "delete_tag": "Eliminar etiqueta", - "delete_tag_confirmation_prompt": "Estás seguro de que queres eliminar a etiqueta {tagName}?", + "delete_tag_confirmation_prompt": "Está seguro de que quere eliminar a etiqueta {tagName}?", "delete_user": "Eliminar usuario", "deleted_shared_link": "Ligazón compartida eliminada", "deletes_missing_assets": "Elimina activos que faltan no disco", "description": "Descrición", "description_input_hint_text": "Engadir descrición...", "description_input_submit_error": "Erro ao actualizar a descrición, comprobe o rexistro para máis detalles", + "deselect_all": "Deseleccionar todo", "details": "Detalles", "direction": "Dirección", "disabled": "Desactivado", "disallow_edits": "Non permitir edicións", + "discord": "Discord", "discover": "Descubrir", + "discovered_devices": "Dispositivos descubertos", "dismiss_all_errors": "Descartar todos os erros", "dismiss_error": "Descartar erro", "display_options": "Opcións de visualización", @@ -765,6 +833,7 @@ "documentation": "Documentación", "done": "Feito", "download": "Descargar", + "download_action_prompt": "Descargando {count} activo(s)", "download_canceled": "Descarga cancelada", "download_complete": "Descarga completada", "download_enqueue": "Descarga en cola", @@ -791,40 +860,56 @@ "edit": "Editar", "edit_album": "Editar álbum", "edit_avatar": "Editar avatar", + "edit_birthday": "Editar aniversario", "edit_date": "Editar data", "edit_date_and_time": "Editar data e hora", - "edit_exclusion_pattern": "Editar padrón de exclusión", + "edit_date_and_time_action_prompt": "{count} data(s) e hora(s) editada(s)", + "edit_date_and_time_by_offset": "Cambiar data por desfase", + "edit_date_and_time_by_offset_interval": "Nova franxa de datas: {from} - {to}", + "edit_description": "Editar descrición", + "edit_description_prompt": "Por favor, seleccione unha nova descrición:", + "edit_exclusion_pattern": "Editar patrón de exclusión", "edit_faces": "Editar caras", "edit_import_path": "Editar ruta de importación", "edit_import_paths": "Editar Rutas de Importación", "edit_key": "Editar chave", "edit_link": "Editar ligazón", - "edit_location": "Editar ubicación", - "edit_location_dialog_title": "Ubicación", + "edit_location": "Editar localización", + "edit_location_action_prompt": "{count} localización(s) editada(s)", + "edit_location_dialog_title": "Localización", "edit_name": "Editar nome", "edit_people": "Editar persoas", "edit_tag": "Editar etiqueta", "edit_title": "Editar Título", "edit_user": "Editar usuario", "edited": "Editado", + "editor": "Editor", "editor_close_without_save_prompt": "Os cambios non se gardarán", "editor_close_without_save_title": "Pechar editor?", "editor_crop_tool_h2_aspect_ratios": "Proporcións de aspecto", "editor_crop_tool_h2_rotation": "Rotación", "email": "Correo electrónico", + "email_notifications": "Notificacións por correo electrónico", "empty_folder": "Este cartafol está baleiro", "empty_trash": "Baleirar lixo", - "empty_trash_confirmation": "Estás seguro de que queres baleirar o lixo? Isto eliminará permanentemente todos os activos no lixo de Immich. Non podes desfacer esta acción!", + "empty_trash_confirmation": "Está seguro de que quere baleirar o lixo? Isto eliminará permanentemente todos os activos no lixo de Immich. Non pode desfacer esta acción!", "enable": "Activar", + "enable_backup": "Activar copia de seguridade", + "enable_biometric_auth_description": "Introduza o seu código PIN para activar a autenticación biométrica", "enabled": "Activado", "end_date": "Data de fin", "enqueued": "En cola", "enter_wifi_name": "Introducir nome da wifi", + "enter_your_pin_code": "Introduza o seu código PIN", + "enter_your_pin_code_subtitle": "Introduza o seu código PIN para acceder ao cartafol bloqueado", "error": "Erro", "error_change_sort_album": "Erro ao cambiar a orde de clasificación do álbum", "error_delete_face": "Erro ao eliminar a cara do activo", + "error_getting_places": "Erro ao obter lugares", "error_loading_image": "Erro ao cargar a imaxe", + "error_loading_partners": "Erro cargando compañeiros/as: {error}", "error_saving_image": "Erro: {error}", + "error_tag_face_bounding_box": "Erro ao etiquetar cara - non se poden obter as coordenadas da caixa delimitadora", "error_title": "Erro - Algo saíu mal", "errors": { "cannot_navigate_next_asset": "Non se pode navegar ao seguinte activo", @@ -841,10 +926,10 @@ "error_adding_users_to_album": "Erro ao engadir usuarios ao álbum", "error_deleting_shared_user": "Erro ao eliminar o usuario compartido", "error_downloading": "Erro ao descargar {filename}", - "error_hiding_buy_button": "Erro ao ocultar o botón de compra", + "error_hiding_buy_button": "Erro ao ocultar o botón de apoio", "error_removing_assets_from_album": "Erro ao eliminar activos do álbum, comprobe a consola para máis detalles", "error_selecting_all_assets": "Erro ao seleccionar todos os activos", - "exclusion_pattern_already_exists": "Este padrón de exclusión xa existe.", + "exclusion_pattern_already_exists": "Este patrón de exclusión xa existe.", "failed_to_create_album": "Erro ao crear o álbum", "failed_to_create_shared_link": "Erro ao crear a ligazón compartida", "failed_to_edit_shared_link": "Erro ao editar a ligazón compartida", @@ -855,6 +940,7 @@ "failed_to_load_notifications": "Erro ao cargar as notificacións", "failed_to_load_people": "Erro ao cargar persoas", "failed_to_remove_product_key": "Erro ao eliminar a chave do produto", + "failed_to_reset_pin_code": "Erro ao restablecer o código PIN", "failed_to_stack_assets": "Erro ao apilar activos", "failed_to_unstack_assets": "Erro ao desapilar activos", "failed_to_update_notification_status": "Erro ao actualizar o estado das notificacións", @@ -863,10 +949,11 @@ "paths_validation_failed": "{paths, plural, one {# ruta fallou} other {# rutas fallaron}} na validación", "profile_picture_transparent_pixels": "As imaxes de perfil non poden ter píxeles transparentes. Por favor, faga zoom e/ou mova a imaxe.", "quota_higher_than_disk_size": "Estableceu unha cota superior ao tamaño do disco", + "something_went_wrong": "Algo fallou", "unable_to_add_album_users": "Non se puideron engadir usuarios ao álbum", "unable_to_add_assets_to_shared_link": "Non se puideron engadir activos á ligazón compartida", "unable_to_add_comment": "Non se puido engadir o comentario", - "unable_to_add_exclusion_pattern": "Non se puido engadir o padrón de exclusión", + "unable_to_add_exclusion_pattern": "Non se puido engadir o patrón de exclusión", "unable_to_add_import_path": "Non se puido engadir a ruta de importación", "unable_to_add_partners": "Non se puideron engadir compañeiros/as", "unable_to_add_remove_archive": "Non se puido {archived, select, true {eliminar activo do} other {engadir activo ao}} arquivo", @@ -874,13 +961,14 @@ "unable_to_archive_unarchive": "Non se puido {archived, select, true {arquivar} other {desarquivar}}", "unable_to_change_album_user_role": "Non se puido cambiar o rol do usuario do álbum", "unable_to_change_date": "Non se puido cambiar a data", + "unable_to_change_description": "Non se puido cambiar a descrición", "unable_to_change_favorite": "Non se puido cambiar o favorito do activo", - "unable_to_change_location": "Non se puido cambiar a ubicación", + "unable_to_change_location": "Non se puido cambiar a localización", "unable_to_change_password": "Non se puido cambiar o contrasinal", "unable_to_change_visibility": "Non se puido cambiar a visibilidade para {count, plural, one {# persoa} other {# persoas}}", "unable_to_complete_oauth_login": "Non se puido completar o inicio de sesión OAuth", "unable_to_connect": "Non se puido conectar", - "unable_to_copy_to_clipboard": "Non se puido copiar ao portapapeis, asegúrate de acceder á páxina a través de https", + "unable_to_copy_to_clipboard": "Non se puido copiar ao portapapeis, asegúrese de acceder á páxina a través de https", "unable_to_create_admin_account": "Non se puido crear a conta de administrador", "unable_to_create_api_key": "Non se puido crear unha nova Chave API", "unable_to_create_library": "Non se puido crear a biblioteca", @@ -888,12 +976,12 @@ "unable_to_delete_album": "Non se puido eliminar o álbum", "unable_to_delete_asset": "Non se puido eliminar o activo", "unable_to_delete_assets": "Erro ao eliminar activos", - "unable_to_delete_exclusion_pattern": "Non se puido eliminar o padrón de exclusión", + "unable_to_delete_exclusion_pattern": "Non se puido eliminar o patrón de exclusión", "unable_to_delete_import_path": "Non se puido eliminar a ruta de importación", "unable_to_delete_shared_link": "Non se puido eliminar a ligazón compartida", "unable_to_delete_user": "Non se puido eliminar o usuario", "unable_to_download_files": "Non se puideron descargar os ficheiros", - "unable_to_edit_exclusion_pattern": "Non se puido editar o padrón de exclusión", + "unable_to_edit_exclusion_pattern": "Non se puido editar o patrón de exclusión", "unable_to_edit_import_path": "Non se puido editar a ruta de importación", "unable_to_empty_trash": "Non se puido baleirar o lixo", "unable_to_enter_fullscreen": "Non se puido entrar en pantalla completa", @@ -917,6 +1005,7 @@ "unable_to_remove_partner": "Non se puido eliminar o/a compañeiro/a", "unable_to_remove_reaction": "Non se puido eliminar a reacción", "unable_to_reset_password": "Non se puido restablecer o contrasinal", + "unable_to_reset_pin_code": "Non é posible reiniciar o código PIN", "unable_to_resolve_duplicate": "Non se puido resolver o duplicado", "unable_to_restore_assets": "Non se puideron restaurar os activos", "unable_to_restore_trash": "Non se puido restaurar o lixo", @@ -938,22 +1027,25 @@ "unable_to_update_album_cover": "Non se puido actualizar a portada do álbum", "unable_to_update_album_info": "Non se puido actualizar a información do álbum", "unable_to_update_library": "Non se puido actualizar a biblioteca", - "unable_to_update_location": "Non se puido actualizar a ubicación", + "unable_to_update_location": "Non se puido actualizar a localización", "unable_to_update_settings": "Non se puido actualizar a configuración", "unable_to_update_timeline_display_status": "Non se puido actualizar o estado de visualización da liña de tempo", "unable_to_update_user": "Non se puido actualizar o usuario", "unable_to_upload_file": "Non se puido cargar o ficheiro" }, + "exif": "Exif", "exif_bottom_sheet_description": "Engadir Descrición...", + "exif_bottom_sheet_description_error": "Erro ao actualizar a descrición", "exif_bottom_sheet_details": "DETALLES", - "exif_bottom_sheet_location": "UBICACIÓN", + "exif_bottom_sheet_location": "LOCALIZACIÓN", "exif_bottom_sheet_people": "PERSOAS", "exif_bottom_sheet_person_add_person": "Engadir nome", "exit_slideshow": "Saír da Presentación", "expand_all": "Expandir todo", "experimental_settings_new_asset_list_subtitle": "Traballo en progreso", "experimental_settings_new_asset_list_title": "Activar grella de fotos experimental", - "experimental_settings_subtitle": "Use baixo o teu propio risco!", + "experimental_settings_subtitle": "Use baixo o seu propio risco!", + "experimental_settings_title": "Experimental", "expire_after": "Caduca despois de", "expired": "Caducado", "expires_date": "Caduca o {date}", @@ -961,6 +1053,8 @@ "explorer": "Explorador", "export": "Exportar", "export_as_json": "Exportar como JSON", + "export_database": "Exportar a Base de Datos", + "export_database_description": "Exportar a base de datos SQLite", "extension": "Extensión", "external": "Externo", "external_libraries": "Bibliotecas Externas", @@ -968,14 +1062,17 @@ "external_network_sheet_info": "Cando non estea na rede wifi preferida, a aplicación conectarase ao servidor a través da primeira das seguintes URLs que poida alcanzar, comezando de arriba a abaixo", "face_unassigned": "Sen asignar", "failed": "Fallado", + "failed_to_authenticate": "Fallou a autenticación", "failed_to_load_assets": "Erro ao cargar activos", "failed_to_load_folder": "Erro ao cargar o cartafol", "favorite": "Favorito", + "favorite_action_prompt": "{count} engadido/a a Favoritos", "favorite_or_unfavorite_photo": "Marcar ou desmarcar como favorito", "favorites": "Favoritos", "favorites_page_no_favorites": "Non se atoparon activos favoritos", "feature_photo_updated": "Foto destacada actualizada", "features": "Funcións", + "features_in_development": "Funcionalidades en Desenvolvemento", "features_setting_description": "Xestionar as funcións da aplicación", "file_name": "Nome do ficheiro", "file_name_or_extension": "Nome do ficheiro ou extensión", @@ -984,20 +1081,27 @@ "filter": "Filtro", "filter_people": "Filtrar persoas", "filter_places": "Filtrar lugares", - "find_them_fast": "Atópaos rápido por nome coa busca", + "find_them_fast": "Atópeos rápido por nome coa busca", + "first": "Primeiro/a", "fix_incorrect_match": "Corrixir coincidencia incorrecta", "folder": "Cartafol", "folder_not_found": "Cartafol non atopado", "folders": "Cartafoles", "folders_feature_description": "Navegar pola vista de cartafoles para as fotos e vídeos no sistema de ficheiros", + "forgot_pin_code_question": "Esqueceu o seu PIN?", "forward": "Adiante", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Esta funcionalidade carga recursos externos de Google para poder funcionar.", "general": "Xeral", + "geolocation_instruction_location": "Prema nun recurso con coordenadas GPS para usar a súa localización, ou seleccione unha localización directamente no mapa", "get_help": "Obter Axuda", - "get_wifiname_error": "Non se puido obter o nome da wifi. Asegúrate de que concedeu os permisos necesarios e está conectado a unha rede wifi", + "get_wifiname_error": "Non se puido obter o nome da wifi. Asegúrese de que concedeu os permisos necesarios e está conectado a unha rede wifi", "getting_started": "Primeiros Pasos", "go_back": "Volver", "go_to_folder": "Ir ao cartafol", "go_to_search": "Ir á busca", + "gps": "GPS", + "gps_missing": "Sen GPS", "grant_permission": "Conceder permiso", "group_albums_by": "Agrupar álbums por...", "group_country": "Agrupar por país", @@ -1008,6 +1112,9 @@ "haptic_feedback_switch": "Activar resposta háptica", "haptic_feedback_title": "Resposta Háptica", "has_quota": "Ten cota", + "hash_asset": "Facer hash do recurso", + "hashed_assets": "Recursos cun hash", + "hashing": "Aplicando hash", "header_settings_add_header_tip": "Engadir Cabeceira", "header_settings_field_validator_msg": "O valor non pode estar baleiro", "header_settings_header_name_input": "Nome da cabeceira", @@ -1017,7 +1124,7 @@ "hi_user": "Ola {name} ({email})", "hide_all_people": "Ocultar todas as persoas", "hide_gallery": "Ocultar galería", - "hide_named_person": "Ocultar persoa {name}", + "hide_named_person": "Ocultar a persoa {name}", "hide_password": "Ocultar contrasinal", "hide_person": "Ocultar persoa", "hide_unnamed_people": "Ocultar persoas sen nome", @@ -1032,10 +1139,16 @@ "home_page_delete_remote_err_local": "Activos locais na selección de eliminación remota, omitindo", "home_page_favorite_err_local": "Non se poden marcar como favoritos activos locais aínda, omitindo", "home_page_favorite_err_partner": "Non se poden marcar como favoritos activos de compañeiro/a aínda, omitindo", - "home_page_first_time_notice": "Se esta é a primeira vez que usas a aplicación, asegúrate de elixir un álbum de copia de seguridade para que a liña de tempo poida encherse con fotos e vídeos nel", + "home_page_first_time_notice": "Se esta é a primeira vez que usa a aplicación, asegúrese de elixir un álbum de copia de seguridade para que a liña de tempo poida encherse con fotos e vídeos nel", + "home_page_locked_error_local": "Non é posíbel mover os recursos locais ao cartafol bloqueado, saltando", + "home_page_locked_error_partner": "Non é posíbel mover os recursos do/a colaborador/a ao cartafol bloqueado, saltando", "home_page_share_err_local": "Non se poden compartir activos locais mediante ligazón, omitindo", "home_page_upload_err_limit": "Só se pode cargar un máximo de 30 activos á vez, omitindo", + "host": "Servidor", "hour": "Hora", + "hours": "Horas", + "id": "ID", + "idle": "Inactivo/a", "ignore_icloud_photos": "Ignorar fotos de iCloud", "ignore_icloud_photos_description": "As fotos que están almacenadas en iCloud non se cargarán ao servidor Immich", "image": "Imaxe", @@ -1063,7 +1176,7 @@ "include_shared_albums": "Incluír álbums compartidos", "include_shared_partner_assets": "Incluír activos de compañeiro/a compartidos", "individual_share": "Compartir individual", - "individual_shares": "Compartires individuais", + "individual_shares": "Comparticións individuais", "info": "Información", "interval": { "day_at_onepm": "Todos os días ás 13:00", @@ -1075,6 +1188,12 @@ "invalid_date_format": "Formato de data inválido", "invite_people": "Invitar Persoas", "invite_to_album": "Invitar ao álbum", + "ios_debug_info_fetch_ran_at": "Obtívose ás {dateTime}", + "ios_debug_info_last_sync_at": "Última sincronización: {dateTime}", + "ios_debug_info_no_processes_queued": "Sen procesos en segundo plano en cola", + "ios_debug_info_no_sync_yet": "Aínda non se executou ningunha tarefa de sincronización en segundo plano", + "ios_debug_info_processes_queued": "{count, plural, one {{count} proceso en segundo plano en cola} other {{count} procesos en segundo plano en cola}}", + "ios_debug_info_processing_ran_at": "O procesamento executouse ás {dateTime}", "items_count": "{count, plural, one {# elemento} other {# elementos}}", "jobs": "Traballos", "keep": "Conservar", @@ -1083,10 +1202,17 @@ "kept_this_deleted_others": "Conservouse este activo e elimináronse {count, plural, one {# activo} other {# activos}}", "keyboard_shortcuts": "Atallos de teclado", "language": "Lingua", - "language_setting_description": "Seleccione a túa lingua preferida", + "language_no_results_subtitle": "Probe axustar o seu termo de busca", + "language_no_results_title": "Non se atopou ningunha lingua", + "language_search_hint": "Buscar linguas...", + "language_setting_description": "Seleccione a súa lingua preferida", + "large_files": "Ficheiros Grandes", + "last": "Último/a", "last_seen": "Visto por última vez", "latest_version": "Última Versión", + "latitude": "Latitude", "leave": "Saír", + "leave_album": "Deixar o álbum", "lens_model": "Modelo da lente", "let_others_respond": "Permitir que outros respondan", "level": "Nivel", @@ -1098,7 +1224,9 @@ "library_page_sort_created": "Data de creación", "library_page_sort_last_modified": "Última modificación", "library_page_sort_title": "Título do álbum", + "licenses": "Licenzas", "light": "Claro", + "like": "Gústame", "like_deleted": "Gústame eliminado", "link_motion_video": "Ligar vídeo en movemento", "link_to_oauth": "Ligar a OAuth", @@ -1106,25 +1234,33 @@ "list": "Lista", "loading": "Cargando", "loading_search_results_failed": "Erro ao cargar os resultados da busca", + "local": "Local", + "local_asset_cast_failed": "Non é posíbel proxectar un recurso que non está cargado no servidor", + "local_assets": "Recursos Locais", + "local_media_summary": "Resumo de Contido Local", "local_network": "Rede local", "local_network_sheet_info": "A aplicación conectarase ao servidor a través desta URL cando use a rede wifi especificada", - "location_permission": "Permiso de ubicación", - "location_permission_content": "Para usar a función de cambio automático, Immich necesita permiso de ubicación precisa para poder ler o nome da rede wifi actual", + "location_permission": "Permiso de localización", + "location_permission_content": "Para usar a función de cambio automático, Immich necesita permiso de localización precisa para poder ler o nome da rede wifi actual", "location_picker_choose_on_map": "Elixir no mapa", "location_picker_latitude_error": "Introducir unha latitude válida", - "location_picker_latitude_hint": "Introduza a túa latitude aquí", + "location_picker_latitude_hint": "Introduza a súa latitude aquí", "location_picker_longitude_error": "Introducir unha lonxitude válida", - "location_picker_longitude_hint": "Introduza a túa lonxitude aquí", + "location_picker_longitude_hint": "Introduza a súa lonxitude aquí", + "lock": "Bloquear", + "locked_folder": "Cartafol Bloqueado", + "log_detail_title": "Detalle do Rexistro", "log_out": "Pechar sesión", "log_out_all_devices": "Pechar Sesión en Todos os Dispositivos", + "logged_in_as": "Sesión iniciada como {user}", "logged_out_all_devices": "Pechouse sesión en todos os dispositivos", "logged_out_device": "Pechouse sesión no dispositivo", "login": "Iniciar sesión", "login_disabled": "O inicio de sesión foi desactivado", "login_form_api_exception": "Excepción da API. Por favor, comprobe a URL do servidor e inténteo de novo.", "login_form_back_button_text": "Atrás", - "login_form_email_hint": "oteuemail@email.com", - "login_form_endpoint_hint": "http://ip-do-teu-servidor:porto", + "login_form_email_hint": "oseuemail@dominio.com", + "login_form_endpoint_hint": "http://ip-do-servidor:porto", "login_form_endpoint_url": "URL do Punto Final do Servidor", "login_form_err_http": "Por favor, especifique http:// ou https://", "login_form_err_invalid_email": "Correo electrónico inválido", @@ -1133,42 +1269,44 @@ "login_form_err_trailing_whitespace": "Espazo en branco final", "login_form_failed_get_oauth_server_config": "Erro ao iniciar sesión usando OAuth, comprobe a URL do servidor", "login_form_failed_get_oauth_server_disable": "A función OAuth non está dispoñible neste servidor", - "login_form_failed_login": "Erro ao iniciar sesión, comproba a URL do servidor, correo electrónico e contrasinal", - "login_form_handshake_exception": "Houbo unha Excepción de Handshake co servidor. Activa o soporte para certificados autofirmados nas configuracións se estás a usar un certificado autofirmado.", + "login_form_failed_login": "Erro ao iniciar sesión, comprobe a URL do servidor, correo electrónico e contrasinal", + "login_form_handshake_exception": "Houbo unha Excepción de Handshake co servidor. Active o soporte para certificados autofirmados nas configuracións se está a usar un certificado autofirmado.", "login_form_password_hint": "contrasinal", "login_form_save_login": "Manter sesión iniciada", "login_form_server_empty": "Introduza unha URL do servidor.", "login_form_server_error": "Non se puido conectar co servidor.", "login_has_been_disabled": "O inicio de sesión foi desactivado.", - "login_password_changed_error": "Houbo un erro ao actualizar o teu contrasinal", + "login_password_changed_error": "Houbo un erro ao actualizar o seu contrasinal", "login_password_changed_success": "Contrasinal actualizado correctamente", - "logout_all_device_confirmation": "Estás seguro de que queres pechar sesión en todos os dispositivos?", - "logout_this_device_confirmation": "Estás seguro de que queres pechar sesión neste dispositivo?", + "logout_all_device_confirmation": "Está seguro de que quere pechar sesión en todos os dispositivos?", + "logout_this_device_confirmation": "Está seguro de que quere pechar sesión neste dispositivo?", + "logs": "Rexistros", "longitude": "Lonxitude", - "look": "Ollar", + "look": "Aspecto", "loop_videos": "Reproducir vídeos en bucle", "loop_videos_description": "Activar para reproducir automaticamente un vídeo en bucle no visor de detalles.", "main_branch_warning": "Está a usar unha versión de desenvolvemento; recomendamos encarecidamente usar unha versión de lanzamento!", "main_menu": "Menú principal", "make": "Marca", + "manage_geolocation": "Xestionar a localización", "manage_shared_links": "Xestionar ligazóns compartidas", "manage_sharing_with_partners": "Xestionar compartición con compañeiros/as", "manage_the_app_settings": "Xestionar a configuración da aplicación", - "manage_your_account": "Xestionar a túa conta", - "manage_your_api_keys": "Xestionar as túas claves API", - "manage_your_devices": "Xestionar os teus dispositivos con sesión iniciada", - "manage_your_oauth_connection": "Xestionar a túa conexión OAuth", + "manage_your_account": "Xestionar a súa conta", + "manage_your_api_keys": "Xestionar as súas claves API", + "manage_your_devices": "Xestionar os seus dispositivos con sesión iniciada", + "manage_your_oauth_connection": "Xestionar a súa conexión OAuth", "map": "Mapa", - "map_assets_in_bounds": "{count} fotos", - "map_cannot_get_user_location": "Non se pode obter a ubicación do usuario", + "map_assets_in_bounds": "{count, plural, =0 {Sen fotos nesta área} one {# foto} other {# fotos}}", + "map_cannot_get_user_location": "Non se pode obter a localización do usuario", "map_location_dialog_yes": "Si", - "map_location_picker_page_use_location": "Usar esta ubicación", - "map_location_service_disabled_content": "O servizo de ubicación debe estar activado para mostrar activos da túa ubicación actual. Queres activalo agora?", - "map_location_service_disabled_title": "Servizo de ubicación deshabilitado", + "map_location_picker_page_use_location": "Usar esta localización", + "map_location_service_disabled_content": "O servizo de localización debe estar activado para mostrar activos da súa localización actual. Quere activalo agora?", + "map_location_service_disabled_title": "Servizo de localización deshabilitado", "map_marker_for_images": "Marcador de mapa para imaxes tomadas en {city}, {country}", "map_marker_with_image": "Marcador de mapa con imaxe", - "map_no_location_permission_content": "Necesítase permiso de ubicación para mostrar activos da súa ubicación actual. Queres permitilo agora?", - "map_no_location_permission_title": "Permiso de ubicación denegado", + "map_no_location_permission_content": "Necesítase permiso de localización para mostrar activos da súa localización actual. Quere permitilo agora?", + "map_no_location_permission_title": "Permiso de localización denegado", "map_settings": "Configuración do mapa", "map_settings_dark_mode": "Modo escuro", "map_settings_date_range_option_day": "Últimas 24 horas", @@ -1180,40 +1318,54 @@ "map_settings_include_show_partners": "Incluír Compañeiros/as", "map_settings_only_show_favorites": "Mostrar Só Favoritos", "map_settings_theme_settings": "Tema do Mapa", - "map_zoom_to_see_photos": "Alonxe o zoom para ver fotos", + "map_zoom_to_see_photos": "Afaste o zoom para ver fotos", "mark_all_as_read": "Marcar todo como lido", "mark_as_read": "Marcar como lido", "marked_all_as_read": "Marcado todo como lido", "matches": "Coincidencias", + "matching_assets": "Recursos Correspondentes", "media_type": "Tipo de medio", "memories": "Recordos", "memories_all_caught_up": "Todo ao día", "memories_check_back_tomorrow": "Volva mañá para máis recordos", - "memories_setting_description": "Xestionar o que ves nos teus recordos", + "memories_setting_description": "Xestionar o que ve nos seus recordos", "memories_start_over": "Comezar de novo", - "memories_swipe_to_close": "Deslizar cara arriba para pechar", + "memories_swipe_to_close": "Deslice cara arriba para pechar", "memory": "Recordo", - "memory_lane_title": "Camiño dos Recordos {title}", + "memory_lane_title": "Camiño dos Recordos: {title}", "menu": "Menú", "merge": "Fusionar", "merge_people": "Fusionar persoas", "merge_people_limit": "Só pode fusionar ata 5 caras á vez", - "merge_people_prompt": "Queres fusionar estas persoas? Esta acción é irreversible.", + "merge_people_prompt": "Quere fusionar estas persoas? Esta acción é irreversible.", "merge_people_successfully": "Persoas fusionadas correctamente", "merged_people_count": "Fusionadas {count, plural, one {# persoa} other {# persoas}}", "minimize": "Minimizar", "minute": "Minuto", + "minutes": "Minutos", "missing": "Faltantes", "model": "Modelo", "month": "Mes", + "monthly_title_text_date_format": "MMMM a", "more": "Máis", + "move": "Mover", + "move_off_locked_folder": "Mover fóra do cartafol bloqueado", + "move_to_lock_folder_action_prompt": "{count} engadido/a ao cartafol bloqueado", + "move_to_locked_folder": "Mover ao cartafol bloqueado", + "move_to_locked_folder_confirmation": "Estas fotos e vídeo eliminaranse de todos os álbums e só serán visíbeis dende o cartafol bloqueado", + "moved_to_archive": "Moveuse {count, plural, one {# recurso} other {# recursos}} ao arquivo", + "moved_to_library": "Moveuse {count, plural, one {# recurso} other {# recursos}} á biblioteca", "moved_to_trash": "Movido ao lixo", "multiselect_grid_edit_date_time_err_read_only": "Non se pode editar a data de activo(s) de só lectura, omitindo", - "multiselect_grid_edit_gps_err_read_only": "Non se pode editar a ubicación de activo(s) de só lectura, omitindo", + "multiselect_grid_edit_gps_err_read_only": "Non se pode editar a localización de activo(s) de só lectura, omitindo", "mute_memories": "Silenciar Recordos", "my_albums": "Os meus álbums", "name": "Nome", "name_or_nickname": "Nome ou alcume", + "network_requirement_photos_upload": "Usar datos móbiles para facer copia de seguridade das fotos", + "network_requirement_videos_upload": "Usar datos móbiles para facer copia de seguridade dos vídeos", + "network_requirements": "Requisitos de rede", + "network_requirements_updated": "Os requisitos de rede cambiaron, reiniciando cola de copia de seguridade", "networking_settings": "Rede", "networking_subtitle": "Xestionar a configuración do punto final do servidor", "never": "Nunca", @@ -1221,29 +1373,34 @@ "new_api_key": "Nova Chave API", "new_password": "Novo contrasinal", "new_person": "Nova persoa", + "new_pin_code": "Novo código PIN", + "new_pin_code_subtitle": "Esta é a túa primeira vez accedendo á carpeta segura. Crea un código PIN para acceder de maneira segura a esta páxina", + "new_timeline": "Nova liña de tempo", "new_user_created": "Novo usuario creado", "new_version_available": "NOVA VERSIÓN DISPOÑIBLE", "newest_first": "Máis recentes primeiro", "next": "Seguinte", "next_memory": "Seguinte recordo", "no": "Non", - "no_albums_message": "Crea un álbum para organizar as túas fotos e vídeos", - "no_albums_with_name_yet": "Parece que aínda non tes ningún álbum con este nome.", - "no_albums_yet": "Parece que aínda non tes ningún álbum.", - "no_archived_assets_message": "Arquiva fotos e vídeos para ocultalos da túa vista de Fotos", + "no_albums_message": "Cree un álbum para organizar as súas fotos e vídeos", + "no_albums_with_name_yet": "Parece que aínda non ten ningún álbum con este nome.", + "no_albums_yet": "Parece que aínda non ten ningún álbum.", + "no_archived_assets_message": "Arquive fotos e vídeos para ocultalos da súa vista de Fotos", "no_assets_message": "PREMA PARA CARGAR A SÚA PRIMEIRA FOTO", "no_assets_to_show": "Non hai activos para mostrar", + "no_cast_devices_found": "Non se atoparon dispositivos de transmisión", "no_duplicates_found": "Non se atoparon duplicados.", - "no_exif_info_available": "Non hai información exif dispoñible", - "no_explore_results_message": "Suba máis fotos para explorar a túa colección.", - "no_favorites_message": "Engade favoritos para atopar rapidamente as túas mellores fotos e vídeos", - "no_libraries_message": "Crea unha biblioteca externa para ver as túas fotos e vídeos", + "no_exif_info_available": "Non hai información EXIF dispoñible", + "no_explore_results_message": "Suba máis fotos para explorar a súa colección.", + "no_favorites_message": "Engada favoritos para atopar rapidamente as súas mellores fotos e vídeos", + "no_libraries_message": "Cree unha biblioteca externa para ver as súas fotos e vídeos", + "no_local_assets_found": "Non se atoparon elementos locais con esta suma de comprobación", "no_name": "Sen Nome", "no_notifications": "Sen notificacións", "no_places": "Sen lugares", "no_results": "Sen resultados", - "no_results_description": "Proba cun sinónimo ou palabra chave máis xeral", - "no_shared_albums_message": "Crea un álbum para compartir fotos e vídeos con persoas na túa rede", + "no_results_description": "Probe cun sinónimo ou palabra chave máis xeral", + "no_shared_albums_message": "Cree un álbum para compartir fotos e vídeos con persoas na súa rede", "not_in_any_album": "Non está en ningún álbum", "not_selected": "Non seleccionado", "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar a Etiqueta de Almacenamento a activos cargados previamente, execute o", @@ -1260,9 +1417,9 @@ "ok": "Aceptar", "oldest_first": "Máis antigos primeiro", "on_this_device": "Neste dispositivo", - "onboarding": "Incorporación", + "onboarding": "Primeiros pasos", "onboarding_privacy_description": "As seguintes funcións (opcionais) dependen de servizos externos e poden desactivarse en calquera momento na configuración da administración.", - "onboarding_theme_description": "Elixe un tema de cor para a túa instancia. Podes cambialo máis tarde na túa configuración.", + "onboarding_theme_description": "Elixa un tema de cor para a súa instancia. Pode cambialo máis tarde na súa configuración.", "onboarding_welcome_user": "Benvido/a, {user}", "online": "En liña", "only_favorites": "Só favoritos", @@ -1272,7 +1429,7 @@ "open_the_search_filters": "Abrir os filtros de busca", "options": "Opcións", "or": "ou", - "organize_your_library": "Organizar a túa biblioteca", + "organize_your_library": "Organizar a súa biblioteca", "original": "orixinal", "other": "Outro", "other_devices": "Outros dispositivos", @@ -1281,8 +1438,8 @@ "owner": "Propietario", "partner": "Compañeiro/a", "partner_can_access": "{partner} pode acceder a", - "partner_can_access_assets": "Todas as túas fotos e vídeos excepto os de Arquivo e Eliminados", - "partner_can_access_location": "A ubicación onde se tomaron as túas fotos", + "partner_can_access_assets": "Todas as súas fotos e vídeos excepto os de Arquivo e Eliminados", + "partner_can_access_location": "A localización onde se tomaron as súas fotos", "partner_list_user_photos": "Fotos de {user}", "partner_list_view_all": "Ver todo", "partner_page_empty_message": "As súas fotos aínda non están compartidas con ningún compañeiro/a.", @@ -1290,7 +1447,7 @@ "partner_page_partner_add_failed": "Erro ao engadir compañeiro/a", "partner_page_select_partner": "Seleccionar compañeiro/a", "partner_page_shared_to_title": "Compartido con", - "partner_page_stop_sharing_content": "{partner} xa non poderá acceder ás túas fotos.", + "partner_page_stop_sharing_content": "{partner} xa non poderá acceder ás súas fotos.", "partner_sharing": "Compartición con Compañeiro/a", "partners": "Compañeiros/as", "password": "Contrasinal", @@ -1303,7 +1460,7 @@ "years": "Últimos {years, plural, one {ano} other {# anos}}" }, "path": "Ruta", - "pattern": "Padrón", + "pattern": "Patrón", "pause": "Pausa", "pause_memories": "Pausar recordos", "paused": "Pausado", @@ -1316,7 +1473,7 @@ "permanent_deletion_warning_setting_description": "Mostrar un aviso ao eliminar permanentemente activos", "permanently_delete": "Eliminar permanentemente", "permanently_delete_assets_count": "Eliminar permanentemente {count, plural, one {activo} other {activos}}", - "permanently_delete_assets_prompt": "Estás seguro de que queres eliminar permanentemente {count, plural, one {este activo?} other {estes # activos?}} Isto tamén {count, plural, one {o eliminará do teu} other {os eliminará dos teus}} álbum(s).", + "permanently_delete_assets_prompt": "Está seguro de que quere eliminar permanentemente {count, plural, one {este activo?} other {estes # activos?}} Isto tamén {count, plural, one {o eliminará do seu} other {os eliminará dos seus}} álbum(s).", "permanently_deleted_asset": "Activo eliminado permanentemente", "permanently_deleted_assets_count": "Eliminados permanentemente {count, plural, one {# activo} other {# activos}}", "permission_onboarding_back": "Atrás", @@ -1325,17 +1482,17 @@ "permission_onboarding_go_to_settings": "Ir a axustes", "permission_onboarding_permission_denied": "Permiso denegado. Para usar Immich, conceda permisos de fotos e vídeos en Axustes.", "permission_onboarding_permission_granted": "Permiso concedido! Xa está todo listo.", - "permission_onboarding_permission_limited": "Permiso limitado. Para permitir que Immich faga copia de seguridade e xestione toda a túa colección da galería, conceda permisos de fotos e vídeos en Configuración.", - "permission_onboarding_request": "Immich require permiso para ver as túas fotos e vídeos.", + "permission_onboarding_permission_limited": "Permiso limitado. Para permitir que Immich faga copia de seguridade e xestione toda a súa colección da galería, conceda permisos de fotos e vídeos en Configuración.", + "permission_onboarding_request": "Immich require permiso para ver as súas fotos e vídeos.", "person": "Persoa", "person_birthdate": "Nacido/a o {date}", "person_hidden": "{name}{hidden, select, true { (oculto)} other {}}", - "photo_shared_all_users": "Parece que compartiches as túas fotos con todos os usuarios ou non tes ningún usuario co que compartir.", + "photo_shared_all_users": "Parece que compartiu as súas fotos con todos os usuarios ou non ten ningún usuario co que compartir.", "photos": "Fotos", "photos_and_videos": "Fotos e Vídeos", "photos_count": "{count, plural, one {{count, number} Foto} other {{count, number} Fotos}}", "photos_from_previous_years": "Fotos de anos anteriores", - "pick_a_location": "Elixir unha ubicación", + "pick_a_location": "Elixir unha localización", "place": "Lugar", "places": "Lugares", "places_count": "{count, plural, one {{count, number} Lugar} other {{count, number} Lugares}}", @@ -1368,28 +1525,28 @@ "purchase_activated_time": "Activado o {date}", "purchase_activated_title": "A súa chave activouse correctamente", "purchase_button_activate": "Activar", - "purchase_button_buy": "Comprar", - "purchase_button_buy_immich": "Comprar Immich", + "purchase_button_buy": "Apoiar", + "purchase_button_buy_immich": "Apoiar Immich", "purchase_button_never_show_again": "Non mostrar nunca máis", "purchase_button_reminder": "Lembrarme en 30 días", "purchase_button_remove_key": "Eliminar chave", "purchase_button_select": "Seleccionar", - "purchase_failed_activation": "Erro ao activar! Por favor, comproba o teu correo electrónico para a chave do produto correcta!", + "purchase_failed_activation": "Erro ao activar! Por favor, comprobe o seu correo electrónico para a chave do produto correcta!", "purchase_individual_description_1": "Para un individuo", "purchase_individual_description_2": "Estado de seguidor/a", "purchase_input_suggestion": "Ten unha chave de produto? Introduza a chave a continuación", - "purchase_license_subtitle": "Compre Immich para apoiar o desenvolvemento continuado do servizo", - "purchase_lifetime_description": "Compra vitalicia", - "purchase_option_title": "OPCIÓNS DE COMPRA", + "purchase_license_subtitle": "Apoie Immich para contribuír ao desenvolvemento continuado do servizo", + "purchase_lifetime_description": "Contribución vitalicia", + "purchase_option_title": "OPCIÓNS DE APOIO", "purchase_panel_info_1": "Construír Immich leva moito tempo e esforzo, e temos enxeñeiros a tempo completo traballando nel para facelo o mellor posible. A nosa misión é que o software de código aberto e as prácticas comerciais éticas se convertan nunha fonte de ingresos sostible para os desenvolvedores e crear un ecosistema respectuoso coa privacidade con alternativas reais aos servizos na nube explotadores.", - "purchase_panel_info_2": "Como estamos comprometidos a non engadir muros de pago, esta compra non che outorgará ningunha función adicional en Immich. Dependemos de usuarios coma ti para apoiar o desenvolvemento continuo de Immich.", + "purchase_panel_info_2": "Como estamos comprometidos a non engadir muros de pago, esta contribución non lle outorgará ningunha función adicional en Immich. Dependemos de usuarios coma vostede para apoiar o desenvolvemento continuo de Immich.", "purchase_panel_title": "Apoiar o proxecto", "purchase_per_server": "Por servidor", "purchase_per_user": "Por usuario", "purchase_remove_product_key": "Eliminar Chave do Produto", - "purchase_remove_product_key_prompt": "Estás seguro de que queres eliminar a chave do produto?", + "purchase_remove_product_key_prompt": "Está seguro de que quere eliminar a chave do produto?", "purchase_remove_server_product_key": "Eliminar chave do produto do Servidor", - "purchase_remove_server_product_key_prompt": "Estás seguro de que queres eliminar a chave do produto do Servidor?", + "purchase_remove_server_product_key_prompt": "Está seguro de que quere eliminar a chave do produto do Servidor?", "purchase_server_description_1": "Para todo o servidor", "purchase_server_description_2": "Estado de seguidor/a", "purchase_server_title": "Servidor", @@ -1409,8 +1566,8 @@ "recent_searches": "Buscas recentes", "recently_added": "Engadido recentemente", "recently_added_page_title": "Engadido Recentemente", - "recently_taken": "Recentemente tomado", - "recently_taken_page_title": "Recentemente Tomado", + "recently_taken": "Tomado recentemente", + "recently_taken_page_title": "Tomado Recentemente", "refresh": "Actualizar", "refresh_encoded_videos": "Actualizar vídeos codificados", "refresh_faces": "Actualizar caras", @@ -1423,8 +1580,8 @@ "refreshing_metadata": "Actualizando metadatos", "regenerating_thumbnails": "Rexenerando miniaturas", "remove": "Eliminar", - "remove_assets_album_confirmation": "Estás seguro de que queres eliminar {count, plural, one {# activo} other {# activos}} do álbum?", - "remove_assets_shared_link_confirmation": "Estás seguro de que queres eliminar {count, plural, one {# activo} other {# activos}} desta ligazón compartida?", + "remove_assets_album_confirmation": "Está seguro de que quere eliminar {count, plural, one {# activo} other {# activos}} do álbum?", + "remove_assets_shared_link_confirmation": "Está seguro de que quere eliminar {count, plural, one {# activo} other {# activos}} desta ligazón compartida?", "remove_assets_title": "Eliminar activos?", "remove_custom_date_range": "Eliminar rango de datas personalizado", "remove_deleted_assets": "Eliminar Activos Eliminados", @@ -1470,7 +1627,7 @@ "saved_api_key": "Chave API gardada", "saved_profile": "Perfil gardado", "saved_settings": "Configuración gardada", - "say_something": "Dicir algo", + "say_something": "Dicir algo...", "scaffold_body_error_occurred": "Ocorreu un erro", "scan_all_libraries": "Escanear Todas as Bibliotecas", "scan_library": "Escanear", @@ -1495,8 +1652,8 @@ "search_filter_display_option_not_in_album": "Non nun álbum", "search_filter_display_options": "Opcións de Visualización", "search_filter_filename": "Buscar por nome de ficheiro", - "search_filter_location": "Ubicación", - "search_filter_location_title": "Seleccionar ubicación", + "search_filter_location": "Localización", + "search_filter_location_title": "Seleccionar localización", "search_filter_media_type": "Tipo de Medio", "search_filter_media_type_title": "Seleccionar tipo de medio", "search_filter_people_title": "Seleccionar persoas", @@ -1512,11 +1669,11 @@ "search_page_no_objects": "Non hai Información de Obxectos Dispoñible", "search_page_no_places": "Non hai Información de Lugares Dispoñible", "search_page_screenshots": "Capturas de pantalla", - "search_page_search_photos_videos": "Busca as túas fotos e vídeos", + "search_page_search_photos_videos": "Busca as súas fotos e vídeos", "search_page_things": "Cousas", "search_page_view_all_button": "Ver todo", - "search_page_your_activity": "A túa actividade", - "search_page_your_map": "O teu Mapa", + "search_page_your_activity": "A súa actividade", + "search_page_your_map": "O seu Mapa", "search_people": "Buscar persoas", "search_places": "Buscar lugares", "search_rating": "Buscar por clasificación...", @@ -1524,11 +1681,11 @@ "search_settings": "Configuración da busca", "search_state": "Buscar estado...", "search_suggestion_list_smart_search_hint_1": "A busca intelixente está activada por defecto, para buscar metadatos use a sintaxe ", - "search_suggestion_list_smart_search_hint_2": "m:o-teu-termo-de-busca", + "search_suggestion_list_smart_search_hint_2": "m:o-seu-termo-de-busca", "search_tags": "Buscar etiquetas...", "search_timezone": "Buscar fuso horario...", "search_type": "Tipo de busca", - "search_your_photos": "Buscar as túas fotos", + "search_your_photos": "Buscar as súas fotos", "searching_locales": "Buscando configuracións rexionais...", "second": "Segundo", "see_all_people": "Ver todas as persoas", @@ -1564,7 +1721,7 @@ "set_date_of_birth": "Establecer data de nacemento", "set_profile_picture": "Establecer imaxe de perfil", "set_slideshow_to_fullscreen": "Poñer Presentación a pantalla completa", - "setting_image_viewer_help": "O visor de detalles carga primeiro a miniatura pequena, despois carga a vista previa de tamaño medio (se está activada), finalmente carga o orixinal (se está activado).", + "setting_image_viewer_help": "O visor de detalles carga primeiro a miniatura pequena, despois carga a vista previa de tamaño medio (se está activada), e finalmente carga o orixinal (se está activado).", "setting_image_viewer_original_subtitle": "Activar para cargar a imaxe orixinal a resolución completa (grande!). Desactivar para reducir o uso de datos (tanto na rede como na caché do dispositivo).", "setting_image_viewer_original_title": "Cargar imaxe orixinal", "setting_image_viewer_preview_subtitle": "Activar para cargar unha imaxe de resolución media. Desactivar para cargar directamente o orixinal ou usar só a miniatura.", @@ -1580,11 +1737,11 @@ "setting_notifications_notify_seconds": "{count} segundos", "setting_notifications_single_progress_subtitle": "Información detallada do progreso da carga por activo", "setting_notifications_single_progress_title": "Mostrar progreso detallado da copia de seguridade en segundo plano", - "setting_notifications_subtitle": "Axustar as túas preferencias de notificación", + "setting_notifications_subtitle": "Axustar as súas preferencias de notificación", "setting_notifications_total_progress_subtitle": "Progreso xeral da carga (feitos/total activos)", "setting_notifications_total_progress_title": "Mostrar progreso total da copia de seguridade en segundo plano", "setting_video_viewer_looping_title": "Bucle", - "setting_video_viewer_original_video_subtitle": "Ao transmitir un vídeo desde o servidor, reproducir o orixinal aínda que haxa unha transcodificación dispoñible. Pode provocar buffering. Os vídeos dispoñibles localmente repródúcense en calidade orixinal independentemente desta configuración.", + "setting_video_viewer_original_video_subtitle": "Ao transmitir un vídeo desde o servidor, reproducir o orixinal aínda que haxa unha transcodificación dispoñible. Pode provocar buffering. Os vídeos dispoñibles localmente reprodúcense en calidade orixinal independentemente desta configuración.", "setting_video_viewer_original_video_title": "Forzar vídeo orixinal", "settings": "Configuración", "settings_require_restart": "Por favor, reinicie Immich para aplicar esta configuración", @@ -1595,7 +1752,7 @@ "share_dialog_preparing": "Preparando...", "shared": "Compartido", "shared_album_activities_input_disable": "O comentario está desactivado", - "shared_album_activity_remove_content": "Queres eliminar esta actividade?", + "shared_album_activity_remove_content": "Quere eliminar esta actividade?", "shared_album_activity_remove_title": "Eliminar Actividade", "shared_album_section_people_action_error": "Erro ao saír/eliminar do álbum", "shared_album_section_people_action_leave": "Eliminar usuario do álbum", @@ -1603,7 +1760,7 @@ "shared_album_section_people_title": "PERSOAS", "shared_by": "Compartido por", "shared_by_user": "Compartido por {user}", - "shared_by_you": "Compartido por ti", + "shared_by_you": "Compartido por vostede", "shared_from_partner": "Fotos de {partner}", "shared_intent_upload_button_progress_text": "{current} / {total} Subidos", "shared_link_app_bar_title": "Ligazóns Compartidas", @@ -1621,14 +1778,14 @@ "shared_link_edit_expire_after_option_year": "{count} ano", "shared_link_edit_password_hint": "Introduza o contrasinal da compartición", "shared_link_edit_submit_button": "Actualizar ligazón", - "shared_link_error_server_url_fetch": "Non se pode obter a url do servidor", + "shared_link_error_server_url_fetch": "Non se pode obter a URL do servidor", "shared_link_expires_day": "Caduca en {count} día", "shared_link_expires_days": "Caduca en {count} días", "shared_link_expires_hour": "Caduca en {count} hora", "shared_link_expires_hours": "Caduca en {count} horas", "shared_link_expires_minute": "Caduca en {count} minuto", "shared_link_expires_minutes": "Caduca en {count} minutos", - "shared_link_expires_never": "Caduca ∞", + "shared_link_expires_never": "Non caduca", "shared_link_expires_second": "Caduca en {count} segundo", "shared_link_expires_seconds": "Caduca en {count} segundos", "shared_link_individual_shared": "Compartido individualmente", @@ -1642,7 +1799,7 @@ "sharing": "Compartir", "sharing_enter_password": "Por favor, introduza o contrasinal para ver esta páxina.", "sharing_page_album": "Álbums compartidos", - "sharing_page_description": "Crea álbums compartidos para compartir fotos e vídeos con persoas na túa rede.", + "sharing_page_description": "Cree álbums compartidos para compartir fotos e vídeos con persoas na súa rede.", "sharing_page_empty_list": "LISTA BALEIRA", "sharing_sidebar_description": "Mostrar unha ligazón a Compartir na barra lateral", "sharing_silver_appbar_create_shared_album": "Novo álbum compartido", @@ -1652,11 +1809,11 @@ "show_albums": "Mostrar álbums", "show_all_people": "Mostrar todas as persoas", "show_and_hide_people": "Mostrar e ocultar persoas", - "show_file_location": "Mostrar ubicación do ficheiro", + "show_file_location": "Mostrar localización do ficheiro", "show_gallery": "Mostrar galería", "show_hidden_people": "Mostrar persoas ocultas", "show_in_timeline": "Mostrar na liña de tempo", - "show_in_timeline_setting_description": "Mostrar fotos e vídeos deste usuario na túa liña de tempo", + "show_in_timeline_setting_description": "Mostrar fotos e vídeos deste usuario na súa liña de tempo", "show_keyboard_shortcuts": "Mostrar atallos de teclado", "show_metadata": "Mostrar metadatos", "show_or_hide_info": "Mostrar ou ocultar información", @@ -1699,10 +1856,10 @@ "state": "Estado", "status": "Estado", "stop_motion_photo": "Deter Foto en Movemento", - "stop_photo_sharing": "Deixar de compartir as túas fotos?", - "stop_photo_sharing_description": "{partner} xa non poderá acceder ás túas fotos.", - "stop_sharing_photos_with_user": "Deixar de compartir as túas fotos con este usuario", - "storage": "Espazo de almacenamento", + "stop_photo_sharing": "Deixar de compartir as súas fotos?", + "stop_photo_sharing_description": "{partner} xa non poderá acceder ás súas fotos.", + "stop_sharing_photos_with_user": "Deixar de compartir as súas fotos con este usuario", + "storage": "Almacenamento", "storage_label": "Etiqueta de almacenamento", "storage_usage": "{used} de {available} usado", "submit": "Enviar", @@ -1710,12 +1867,12 @@ "sunrise_on_the_beach": "Amencer na praia", "support": "Soporte", "support_and_feedback": "Soporte e Comentarios", - "support_third_party_description": "A túa instalación de Immich foi empaquetada por un terceiro. Os problemas que experimente poden ser causados por ese paquete, así que por favor, comunica os problemas con eles en primeira instancia usando as ligazóns a continuación.", + "support_third_party_description": "A súa instalación de Immich foi empaquetada por un terceiro. Os problemas que experimente poden ser causados por ese paquete, así que por favor, comunique os problemas con eles en primeira instancia usando as ligazóns a continuación.", "swap_merge_direction": "Intercambiar dirección de fusión", "sync": "Sincronizar", "sync_albums": "Sincronizar álbums", "sync_albums_manual_subtitle": "Sincronizar todos os vídeos e fotos cargados aos álbums de copia de seguridade seleccionados", - "sync_upload_album_setting_subtitle": "Crear e suba as túas fotos e vídeos aos álbums seleccionados en Immich", + "sync_upload_album_setting_subtitle": "Crear e subir as súas fotos e vídeos aos álbums seleccionados en Immich", "tag": "Etiqueta", "tag_assets": "Etiquetar activos", "tag_created": "Etiqueta creada: {tag}", @@ -1728,8 +1885,8 @@ "template": "Modelo", "theme": "Tema", "theme_selection": "Selección de tema", - "theme_selection_description": "Establecer automaticamente o tema a claro ou escuro baseándose na preferencia do sistema do teu navegador", - "theme_setting_asset_list_storage_indicator_title": "Mostrar indicador de almacenamento nas tellas de activos", + "theme_selection_description": "Establecer automaticamente o tema a claro ou escuro baseándose na preferencia do sistema do seu navegador", + "theme_setting_asset_list_storage_indicator_title": "Mostrar indicador de almacenamento nas celas de activos", "theme_setting_asset_list_tiles_per_row_title": "Número de activos por fila ({count})", "theme_setting_colorful_interface_subtitle": "Aplicar cor primaria ás superficies de fondo.", "theme_setting_colorful_interface_title": "Interface colorida", @@ -1757,12 +1914,12 @@ "total_usage": "Uso total", "trash": "Lixo", "trash_all": "Mover Todo ao Lixo", - "trash_count": "Lixo {count, number}", + "trash_count": "Lixo ({count, number})", "trash_delete_asset": "Mover ao Lixo/Eliminar Activo", "trash_emptied": "Lixo baleirado", "trash_no_results_message": "As fotos e vídeos movidos ao lixo aparecerán aquí.", "trash_page_delete_all": "Eliminar Todo", - "trash_page_empty_trash_dialog_content": "Queres baleirar os teus activos no lixo? Estes elementos eliminaranse permanentemente de Immich", + "trash_page_empty_trash_dialog_content": "Quere baleirar os seus activos no lixo? Estes elementos eliminaranse permanentemente de Immich", "trash_page_info": "Os elementos no lixo eliminaranse permanentemente despois de {days} días", "trash_page_no_assets": "Non hai activos no lixo", "trash_page_restore_all": "Restaurar Todo", @@ -1783,8 +1940,8 @@ "unlinked_oauth_account": "Conta OAuth desvinculada", "unmute_memories": "Desilenciar Recordos", "unnamed_album": "Álbum Sen Nome", - "unnamed_album_delete_confirmation": "Estás seguro de que queres eliminar este álbum?", - "unnamed_share": "Compartir Sen Nome", + "unnamed_album_delete_confirmation": "Está seguro de que quere eliminar este álbum?", + "unnamed_share": "Compartición Sen Nome", "unsaved_change": "Cambio sen gardar", "unselect_all": "Deseleccionar todo", "unselect_all_duplicates": "Deseleccionar todos os duplicados", @@ -1794,25 +1951,29 @@ "updated_password": "Contrasinal actualizado", "upload": "Subir", "upload_concurrency": "Concorrencia de subida", - "upload_dialog_info": "Queres facer copia de seguridade do(s) Activo(s) seleccionado(s) no servidor?", + "upload_dialog_info": "Quere facer copia de seguridade do(s) Activo(s) seleccionado(s) no servidor?", "upload_dialog_title": "Subir Activo", - "upload_errors": "Subida completada con {count, plural, one {# erro} other {# erros}}, actualice a páxina para ver os novos activos subidos.", + "upload_errors": "Subida completada con {count, plural, one {# erro} other {# erros}}. Actualice a páxina para ver os novos activos subidos.", "upload_progress": "Restantes {remaining, number} - Procesados {processed, number}/{total, number}", "upload_skipped_duplicates": "Omitidos {count, plural, one {# activo duplicado} other {# activos duplicados}}", "upload_status_duplicates": "Duplicados", "upload_status_errors": "Erros", "upload_status_uploaded": "Subido", - "upload_success": "Subida exitosa, actualice a páxina para ver os novos activos subidos.", + "upload_success": "Subida exitosa. Actualice a páxina para ver os novos activos subidos.", "upload_to_immich": "Subir a Immich ({count})", "uploading": "Subindo", "usage": "Uso", "use_current_connection": "usar conexión actual", "use_custom_date_range": "Usar rango de datas personalizado no seu lugar", "user": "Usuario", + "user_has_been_deleted": "Este usuario foi eliminado.", "user_id": "ID de Usuario", "user_liked": "A {user} gustoulle {type, select, photo {esta foto} video {este vídeo} asset {este activo} other {isto}}", - "user_purchase_settings": "Compra", - "user_purchase_settings_description": "Xestionar a túa compra", + "user_pin_code_settings": "Código PIN", + "user_pin_code_settings_description": "Xestionar seu código PIN", + "user_privacy": "Privacidade do usuario", + "user_purchase_settings": "Contribución", + "user_purchase_settings_description": "Xestionar a súa contribución", "user_role_set": "Establecer {user} como {role}", "user_usage_detail": "Detalle de uso do usuario", "user_usage_stats": "Estatísticas de uso da conta", @@ -1822,9 +1983,10 @@ "utilities": "Utilidades", "validate": "Validar", "validate_endpoint_error": "Por favor, introduza unha URL válida", + "variables": "Variables", "version": "Versión", "version_announcement_closing": "O seu amigo, Alex", - "version_announcement_message": "Ola! Unha nova versión de Immich está dispoñible. Por favor, toma un tempo para ler as notas de lanzamento para asegurarse de que a túa configuración está actualizada para evitar calquera configuración incorrecta, especialmente se usas WatchTower ou calquera mecanismo que xestione a actualización automática da túa instancia de Immich.", + "version_announcement_message": "Ola! Unha nova versión de Immich está dispoñible. Por favor, tome un tempo para ler as notas de lanzamento para asegurarse de que a súa configuración está actualizada para evitar calquera configuración incorrecta, especialmente se usa WatchTower ou calquera mecanismo que xestione a actualización automática da súa instancia de Immich.", "version_history": "Historial de Versións", "version_history_item": "Instalado {version} o {date}", "video": "Vídeo", @@ -1836,6 +1998,7 @@ "view_album": "Ver Álbum", "view_all": "Ver Todo", "view_all_users": "Ver todos os usuarios", + "view_details": "Ver detalles", "view_in_timeline": "Ver na liña de tempo", "view_link": "Ver ligazón", "view_links": "Ver ligazóns", @@ -1857,7 +2020,7 @@ "year": "Ano", "years_ago": "Hai {years, plural, one {# ano} other {# anos}}", "yes": "Si", - "you_dont_have_any_shared_links": "Non tes ningunha ligazón compartida", - "your_wifi_name": "O nome da túa wifi", + "you_dont_have_any_shared_links": "Non ten ningunha ligazón compartida", + "your_wifi_name": "O nome da súa wifi", "zoom_image": "Ampliar Imaxe" } diff --git a/i18n/he.json b/i18n/he.json index 3bf4f703e8..458062832f 100644 --- a/i18n/he.json +++ b/i18n/he.json @@ -15,7 +15,7 @@ "add_a_name": "הוספת שם", "add_a_title": "הוספת כותרת", "add_birthday": "הוספת יום הולדת", - "add_endpoint": "הוסף נקודת קצה", + "add_endpoint": "הוסף כתובת URL", "add_exclusion_pattern": "הוספת דפוס החרגה", "add_import_path": "הוספת נתיב יבוא", "add_location": "הוספת מיקום", @@ -28,9 +28,10 @@ "add_to_album": "הוספה לאלבום", "add_to_album_bottom_sheet_added": "נוסף ל {album}", "add_to_album_bottom_sheet_already_exists": "כבר ב {album}", + "add_to_album_bottom_sheet_some_local_assets": "לא ניתן היה להוסיף חלק מהקבצים המקומיים לאלבום", "add_to_album_toggle": "החלפת מצב בחירה עבור {album}", "add_to_albums": "הוספה לאלבומים", - "add_to_albums_count": "Add to albums ({count})", + "add_to_albums_count": "הוסף ({count}) לאלבום", "add_to_shared_album": "הוספה לאלבום משותף", "add_url": "הוספת קישור", "added_to_archive": "נוסף לארכיון", @@ -124,8 +125,12 @@ "logging_level_description": "כאשר פועל, באיזה רמת יומן לתעד.", "logging_settings": "רישום ביומן", "machine_learning_availability_checks": "בדיקת זמינות", + "machine_learning_availability_checks_description": "זהה ותעדף אוטומטית שרתי למידת מכונה זמינים", "machine_learning_availability_checks_enabled": "הפעלת בדיקות זמינות", "machine_learning_availability_checks_interval": "תזמון בדיקה", + "machine_learning_availability_checks_interval_description": "מרווח זמן במילישניות בין בדיקות זמינות", + "machine_learning_availability_checks_timeout": "פסק זמן לבקשה", + "machine_learning_availability_checks_timeout_description": "פסק זמן במילישניות עבור בדיקות זמינות", "machine_learning_clip_model": "מודל CLIP", "machine_learning_clip_model_description": "שמו של מודל CLIP רשום כאן. שים לב שעליך להפעיל מחדש את המשימה 'חיפוש חכם' עבור כל התמונות בעת שינוי מודל.", "machine_learning_duplicate_detection": "איתור כפילויות", @@ -399,7 +404,7 @@ "advanced_settings_proxy_headers_title": "כותרות פרוקסי", "advanced_settings_readonly_mode_subtitle": "מאפשר את מצב לקריאה בלבד בו התמונות ניתנות לצפייה בלבד, דברים כמו בחירת תמונות מרובות, שיתוף, שידור, מחיקה הם כולם מושבתים. אפשר/השבת מצב לקריאה בלבד באמצעות יצגן המשתמש מהמסך הראשי", "advanced_settings_readonly_mode_title": "מצב לקריאה בלבד", - "advanced_settings_self_signed_ssl_subtitle": "מדלג על אימות תעודת SSL עבור נקודת הקצה של השרת. דרוש עבור תעודות בחתימה עצמית.", + "advanced_settings_self_signed_ssl_subtitle": "מדלג על אימות תעודת SSL עבור כתובת URL של השרת. דרוש עבור תעודות בחתימה עצמית.", "advanced_settings_self_signed_ssl_title": "התר תעודות SSL בחתימה עצמית", "advanced_settings_sync_remote_deletions_subtitle": "מחק או שחזר תמונה במכשיר זה באופן אוטומטי כאשר פעולה זו נעשית בדפדפן", "advanced_settings_sync_remote_deletions_title": "סנכרן מחיקות שבוצעו במכשירים אחרים [נסיוני]", @@ -498,6 +503,8 @@ "asset_restored_successfully": "תמונה שוחזרה בהצלחה", "asset_skipped": "דילג", "asset_skipped_in_trash": "באשפה", + "asset_trashed": "התמונה הועברה לאשפה", + "asset_troubleshoot": "פתרון בעיות בתמונות", "asset_uploaded": "הועלה", "asset_uploading": "מעלה…", "asset_viewer_settings_subtitle": "ניהול הגדרות מציג הגלריה שלך", @@ -531,8 +538,10 @@ "autoplay_slideshow": "מצגת תמונות אוטומטית", "back": "חזרה", "back_close_deselect": "חזור, סגור, או בטל בחירה", + "background_backup_running_error": "גיבוי ברקע פועל כעת, לא ניתן להפעיל גיבוי ידני", "background_location_permission": "הרשאת מיקום ברקע", "background_location_permission_content": "כדי להחליף רשתות בעת ריצה ברקע, היישום צריך *תמיד* גישה למיקום מדויק על מנת לקרוא את השם של רשת האינטרנט האלחוטי", + "background_options": "אפשרויות רקע", "backup": "גיבוי", "backup_album_selection_page_albums_device": "({count}) אלבומים במכשיר", "backup_album_selection_page_albums_tap": "הקש כדי לכלול, הקש פעמיים כדי להחריג", @@ -540,6 +549,7 @@ "backup_album_selection_page_select_albums": "בחירת אלבומים", "backup_album_selection_page_selection_info": "פרטי בחירה", "backup_album_selection_page_total_assets": "סה״כ תמונות ייחודיות", + "backup_albums_sync": "סנכרון אלבומי גיבוי", "backup_all": "הכל", "backup_background_service_backup_failed_message": "נכשל בגיבוי תמונות. מנסה שוב…", "backup_background_service_connection_failed_message": "נכשל בהתחברות לשרת. מנסה שוב…", @@ -582,13 +592,14 @@ "backup_controller_page_start_backup": "התחל גיבוי", "backup_controller_page_status_off": "גיבוי חזית אוטומטי כבוי", "backup_controller_page_status_on": "גיבוי חזית אוטומטי מופעל", - "backup_controller_page_storage_format": "{used}מתוך {total} בשימוש", + "backup_controller_page_storage_format": "{used} מתוך {total} בשימוש", "backup_controller_page_to_backup": "אלבומים לגבות", "backup_controller_page_total_sub": "כל התמונות והסרטונים הייחודיים מאלבומים שנבחרו", "backup_controller_page_turn_off": "כיבוי גיבוי חזית", "backup_controller_page_turn_on": "הפעל גיבוי חזית", "backup_controller_page_uploading_file_info": "מעלה מידע על הקובץ", "backup_err_only_album": "לא ניתן להסיר את האלבום היחיד", + "backup_error_sync_failed": "הסינכרון נכשל. לא ניתן להשלים את הגיבוי.", "backup_info_card_assets": "תמונות", "backup_manual_cancelled": "בוטל", "backup_manual_in_progress": "העלאה כבר בתהליך. נסה אחרי זמן מה", @@ -656,6 +667,8 @@ "change_pin_code": "שנה קוד PIN", "change_your_password": "החלף את הסיסמה שלך", "changed_visibility_successfully": "הנראות שונתה בהצלחה", + "charging": "טוען", + "charging_requirement_mobile_backup": "גיבוי ברקע דורש טעינה של המכשיר", "check_corrupt_asset_backup": "בדוק גיבויים פגומים של תמונות", "check_corrupt_asset_backup_button": "בצע בדיקה", "check_corrupt_asset_backup_description": "הרץ בדיקה זו רק על Wi-Fi ולאחר שכל התמונות גובו. ההליך עשוי לקחת כמה דקות.", @@ -742,6 +755,7 @@ "create_user": "צור משתמש", "created": "נוצר", "created_at": "נוצר", + "creating_linked_albums": "יוצר אלבומים מקושרים...", "crop": "חתוך", "curated_object_page_title": "דברים", "current_device": "מכשיר נוכחי", @@ -891,7 +905,9 @@ "error": "שגיאה", "error_change_sort_album": "שינוי סדר מיון אלבום נכשל", "error_delete_face": "שגיאה במחיקת פנים מתמונה", + "error_getting_places": "שגיאה בקבלת מקומות", "error_loading_image": "שגיאה בטעינת התמונה", + "error_loading_partners": "שגיאה בטעינת שותפים: {error}", "error_saving_image": "שגיאה: {error}", "error_tag_face_bounding_box": "שגיאה בתיוג הפנים – לא ניתן לקבל את קואורדינטות המסגרת", "error_title": "שגיאה - משהו השתבש", @@ -1056,6 +1072,7 @@ "favorites_page_no_favorites": "לא נמצאו תמונות מועדפים", "feature_photo_updated": "תמונה מייצגת עודכנה", "features": "תכונות", + "features_in_development": "תכונות בפיתוח", "features_setting_description": "ניהול תכונות היישום", "file_name": "שם הקובץ", "file_name_or_extension": "שם קובץ או סיומת", @@ -1220,6 +1237,7 @@ "local": "מקומי", "local_asset_cast_failed": "לא ניתן לשדר תמונה שלא הועלתה לשרת", "local_assets": "תמונות מקומיות", + "local_media_summary": "סיכום של מדיה מקומית", "local_network": "רשת מקומית", "local_network_sheet_info": "היישום יתחבר לשרת דרך הכתובת הזאת כאשר משתמשים ברשת האינטרנט האלחוטי שמצוינת", "location_permission": "הרשאת מיקום", @@ -1231,6 +1249,7 @@ "location_picker_longitude_hint": "הזן את קו האורך שלך כאן", "lock": "נעל", "locked_folder": "תיקיה נעולה", + "log_detail_title": "פרטי יומן", "log_out": "התנתק", "log_out_all_devices": "התנתק מכל המכשירים", "logged_in_as": "מחובר כ {user}", @@ -1242,7 +1261,7 @@ "login_form_back_button_text": "חזרה", "login_form_email_hint": "yourmail@email.com", "login_form_endpoint_hint": "http://your-server-ip:port", - "login_form_endpoint_url": "כתובת נקודת קצה השרת", + "login_form_endpoint_url": "כתובת URL של השרת", "login_form_err_http": "נא לציין //:http או //:https", "login_form_err_invalid_email": "דוא\"ל שגוי", "login_form_err_invalid_url": "כתובת לא חוקית", @@ -1261,6 +1280,7 @@ "login_password_changed_success": "סיסמה עודכנה בהצלחה", "logout_all_device_confirmation": "האם באמת ברצונך להתנתק מכל המכשירים?", "logout_this_device_confirmation": "האם באמת ברצונך להתנתק מהמכשיר הזה?", + "logs": "יומנים", "longitude": "קו אורך", "look": "מראה", "loop_videos": "הפעלה חוזרת של סרטונים", @@ -1303,6 +1323,7 @@ "mark_as_read": "סמן כנקרא", "marked_all_as_read": "כל ההתראות סומנו כנקראו", "matches": "התאמות", + "matching_assets": "תמונות תואמות", "media_type": "סוג מדיה", "memories": "זכרונות", "memories_all_caught_up": "ראית הכל", @@ -1343,9 +1364,10 @@ "name_or_nickname": "שם או כינוי", "network_requirement_photos_upload": "השתמש בנתונים ניידים לגיבוי תמונות", "network_requirement_videos_upload": "השתמש בנתונים ניידים לגיבוי סרטונים", + "network_requirements": "דרישות רשת", "network_requirements_updated": "דרישות הרשת השתנו, תור הגיבוי אופס", "networking_settings": "רשת", - "networking_subtitle": "ניהול הגדרות נקודת קצה שרת", + "networking_subtitle": "ניהול הגדרות כתובת URL של השרת", "never": "אף פעם", "new_album": "אלבום חדש", "new_api_key": "מפתח API חדש", @@ -1353,6 +1375,7 @@ "new_person": "אדם חדש", "new_pin_code": "קוד PIN חדש", "new_pin_code_subtitle": "זאת הפעם הראשונה שנכנסת לתיקיה הנעולה. צור קוד PIN כדי לאבטח את הגישה לדף זה", + "new_timeline": "ציר הזמן החדש", "new_user_created": "משתמש חדש נוצר", "new_version_available": "גרסה חדשה זמינה", "newest_first": "החדש ביותר ראשון", @@ -1366,20 +1389,25 @@ "no_assets_message": "לחץ כדי להעלות את התמונה הראשונה שלך", "no_assets_to_show": "אין תמונות להצגה", "no_cast_devices_found": "לא נמצאו מכשירי שידור", + "no_checksum_local": "אין Checksum זמין - לא ניתן לאחזר תמונות מקומיות", + "no_checksum_remote": "אין Checksum זמין - לא ניתן לאחזר תמונות מהשרת", "no_duplicates_found": "לא נמצאו כפילויות.", "no_exif_info_available": "אין מידע זמין על מטא-נתונים (exif)", "no_explore_results_message": "העלה תמונות נוספות כדי לחקור את האוסף שלך.", "no_favorites_message": "הוסף מועדפים כדי למצוא במהירות את התמונות והסרטונים הכי טובים שלך", "no_libraries_message": "צור ספרייה חיצונית כדי לראות את התמונות והסרטונים שלך", + "no_local_assets_found": "לא נמצאו תמונות עם Checksum זהה", "no_locked_photos_message": "תמונות וסרטונים בתיקייה הנעולה מוסתרים ולא יופיעו בזמן הגלישה או החיפוש בספרייה שלך.", "no_name": "אין שם", "no_notifications": "אין התראות", "no_people_found": "לא נמצאו אנשים תואמים", "no_places": "אין מקומות", + "no_remote_assets_found": "לא נמצאו תמונות בשרת עם Checksum זהה", "no_results": "אין תוצאות", "no_results_description": "נסה להשתמש במילה נרדפת או במילת מפתח יותר כללית", "no_shared_albums_message": "צור אלבום כדי לשתף תמונות וסרטונים עם אנשים ברשת שלך", "no_uploads_in_progress": "אין העלאות בתהליך", + "not_available": "לא רלוונטי", "not_in_any_album": "לא בשום אלבום", "not_selected": "לא נבחרו", "note_apply_storage_label_to_previously_uploaded assets": "הערה: כדי להחיל את תווית האחסון על תמונות שהועלו בעבר, הפעל את", @@ -1475,7 +1503,7 @@ "permission_onboarding_permission_limited": "הרשאה מוגבלת. כדי לתת ליישום לגבות ולנהל את כל אוסף הגלריה שלך, הענק הרשאה לתמונות וסרטונים בהגדרות.", "permission_onboarding_request": "היישום דורש הרשאה כדי לראות את התמונות והסרטונים שלך.", "person": "אדם", - "person_age_months": "בגיל {months, plural, one {חודש #} other {# חודשים}}", + "person_age_months": "{months, plural, one {חודש #} other {# חודשים}}", "person_age_year_months": "בגיל שנה ו{months, plural, one {חודש #} other {# חודשים}}", "person_age_years": "בגיל {years, plural, other {# שנים}}", "person_birthdate": "נולד בתאריך {date}", @@ -1501,6 +1529,7 @@ "port": "יציאה", "preferences_settings_subtitle": "ניהול העדפות יישום", "preferences_settings_title": "העדפות", + "preparing": "בהכנה", "preset": "הגדרות קבועות מראש", "preview": "תצוגה מקדימה", "previous": "הקודם", @@ -1741,7 +1770,7 @@ "selected_gps_coordinates": "קואורדינטות GPS שנבחרו", "send_message": "שלח הודעה", "send_welcome_email": "שלח דוא\"ל קבלת פנים", - "server_endpoint": "נקודת קצה שרת", + "server_endpoint": "כתובת URL של השרת", "server_info_box_app_version": "גרסת יישום", "server_info_box_server_url": "כתובת שרת", "server_offline": "השרת מנותק", @@ -1867,6 +1896,7 @@ "show_slideshow_transition": "הצג מעבר מצגת", "show_supporter_badge": "תג תומך", "show_supporter_badge_description": "הצג תג תומך", + "show_text_search_menu": "הצג תפריט חיפוש טקסט", "shuffle": "ערבוב", "sidebar": "סרגל צד", "sidebar_display_description": "הצג קישור לתצוגה בסרגל הצד", @@ -1897,6 +1927,7 @@ "stacktrace": "Stack trace", "start": "התחל", "start_date": "תאריך התחלה", + "start_date_before_end_date": "תאריך ההתחלה חייב להיות לפני תאריך הסיום", "state": "מדינה", "status": "מצב", "stop_casting": "הפסקת שידור", @@ -1920,8 +1951,8 @@ "sync_albums": "סנכרן אלבומים", "sync_albums_manual_subtitle": "סנכרן את כל הסרטונים והתמונות שהועלו לאלבומי הגיבוי שנבחרו", "sync_local": "סנכרן מקומי", - "sync_remote": "סנכרן נקודת קצה מרוחקת", - "sync_status": "סנכרן מצב", + "sync_remote": "סנכרן מהשרת", + "sync_status": "סטטוס סנכרון", "sync_status_subtitle": "הצג ונהל את מערכת הסנכרון", "sync_upload_album_setting_subtitle": "צור והעלה תמונות וסרטונים שלך לאלבומים שנבחרו ביישום", "tag": "תג", @@ -2056,7 +2087,7 @@ "users_added_to_album_count": "נוספו {count, plural, one {משתמש #} other {# משתמשים}} לאלבום", "utilities": "כלים", "validate": "לאמת", - "validate_endpoint_error": "נא להזין כתובת תקנית", + "validate_endpoint_error": "נא להזין כתובת URL תקנית", "variables": "משתנים", "version": "גרסה", "version_announcement_closing": "החבר שלך, אלכס", @@ -2099,5 +2130,6 @@ "yes": "כן", "you_dont_have_any_shared_links": "אין לך קישורים משותפים", "your_wifi_name": "שם אינטרנט אלחוטי שלך", - "zoom_image": "זום לתמונה" + "zoom_image": "זום לתמונה", + "zoom_to_bounds": "התמקד באזור" } diff --git a/i18n/hi.json b/i18n/hi.json index 385a85c2e2..aaf5bcbe7a 100644 --- a/i18n/hi.json +++ b/i18n/hi.json @@ -28,6 +28,7 @@ "add_to_album": "एल्बम में डालें", "add_to_album_bottom_sheet_added": "{album} में डालें", "add_to_album_bottom_sheet_already_exists": "{album} में पहले से है", + "add_to_albums": "एकाधिक एल्बम में डाले", "add_to_shared_album": "शेयर किए गए एल्बम में डालें", "add_url": "URL डालें", "added_to_archive": "संग्रहीत कर दिया गया है", diff --git a/i18n/hr.json b/i18n/hr.json index 32331976c1..4e54541c2d 100644 --- a/i18n/hr.json +++ b/i18n/hr.json @@ -15,9 +15,9 @@ "add_a_name": "Dodaj ime", "add_a_title": "Dodaj naslov", "add_birthday": "Dodaj rođendan", - "add_endpoint": "Dodaj krajnju točnu", + "add_endpoint": "Dodaj krajnju točku", "add_exclusion_pattern": "Dodaj uzorak izuzimanja", - "add_import_path": "Dodaj import folder", + "add_import_path": "Dodaj putanju uvoza", "add_location": "Dodaj lokaciju", "add_more_users": "Dodaj još korisnika", "add_partner": "Dodaj partnera", @@ -28,35 +28,37 @@ "add_to_album": "Dodaj u album", "add_to_album_bottom_sheet_added": "Dodano u {album}", "add_to_album_bottom_sheet_already_exists": "Već u {album}", + "add_to_album_bottom_sheet_some_local_assets": "Neke lokalne stavke nije moguće dodati u album", "add_to_album_toggle": "Uključi/isključi odabir za {album}", "add_to_albums": "Dodaj u albume", "add_to_albums_count": "Dodaj u albume ({count})", "add_to_shared_album": "Dodaj u dijeljeni album", + "add_upload_to_stack": "Dodaj preneseno u skup", "add_url": "Dodaj URL", "added_to_archive": "Dodano u arhivu", "added_to_favorites": "Dodano u omiljeno", "added_to_favorites_count": "Dodano {count, number} u omiljeno", "admin": { - "add_exclusion_pattern_description": "Dodajte uzorke izuzimanja. Globiranje pomoću *, ** i ? je podržano. Za ignoriranje svih datoteka u bilo kojem direktoriju pod nazivom \"Raw\", koristite \"**/Raw/**\". Da biste zanemarili sve datoteke koje završavaju na \".tif\", koristite \"**/*.tif\". Da biste zanemarili apsolutni put, koristite \"/path/to/ignore/**\".", + "add_exclusion_pattern_description": "Dodajte uzorke izuzimanja. Globiranje pomoću *, ** i ? je podržano. Za ignoriranje svih datoteka u bilo kojem direktoriju pod nazivom \"Raw\", koristite \"**/Raw/**\". Kako biste ignorirali sve datoteke koje završavaju na \".tif\", koristite \"**/*.tif\". Kako biste ignorirali apsolutnu putanju, koristite \"/putanja/za/ignoriranje/**\".", "admin_user": "Administrator", - "asset_offline_description": "Ovo sredstvo vanjske knjižnice više nije pronađeno na disku i premješteno je u smeće. Ako je datoteka premještena unutar biblioteke, provjerite svoju vremensku traku za novo odgovarajuće sredstvo. Da biste vratili ovo sredstvo, provjerite može li Immich pristupiti donjoj stazi datoteke i skenirajte biblioteku.", - "authentication_settings": "Postavke autentikacije", - "authentication_settings_description": "Uredi lozinku, OAuth, i druge postavke autentikacije", - "authentication_settings_disable_all": "Jeste li sigurni da želite onemogućenit sve načine prijave? Prijava će biti potpuno onemogućena.", - "authentication_settings_reenable": "Za ponovno uključivanje upotrijebite naredbu poslužitelja.", + "asset_offline_description": "Ova stavka vanjske biblioteke nije pronađena na disku i premještena je u smeće. Ako je datoteka premještena unutar biblioteke, provjerite svoju vremensku traku za novu odgovarajuću stavku. Da biste vratili ovu stavku, provjerite može li Immich pristupiti donjoj putanji datoteke i skenirajte biblioteku.", + "authentication_settings": "Postavke autentifikacije", + "authentication_settings_description": "Upravljajte lozinkom, OAuthom i drugim postavkama autentifikacije", + "authentication_settings_disable_all": "Jeste li sigurni da želite onemogućiti sve načine prijave? Prijava će biti potpuno onemogućena.", + "authentication_settings_reenable": "Za ponovno uključivanje upotrijebite naredbu servera.", "background_task_job": "Pozadinski zadaci", "backup_database": "Kreiraj sigurnosnu kopiju baze podataka", "backup_database_enable_description": "Omogućite sigurnosne kopije baze podataka", "backup_keep_last_amount": "Količina prethodnih sigurnosnih kopija za čuvanje", "backup_onboarding_1_description": "kopija izvan lokacije u oblaku ili na drugoj fizičkoj lokaciji.", - "backup_onboarding_2_description": "lokalne kopije na različitim uređajima. To uključuje glavne datoteke i sigurnosnu kopiju tih datoteka lokalno.", + "backup_onboarding_2_description": "lokalne kopije na različitim uređajima. To uključuje glavne datoteke i lokalnu sigurnosnu kopiju tih datoteka.", "backup_onboarding_3_description": "ukupne kopije vaših podataka, uključujući izvorne datoteke. To uključuje 1 kopiju izvan lokacije i 2 lokalne kopije.", "backup_onboarding_description": "Preporučuje se 3-2-1 strategija sigurnosnog kopiranja za zaštitu vaših podataka. Trebali biste čuvati kopije svojih prenesenih fotografija/videozapisa kao i Immich bazu podataka za sveobuhvatno rješenje sigurnosne kopije.", - "backup_onboarding_footer": "Za više informacija o sigurnosnom kopiranju Immich, molimo pogledajte dokumentaciju.", + "backup_onboarding_footer": "Za više informacija o sigurnosnom kopiranju Immicha, molimo pogledajte dokumentaciju.", "backup_onboarding_parts_title": "3-2-1 sigurnosna kopija uključuje:", "backup_onboarding_title": "Sigurnosne kopije", - "backup_settings": "Postavke sigurnosne kopije", - "backup_settings_description": "Upravljajte postavkama izvoza baze podataka.", + "backup_settings": "Postavke sigurnosne kopije baze podataka", + "backup_settings_description": "Upravljajte postavkama sigurnosne kopije baze podataka.", "cleared_jobs": "Izbrisani poslovi za: {job}", "config_set_by_file": "Konfiguracija je trenutno postavljena konfiguracijskom datotekom", "confirm_delete_library": "Jeste li sigurni da želite izbrisati biblioteku {library}?", @@ -65,23 +67,23 @@ "confirm_reprocess_all_faces": "Jeste li sigurni da želite ponovno obraditi sva lica? Ovo će također obrisati imenovane osobe.", "confirm_user_password_reset": "Jeste li sigurni da želite poništiti lozinku korisnika {user}?", "confirm_user_pin_code_reset": "Jeste li sigurni da želite resetirati PIN korisnika {user}?", - "create_job": "Izradi zadatak", - "cron_expression": "Cron izraz (expression)", + "create_job": "Stvori posao", + "cron_expression": "Cron izraz", "cron_expression_description": "Postavite interval skeniranja koristeći cron format. Za više informacija pogledajte npr. Crontab Guru", - "cron_expression_presets": "Cron unaprijed postavljene postavke izraza", + "cron_expression_presets": "Unaprijed postavljene postavke cron izraza", "disable_login": "Onemogući prijavu", - "duplicate_detection_job_description": "Pokrenite strojno učenje na materijalima kako biste otkrili slične slike. Oslanja se na Pametno Pretraživanje", - "exclusion_pattern_description": "Uzorci izuzimanja omogućuju vam da zanemarite datoteke i mape prilikom skeniranja svoje biblioteke. Ovo je korisno ako imate mape koje sadrže datoteke koje ne želite uvesti, kao što su RAW datoteke.", - "external_library_management": "Upravljanje vanjskom knjižnicom", + "duplicate_detection_job_description": "Pokrenite strojno učenje na stavkama kako biste otkrili slične slike. Oslanja se na Pametno pretraživanje", + "exclusion_pattern_description": "Uzorci izuzimanja omogućuju vam da ignorirate datoteke i mape prilikom skeniranja svoje biblioteke. Ovo je korisno ako imate mape koje sadrže datoteke koje ne želite uvesti, kao što su RAW datoteke.", + "external_library_management": "Upravljanje vanjskom bibliotekom", "face_detection": "Detekcija lica", - "face_detection_description": "Prepoznajte lica u sredstvima pomoću strojnog učenja. Za videozapise u obzir se uzima samo minijaturni prikaz. \"Sve\" (ponovno) obrađuje svu imovinu. \"Nedostaje\" stavlja u red čekanja sredstva koja još nisu obrađena. Otkrivena lica bit će stavljena u red čekanja za prepoznavanje lica nakon dovršetka prepoznavanja lica, grupirajući ih u postojeće ili nove osobe.", - "facial_recognition_job_description": "Grupirajte otkrivena lica u osobe. Ovaj se korak pokreće nakon dovršetka prepoznavanja lica. \"Sve\" (ponovno) grupira sva lica. \"Nedostajuća\" lica u redovima kojima nije dodijeljena osoba.", + "face_detection_description": "Detektirajte lica u stavkama pomoću strojnog učenja. Za videozapise se uzima u obzir samo sličica. \"Osvježi\" (ponovno) obrađuje sve stavke. \"Poništi\" dodatno briše sve trenutne podatke o licu. \"Nedostaje\" stavlja u red čekanja stavke koje još nisu obrađene. Detektirana lica bit će stavljena u red čekanja za Prepoznavanje lica nakon što se dovrši Detekcija lica, grupirajući ih u postojeće ili nove osobe.", + "facial_recognition_job_description": "Grupirajte otkrivena lica u osobe. Ovaj korak se izvršava nakon što je Detekcija lica dovršena. \"Resetiraj\" (ponovno) grupira sva lica. \"Nedostaje\" stavlja u red lica kojima nije dodijeljena osoba.", "failed_job_command": "Naredba {command} nije uspjela za posao: {job}", - "force_delete_user_warning": "UPOZORENJE: Ovo će odmah ukloniti korisnika i sve pripadajuće podatke. Ovo se ne može poništiti i datoteke se ne mogu vratiti.", + "force_delete_user_warning": "UPOZORENJE: Ovo će odmah ukloniti korisnika i sve pripadajuće stavke. Ovo se ne može poništiti i datoteke se ne mogu vratiti.", "image_format": "Format", "image_format_description": "WebP proizvodi manje datoteke od JPEG-a, ali se sporije kodira.", - "image_fullsize_description": "Slika pune veličine bez meta podataka, koristi se prilikom zumiranja", - "image_fullsize_enabled": "Omogući generiranje slike pune veličine", + "image_fullsize_description": "Slika pune veličine bez metapodataka, koristi se prilikom zumiranja", + "image_fullsize_enabled": "Omogući generiranje slika pune veličine", "image_fullsize_enabled_description": "Generiraj sliku pune veličine za formate koji nisu prilagođeni webu. Kada je opcija \"Preferiraj ugrađeni pregled\" omogućena, ugrađeni pregledi koriste se izravno bez konverzije. Ne utječe na formate prilagođene webu kao što je JPEG.", "image_fullsize_quality_description": "Kvaliteta slike pune veličine od 1 do 100. Veća vrijednost znači bolja kvaliteta, ali stvara veće datoteke.", "image_fullsize_title": "Postavke slike pune veličine", @@ -89,7 +91,7 @@ "image_prefer_embedded_preview_setting_description": "Koristite ugrađene preglede u RAW fotografije kao ulaz za obradu slike kada su dostupni. To može proizvesti preciznije boje za neke slike, ali kvaliteta pregleda ovisi o kameri i slika može imati više artifakta kompresije.", "image_prefer_wide_gamut": "Preferirajte široku gamu", "image_prefer_wide_gamut_setting_description": "Koristite Display P3 za sličice. Ovo bolje čuva živost slika sa širokim prostorima boja, ali slike mogu izgledati drugačije na starim uređajima sa starom verzijom preglednika. sRGB slike čuvaju se kao sRGB kako bi se izbjegle promjene boja.", - "image_preview_description": "Slika srednje veličine s ogoljenim metapodacima, koristi se prilikom pregledavanja jednog sredstva i za strojno učenje", + "image_preview_description": "Slika srednje veličine s uklonjenim metapodacima, koristi se prilikom pregledavanja jedne stavke i za strojno učenje", "image_preview_quality_description": "Kvaliteta pregleda od 1-100. Više je bolje, ali proizvodi veće datoteke i može smanjiti odziv aplikacije. Postavljanje niske vrijednosti može utjecati na kvalitetu strojnog učenja.", "image_preview_title": "Postavke pregleda", "image_quality": "Kvaliteta", @@ -116,27 +118,31 @@ "library_scanning_enable_description": "Omogući periodično skeniranje biblioteke", "library_settings": "Externa biblioteka", "library_settings_description": "Upravljajte postavkama vanjske biblioteke", - "library_tasks_description": "Skeniraj eksterne biblioteke za nove i/ili promijenjene resurse", + "library_tasks_description": "Skeniraj vanjske biblioteke za nove i/ili promijenjene stavke", "library_watching_enable_description": "Pratite vanjske biblioteke za promjena datoteke", "library_watching_settings": "Gledanje biblioteke (EKSPERIMENTALNO)", "library_watching_settings_description": "Automatsko praćenje promijenjenih datoteke", "logging_enable_description": "Omogući zapisivanje", "logging_level_description": "Kada je omogućeno, koju razinu zapisivanja koristiti.", "logging_settings": "Zapisivanje", + "machine_learning_availability_checks": "Provjere dostupnosti", + "machine_learning_availability_checks_enabled": "Omogući provjere dostupnosti", + "machine_learning_availability_checks_interval": "Provjeri interval", + "machine_learning_availability_checks_interval_description": "Interval u milisekundama između provjera dostupnosti", "machine_learning_clip_model": "CLIP model", "machine_learning_clip_model_description": "Naziv CLIP modela navedenog ovdje. Imajte na umu da morate ponovno pokrenuti posao 'Pametno Pretraživanje' za sve slike nakon promjene modela.", "machine_learning_duplicate_detection": "Detekcija Duplikata", "machine_learning_duplicate_detection_enabled": "Omogući detekciju duplikata", - "machine_learning_duplicate_detection_enabled_description": "Ako je onemogućeno, potpuno identična sredstva i dalje će biti deduplicirana.", + "machine_learning_duplicate_detection_enabled_description": "Ako je onemogućeno, potpuno identične stavke i dalje će biti deduplicirane.", "machine_learning_duplicate_detection_setting_description": "Upotrijebite CLIP ugradnje da biste pronašli vjerojatne duplikate", "machine_learning_enabled": "Uključi strojsko učenje", "machine_learning_enabled_description": "Ukoliko je ovo isključeno, sve funkcije strojnoga učenja biti će isključene bez obzira na postavke ispod.", - "machine_learning_facial_recognition": "Detekcija lica", + "machine_learning_facial_recognition": "Prepoznavanje lica", "machine_learning_facial_recognition_description": "Detektiraj, prepoznaj i grupiraj lica u fotografijama", "machine_learning_facial_recognition_model": "Model prepoznavanja lica", "machine_learning_facial_recognition_model_description": "Modeli su navedeni silaznim redoslijedom veličine. Veći modeli su sporiji i koriste više memorije, ali daju bolje rezultate. Imajte na umu da morate ponovno pokrenuti posao detekcije lica za sve slike nakon promjene modela.", "machine_learning_facial_recognition_setting": "Omogući prepoznavanje lica", - "machine_learning_facial_recognition_setting_description": "Ako je onemogućeno, slike neće biti kodirane za prepoznavanje lica i neće popuniti odjeljak Ljudi na stranici Istraživanje.", + "machine_learning_facial_recognition_setting_description": "Ako je onemogućeno, slike neće biti kodirane za prepoznavanje lica i neće popuniti odjeljak Osobe na stranici Istraži.", "machine_learning_max_detection_distance": "Maksimalna udaljenost za detektiranje", "machine_learning_max_detection_distance_description": "Maksimalna udaljenost između dvije slike da bi se smatrale duplikatima, u rasponu od 0,001-0,1. Više vrijednosti otkrit će više duplikata, ali mogu rezultirati netočnim rezultatima.", "machine_learning_max_recognition_distance": "Maksimalna udaljenost za detekciju", @@ -144,7 +150,7 @@ "machine_learning_min_detection_score": "Minimalni rezultat otkrivanja", "machine_learning_min_detection_score_description": "Minimalni rezultat pouzdanosti za detektirano lice od 0-1. Niže vrijednosti otkrit će više lica, ali mogu dovesti do lažno pozitivnih rezultata.", "machine_learning_min_recognized_faces": "Minimum prepoznatih lica", - "machine_learning_min_recognized_faces_description": "Najmanji broj prepoznatih lica za osobu koja se stvara. Povećanje toga čini prepoznavanje lica preciznijim po cijenu povećanja šanse da lice nije dodijeljeno osobi.", + "machine_learning_min_recognized_faces_description": "Najmanji broj prepoznatih lica za osobu koja se stvara. Povećanje toga čini Prepoznavanje lica preciznijim po cijenu povećanja šanse da lice nije dodijeljeno osobi.", "machine_learning_settings": "Postavke strojnog učenja", "machine_learning_settings_description": "Upravljajte značajkama i postavkama strojnog učenja", "machine_learning_smart_search": "Pametna pretraga", @@ -170,21 +176,21 @@ "memory_cleanup_job": "Čišćenje memorije", "memory_generate_job": "Generiranje memorije", "metadata_extraction_job": "Izdvoj metapodatke", - "metadata_extraction_job_description": "Izdvojite podatke o metapodacima iz svakog sredstva, kao što su GPS, lica i rezolucija", + "metadata_extraction_job_description": "Izdvojite metapodatke iz svake stavke, kao što su GPS, lica i rezolucija", "metadata_faces_import_setting": "Omogući uvoz lica", "metadata_faces_import_setting_description": "Uvezite lica iz EXIF podataka slike i sidecar datoteka", "metadata_settings": "Postavke Metapodataka", "metadata_settings_description": "Upravljanje postavkama metapodataka", "migration_job": "Migracija", - "migration_job_description": "Premjestite minijature za sredstva i lica u najnoviju strukturu mapa", + "migration_job_description": "Premjestite sličice za stavke i lica u najnoviju strukturu mapa", "nightly_tasks_cluster_faces_setting_description": "Pokreni prepoznavanje lica na novootkrivenim licima", "nightly_tasks_cluster_new_faces_setting": "Grupiraj nova lica", "nightly_tasks_database_cleanup_setting": "Zadaci čišćenja baze podataka", "nightly_tasks_database_cleanup_setting_description": "Očisti stare, istekle podatke iz baze podataka", "nightly_tasks_generate_memories_setting": "Generiraj uspomene", - "nightly_tasks_generate_memories_setting_description": "Stvori nove uspomene iz sadržaja", + "nightly_tasks_generate_memories_setting_description": "Stvori nove uspomene iz stavki", "nightly_tasks_missing_thumbnails_setting": "Generiraj nedostajuće sličice", - "nightly_tasks_missing_thumbnails_setting_description": "Stavi u red čekanja sadržaje bez sličica za generiranje sličica", + "nightly_tasks_missing_thumbnails_setting_description": "Stavke bez sličica stavi u red čekanja za generiranje sličica", "nightly_tasks_settings": "Postavke noćnih zadataka", "nightly_tasks_settings_description": "Upravljanje noćnim zadacima", "nightly_tasks_start_time_setting": "Vrijeme početka", @@ -193,7 +199,7 @@ "nightly_tasks_sync_quota_usage_setting_description": "Ažuriraj korisničku kvotu za pohranu na temelju trenutne potrošnje", "no_paths_added": "Nema dodanih putanja", "no_pattern_added": "Nije dodan uzorak", - "note_apply_storage_label_previous_assets": "Napomena: da biste primijenili Oznaku Pohrane na prethodno prenesena sredstva, pokrenite", + "note_apply_storage_label_previous_assets": "Napomena: Da biste primijenili Oznaku pohrane na prethodno prenesene stavke, pokrenite", "note_cannot_be_changed_later": "NAPOMENA: Ovo se ne može promijeniti kasnije!", "notification_email_from_address": "Od adrese", "notification_email_from_address_description": "E-mail adresa pošiljatelja, na primjer: \"Immich Photo Server \". Obavezno koristite adresu s koje vam je dopušteno slanje e-pošte.", @@ -260,21 +266,21 @@ "sidecar_job": "Sidecar metapodaci", "sidecar_job_description": "Otkrijte ili sinkronizirajte sidecar metapodatke iz datotečnog sustava", "slideshow_duration_description": "Broj sekundi za prikaz svake slike", - "smart_search_job_description": "Pokrenite strojno učenje na sredstvima za podršku pametnog pretraživanja", - "storage_template_date_time_description": "Vremenska oznaka stvaranja sredstva koristi se za informacije o datumu i vremenu", + "smart_search_job_description": "Pokrenite strojno učenje na stavkama za korištenje Pametnog pretraživanja", + "storage_template_date_time_description": "Vremenska oznaka stvaranja stavke koristi se za informacije o datumu i vremenu", "storage_template_date_time_sample": "Vrijeme uzorka {date}", "storage_template_enable_description": "Omogući mehanizam predloška za pohranu", "storage_template_hash_verification_enabled": "Omogućena hash provjera", "storage_template_hash_verification_enabled_description": "Omogućuje hash provjeru, nemojte je onemogućiti osim ako niste sigurni u implikacije", "storage_template_migration": "Migracija predloška za pohranu", - "storage_template_migration_description": "Primijenite trenutni {template} na prethodno prenesena sredstva", - "storage_template_migration_info": "Predložak za pohranu će sve nastavke (ekstenzije) pretvoriti u mala slova. Promjene predloška primjenjivat će se samo na nova sredstva. Za retroaktivnu primjenu predloška na prethodno prenesena sredstva, pokrenite {job}.", + "storage_template_migration_description": "Primijenite trenutni {template} na prethodno prenesene stavke", + "storage_template_migration_info": "Predložak za pohranu pretvorit će sve datotečne nastavke u mala slova. Promjene predloška primijenit će se samo na nove stavke. Da biste retroaktivno primijenili predložak na prethodno prenesene stavke, pokrenite {job}.", "storage_template_migration_job": "Posao Migracije Predloška Pohrane", "storage_template_more_details": "Za više pojedinosti o ovoj značajci pogledajte Predložak pohrane i njegove implikacije", "storage_template_onboarding_description_v2": "Kada je omogućena, ova će značajka automatski organizira datoteke prema predlošku koji je definirao korisnik. Za više informacija pogledajte dokumentaciju.", "storage_template_path_length": "Približno ograničenje duljine putanje: {length, number}/{limit, number}", "storage_template_settings": "Predložak pohrane", - "storage_template_settings_description": "Upravljajte strukturom mape i nazivom datoteke učitanog sredstva", + "storage_template_settings_description": "Upravljajte strukturom mape i nazivom datoteke učitane stavke", "storage_template_user_label": "{label} je korisnička oznaka za pohranu", "system_settings": "Postavke Sustava", "tag_cleanup_job": "Čišćenje oznaka", @@ -292,7 +298,7 @@ "theme_settings": "Postavke tema", "theme_settings_description": "Upravljajte prilagodbom Immich web sučelja", "thumbnail_generation_job": "Generirajte sličice", - "thumbnail_generation_job_description": "Generirajte velike, male i zamućene sličice za svaki materijal, kao i sličice za svaku osobu", + "thumbnail_generation_job_description": "Generirajte velike, male i zamućene sličice za svaku stavku, kao i sličice za svaku osobu", "transcoding_acceleration_api": "API ubrzanja", "transcoding_acceleration_api_description": "API koji će komunicirati s vašim uređajem radi ubrzanja transkodiranja. Ova postavka je 'najveći trud': vratit će se na softversko transkodiranje u slučaju kvara. VP9 može ili ne mora raditi ovisno o vašem hardveru.", "transcoding_acceleration_nvenc": "NVENC (zahtjeva NVIDIA GPU)", @@ -355,20 +361,20 @@ "transcoding_video_codec_description": "VP9 ima visoku učinkovitost i web-kompatibilnost, ali treba dulje za transkodiranje. HEVC ima sličnu izvedbu, ali ima slabiju web kompatibilnost. H.264 široko je kompatibilan i brzo se transkodira, ali proizvodi mnogo veće datoteke. AV1 je najučinkovitiji kodek, ali nema podršku na starijim uređajima.", "trash_enabled_description": "Omogućite značajke Smeća", "trash_number_of_days": "Broj dana", - "trash_number_of_days_description": "Broj dana za držanje sredstava u smeću prije njihovog trajnog uklanjanja", + "trash_number_of_days_description": "Broj dana za čuvanje stavki u smeću prije njihovog trajnog uklanjanja", "trash_settings": "Postavke Smeća", "trash_settings_description": "Upravljanje postavkama smeća", "unlink_all_oauth_accounts": "Odspoji sve OAuth račune", "unlink_all_oauth_accounts_description": "Zapamtite da odspojite sve OAuth račune prije prelaska na novog pružatelja usluge.", "unlink_all_oauth_accounts_prompt": "Jeste li sigurni da želite odspojiti sve OAuth račune? Ovo će resetirati OAuth ID za svakog korisnika i ne može se poništiti.", "user_cleanup_job": "Čišćenje korisnika", - "user_delete_delay": "Račun i sredstva korisnika {user} bit će zakazani za trajno brisanje za {delay, plural, one {# day} other {# days}}.", + "user_delete_delay": "Račun i stavke korisnika {user} bit će stavljeni u red čekanja trajnog brisanja za {delay, plural, one {# dan} other {# dana}}.", "user_delete_delay_settings": "Brisanje odgode", - "user_delete_delay_settings_description": "Broj dana nakon uklanjanja za trajno brisanje korisničkog računa i imovine. Posao brisanja korisnika pokreće se u ponoć kako bi se provjerili korisnici koji su spremni za brisanje. Promjene ove postavke bit će procijenjene pri sljedećem izvršavanju.", - "user_delete_immediately": "Račun i sredstva korisnika {user} bit će stavljeni u red čekanja za trajno brisanje odmah.", - "user_delete_immediately_checkbox": "Stavite korisnika i imovinu u red za trenutačno brisanje", + "user_delete_delay_settings_description": "Broj dana nakon uklanjanja za trajno brisanje korisničkog računa i stavki. Posao brisanja korisnika pokreće se u ponoć kako bi se provjerili korisnici koji su spremni za brisanje. Promjene ove postavke bit će procijenjene pri sljedećem izvršavanju.", + "user_delete_immediately": "Račun i stavke korisnika {user} bit će stavljeni u red čekanja za trajno brisanje odmah.", + "user_delete_immediately_checkbox": "Stavite korisnika i stavke u red čekanja za trenutno brisanje", "user_details": "Detalji korisnika", - "user_management": "Upravljanje Korisnicima", + "user_management": "Upravljanje korisnicima", "user_password_has_been_reset": "Korisnička lozinka je poništena:", "user_password_reset_description": "Molimo dostavite privremenu lozinku korisniku i obavijestite ga da će morati promijeniti lozinku pri sljedećoj prijavi.", "user_restore_description": "Račun korisnika {user} bit će vraćen.", @@ -390,13 +396,13 @@ "advanced_settings_enable_alternate_media_filter_subtitle": "Koristite ovu opciju za filtriranje medija tijekom sinkronizacije na temelju alternativnih kriterija. Pokušajte ovo samo ako imate problema s aplikacijom koja ne prepoznaje sve albume.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTALNO] Koristite alternativni filter za sinkronizaciju albuma na uređaju", "advanced_settings_log_level_title": "Razina zapisivanja: {level}", - "advanced_settings_prefer_remote_subtitle": "Neki uređaji sporo učitavaju sličice s lokalnih resursa. Aktivirajte ovu postavku kako biste umjesto toga učitali slike s udaljenih izvora.", + "advanced_settings_prefer_remote_subtitle": "Neki uređaji sporo učitavaju sličice s lokalnih stavki. Aktivirajte ovu postavku kako biste umjesto toga učitali slike s udaljenih izvora.", "advanced_settings_prefer_remote_title": "Preferiraj udaljene slike", "advanced_settings_proxy_headers_subtitle": "Definirajte zaglavlja posrednika koja Immich treba slati sa svakim mrežnim zahtjevom.", "advanced_settings_proxy_headers_title": "Proxy zaglavlja", "advanced_settings_self_signed_ssl_subtitle": "Preskoči provjeru SSL certifikata za krajnju točku poslužitelja. Potrebno za samo-potpisane certifikate.", "advanced_settings_self_signed_ssl_title": "Dopusti samo-potpisane SSL certifikate", - "advanced_settings_sync_remote_deletions_subtitle": "Automatski izbriši ili obnovi resurs na ovom uređaju kada se ta radnja izvrši na webu", + "advanced_settings_sync_remote_deletions_subtitle": "Automatski izbriši ili obnovi stavku na ovom uređaju kada se ta radnja izvrši na webu", "advanced_settings_sync_remote_deletions_title": "Sinkroniziraj udaljena brisanja [EKSPERIMENTALNO]", "advanced_settings_tile_subtitle": "Postavke za napredne korisnike", "advanced_settings_troubleshooting_subtitle": "Omogući dodatne značajke za rješavanje problema", @@ -421,14 +427,15 @@ "album_remove_user_confirmation": "Jeste li sigurni da želite ukloniti {user}?", "album_search_not_found": "Nema albuma koji odgovaraju vašem pretraživanju", "album_share_no_users": "Čini se da ste podijelili ovaj album sa svim korisnicima ili nemate nijednog korisnika s kojim biste ga dijelili.", + "album_summary": "Sažetak albuma", "album_updated": "Album ažuriran", - "album_updated_setting_description": "Primite obavijest e-poštom kada dijeljeni album ima nova sredstva", + "album_updated_setting_description": "Primite obavijest e-poštom kada dijeljeni album ima nove stavke", "album_user_left": "Napušten {album}", "album_user_removed": "Uklonjen {user}", "album_viewer_appbar_delete_confirm": "Jeste li sigurni da želite izbrisati ovaj album s vašeg računa?", "album_viewer_appbar_share_err_delete": "Neuspješno brisanje albuma", "album_viewer_appbar_share_err_leave": "Neuspješno napuštanje albuma", - "album_viewer_appbar_share_err_remove": "Postoje problemi s uklanjanjem resursa iz albuma", + "album_viewer_appbar_share_err_remove": "Postoje problemi s uklanjanjem stavki iz albuma", "album_viewer_appbar_share_err_title": "Neuspješno mijenjanje naslova albuma", "album_viewer_appbar_share_leave": "Napusti album", "album_viewer_appbar_share_to": "Podijeli s", @@ -437,8 +444,8 @@ "albums": "Albumi", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albumi}}", "albums_default_sort_order": "Zadani redoslijed sortiranja albuma", - "albums_default_sort_order_description": "Početni redoslijed sortiranja elemenata prilikom izrade novih albuma.", - "albums_feature_description": "Zbirke resursa koje se mogu dijeliti s drugim korisnicima.", + "albums_default_sort_order_description": "Početni redoslijed sortiranja stavki prilikom izrade novih albuma.", + "albums_feature_description": "Zbirke stavki koje se mogu dijeliti s drugim korisnicima.", "albums_on_device_count": "Albumi na uređaju ({count})", "all": "Sve", "all_albums": "Svi albumi", @@ -457,58 +464,60 @@ "app_bar_signout_dialog_content": "Jeste li sigurni da se želite odjaviti?", "app_bar_signout_dialog_ok": "Da", "app_bar_signout_dialog_title": "Odjavi se", - "app_settings": "Postavke Aplikacije", + "app_settings": "Postavke aplikacije", "appears_in": "Pojavljuje se u", "archive": "Arhiva", "archive_action_prompt": "{count} dodano u arhivu", "archive_or_unarchive_photo": "Arhivirajte ili dearhivirajte fotografiju", - "archive_page_no_archived_assets": "Nema arhiviranih resursa", + "archive_page_no_archived_assets": "Nema arhiviranih stavki", "archive_page_title": "Arhiviraj ({count})", "archive_size": "Veličina arhive", "archive_size_description": "Konfigurirajte veličinu arhive za preuzimanja (u GiB)", - "archived": "Ahrivirano", + "archived": "Arhivirano", "archived_count": "{count, plural, other {Archived #}}", "are_these_the_same_person": "Je li ovo ista osoba?", "are_you_sure_to_do_this": "Jeste li sigurni da to želite učiniti?", - "asset_action_delete_err_read_only": "Nije moguće izbrisati resurse samo za čitanje, preskačem", - "asset_action_share_err_offline": "Nije moguće dohvatiti izvanmrežne resurse, preskačem", + "asset_action_delete_err_read_only": "Nije moguće izbrisati stavke samo za čitanje, preskakanje", + "asset_action_share_err_offline": "Nije moguće dohvatiti izvanmrežne stavke, preskakanje", "asset_added_to_album": "Dodano u album", "asset_adding_to_album": "Dodavanje u album…", - "asset_description_updated": "Opis imovine je ažuriran", - "asset_filename_is_offline": "Sredstvo {filename} je izvan mreže", - "asset_has_unassigned_faces": "Materijal ima nedodijeljena lica", - "asset_hashing": "Sažimanje…", + "asset_description_updated": "Opis stavke je ažuriran", + "asset_filename_is_offline": "Stavka {filename} je izvan mreže", + "asset_has_unassigned_faces": "Stavka ima nedodijeljena lica", + "asset_hashing": "Hashiranje…", "asset_list_group_by_sub_title": "Grupiraj po", "asset_list_layout_settings_dynamic_layout_title": "Dinamički raspored", "asset_list_layout_settings_group_automatically": "Automatski", - "asset_list_layout_settings_group_by": "Grupiraj resurse po", + "asset_list_layout_settings_group_by": "Grupiraj stavke po", "asset_list_layout_settings_group_by_month_day": "Mjesec + dan", "asset_list_layout_sub_title": "Raspored", - "asset_list_settings_subtitle": "Postavke izgleda mreže fotografija", - "asset_list_settings_title": "Mreža Fotografija", - "asset_offline": "Sredstvo izvan mreže", - "asset_offline_description": "Ovaj materijal je izvan mreže. Immich ne može pristupiti lokaciji datoteke. Provjerite je li sredstvo dostupno, a zatim ponovno skenirajte biblioteku.", - "asset_restored_successfully": "Resurs uspješno obnovljen", + "asset_list_settings_subtitle": "Postavke izgleda Mreže fotografija", + "asset_list_settings_title": "Mreža fotografija", + "asset_offline": "Stavka izvan mreže", + "asset_offline_description": "Ova vanjska stavka nije pronađena na disku. Za pomoć se obratite Immich administratoru.", + "asset_restored_successfully": "Stavka uspješno obnovljena", "asset_skipped": "Preskočeno", "asset_skipped_in_trash": "U smeću", - "asset_uploaded": "Učitano", - "asset_uploading": "Šaljem…", - "asset_viewer_settings_subtitle": "Upravljajte postavkama preglednika vaše galerije", - "asset_viewer_settings_title": "Preglednik Resursa", - "assets": "Sredstva", - "assets_added_count": "Dodano {count, plural, one {# asset} other {# assets}}", - "assets_added_to_album_count": "Dodano {count, plural, one {# asset} other {# assets}} u album", - "assets_added_to_albums_count": "Dodano je {assetTotal} datoteka u {albumTotal} albuma", - "assets_cannot_be_added_to_album_count": "{count, plural, one {Sadržaj se ne može dodati u album} other {{count} sadržaja se ne mogu dodati u album}}", - "assets_cannot_be_added_to_albums": "{count, plural, one {Datoteka se ne može dodati ni u jedan album} few {Datoteke se ne mogu dodati ni u jedan album} other {Datoteka se ne može dodati ni u jedan album}}", - "assets_count": "{count, plural, one {# asset} other {# assets}}", - "assets_deleted_permanently": "{count} resurs(i) uspješno uklonjeni", - "assets_deleted_permanently_from_server": "{count} resurs(i) trajno obrisan(i) sa Immich poslužitelja", - "assets_downloaded_failed": "{count, plural, one {Preuzeta # datoteka – {error} datoteka nije uspjela} other {Preuzeto je # datoteka – {error} datoteke nisu uspjele}}", - "assets_downloaded_successfully": "{count, plural, one {Uspješno preuzeta # datoteka} other {Uspješno preuzete # datoteke}}", - "assets_moved_to_trash_count": "{count, plural, one {# asset} other {# asset}} premješteno u smeće", - "assets_permanently_deleted_count": "Trajno izbrisano {count, plural, one {# asset} other {# assets}}", - "assets_removed_count": "Uklonjeno {count, plural, one {# asset} other {# assets}}", + "asset_trashed": "Stavka premještena u smeće", + "asset_troubleshoot": "Rješavanje problema sa stavkom", + "asset_uploaded": "Preneseno", + "asset_uploading": "Prenošenje…", + "asset_viewer_settings_subtitle": "Upravljajte postavkama vašeg preglednika galerije", + "asset_viewer_settings_title": "Preglednik stavki", + "assets": "Stavke", + "assets_added_count": "{count, plural, one {Dodana # stavka} few {Dodane # stavke} other {Dodano # stavki}}", + "assets_added_to_album_count": "{count, plural, one {Dodana # stavka} few {Dodane # stavke} other {Dodano # stavki}} u album", + "assets_added_to_albums_count": "{assetTotal, plural, one {Dodana # stavka} other {Dodano # stavki}} u {albumTotal, plural, one {# album} other {# albuma}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Stavka se ne može} other {Stavke se ne mogu}} dodati u album", + "assets_cannot_be_added_to_albums": "{count, plural, one {Stavka se ne može} few {Stavke se ne mogu} other {Stavki se ne može}} dodati ni u jedan album", + "assets_count": "{count, plural, one {# stavka} few {# stavke} other {# stavki}}", + "assets_deleted_permanently": "Trajno {count, plural, one {izbrisana # stavka} few {izbrisane # stavke} other {izbrisano # stavki}}", + "assets_deleted_permanently_from_server": "Trajno {count, plural, one {izbrisana # stavka} few {izbrisane # stavke} other {izbrisano # stavki}} s Immich servera", + "assets_downloaded_failed": "{count, plural, one {Preuzeta # datoteka – {error} datoteka nije uspjela} few {Preuzete # datoteke - {error} datoteke nisu uspjele} other {Preuzeto # datoteka – {error} datoteke nisu uspjele}}", + "assets_downloaded_successfully": "{count, plural, one {Uspješno preuzeta # datoteka} few {Uspješno preuzete # datoteke} other {Uspješno preueto # datoteka}}", + "assets_moved_to_trash_count": "{count, plural, one {# stavka premještena} few {# stavke premještene} other {# stavk premještenoi}} u smeće", + "assets_permanently_deleted_count": "Trajno {count, plural, one {izbrisana # stavka} few {izbrisane # stavke} other {izbrisano # stavki}}", + "assets_removed_count": "{count, plural, one {Uklonjena # stavka} few {Uklonjene # stavke} other {Uklonjeno # stavki}}", "assets_removed_permanently_from_device": "{count} resurs(i) trajno uklonjen(i) s vašeg uređaja", "assets_restore_confirmation": "Jeste li sigurni da želite obnoviti sve svoje resurse bačene u otpad? Ne možete poništiti ovu radnju! Imajte na umu da se bilo koji izvanmrežni resursi ne mogu obnoviti na ovaj način.", "assets_restored_count": "Vraćeno {count, plural, one {# asset} other {# assets}}", @@ -518,7 +527,7 @@ "assets_trashed_from_server": "{count} resurs(i) premješten(i) u smeće s Immich poslužitelja", "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} već dio albuma", "assets_were_part_of_albums_count": "{count, plural, one {Datoteka je već bila dio albuma} few {Datoteke su već bile dio albuma} other {Datoteka je već bila dio albuma}}", - "authorized_devices": "Ovlašteni Uređaji", + "authorized_devices": "Ovlašteni uređaji", "automatic_endpoint_switching_subtitle": "Povežite se lokalno preko naznačene Wi-Fi mreže kada je dostupna i koristite alternativne veze na drugim lokacijama", "automatic_endpoint_switching_title": "Automatsko prebacivanje URL-a", "autoplay_slideshow": "Automatsko prikazivanje slajdova", @@ -1034,7 +1043,7 @@ "export_database_description": "Izvezi SQLite bazu podataka", "extension": "Proširenje (Extension)", "external": "Vanjski", - "external_libraries": "Vanjske Biblioteke", + "external_libraries": "Vanjske biblioteke", "external_network": "Vanjska mreža", "external_network_sheet_info": "Kada niste na željenoj Wi-Fi mreži, aplikacija će se povezati s poslužiteljem putem prve dostupne URL adrese s popisa ispod, redom od vrha prema dnu", "face_unassigned": "Nedodijeljeno", @@ -1048,7 +1057,7 @@ "favorites": "Omiljene", "favorites_page_no_favorites": "Nema pronađenih omiljenih stavki", "feature_photo_updated": "Istaknuta fotografija ažurirana", - "features": "Značajke (Features)", + "features": "Značajke", "features_setting_description": "Upravljajte značajkama aplikacije", "file_name": "Naziv datoteke", "file_name_or_extension": "Naziv ili ekstenzija datoteke", @@ -1220,7 +1229,7 @@ "location_picker_longitude_error": "Unesite valjanu geografsku dužinu", "location_picker_longitude_hint": "Unesite ovdje svoju geografsku dužinu", "lock": "Zaključaj", - "locked_folder": "Zaključana Mapa", + "locked_folder": "Zaključana mapa", "log_out": "Odjavi se", "log_out_all_devices": "Odjava sa svih uređaja", "logged_in_as": "Prijavljeni kao {user}", @@ -1900,6 +1909,8 @@ "sync_albums_manual_subtitle": "Sinkroniziraj sve prenesene videozapise i fotografije u odabrane albume za sigurnosnu kopiju", "sync_local": "Sinkroniziraj lokalno", "sync_remote": "Sinkroniziraj udaljeno", + "sync_status": "Status sinkronizacije", + "sync_status_subtitle": "Pregledajte i upravljajte sistemom sinkronizacije", "sync_upload_album_setting_subtitle": "Kreiraj i prenesi svoje fotografije i videozapise u odabrane albume na Immichu", "tag": "Oznaka", "tag_assets": "Označi stavke", diff --git a/i18n/hu.json b/i18n/hu.json index 131df91a79..081bc12f99 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -28,6 +28,7 @@ "add_to_album": "Felvétel albumba", "add_to_album_bottom_sheet_added": "Hozzáadva a(z) \"{album}\" albumhoz", "add_to_album_bottom_sheet_already_exists": "Már benne van a(z) \"{album}\" albumban", + "add_to_album_bottom_sheet_some_local_assets": "Néhány helyi elem nem adható hozzá az albumhoz", "add_to_album_toggle": "{album} kijelölésének váltása", "add_to_albums": "Hozzáadás albumokhoz", "add_to_albums_count": "Hozzáadás albumokhoz ({count})", @@ -175,7 +176,7 @@ "map_settings_description": "Térkép beállítások kezelése", "map_style_description": "Egy style.json térképtémára mutató URL cím", "memory_cleanup_job": "Memória takarítás", - "memory_generate_job": "Emlék generálása", + "memory_generate_job": "Emlékek generálása", "metadata_extraction_job": "Metaadatok kinyerése", "metadata_extraction_job_description": "Metaadat információk (pl. GPS, arcok és felbontás) kinyerése minden elemből", "metadata_faces_import_setting": "Arc importálás engedélyezése", @@ -548,6 +549,7 @@ "backup_album_selection_page_select_albums": "Válassz albumokat", "backup_album_selection_page_selection_info": "Összegzés", "backup_album_selection_page_total_assets": "Összes egyedi elem", + "backup_albums_sync": "Backup albumok szinkronizálása", "backup_all": "Összes", "backup_background_service_backup_failed_message": "Az elemek mentése sikertelen. Újrapróbálkozás…", "backup_background_service_connection_failed_message": "A szerverhez csatlakozás sikertelen. Újrapróbálkozás…", @@ -597,6 +599,7 @@ "backup_controller_page_turn_on": "Előtérben mentés bekapcsolása", "backup_controller_page_uploading_file_info": "Fájl információk feltöltése", "backup_err_only_album": "Az utolsó albumot nem tudod törölni", + "backup_error_sync_failed": "A szinkronizálás nem sikerült. A biztonsági mentés nem elkészíthető.", "backup_info_card_assets": "elemek", "backup_manual_cancelled": "Megszakítva", "backup_manual_in_progress": "Feltöltés már folyamatban. Próbáld meg később", @@ -1386,20 +1389,25 @@ "no_assets_message": "KATTINTS AZ ELSŐ FÉNYKÉP FELTÖLTÉSÉHEZ", "no_assets_to_show": "Nincs megjeleníthető elem", "no_cast_devices_found": "Nem található eszköz vetítéshez", + "no_checksum_local": "Nincs elérhető ellenőrzőösszeg - a helyi eszközök nem kérhetők le", + "no_checksum_remote": "Nincs elérhető ellenőrzőösszeg - a távoli eszköz nem kérhető le", "no_duplicates_found": "Nem találhatók duplikátumok.", "no_exif_info_available": "Nincs elérhető Exif információ", "no_explore_results_message": "Tölts fel több képet, hogy böngészhesd a gyűjteményed.", "no_favorites_message": "Add hozzá a kedvencekhez, hogy gyorsan megtaláld a legjobb képeidet és videóidat", "no_libraries_message": "Hozz létre külső képtárat a fényképeid és videóid megtekintéséhez", + "no_local_assets_found": "Nem találhatók helyi eszközök ezzel az ellenőrzőösszeggel", "no_locked_photos_message": "A zárolt mappában elhelyezett fotók és videók rejtettek, és nem jelennek meg a könyvtárad böngészése vagy keresése közben sem.", "no_name": "Nincs Név", "no_notifications": "Nincsenek értesítések", "no_people_found": "Nem található személy", "no_places": "Nincsenek helyek", + "no_remote_assets_found": "Nem találhatók távoli eszközök ezzel az ellenőrzőösszeggel", "no_results": "Nincs találat", "no_results_description": "Próbálkozz szinonimákkal vagy általánosabb kulcsszavakkal", "no_shared_albums_message": "Hozz létre egy új albumot, hogy megoszthasd fényképeid és videóid másokkal", "no_uploads_in_progress": "Nincs folyamatban lévő feltöltés", + "not_available": "N/A", "not_in_any_album": "Nincs albumban", "not_selected": "Nincs kiválasztva", "note_apply_storage_label_to_previously_uploaded assets": "Megjegyzés: a korábban feltöltött elemek Tárhely Címkézéséhez futtasd a(z)", @@ -1434,6 +1442,8 @@ "open_the_search_filters": "Keresési szűrők megnyitása", "options": "Beállítások", "or": "vagy", + "organize_into_albums": "Albumokba rendezés", + "organize_into_albums_description": "Meglévő fotók albumokba helyezése, a jelenlegi szinkronizációs beállítások alapján", "organize_your_library": "Rendszerezd a képtáradat", "original": "eredeti", "other": "Egyéb", @@ -1519,6 +1529,7 @@ "port": "Port", "preferences_settings_subtitle": "Alkalmazásbeállítások kezelése", "preferences_settings_title": "Beállítások", + "preparing": "Előkészítés", "preset": "Sablon", "preview": "Előnézet", "previous": "Előző", @@ -1535,6 +1546,7 @@ "profile_drawer_client_out_of_date_minor": "A mobilalkalmazás elavult. Kérjük, frissítsd a legfrisebb alverzióra.", "profile_drawer_client_server_up_to_date": "A Kliens és a Szerver is naprakész", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Csak olvasható mód engedélyezve. A kilépéshez hosszan nyomja meg a felhasználói avatar ikont.", "profile_drawer_server_out_of_date_major": "A szerver elavult. Kérjük, frissítsd a legfrisebb főverzióra.", "profile_drawer_server_out_of_date_minor": "A szerver elavult. Kérjük, frissítsd a legfrisebb alverzióra.", "profile_image_of_user": "{user} profilképe", @@ -1573,6 +1585,7 @@ "purchase_server_description_2": "Támogató státusz", "purchase_server_title": "Szerver", "purchase_settings_server_activated": "A szerver termékkulcsot az admin kezeli", + "query_asset_id": "Lekérdezési eszköz azonosítója", "queue_status": "Feldolgozva {count}/{total}", "rating": "Értékelés csillagokkal", "rating_clear": "Értékelés törlése", @@ -1580,6 +1593,9 @@ "rating_description": "Exif értékelés megjelenítése az infópanelen", "reaction_options": "Reakció lehetőségek", "read_changelog": "Változásnapló Elolvasása", + "readonly_mode_disabled": "Csak olvasható mód kikapcsolva", + "readonly_mode_enabled": "Csak olvasható mód bekapcsolva", + "ready_for_upload": "Készen áll a feltöltésre", "reassign": "Hozzárendel", "reassigned_assets_to_existing_person": "{count, plural, other {# elem}} hozzárendelve{name, select, null { egy létező személyhez} other {: {name}}}", "reassigned_assets_to_new_person": "{count, plural, other {# elem}} hozzárendelve egy új személyhez", @@ -1604,6 +1620,7 @@ "regenerating_thumbnails": "Bélyegképek újragenerálása folyamatban", "remote": "Távoli", "remote_assets": "Távoli Elemek", + "remote_media_summary": "Távoli médiaösszefoglaló", "remove": "Eltávolítás", "remove_assets_album_confirmation": "Biztosan el szeretnél távolítani {count, plural, one {# elemet} other {# elemet}} az albumból?", "remove_assets_shared_link_confirmation": "Biztosan el szeretnél távolítani {count, plural, one {# elemet} other {# elemet}} ebből a megosztott linkből?", @@ -1656,6 +1673,7 @@ "restore_user": "Felhasználó visszaállítása", "restored_asset": "Visszaállított elem", "resume": "Folytatás", + "resume_paused_jobs": "Folytatás {count, plural, one {# paused job} other {# paused jobs}}", "retry_upload": "Feltöltés újrapróbálása", "review_duplicates": "Duplikátumok áttekintése", "review_large_files": "Nagy fájlok áttekintése", @@ -1749,6 +1767,7 @@ "select_user_for_sharing_page_err_album": "Az album létrehozása sikertelen", "selected": "Kiválasztott", "selected_count": "{count, plural, other {# kiválasztva}}", + "selected_gps_coordinates": "Kiválasztott GPS Kordináták", "send_message": "Üzenet küldése", "send_welcome_email": "Üdvözlő email küldése", "server_endpoint": "Szerver Végpont", @@ -1877,6 +1896,7 @@ "show_slideshow_transition": "Vetítés áttűnési effekt mutatása", "show_supporter_badge": "Támogató jelvény", "show_supporter_badge_description": "Támogató jelvény mutatása", + "show_text_search_menu": "Mutasd a szövegkeresési menüt", "shuffle": "Véletlenszerű", "sidebar": "Oldalsáv", "sidebar_display_description": "Nézet link megjelenítése az oldalsávban", @@ -1907,6 +1927,7 @@ "stacktrace": "Hiba leírása", "start": "Elindít", "start_date": "Kezdő dátum", + "start_date_before_end_date": "A kezdeti dátumnak a befejezési dátum előtt kell lennie", "state": "Megye/Állam", "status": "Állapot", "stop_casting": "Vetítés megszüntetése", @@ -1970,7 +1991,9 @@ "to_change_password": "Jelszó megváltoztatása", "to_favorite": "Kedvenc", "to_login": "Bejelentkezés", + "to_multi_select": "több elem kiválasztásához", "to_parent": "Egy szinttel feljebb", + "to_select": "a kiválasztáshoz", "to_trash": "Lomtárba helyezés", "toggle_settings": "Beállítások átállítása", "total": "Összesen", @@ -1990,6 +2013,7 @@ "trash_page_select_assets_btn": "Elemek kiválasztása", "trash_page_title": "Lomtár ({count})", "trashed_items_will_be_permanently_deleted_after": "A lomtárban lévő elemek véglegesen törlésre kerülnek {days, plural, other {# nap}} múlva.", + "troubleshoot": "Hibaelhárítás", "type": "Típus", "unable_to_change_pin_code": "Sikertelen PIN kód változtatás", "unable_to_setup_pin_code": "Sikertelen PIN kód beállítás", @@ -2106,5 +2130,6 @@ "yes": "Igen", "you_dont_have_any_shared_links": "Nincsenek megosztott linkjeid", "your_wifi_name": "A Wi-Fi hálózatod neve", - "zoom_image": "Kép Nagyítása" + "zoom_image": "Kép Nagyítása", + "zoom_to_bounds": "Nagyítás a határokhoz" } diff --git a/i18n/id.json b/i18n/id.json index 8dba86752e..abf8ef4747 100644 --- a/i18n/id.json +++ b/i18n/id.json @@ -28,10 +28,12 @@ "add_to_album": "Tambahkan ke album", "add_to_album_bottom_sheet_added": "Ditambahkan ke {album}", "add_to_album_bottom_sheet_already_exists": "Sudah ada di {album}", + "add_to_album_bottom_sheet_some_local_assets": "Beberapa aset lokal tidak dapat ditambahkan ke album", "add_to_album_toggle": "Masukkan ke {album} / Batalkan dari {album}", "add_to_albums": "Tambahkan ke album", "add_to_albums_count": "Tambahkan ke album ({count})", "add_to_shared_album": "Tambahkan ke album terbagi", + "add_upload_to_stack": "Tambahkan unggahan ke tumpukan", "add_url": "Tambahkan URL", "added_to_archive": "Ditambahkan ke arsip", "added_to_favorites": "Ditambahkan ke favorit", @@ -598,6 +600,7 @@ "backup_controller_page_turn_on": "Aktifkan pencadangan latar depan", "backup_controller_page_uploading_file_info": "Mengunggah info file", "backup_err_only_album": "Tidak dapat menghapus album", + "backup_error_sync_failed": "Sinkronisasi gagal. Tidak dapat memproses cadangan.", "backup_info_card_assets": "aset", "backup_manual_cancelled": "Dibatalkan", "backup_manual_in_progress": "Dalam proses unggah. Coba lagi nanti", diff --git a/i18n/it.json b/i18n/it.json index 39c296a0e7..b1aeabd33e 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -14,7 +14,7 @@ "add_a_location": "Aggiungi una posizione", "add_a_name": "Aggiungi un nome", "add_a_title": "Aggiungi un titolo", - "add_birthday": "Aggiungi un compleanno", + "add_birthday": "Aggiungi compleanno", "add_endpoint": "Aggiungi un endpoint", "add_exclusion_pattern": "Aggiungi un pattern di esclusione", "add_import_path": "Aggiungi un percorso per l’importazione", @@ -28,10 +28,12 @@ "add_to_album": "Aggiungi all'album", "add_to_album_bottom_sheet_added": "Aggiunto in {album}", "add_to_album_bottom_sheet_already_exists": "Già presente in {album}", + "add_to_album_bottom_sheet_some_local_assets": "Alcune risorse locali non possono essere aggiunte all'album", "add_to_album_toggle": "Attiva/disattiva selezione per {album}", "add_to_albums": "Aggiungi ad album", "add_to_albums_count": "Aggiungi ad album ({count})", "add_to_shared_album": "Aggiungi ad album condiviso", + "add_upload_to_stack": "Aggiungi caricamento allo stack", "add_url": "Aggiungi URL", "added_to_archive": "Aggiunto all'archivio", "added_to_favorites": "Aggiunto ai preferiti", @@ -176,7 +178,7 @@ "map_style_description": "URL per un tema della mappa style.json", "memory_cleanup_job": "Pulizia dei vecchi Ricordi", "memory_generate_job": "Generazione dei Ricordi", - "metadata_extraction_job": "Estrazione Metadata", + "metadata_extraction_job": "Estrazione Metadati", "metadata_extraction_job_description": "Estrai informazioni dai metadati di ciascuna risorsa, come coordinate GPS, volti e risoluzione", "metadata_faces_import_setting": "Abilita l'importazione dei volti", "metadata_faces_import_setting_description": "Importa i volti dai dati EXIF dell'immagine e dai file sidecar", @@ -307,11 +309,11 @@ "transcoding_acceleration_rkmpp": "RKMPP (Solo per SOC Rockchip)", "transcoding_acceleration_vaapi": "VAAPI", "transcoding_accepted_audio_codecs": "Codifiche audio accettate", - "transcoding_accepted_audio_codecs_description": "Seleziona quali codifiche audio non devono essere trascodificate. Solo usato per alcune politiche di trascodifica.", + "transcoding_accepted_audio_codecs_description": "Seleziona quali codifiche audio non devono essere transcodificate. Solo usato per alcune politiche di transcodifica.", "transcoding_accepted_containers": "Contenitori accettati", "transcoding_accepted_containers_description": "Seleziona quali formati non hanno bisogno di essere remuxati in MP4. Usato solo per certe politiche di transcodifica.", "transcoding_accepted_video_codecs": "Codifiche video accettate", - "transcoding_accepted_video_codecs_description": "Seleziona quali codifiche video non devono essere trascodificate. Usato solo per alcune politiche di trascodifica.", + "transcoding_accepted_video_codecs_description": "Seleziona quali codifiche video non devono essere transcodificate. Usato solo per alcune politiche di transcodifica.", "transcoding_advanced_options_description": "Impostazioni che la maggior parte degli utenti non dovrebbero cambiare", "transcoding_audio_codec": "Codifica Audio", "transcoding_audio_codec_description": "Opus è l'opzione con la qualità più alta, ma è meno compatibile con dispositivi o software vecchi.", @@ -344,7 +346,7 @@ "transcoding_reference_frames": "Frame di riferimento", "transcoding_reference_frames_description": "Il numero di frame da prendere in considerazione nel comprimere un determinato frame. Valori più alti migliorano l'efficienza di compressione, ma rallentano la codifica. 0 imposta questo valore automaticamente.", "transcoding_required_description": "Solo video che non sono in un formato accettato", - "transcoding_settings": "Impostazioni Trascodifica Video", + "transcoding_settings": "Impostazioni Transcodifica Video", "transcoding_settings_description": "Gestisci quali video transcodificare e come processarli", "transcoding_target_resolution": "Risoluzione desiderata", "transcoding_target_resolution_description": "Risoluzioni più elevate possono preservare più dettagli ma richiedono più tempo per la codifica, producono file di dimensioni maggiori e possono ridurre la reattività dell'applicazione.", @@ -355,11 +357,11 @@ "transcoding_tone_mapping": "Mappatura della tonalità", "transcoding_tone_mapping_description": "Tenta di preservare l'aspetto dei video HDR quando convertiti in SDR. Ciascun algoritmo fa diversi compromessi per colore, dettaglio e luminosità. Hable conserva il dettaglio, Mobius conserva il colore e Reinhard conserva la luminosità.", "transcoding_transcode_policy": "Politica di transcodifica", - "transcoding_transcode_policy_description": "Politica che determina quando un video deve essere trascodificato. I video HDR verranno sempre trascodificati (eccetto quando la trascodifica è disabilitata).", + "transcoding_transcode_policy_description": "Politica che determina quando un video deve essere transcodificato. I video HDR verranno sempre transcodificati (eccetto quando la transcodifica è disabilitata).", "transcoding_two_pass_encoding": "Codifica a due passaggi", - "transcoding_two_pass_encoding_setting_description": "Trascodifica in due passaggi per produrre video codificati migliori. Quando il bitrate massimo è abilitato (necessario affinché funzioni con H.264 e HEVC), questa modalità utilizza un intervallo di bitrate basato sul bitrate massimo e ignora CRF. Per VP9, CRF può essere utilizzato se il bitrate massimo è disabilitato.", + "transcoding_two_pass_encoding_setting_description": "Transcodifica in due passaggi per produrre video codificati migliori. Quando il bitrate massimo è abilitato (necessario affinché funzioni con H.264 e HEVC), questa modalità utilizza un intervallo di bitrate basato sul bitrate massimo e ignora CRF. Per VP9, CRF può essere utilizzato se il bitrate massimo è disabilitato.", "transcoding_video_codec": "Codec video", - "transcoding_video_codec_description": "VP9 ha alta efficienza e compatibilità web, ma richiede più tempo per la trascodifica. HEVC ha prestazioni simili, ma una minore compatibilità web. H.264 è ampiamente compatibile e veloce da transcodificare, ma produce file molto più grandi. AV1 è il codec più efficiente, ma non è supportato sui dispositivi più vecchi.", + "transcoding_video_codec_description": "VP9 ha alta efficienza e compatibilità web, ma richiede più tempo per la transcodifica. HEVC ha prestazioni simili, ma una minore compatibilità web. H.264 è ampiamente compatibile e veloce da transcodificare, ma produce file molto più grandi. AV1 è il codec più efficiente, ma non è supportato sui dispositivi più vecchi.", "trash_enabled_description": "Abilita Funzionalità Cestino", "trash_number_of_days": "Numero di giorni", "trash_number_of_days_description": "Numero di giorni per cui mantenere gli asset nel cestino prima di rimuoverli definitivamente", @@ -387,8 +389,8 @@ "version_check_implications": "La funzione di controllo della versione fa uso di una comunicazione periodica con github.com", "version_check_settings": "Controllo Versione", "version_check_settings_description": "Abilita/disabilita la notifica per nuove versioni", - "video_conversion_job": "Trascodifica video", - "video_conversion_job_description": "Trascodifica video per maggiore compatibilità con browser e dispositivi" + "video_conversion_job": "Transcodifica video", + "video_conversion_job_description": "Transcodifica video per maggiore compatibilità con browser e dispositivi" }, "admin_email": "Email Amministratore", "admin_password": "Password Amministratore", @@ -598,6 +600,7 @@ "backup_controller_page_turn_on": "Attiva backup", "backup_controller_page_uploading_file_info": "Caricamento informazioni file", "backup_err_only_album": "Non è possibile rimuovere l'unico album", + "backup_error_sync_failed": "Sincronizzazione non riuscita. Impossibile elaborare il backup.", "backup_info_card_assets": "risorse", "backup_manual_cancelled": "Annullato", "backup_manual_in_progress": "Caricamento già in corso. Riprova più tardi", @@ -617,7 +620,7 @@ "blurred_background": "Sfondo sfocato", "bugs_and_feature_requests": "Bug & Richieste di nuove funzionalità", "build": "Compilazione", - "build_image": "Compila Immagine", + "build_image": "Immagine Compilata", "bulk_delete_duplicates_confirmation": "Sei sicuro di voler cancellare {count, plural, one {# asset duplicato} other {# assets duplicati}}? Questa operazione manterrà l'asset più pesante di ogni gruppo e cancellerà permanentemente tutti gli altri duplicati. Non puoi annullare questa operazione!", "bulk_keep_duplicates_confirmation": "Sei sicuro di voler tenere {count, plural, one {# asset duplicato} other {# assets duplicati}}? Questa operazione risolverà tutti i gruppi duplicati senza cancellare nulla.", "bulk_trash_duplicates_confirmation": "Sei davvero sicuro di voler cancellare {count, plural, one {# asset duplicato} other {# assets duplicati}}? Questa operazione manterrà l'asset più pesante di ogni gruppo e cancellerà permanentemente tutti gli altri duplicati.", @@ -733,7 +736,7 @@ "copy_to_clipboard": "Copia negli appunti", "country": "Nazione", "cover": "Riempi la finestra", - "covers": "Copre", + "covers": "Copertine", "create": "Crea", "create_album": "Crea album", "create_album_page_untitled": "Senza titolo", @@ -766,9 +769,9 @@ "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Scuro", "dark_theme": "Imposta tema scuro", - "date_after": "Data dopo", + "date_after": "Dopo la data", "date_and_time": "Data e ora", - "date_before": "Data prima", + "date_before": "Prima della data", "date_format": "E, d LLL, y • hh:mm", "date_of_birth_saved": "Data di nascita salvata con successo", "date_range": "Intervallo di date", @@ -1036,6 +1039,7 @@ "exif_bottom_sheet_description_error": "Errore durante l'aggiornamento della descrizione", "exif_bottom_sheet_details": "DETTAGLI", "exif_bottom_sheet_location": "POSIZIONE", + "exif_bottom_sheet_no_description": "Nessuna descrizione", "exif_bottom_sheet_people": "PERSONE", "exif_bottom_sheet_person_add_person": "Aggiungi nome", "exit_slideshow": "Esci dalla presentazione", @@ -1172,7 +1176,7 @@ "in_archive": "In archivio", "include_archived": "Includi Archiviati", "include_shared_albums": "Includi album condivisi", - "include_shared_partner_assets": "Includi asset condivisi del compagno", + "include_shared_partner_assets": "Includi elementi condivisi dai compagni", "individual_share": "Condivisione individuale", "individual_shares": "Condivisioni individuali", "info": "Info", @@ -1720,11 +1724,11 @@ "search_no_people": "Nessuna persona", "search_no_people_named": "Nessuna persona chiamate \"{name}\"", "search_no_result": "Nessun risultato trovato, prova con un termine o combinazione diversi", - "search_options": "Opzioni Ricerca", + "search_options": "Opzioni di ricerca", "search_page_categories": "Categoria", "search_page_motion_photos": "Foto in movimento", - "search_page_no_objects": "Nessuna informazione relativa all'oggetto disponibile", - "search_page_no_places": "Nessun informazione sul luogo disponibile", + "search_page_no_objects": "Nessuna informazione sugli oggetti disponibile", + "search_page_no_places": "Nessuna informazione sui luoghi disponibile", "search_page_screenshots": "Screenshot", "search_page_search_photos_videos": "Ricerca le tue foto e i tuoi video", "search_page_selfies": "Selfie", @@ -1920,7 +1924,7 @@ "stack_action_prompt": "{count} elementi raggruppati", "stack_duplicates": "Raggruppa i duplicati", "stack_select_one_photo": "Seleziona una foto principale per il gruppo", - "stack_selected_photos": "Impila foto selezionate", + "stack_selected_photos": "Raggruppa foto selezionate", "stacked_assets_count": "{count, plural, one {Raggruppato # asset} other {Raggruppati # asset}}", "stacktrace": "Traccia dell'errore", "start": "Avvia", @@ -2037,7 +2041,7 @@ "unselect_all": "Deseleziona tutto", "unselect_all_duplicates": "Deseleziona tutti i duplicati", "unselect_all_in": "Deseleziona tutto in {group}", - "unstack": "Rimuovi dal gruppo", + "unstack": "Separa dal gruppo", "unstack_action_prompt": "{count} separati", "unstacked_assets_count": "{count, plural, one {Separato # asset} other {Separati # asset}}", "untagged": "Senza tag", @@ -2112,9 +2116,9 @@ "view_similar_photos": "Visualizza le foto simili", "view_stack": "Visualizza Raggruppamento", "view_user": "Visualizza Utente", - "viewer_remove_from_stack": "Rimuovi dalla pila", + "viewer_remove_from_stack": "Rimuovi dal gruppo", "viewer_stack_use_as_main_asset": "Usa come risorsa principale", - "viewer_unstack": "Rimuovi dal gruppo", + "viewer_unstack": "Separa dal gruppo", "visibility_changed": "Visibilità modificata per {count, plural, one {# persona} other {# persone}}", "waiting": "In Attesa", "warning": "Attenzione", @@ -2125,8 +2129,8 @@ "wrong_pin_code": "Codice PIN errato", "year": "Anno", "years_ago": "{years, plural, one {# anno} other {# anni}} fa", - "yes": "Si", - "you_dont_have_any_shared_links": "Non è presente alcun link condiviso", + "yes": "Sì", + "you_dont_have_any_shared_links": "Non hai nessun link condiviso", "your_wifi_name": "Nome della tua rete Wi-Fi", "zoom_image": "Ingrandisci immagine", "zoom_to_bounds": "Ingrandisci fino ai bordi" diff --git a/i18n/ja.json b/i18n/ja.json index b03614d65c..bec51aeca8 100644 --- a/i18n/ja.json +++ b/i18n/ja.json @@ -28,6 +28,7 @@ "add_to_album": "アルバムに追加", "add_to_album_bottom_sheet_added": "{album}に追加", "add_to_album_bottom_sheet_already_exists": "{album}に追加済み", + "add_to_album_bottom_sheet_some_local_assets": "いくつかの項目はまだサーバーへアップロードされていないためアルバムに追加できませんでした", "add_to_album_toggle": "{album}の選択を切り替え", "add_to_albums": "アルバムに追加", "add_to_albums_count": "{count}つのアルバムへ追加", @@ -598,6 +599,7 @@ "backup_controller_page_turn_on": "バックアップをオンにする", "backup_controller_page_uploading_file_info": "アップロード中のファイル", "backup_err_only_album": "最低1つのアルバムを選択してください", + "backup_error_sync_failed": "同期に失敗しました。バックアップができません。", "backup_info_card_assets": "写真と動画", "backup_manual_cancelled": "キャンセルされました", "backup_manual_in_progress": "アップロードが進行中です。後でもう一度試してください", diff --git a/i18n/ka.json b/i18n/ka.json index 13b0e1d065..bc1c05492b 100644 --- a/i18n/ka.json +++ b/i18n/ka.json @@ -14,6 +14,7 @@ "add_a_location": "დაამატე ადგილი", "add_a_name": "დაამატე სახელი", "add_a_title": "დაასათაურე", + "add_birthday": "დაბადების დღის დამატება", "add_exclusion_pattern": "დაამატე გამონაკლისი ნიმუში", "add_import_path": "დაამატე საიმპორტო მისამართი", "add_location": "დაამატე ადგილი", @@ -21,16 +22,20 @@ "add_partner": "დაამატე პარტნიორი", "add_path": "დაამატე მისამართი", "add_photos": "დაამატე ფოტოები", + "add_tag": "დაამატე თეგი", "add_to": "დაამატე ...ში", "add_to_album": "დაამატე ალბომში", "add_to_album_bottom_sheet_added": "დამატებულია {album}-ში", "add_to_album_bottom_sheet_already_exists": "{album}-ში უკვე არსებობს", + "add_to_albums": "დაამატე ალბომებში", + "add_to_albums_count": "დაამატე ალბომში ({count})", "add_to_shared_album": "დაამატე საზიარო ალბომში", "add_url": "დაამატე URL", "added_to_archive": "დაარქივდა", "added_to_favorites": "დაამატე რჩეულებში", "added_to_favorites_count": "{count, number} დაემატა რჩეულებში", "admin": { + "admin_user": "ადმინ მომხმარებელი", "asset_offline_description": "ეს საგარეო ბიბლიოთეკის აქტივი დისკზე ვერ მოიძებნა და სანაგვეში იქნა მოთავსებული. თუ ფაილი ბიბლიოთეკის შიგნით მდებარეობს, შეამოწმეთ შესაბამისი აქტივი ტაიმლაინზე. ამ აქტივის აღსადგენად, დარწმუნდით რომ ქვემოთ მოცემული ფაილის მისამართი Immich-ის მიერ წვდომადია და დაასკანერეთ ბიბლიოთეკა.", "authentication_settings": "ავთენტიკაციის პარამეტრები", "authentication_settings_description": "პაროლის, OAuth-ის და სხვა ავტენთიფიკაციის პარამეტრების მართვა", @@ -41,7 +46,7 @@ "backup_database_enable_description": "ბაზის დამპების ჩართვა", "backup_keep_last_amount": "წინა დამპების შესანარჩუნებელი რაოდენობა", "backup_settings": "მონაცემთა ბაზის დამპის მორგება", - "backup_settings_description": "მონაცემთა ბაზის პარამეტრების ამრთვა. შენიშვნა: ამ დავალებების მონიტორინგი არ ხდება და თქვენ არ მოგივათ შეტყობინება, თუ ის ჩავარდება.", + "backup_settings_description": "მონაცემთა ბაზის ასლის შექმნის პარამეტრების მრთვა.", "cleared_jobs": "დავალებები {job}-ისათვის გაწმენდილია", "config_set_by_file": "მიმდინარე კონფიგურაცია ფაილის მიერ არის დაყენებული", "confirm_delete_library": "ნამდვილად გინდა {library} ბიბლიოთეკის წაშლა?", @@ -58,6 +63,7 @@ "image_format_description": "WebP ფორმატი JPEG-ზე პატარა ფაილებს აწარმოებს, მაგრამ მის დამზადებას უფრო მეტი დრო სჭირდება.", "image_fullsize_title": "სრული ზომის გამოსახულების პარამეტრები", "image_prefer_wide_gamut": "უპირატესობა მიენიჭოს ფერის ფართე დიაპაზონს", + "image_preview_title": "გამოსახულების გადახედვის პარამეტრები", "image_quality": "ხარისხი", "image_resolution": "გაფართოება", "image_settings": "გამოსახულების პარამეტრები", @@ -68,6 +74,7 @@ "library_created": "შეიქმნა ბიბლიოთეკა: {library}", "library_deleted": "ბიბლიოთეკა წაიშალა", "library_import_path_description": "აირჩიე დასაიმპორტებელი საქაღალდე. ფოტოები და ვიდეოები მოიძებნება ამ საქაღალდესა და მასში არსებულ საქაღალდეებში.", + "library_settings": "გარე ბიბლიოთეკა", "library_settings_description": "გარე ბიბლიოთეკების პარამეტრების მართვა", "logging_settings": "ჟურნალი", "map_settings": "რუკა", diff --git a/i18n/kn.json b/i18n/kn.json index 388f06704c..111c802a1e 100644 --- a/i18n/kn.json +++ b/i18n/kn.json @@ -8,11 +8,13 @@ "actions": "ಕ್ರಿಯೆಗಳು", "active": "ಸಕ್ರಿಯ", "activity": "ಚಟುವಟಿಕೆ", + "activity_changed": "ಚಟುವಟಿಕೆ {enabled, select, true{ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ} other {ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ}}", "add": "ಸೇರಿಸಿ", "add_a_description": "ವಿವರಣೆಯನ್ನು ಸೇರಿಸಿ", "add_a_location": "ಸ್ಥಳವನ್ನು ಸೇರಿಸಿ", "add_a_name": "ಹೆಸರನ್ನು ಸೇರಿಸಿ", "add_a_title": "ಶೀರ್ಷಿಕೆಯನ್ನು ಸೇರಿಸಿ", + "add_birthday": "ಜನ್ಮದಿನ ಸೇರಿಸಿ", "add_endpoint": "ಎಂಡ್‌ಪಾಯಿಂಟ್ ಸೇರಿಸಿ", "add_exclusion_pattern": "ಹೊರಗಿಡುವಿಕೆ ಮಾದರಿಯನ್ನು ಸೇರಿಸಿ", "add_import_path": "ಆಮದು ಮಾರ್ಗವನ್ನು ಸೇರಿಸಿ", @@ -22,5 +24,6 @@ "add_path": "ಹಾದಿಯನ್ನು ಸೇರಿಸಿ", "add_photos": "ಫೋಟೋಗಳನ್ನು ಸೇರಿಸಿ", "add_to": "ಸೇರಿಸಿ…", - "add_to_album": "ಆಲ್ಬಮ್‌ಗೆ ಸೇರಿಸಿ" + "add_to_album": "ಆಲ್ಬಮ್‌ಗೆ ಸೇರಿಸಿ", + "add_to_album_bottom_sheet_added": "{album}ಗೆ ಸೇರಿಸಿದೆ" } diff --git a/i18n/ko.json b/i18n/ko.json index 815f1b2b2f..0c01c03263 100644 --- a/i18n/ko.json +++ b/i18n/ko.json @@ -8,7 +8,7 @@ "actions": "작업", "active": "활성", "activity": "활동", - "activity_changed": "활동이 {enabled, select, true {활성화} other {비활성화}}되었습니다.", + "activity_changed": "활동이 {enabled, select, true {활성화} other {비활성화}}되었습니다", "add": "추가", "add_a_description": "설명 추가", "add_a_location": "위치 추가", @@ -28,20 +28,21 @@ "add_to_album": "앨범에 추가", "add_to_album_bottom_sheet_added": "{album}에 추가됨", "add_to_album_bottom_sheet_already_exists": "이미 {album}에 있음", + "add_to_album_bottom_sheet_some_local_assets": "몇 개의 로컬 항목이 앨범에 추가되지 않았습니다", "add_to_album_toggle": "{album} 선택/해제", "add_to_albums": "여러 앨범에 추가", "add_to_albums_count": "여러 앨범에 추가 ({count})", "add_to_shared_album": "공유 앨범에 추가", "add_url": "URL 추가", - "added_to_archive": "보관함으로 이동되었습니다.", - "added_to_favorites": "즐겨찾기에 추가되었습니다.", + "added_to_archive": "보관함으로 이동되었습니다", + "added_to_favorites": "즐겨찾기에 추가되었습니다", "added_to_favorites_count": "즐겨찾기에 항목 {count, number}개 추가됨", "admin": { "add_exclusion_pattern_description": "*, **, ? 등의 glob 패턴을 사용할 수 있습니다. 예를 들어 \"Raw\" 폴더 내 모든 파일을 제외하려면 \"**/Raw/**\"를, .tif 파일을 제외하려면 \"**/*.tif\", 특정한 절대 경로를 제외하려면 \"/path/to/ignore/**\" 처럼 사용합니다.", "admin_user": "관리자", "asset_offline_description": "이 항목은 외부 라이브러리에 등록되었으나 디스크에서 찾을 수 없어 휴지통으로 이동했습니다. 파일이 라이브러리 경로 내에서 이동된 경우 타임라인에서 새로 인식된 항목이 있는지 확인해보세요. 이 항목을 복원하려면 아래 경로에 Immich가 접근할 수 있는지 확인하고 라이브러리를 다시 스캔하세요.", "authentication_settings": "인증 설정", - "authentication_settings_description": "비밀번호, OAuth 및 기타 인증 설정을 관리합니다.", + "authentication_settings_description": "비밀번호, OAuth 및 기타 인증 설정을 관리합니다", "authentication_settings_disable_all": "모든 로그인 수단을 비활성화하시겠습니까? 더이상 로그인할 수 없습니다.", "authentication_settings_reenable": "다시 활성화하려면 서버 명령어를 사용하세요.", "background_task_job": "백그라운드 작업", @@ -49,7 +50,7 @@ "backup_database_enable_description": "데이터베이스 덤프 활성화", "backup_keep_last_amount": "보관할 이전 덤프 수", "backup_onboarding_1_description": "개는 클라우드나 다른 물리적 위치에 보관합니다.", - "backup_onboarding_2_description": "개는 서로 다른 로컬 장치에 보관하고,", + "backup_onboarding_2_description": "다른 기기의 로컬 사본. 메인 파일과 로컬 백업을 포함합니다.", "backup_onboarding_3_description": "개의 데이터 사본을 만듭니다.", "backup_onboarding_description": "소중한 데이터를 안전하게 보호하기 위해 3-2-1 백업 전략 사용을 권장합니다. Immich를 백업할 때 업로드한 사진 및 동영상뿐 아니라 데이터베이스도 함께 보관해야 한다는 점을 잊지 마세요.", "backup_onboarding_footer": "Immich 백업에 대한 자세한 내용은 공식 문서를 참조하세요.", @@ -58,10 +59,10 @@ "backup_settings": "데이터베이스 덤프 설정", "backup_settings_description": "데이터베이스 덤프 주기와 보관 기간을 설정합니다.", "cleared_jobs": "작업 중단: {job}", - "config_set_by_file": "설정이 구성 파일을 통해 관리되고 있습니다.", + "config_set_by_file": "설정이 구성 파일을 통해 관리되고 있습니다", "confirm_delete_library": "{library} 라이브러리를 삭제하시겠습니까?", "confirm_delete_library_assets": "이 라이브러리를 삭제하시겠습니까? Immich에서 {count, plural, one {항목 #개가} other {항목 #개가}} 삭제되며 되돌릴 수 없습니다. 원본 파일은 디스크에 남아 있습니다.", - "confirm_email_below": "계속하려면 아래에 \"{email}\"을(를) 입력하세요.", + "confirm_email_below": "계속하려면 아래에 \"{email}\"을(를) 입력하세요", "confirm_reprocess_all_faces": "모든 얼굴을 다시 처리하시겠습니까? 이름이 지정된 인물도 초기화됩니다.", "confirm_user_password_reset": "{user}님의 비밀번호를 초기화하시겠습니까?", "confirm_user_pin_code_reset": "{user}님의 PIN 코드를 초기화하시겠습니까?", @@ -70,7 +71,7 @@ "cron_expression_description": "Cron 표현식으로 스캔 주기를 설정합니다. 자세한 내용은 다음을 참조하세요, Crontab Guru", "cron_expression_presets": "Cron 표현식 프리셋", "disable_login": "로그인 비활성화", - "duplicate_detection_job_description": "기계 학습으로 유사한 이미지를 감지합니다. 스마트 검색이 활성화되어 있어야 합니다.", + "duplicate_detection_job_description": "기계 학습으로 유사한 이미지를 감지합니다. 스마트 검색이 활성화되어 있어야 합니다", "exclusion_pattern_description": "라이브러리 스캔에서 제외할 파일이나 폴더 규칙을 설정합니다. 폴더에 원하지 않는 파일(RAW 파일 등)이 함께 존재하는 경우 유용합니다.", "external_library_management": "외부 라이브러리 관리", "face_detection": "얼굴 감지", @@ -123,6 +124,13 @@ "logging_enable_description": "로그 기록 활성화", "logging_level_description": "활성화 시 사용할 로그 레벨을 선택합니다.", "logging_settings": "로깅", + "machine_learning_availability_checks": "가용성 확인", + "machine_learning_availability_checks_description": "사용 가능한 머신 러닝 서버를 자동으로 감지하고 우선적으로 선택합니다", + "machine_learning_availability_checks_enabled": "가용성 확인 활성화", + "machine_learning_availability_checks_interval": "확인 주기", + "machine_learning_availability_checks_interval_description": "가용성 확인 주기 (밀리초 단위)", + "machine_learning_availability_checks_timeout": "요청 타임아웃", + "machine_learning_availability_checks_timeout_description": "가용성 확인 요청 타임아웃 (밀리초 단위)", "machine_learning_clip_model": "CLIP 모델", "machine_learning_clip_model_description": "CLIP 모델의 종류는 이곳을 참조하세요. 한국어 등 여러 언어로 검색하려면 Multilingual CLIP 모델을 선택하세요. 모델을 변경한 경우 모든 이미지의 '스마트 검색' 작업을 다시 실행해야 합니다.", "machine_learning_duplicate_detection": "비슷한 항목 감지", @@ -394,6 +402,7 @@ "advanced_settings_prefer_remote_title": "서버 이미지 선호", "advanced_settings_proxy_headers_subtitle": "Immich가 네트워크 요청 시 사용할 프록시 헤더를 정의합니다.", "advanced_settings_proxy_headers_title": "프록시 헤더", + "advanced_settings_readonly_mode_subtitle": "읽기 전용 모드를 활성화하면 여러 이미지 선택, 공유, 캐스트, 삭제 동작이 모두 비활성화됩니다. 메인 화면에서 사용자 프로필을 통해 읽기 전용 모드의 활성 상태를 전환하세요", "advanced_settings_readonly_mode_title": "읽기 전용 모드", "advanced_settings_self_signed_ssl_subtitle": "서버 엔드포인트의 SSL 인증서 검증을 건너뜁니다. 자체 서명 인증서를 사용하는 경우 활성화하세요.", "advanced_settings_self_signed_ssl_title": "자체 서명된 SSL 인증서 허용", @@ -422,6 +431,7 @@ "album_remove_user_confirmation": "{user}님을 앨범에서 제거하시겠습니까?", "album_search_not_found": "검색 결과에 해당하는 앨범이 없습니다.", "album_share_no_users": "이미 모든 사용자와 앨범을 공유했거나 공유할 사용자가 없습니다.", + "album_summary": "앨범 요약", "album_updated": "항목 추가 알림", "album_updated_setting_description": "공유 앨범에 항목이 추가된 경우 이메일 알림 받기", "album_user_left": "{album} 앨범에서 나옴", @@ -493,6 +503,8 @@ "asset_restored_successfully": "항목이 복원되었습니다.", "asset_skipped": "건너뜀", "asset_skipped_in_trash": "휴지통의 항목", + "asset_trashed": "항목 삭제됨", + "asset_troubleshoot": "항목 트러블슈팅", "asset_uploaded": "업로드 완료", "asset_uploading": "업로드 중…", "asset_viewer_settings_subtitle": "갤러리 보기 설정을 관리합니다.", @@ -526,8 +538,10 @@ "autoplay_slideshow": "슬라이드 쇼 자동 재생", "back": "뒤로", "back_close_deselect": "뒤로, 닫기 또는 선택 해제", + "background_backup_running_error": "백그라운드 백업이 현재 진행 중이므로 수동 백업을 시작할 수 없습니다", "background_location_permission": "백그라운드 위치 권한", "background_location_permission_content": "Immich가 백그라운드에서 실행 중일 때 네트워크를 전환하려면 Wi-Fi 네트워크 이름을 확인해야 하며, 이를 위해 '정확한 위치' 권한을 항상 허용해야 합니다.", + "background_options": "백그라운드 옵션", "backup": "백업", "backup_album_selection_page_albums_device": "기기의 앨범 ({count})", "backup_album_selection_page_albums_tap": "탭하여 포함, 두 번 탭하여 제외", @@ -535,6 +549,7 @@ "backup_album_selection_page_select_albums": "앨범 선택", "backup_album_selection_page_selection_info": "선택한 앨범", "backup_album_selection_page_total_assets": "전체 항목", + "backup_albums_sync": "앨범 동기화 백업", "backup_all": "모두", "backup_background_service_backup_failed_message": "항목 백업에 실패했습니다. 다시 시도하는 중…", "backup_background_service_connection_failed_message": "서버 연결에 실패했습니다. 다시 시도하는 중…", @@ -584,6 +599,7 @@ "backup_controller_page_turn_on": "활성화", "backup_controller_page_uploading_file_info": "파일 정보 업로드 중", "backup_err_only_album": "유일한 앨범은 삭제할 수 없습니다.", + "backup_error_sync_failed": "동기화에 실패했습니다. 백업을 진행할 수 없습니다.", "backup_info_card_assets": "항목", "backup_manual_cancelled": "취소됨", "backup_manual_in_progress": "업로드가 이미 진행 중입니다. 잠시 후 다시 시도하세요", @@ -651,6 +667,8 @@ "change_pin_code": "PIN 코드 변경", "change_your_password": "사용자 계정의 비밀번호를 변경합니다.", "changed_visibility_successfully": "숨김 여부가 변경되었습니다.", + "charging": "충전 중", + "charging_requirement_mobile_backup": "백그라운드 백업은 기기 충전 상태에서 가능합니다", "check_corrupt_asset_backup": "백업된 항목의 손상 여부 확인", "check_corrupt_asset_backup_button": "확인 수행", "check_corrupt_asset_backup_description": "이 검사는 모든 항목이 백업된 후 Wi-Fi가 연결된 상태에서만 실행하세요. 이 작업은 몇 분 정도 소요될 수 있습니다.", @@ -737,6 +755,7 @@ "create_user": "사용자 계정 생성", "created": "생성됨", "created_at": "생성됨", + "creating_linked_albums": "링크 연결된 앨범 생성 중...", "crop": "자르기", "curated_object_page_title": "사물", "current_device": "현재 기기", @@ -886,7 +905,9 @@ "error": "오류", "error_change_sort_album": "앨범 표시 순서 변경 실패", "error_delete_face": "항목에서 얼굴 삭제 중 오류 발생", + "error_getting_places": "장소 정보 입력 실패", "error_loading_image": "이미지를 불러오는 중 오류 발생", + "error_loading_partners": "파트너 불러오기 실패: {error}", "error_saving_image": "오류: {error}", "error_tag_face_bounding_box": "얼굴 태그 실패 - 얼굴의 위치를 가져올 수 없습니다.", "error_title": "오류 - 문제가 발생했습니다", @@ -1051,6 +1072,7 @@ "favorites_page_no_favorites": "즐겨찾기된 항목 없음", "feature_photo_updated": "대표 사진 업데이트됨", "features": "기능", + "features_in_development": "개발 중인 기능", "features_setting_description": "사진 및 동영상 관리 기능을 설정합니다.", "file_name": "파일 이름", "file_name_or_extension": "파일명 또는 확장자", @@ -1071,6 +1093,7 @@ "gcast_enabled": "구글 캐스트", "gcast_enabled_description": "이 기능은 Google의 외부 리소스를 사용하여 실행됩니다.", "general": "일반", + "geolocation_instruction_location": "GPS 좌표가 포함된 항목을 클릭해 위치를 사용하거나, 지도에서 직접 위치를 선택하세요", "get_help": "도움 얻기", "get_wifiname_error": "Wi-Fi 이름을 가져올 수 없습니다. 필수 권한이 부여되었는지, Wi-Fi 네트워크에 연결되어 있는지 확인하세요.", "getting_started": "시작하기", @@ -1214,6 +1237,7 @@ "local": "로컬", "local_asset_cast_failed": "서버에 업로드되지 않은 항목을 캐스팅할 수 없음", "local_assets": "로컬 항목", + "local_media_summary": "로컬 미디어 요약", "local_network": "로컬 네트워크", "local_network_sheet_info": "지정된 Wi-Fi를 사용할 때 앱이 아래 URL로 서버에 연결합니다.", "location_permission": "위치 권한", @@ -1225,6 +1249,7 @@ "location_picker_longitude_hint": "여기에 경도를 입력하세요", "lock": "잠금", "locked_folder": "잠금 폴더", + "log_detail_title": "상세 로그", "log_out": "로그아웃", "log_out_all_devices": "모든 기기에서 로그아웃", "logged_in_as": "{user}로 로그인됨", @@ -1255,6 +1280,7 @@ "login_password_changed_success": "비밀번호가 변경되었습니다.", "logout_all_device_confirmation": "모든 기기에서 로그아웃하시겠습니까?", "logout_this_device_confirmation": "이 기기에서 로그아웃하시겠습니까?", + "logs": "로그", "longitude": "경도", "look": "보기", "loop_videos": "동영상 반복", @@ -1297,6 +1323,7 @@ "mark_as_read": "읽음으로 표시", "marked_all_as_read": "모두 읽음으로 표시했습니다.", "matches": "일치", + "matching_assets": "일치하는 항목", "media_type": "미디어 종류", "memories": "추억", "memories_all_caught_up": "모두 확인함", @@ -1337,6 +1364,7 @@ "name_or_nickname": "이름 또는 닉네임", "network_requirement_photos_upload": "사진 백업에 모바일 데이터 사용", "network_requirement_videos_upload": "동영상 백업에 모바일 데이터 사용", + "network_requirements": "네트워크 요구사항", "network_requirements_updated": "네트워크 상태가 변경되었습니다. 백업 대기열을 초기화합니다.", "networking_settings": "연결", "networking_subtitle": "서버 엔드포인트 설정을 관리합니다.", @@ -1347,6 +1375,7 @@ "new_person": "새 인물 생성", "new_pin_code": "새 PIN 코드", "new_pin_code_subtitle": "잠금 폴더에 처음 접근하셨습니다. 이곳에 안전하게 접근하기 위한 PIN 코드를 설정하세요.", + "new_timeline": "새 타임라인", "new_user_created": "사용자 계정이 생성되었습니다.", "new_version_available": "새 버전 사용 가능", "newest_first": "최신순", @@ -1360,20 +1389,25 @@ "no_assets_message": "여기를 클릭해 첫 사진을 업로드하세요.", "no_assets_to_show": "표시할 항목 없음", "no_cast_devices_found": "캐스트 기기 없음", + "no_checksum_local": "체크섬이 없습니다. 로컬 항목을 불러올 수 없습니다", + "no_checksum_remote": "체크섬이 없습니다. 외부 항목을 불러올 수 없습니다", "no_duplicates_found": "비슷한 항목이 없습니다.", "no_exif_info_available": "EXIF 정보 없음", "no_explore_results_message": "더 많은 사진을 업로드하여 탐색 기능을 사용하세요.", "no_favorites_message": "즐겨찾기에서 사진과 동영상을 빠르게 찾기", "no_libraries_message": "외부 라이브러리로 다른 경로의 사진과 동영상을 확인하세요.", + "no_local_assets_found": "체크섬에 맞는 로컬 항목을 찾을 수 없습니다", "no_locked_photos_message": "잠금 폴더의 사진 및 동영상은 숨겨지며 라이브러리를 탐색할 때 표시되지 않습니다.", "no_name": "이름 없음", "no_notifications": "알림 없음", "no_people_found": "일치하는 인물 없음", "no_places": "장소 없음", + "no_remote_assets_found": "체크섬에 맞는 외부 항목을 찾을 수 없습니다", "no_results": "결과 없음", "no_results_description": "동의어 또는 더 일반적인 단어를 사용해 보세요.", "no_shared_albums_message": "앨범을 만들어 주변 사람들과 사진 및 동영상을 공유하세요.", "no_uploads_in_progress": "진행 중인 업로드 없음", + "not_available": "없음", "not_in_any_album": "앨범에 없음", "not_selected": "선택되지 않음", "note_apply_storage_label_to_previously_uploaded assets": "참고: 이전에 업로드한 항목에도 스토리지 레이블을 적용하려면 다음을 실행합니다,", @@ -1408,6 +1442,8 @@ "open_the_search_filters": "검색 필터 열기", "options": "옵션", "or": "또는", + "organize_into_albums": "앨범으로 정리하기", + "organize_into_albums_description": "현재 동기화 설정을 사용하여 기존 사진을 앨범으로 정리합니다", "organize_your_library": "라이브러리 정리", "original": "원본", "other": "기타", @@ -1493,6 +1529,7 @@ "port": "포트", "preferences_settings_subtitle": "앱 개인 설정을 관리합니다.", "preferences_settings_title": "개인 설정", + "preparing": "준비 중", "preset": "프리셋", "preview": "미리 보기", "previous": "이전", @@ -1509,6 +1546,7 @@ "profile_drawer_client_out_of_date_minor": "모바일 앱이 최신 버전이 아닙니다. 최신 버전으로 업데이트하세요.", "profile_drawer_client_server_up_to_date": "클라이언트와 서버가 최신 상태입니다.", "profile_drawer_github": "Github", + "profile_drawer_readonly_mode": "읽기 전용 모드 활성화. 유저 아바타 아이콘을 길게 눌러 해제할 수 있습니다.", "profile_drawer_server_out_of_date_major": "서버 버전이 최신이 아닙니다. 최신 버전으로 업데이트하세요.", "profile_drawer_server_out_of_date_minor": "서버 버전이 최신이 아닙니다. 최신 버전으로 업데이트하세요.", "profile_image_of_user": "{user}님의 프로필 이미지", @@ -1547,6 +1585,7 @@ "purchase_server_description_2": "서포터 배지", "purchase_server_title": "서버", "purchase_settings_server_activated": "서버 제품 키는 관리자가 제어합니다.", + "query_asset_id": "쿼리 항목 ID", "queue_status": "전체 {total}, {count} 대기 중", "rating": "등급", "rating_clear": "등급 초기화", @@ -1554,6 +1593,9 @@ "rating_description": "상세 정보 패널에 EXIF 등급 태그 표시", "reaction_options": "반응 옵션", "read_changelog": "변경 내역 보기", + "readonly_mode_disabled": "읽기 전용 모드 비활성화", + "readonly_mode_enabled": "읽기 전용 모드 활성화", + "ready_for_upload": "업로드 준비 완료", "reassign": "다시 할당", "reassigned_assets_to_existing_person": "{count, plural, one {항목 #개} other {항목 #개}}를 {name, select, null {기존 인물} other {기존 인물 {name}}}에게 재지정했습니다.", "reassigned_assets_to_new_person": "{count, plural, one {항목 #개} other {항목 #개}}를 새 인물에게 재지정했습니다.", @@ -1578,6 +1620,7 @@ "regenerating_thumbnails": "섬네일을 다시 생성하는 중...", "remote": "원격", "remote_assets": "원격 항목", + "remote_media_summary": "원격 미디어 요약", "remove": "제거", "remove_assets_album_confirmation": "앨범에서 항목 {count, plural, one {#개} other {#개}}를 제거하시겠습니까?", "remove_assets_shared_link_confirmation": "공유 링크에서 항목 {count, plural, one {#개} other {#개}}를 제거하시겠습니까?", @@ -1630,6 +1673,7 @@ "restore_user": "사용자 복원", "restored_asset": "항목이 복원되었습니다.", "resume": "재개", + "resume_paused_jobs": "재개 {count, plural, one {# 일시 중지된 작업} other {# 일시 중지된 작업}}", "retry_upload": "다시 시도", "review_duplicates": "비슷한 항목 확인", "review_large_files": "용량이 큰 파일 확인", @@ -1723,6 +1767,7 @@ "select_user_for_sharing_page_err_album": "앨범을 생성하지 못했습니다.", "selected": "선택됨", "selected_count": "{count, plural, other {#개 선택됨}}", + "selected_gps_coordinates": "선택한 GPS 좌표", "send_message": "메시지 전송", "send_welcome_email": "환영 이메일 전송", "server_endpoint": "서버 엔드포인트", @@ -1851,6 +1896,7 @@ "show_slideshow_transition": "슬라이드 전환 표시", "show_supporter_badge": "서포터 배지", "show_supporter_badge_description": "서포터 배지 표시", + "show_text_search_menu": "텍스트 검색 메뉴 표시", "shuffle": "셔플", "sidebar": "사이드바", "sidebar_display_description": "보기 링크를 사이드바에 표시", @@ -1881,6 +1927,7 @@ "stacktrace": "스택 추적", "start": "시작", "start_date": "시작일", + "start_date_before_end_date": "시작일은 종료일보다 이전이어야 합니다", "state": "지역", "status": "상태", "stop_casting": "캐스팅 중단", @@ -1905,6 +1952,8 @@ "sync_albums_manual_subtitle": "업로드한 모든 동영상과 사진을 선택한 백업 앨범에 동기화", "sync_local": "로컬 동기화", "sync_remote": "원격 동기화", + "sync_status": "동기화 상태", + "sync_status_subtitle": "동기화 시스템 확인 및 관리", "sync_upload_album_setting_subtitle": "선택한 앨범을 Immich에 생성하고 사진 및 동영상 업로드", "tag": "태그", "tag_assets": "항목 태그", @@ -1964,6 +2013,7 @@ "trash_page_select_assets_btn": "항목 선택", "trash_page_title": "휴지통 ({count})", "trashed_items_will_be_permanently_deleted_after": "휴지통으로 이동된 항목은 {days, plural, one {#일} other {#일}} 후 영구적으로 삭제됩니다.", + "troubleshoot": "트러블슈팅", "type": "형식", "unable_to_change_pin_code": "PIN 코드를 변경할 수 없음", "unable_to_setup_pin_code": "PIN 코드를 설정할 수 없음", @@ -1994,6 +2044,7 @@ "unstacked_assets_count": "항목 {count, plural, one {#개} other {#개}}의 스택을 풀었습니다.", "untagged": "태그 해제됨", "up_next": "다음", + "update_location_action_prompt": "선택한 {count}개 항목 위치 업데이트:", "updated_at": "업데이트됨", "updated_password": "비밀번호가 변경되었습니다.", "upload": "업로드", @@ -2060,6 +2111,7 @@ "view_next_asset": "다음 항목 보기", "view_previous_asset": "이전 항목 보기", "view_qr_code": "QR 코드 보기", + "view_similar_photos": "비슷한 사진 보기", "view_stack": "스택 보기", "view_user": "사용자 보기", "viewer_remove_from_stack": "스택에서 제거", @@ -2078,5 +2130,6 @@ "yes": "네", "you_dont_have_any_shared_links": "공유 링크가 없습니다.", "your_wifi_name": "Wi-Fi 네트워크 이름", - "zoom_image": "이미지 확대" + "zoom_image": "이미지 확대", + "zoom_to_bounds": "화면에 맞춰 확대" } diff --git a/i18n/lv.json b/i18n/lv.json index fa4981fd0e..053a156a17 100644 --- a/i18n/lv.json +++ b/i18n/lv.json @@ -28,10 +28,12 @@ "add_to_album": "Pievienot albumam", "add_to_album_bottom_sheet_added": "Pievienots {album}", "add_to_album_bottom_sheet_already_exists": "Jau pievienots {album}", + "add_to_album_bottom_sheet_some_local_assets": "Dažus lokālos failus albumam nevarēja pievienot", "add_to_album_toggle": "Pārslēgt izvēli {album}", "add_to_albums": "Pievienot albumiem", "add_to_albums_count": "Pievienot albumiem ({count})", "add_to_shared_album": "Pievienot koplietotam albumam", + "add_upload_to_stack": "Pievienot augšupielādi kaudzei", "add_url": "Pievienot URL", "added_to_archive": "Pievienots arhīvam", "added_to_favorites": "Pievienots izlasei", @@ -44,12 +46,16 @@ "authentication_settings_description": "Paroļu, OAuth un citu autentifikācijas iestatījumu pārvaldība", "authentication_settings_disable_all": "Vai tiešām vēlaties atspējot visas pieteikšanās metodes? Pieteikšanās tiks pilnībā atspējota.", "authentication_settings_reenable": "Lai atkārtoti iespējotu, izmantojiet Servera Komandu.", - "background_task_job": "Fona Uzdevumi", + "background_task_job": "Fona uzdevumi", "backup_database": "Izveidot datu bāzes izrakstu", "backup_database_enable_description": "Iespējot datu bāzes izrakstus", "backup_keep_last_amount": "Iepriekšējo izrakstu daudzums, kas jāsaglabā", "backup_onboarding_1_description": "ārēja kopija mākonī vai citā fiziskā atrašanās vietā.", - "backup_onboarding_2_description": "vietējās kopijas citās ierīcēs. Tas ietver galvenos failus un šo failu vietējo rezerves kopiju.", + "backup_onboarding_2_description": "lokālās kopijas citās ierīcēs. Tas ietver galvenos failus un šo failu lokālo rezerves kopiju.", + "backup_onboarding_3_description": "kopiju skaits, ieskaitot oriģinālos failus. Tas ietver 1 ārējo kopiju un 2 lokālās kopijas.", + "backup_onboarding_description": "Lai aizsargātu savus datus, ieteicams izmantot 3-2-1 rezerves kopiju stratēģiju. Lai nodrošinātu visaptverošu dublēšanas risinājumu, vajadzētu veidot kopijas saviem augšupielādētajiem fotoattēliem/videoklipiem, kā arī Immich datubāzei.", + "backup_onboarding_footer": "Lai iegūtu vairāk informācijas par Immich rezerves kopiju veidošanu, lūdzu, apskatiet dokumentāciju.", + "backup_onboarding_parts_title": "3-2-1 rezerves kopija ietver:", "backup_onboarding_title": "Rezerves kopijas", "backup_settings": "Datubāzes izrakstu iestatījumi", "backup_settings_description": "Datubāzes izrakstu iestatījumu pārvaldība", @@ -61,52 +67,78 @@ "confirm_user_password_reset": "Vai tiešām vēlaties atiestatīt lietotāja {user} paroli?", "create_job": "Izveidot uzdevumu", "cron_expression": "Cron izteiksme", + "cron_expression_description": "Iestatiet skenēšanas intervālu, izmantojot cron formātu. Papildu informācijai skatiet, piemēram, Crontab Guru", + "cron_expression_presets": "Cron izteiksmju sagataves", "disable_login": "Atspējot pieteikšanos", - "duplicate_detection_job_description": "Palaidiet mašīnmācīšanos uz failiem, lai noteiktu līdzīgus attēlus. Paļaujas uz viedo meklēšanu", + "duplicate_detection_job_description": "Analizēt failus ar mašīnmācīšanos, lai noteiktu līdzīgus attēlus. Šī funkcija izmanto viedo meklēšanu", "external_library_management": "Ārējo bibliotēku pārvaldība", "face_detection": "Seju noteikšana", + "face_detection_description": "Atpazīt attēlos sejas, izmantojot mašīnmācīšanos. Video gadījumā tiek ņemta vērā tikai sīktēls. \"Atsvaidzināt\" atkārtoti apstrādā visus attēlus. \"Atiestatīt\" izdzēš visus pašreizējos seju datus. \"Trūkstošie\" ierindo attēlus, kas vēl nav apstrādāti. Pēc seju noteikšanas pabeigšanas atrastās sejas tiek ierindotas seju atpazīšanai, grupējot tās pēc esošas vai jauns personas.", + "facial_recognition_job_description": "Grupēt atpazītās sejas pēc cilvēkiem. Šis solis tiek veikts pēc seju noteikšanas pabeigšanas. \"Atiestatīt\" atkārtoti sagrupē visas sejas. \"Trūkstošie\" ierindo sejas, kurām nav piešķirta persona.", + "force_delete_user_warning": "BRĪDINĀJUMS: Tas uzreiz izdzēsīs lietotāju ar visiem failiem. Šo darbību nevar atcelt, un failus nevarēs atgūt.", "image_format": "Formāts", "image_format_description": "WebP veido mazākus failus nekā JPEG, taču to kodēšana ir lēnāka.", - "image_fullsize_enabled_description": "Ģenerēt pilna izmēra attēlu formātiem, kas nav piemēroti izmantošanai tīmeklī. Ja ir iespējota opcija \"Priekšroka iegultajam priekšskatījumam\", tiks izmantoti iegultie priekšskatījumi bez konvertēšanas. Neietekmē tīmeklim draudzīgus formātus, piemēram, JPEG.", + "image_fullsize_description": "Pilnizmēra attēls ar noņemtiem metadatiem, ko izmanto, kad attēls ir tuvināts", + "image_fullsize_enabled": "Iespējot pilnizmēra attēlu ģenerēšanu", + "image_fullsize_enabled_description": "Ģenerēt pilnizmēra attēlu formātiem, kas nav piemēroti izmantošanai tīmeklī. Ja ir iespējota opcija \"Priekšroka iegultajam priekšskatījumam\", tiks izmantoti iegultie priekšskatījumi bez konvertēšanas. Neietekmē tīmeklim draudzīgus formātus, piemēram, JPEG.", "image_fullsize_quality_description": "Pilnizmēra attēlu kvalitāte no 1-100. Augstāka vērtība dos labāku kvalitāti, taču faili būs lielāka izmēra.", "image_fullsize_title": "Pilnizmēra attēlu iestatījumi", "image_prefer_embedded_preview": "Priekšroka iegultajam priekšskatījumam", "image_prefer_embedded_preview_setting_description": "Izmanto RAW fotoattēlos iestrādātos priekšskatījumus, ja tādi ir pieejami, kā ievades datus attēlu apstrādei. Tādējādi dažiem attēliem var iegūt precīzākas krāsas, taču priekšskatījuma kvalitāte ir atkarīga no fotokameras un attēlam var būt vairāk saspiešanas artefaktu.", + "image_prefer_wide_gamut": "Dot priekšroku plašai krāsu gammai", "image_prefer_wide_gamut_setting_description": "Sīktēliem izmanto Display P3. Tas labāk saglabā attēlu dzīvīgumu ar plašu krāsu gammu, bet attēli var izskatīties atšķirīgi vecās ierīcēs ar vecu pārlūka versiju. sRGB attēli tiek saglabāti kā sRGB, lai izvairītos no krāsu izmaiņām.", + "image_preview_quality_description": "Priekšskatījuma kvalitāte no 1 līdz 100. Augstāka kvalitāte ir labāka, bet veido lielākus failus un var samazināt lietotnes reaģēšanas ātrumu. Zemas vērtības iestatīšana var ietekmēt mašīnmācīšanās kvalitāti.", "image_preview_title": "Priekšskatījuma iestatījumi", "image_quality": "Kvalitāte", "image_resolution": "Izšķirtspēja", + "image_resolution_description": "Augstāka izšķirtspēja ļauj saglabāt vairāk detaļu, taču kodēšana aizņem vairāk laika, failu izmērs ir lielāks un var samazināties lietotnes reaģēšanas ātrums.", "image_settings": "Attēlu iestatījumi", "image_settings_description": "Ģenerēto attēlu kvalitātes un izšķirtspējas pārvaldība", "image_thumbnail_description": "Neliels sīktēls bez metadatiem, ko izmanto, lai apskatītu vairākus fotoattēlus, piemēram, galvenajā laika skalā", + "image_thumbnail_quality_description": "Sīktēlu kvalitāte no 1 līdz 100. Augstāka kvalitāte ir labāka, bet veido lielākus failus un var samazināt lietotnes reaģēšanas ātrumu.", "image_thumbnail_title": "Sīktēlu iestatījumi", "job_concurrency": "{job} vienlaicīgi", "job_created": "Uzdevums izveidots", + "job_not_concurrency_safe": "Šis uzdevums nav drošs vienlaicīgai izpildei.", "job_settings": "Uzdevumu iestatījumi", "job_settings_description": "Uzdevumu izpildes vienlaicīguma pārvaldība", "job_status": "Uzdevumu statuss", "library_created": "Izveidoja bibliotēku: {library}", "library_deleted": "Bibliotēka dzēsta", + "library_import_path_description": "Norādi importējamo mapi. Šī mape un tās apakšmapes tiks pārbaudīta, lai atrastu attēlus un videoklipus.", "library_scanning": "Periodiska skenēšana", "library_scanning_description": "Konfigurē periodisku bibliotēku skenēšanu", "library_scanning_enable_description": "Iespējot periodisku bibliotēku skenēšanu", - "library_settings": "Ārējā bibliotēka", + "library_settings": "Ārējās bibliotēkas", "library_settings_description": "Ārējo bibliotēku iestatījumu pārvaldība", + "library_tasks_description": "Pārbaudīt ārējās bibliotēkas, lai atrastu jaunus un/vai mainītus failus", + "library_watching_enable_description": "Uzraudzīt ārējo bibliotēku failu izmaiņas", "library_watching_settings": "Bibliotēku uzraudzīšana (EKSPERIMENTĀLA)", "library_watching_settings_description": "Automātiski uzraudzīt, vai ir mainīti faili", + "machine_learning_availability_checks": "Pieejamības pārbaudes", + "machine_learning_availability_checks_enabled": "Iespējot pieejamības pārbaudes", "machine_learning_clip_model": "CLIP modelis", "machine_learning_duplicate_detection": "Dublikātu noteikšana", + "machine_learning_duplicate_detection_enabled": "Iespējot dublikātu noteikšanu", + "machine_learning_duplicate_detection_enabled_description": "Ja šī funkcija ir atspējota, joprojām tiks izlaisti identiski faili.", + "machine_learning_enabled": "Iespējot mašīnmācīšanos", + "machine_learning_enabled_description": "Ja funkcija ir atspējota, tiks atspējotas visas ML funkcijas neatkarīgi no zemāk esošajiem iestatījumiem.", "machine_learning_facial_recognition": "Seju atpazīšana", "machine_learning_facial_recognition_model": "Seju atpazīšanas modelis", + "machine_learning_facial_recognition_model_description": "Modeļi ir uzskaitīti pēc to izmēra dilstošā secībā. Lielāki modeļi ir lēnāki un izmanto vairāk atmiņas, bet nodrošina labākus rezultātus. Ņem vērā, ka, mainot modeli, ir atkārtoti jāpalaiž sejas atpazīšanas uzdevums visiem attēliem.", + "machine_learning_facial_recognition_setting": "Iespējot seju atpazīšanu", "machine_learning_settings": "Mašīnmācīšanās iestatījumi", "machine_learning_settings_description": "Mašīnmācīšanās funkciju un iestatījumu pārvaldība", "machine_learning_smart_search": "Viedā meklēšana", + "machine_learning_smart_search_enabled": "Iespējot viedo meklēšanu", + "machine_learning_smart_search_enabled_description": "Ja funkcija ir atspējota, attēli netiks kodēti viedai meklēšanai.", "machine_learning_url_description": "Mašīnmācīšanās servera URL. Ja ir norādīts vairāk nekā viens URL, katrs serveris, sākot no pirmā līdz pēdējam, tiks pārbaudīts pa vienam, līdz kāds no tiem atbildēs veiksmīgi. Serveri, kas neatbild, tiks īslaicīgi ignorēti, līdz tie atkal būs pieejami tiešsaistē.", "manage_concurrency": "Vienlaicīgas darbības pārvaldība", "manage_log_settings": "Žurnāla iestatījumu pārvaldība", "map_dark_style": "Tumšais stils", "map_gps_settings": "Kartes un GPS iestatījumi", "map_gps_settings_description": "Karšu un GPS (apgrieztās ģeokodēšanas) iestatījumu pārvaldība", + "map_implications": "Kartes funkcija izmanto ārējo kartes fragmentu pakalpojumu (tiles.immich.cloud)", "map_light_style": "Gaišais stils", "map_manage_reverse_geocoding_settings": "Reversās ģeokodēšanas iestatījumu pārvaldība", "map_reverse_geocoding": "Reversā ģeokodēšana", @@ -114,10 +146,15 @@ "map_settings": "Karte", "map_settings_description": "Kartes iestatījumu pārvaldība", "map_style_description": "URL uz style.json kartes tēmu", + "memory_generate_job": "Atmiņu ģenerēšana", "metadata_extraction_job": "Metadatu iegūšana", + "metadata_extraction_job_description": "iegūt metadatu informāciju no katra faila, piemēram, GPS, sejas un izšķirtspēju", + "metadata_faces_import_setting": "Iespējot seju importēšanu", + "metadata_faces_import_setting_description": "Importēt sejas no attēla EXIF datiem un blakusfailiem", "metadata_settings": "Metadatu iestatījumi", "metadata_settings_description": "Metadatu iestatījumu pārvaldība", "migration_job": "Migrācija", + "migration_job_description": "Pārvietot failu un seju sīktēlus uz jaunāko mapju struktūru", "nightly_tasks_cluster_faces_setting_description": "Veikt sejas atpazīšanu jaunatklātajām sejām", "nightly_tasks_cluster_new_faces_setting": "Sagrupēt jaunās sejas", "nightly_tasks_database_cleanup_setting": "Datubāzes apkopes uzdevumi", @@ -134,6 +171,7 @@ "nightly_tasks_sync_quota_usage_setting_description": "Pārrēķināt lietotāja uzglabāšanas kvotu, pamatojoties uz pašreizējo izmantošanu", "no_paths_added": "Nav pievienots neviens ceļš", "no_pattern_added": "Nav pievienots neviens izslēgšanas šablons", + "note_apply_storage_label_previous_assets": "Piezīme: Lai piemērotu glabātuves nosaukumu iepriekš augšupielādētiem failiem, izpildiet", "note_cannot_be_changed_later": "PIEZĪME: Vēlāk to vairs nevar mainīt!", "notification_email_from_address": "No adreses", "notification_email_from_address_description": "Sūtītāja e-pasta adrese, piemēram: “Immich foto serveris ”", @@ -141,7 +179,9 @@ "notification_email_ignore_certificate_errors_description": "Ignorēt TLS sertifikāta apstiprināšanas kļūdas (nav ieteicams)", "notification_email_port_description": "e-pasta servera ports (piemēram, 25, 465 vai 587)", "notification_email_sent_test_email_button": "Nosūtīt testa e-pastu un saglabāt", + "notification_email_setting_description": "E-pasta paziņojumu sūtīšanas iestatījumi", "notification_email_test_email": "Nosūtīt testa e-pastu", + "notification_email_test_email_failed": "Neizdevās nosūtīt pārbaudes e-pastu, pārbaudi ievadītās vērtības", "notification_email_test_email_sent": "Uz {email} ir nosūtīts testa e-pasts. Lūdzu, pārbaudi savu iesūtni.", "notification_settings": "Paziņojumu iestatījumi", "notification_settings_description": "Paziņojumu iestatījumu, tostarp e-pasta, pārvaldība", @@ -153,6 +193,8 @@ "oauth_enable_description": "Pieslēgties ar OAuth", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth pieteikšanās iestatījumu pārvaldība", + "oauth_storage_label_claim": "Glabātuves nosaukuma pieteikums", + "oauth_storage_label_claim_description": "Automātiski iestatīt lietotāja glabātuves nosaukumu uz šī pieteikuma vērtību.", "oauth_storage_quota_default": "Noklusējuma krātuves kvota (GiB)", "password_enable_description": "Pieteikšanās ar e-pasta adresi un paroli", "password_settings": "Pieteikšanās ar paroli", @@ -176,11 +218,18 @@ "server_settings_description": "Servera iestatījumu pārvaldība", "server_welcome_message": "Sveiciena ziņa", "server_welcome_message_description": "Ziņojums, kas tiek parādīts pieslēgšanās lapā.", + "sidecar_job": "Blakusfailu metadati", + "sidecar_job_description": "Atklāt vai sinhronizēt blakusfailu metadatus no failu sistēmas", + "slideshow_duration_description": "Katra attēla rādīšanas ilgums sekundēs", + "smart_search_job_description": "Analizēt failus ar mašīnmācīšanos lai sagatavotu datus viedajai meklēšanai", "storage_template_date_time_sample": "Laika paraugs {date}", "storage_template_migration": "Krātuves veidņu migrācija", - "storage_template_migration_job": "Krātuves veidņu migrācijas uzdevums", + "storage_template_migration_description": "Piemēro pašreizējo {template} iepriekš augšupielādētajiem failiem", + "storage_template_migration_info": "Krātuves veidne pārveidos visus failu paplašinājumus uz mazajiem burtiem. Veidnes izmaiņas attieksies tikai uz jauniem failiem. Lai veidni piemērotu ar atpakaļejošu efektu iepriekš augšupielādētiem failiem, palaidiet {job}.", + "storage_template_migration_job": "Krātuves veidņu migrācijas uzdevumu", "storage_template_path_length": "Aptuvenais ceļa garuma ierobežojums: {length, number}/{limit, number}", "storage_template_settings": "Krātuves veidne", + "storage_template_user_label": "{label} ir lietotāja glabātuves nosaukums", "system_settings": "Sistēmas iestatījumi", "template_email_available_tags": "Sagatavē var izmantot šos mainīgos: {tags}", "template_email_if_empty": "Ja sagatave ir tukša, tiks izmantots noklusējuma e-pasts.", @@ -199,15 +248,19 @@ "transcoding_acceleration_qsv": "Quick Sync (nepieciešams 7. paaudzes vai jaunāks Intel procesors)", "transcoding_acceleration_rkmpp": "RKMPP (tikai Rockchip SOC)", "transcoding_acceleration_vaapi": "VAAPI", + "transcoding_accepted_video_codecs": "Akceptētie video kodeki", "transcoding_advanced_options_description": "Lielākajai daļai lietotāju nevajadzētu mainīt šīs opcijas", "transcoding_audio_codec": "Audio kodeks", + "transcoding_audio_codec_description": "Opus ir augstākās kvalitātes izvēle, bet tā ir mazāk saderīga ar vecām ierīcēm vai programmatūru.", "transcoding_codecs_learn_more": "Lai uzzinātu vairāk par šeit lietoto terminoloģiju, skatiet FFmpeg dokumentāciju par H.264 kodeku, HEVC kodeku un VP9 kodeku.", + "transcoding_constant_quality_mode": "Nemainīgas kvalitātes režīms", "transcoding_constant_quality_mode_description": "ICQ ir labāks nekā CQP, bet dažas aparatūras paātrinājuma ierīces neatbalsta šo režīmu. Iestatot šo opciju, tiks izmantots norādītais režīms, ja tiek izmantota kvalitātē balstīta kodēšana. NVENC to ignorē, jo neatbalsta ICQ.", "transcoding_constant_rate_factor_description": "Video kvalitātes līmenis. Tipiskās vērtības ir 23 priekš H.264, 28 priekš HEVC, 31 priekš VP9 un 35 priekš AV1. Zemāka vērtība ir labāka, bet rada lielākus failus.", "transcoding_hardware_acceleration": "Aparatūras paātrinājums", "transcoding_required_description": "Tikai video, kas nav atbalstītā formātā", "transcoding_settings": "Video transkodēšanas iestatījumi", "transcoding_threads": "Pavedieni", + "transcoding_threads_description": "Augstākas vērtības nodrošina ātrāku kodēšanu, bet atstāj mazāk jaudas serverim, lai apstrādātu citus aktīvos uzdevumus. Šai vērtībai nevajadzētu pārsniegt CPU kodolu skaitu. Ja iestatīta uz 0, maksimizē izmantošanu.", "transcoding_video_codec": "Video kodeks", "trash_number_of_days": "Dienu skaits", "trash_settings": "Atkritnes iestatījumi", @@ -225,14 +278,15 @@ "user_settings_description": "Lietotāju iestatījumu pārvaldība", "version_check_enabled_description": "Ieslēgt versijas pārbaudi", "version_check_implications": "Versiju pārbaudes funkcija ir atkarīga no periodiskas saziņas ar github.com", - "version_check_settings": "Versijas pārbaude" + "version_check_settings": "Versijas pārbaude", + "version_check_settings_description": "Ieslēgt/izslēgt paziņojumus par jaunu versiju" }, "admin_email": "Administratora e-pasts", "admin_password": "Administratora parole", "administration": "Administrēšana", "advanced": "Papildu", "advanced_settings_log_level_title": "Žurnalēšanas līmenis: {level}", - "advanced_settings_prefer_remote_subtitle": "Dažās ierīcēs sīktēli no ierīcē esošajiem resursiem tiek ielādēti ļoti lēni. Aktivizējiet šo iestatījumu, lai tā vietā ielādētu attālus attēlus.", + "advanced_settings_prefer_remote_subtitle": "Dažās ierīcēs sīktēli no ierīces atmiņas ielādējas ļoti lēni. Aktivizējiet šo iestatījumu, lai tā vietā ielādētu attālus attēlus.", "advanced_settings_prefer_remote_title": "Dot priekšroku attāliem attēliem", "advanced_settings_proxy_headers_title": "Starpniekservera galvenes", "advanced_settings_self_signed_ssl_subtitle": "Izlaiž servera galapunkta SSL sertifikātu verifikāciju. Nepieciešams pašparakstītajiem sertifikātiem.", @@ -245,7 +299,7 @@ "age_years": "{years, plural, zero {# gadu} one {# gads} other {# gadi}}", "album_added": "Albums pievienots", "album_added_notification_setting_description": "Saņemt e-pasta paziņojumu, kad tevi pievieno kopīgam albumam", - "album_cover_updated": "Albuma attēls atjaunināts", + "album_cover_updated": "Albuma vāciņš atjaunināts", "album_delete_confirmation_description": "Ja šis albums tiek kopīgots, citi lietotāji vairs nevarēs tam piekļūt.", "album_deleted": "Albums dzēsts", "album_info_card_backup_album_excluded": "NEIEKĻAUTS", @@ -270,7 +324,7 @@ "albums_default_sort_order_description": "Sākotnējā failu kārtošanas secība, veidojot jaunus albumus.", "albums_feature_description": "Failu kolekcijas, kuras var koplietot ar citiem lietotājiem.", "albums_on_device_count": "Albumi ierīcē ({count})", - "all": "Viss", + "all": "Visi", "all_albums": "Visi albumi", "all_people": "Visas personas", "all_videos": "Visi video", @@ -303,7 +357,7 @@ "asset_list_group_by_sub_title": "Grupēt pēc", "asset_list_layout_settings_dynamic_layout_title": "Dinamiskais izkārtojums", "asset_list_layout_settings_group_automatically": "Automātiski", - "asset_list_layout_settings_group_by": "Grupēt aktīvus pēc", + "asset_list_layout_settings_group_by": "Grupēt failus pēc", "asset_list_layout_settings_group_by_month_day": "Mēnesis + diena", "asset_list_layout_sub_title": "Izvietojums", "asset_list_settings_subtitle": "Fotorežģa izkārtojuma iestatījumi", @@ -312,7 +366,7 @@ "asset_skipped_in_trash": "Atkritnē", "asset_uploaded": "Augšupielādēts", "asset_uploading": "Augšupielādē…", - "asset_viewer_settings_title": "Aktīvu Skatītājs", + "asset_viewer_settings_title": "Failu skatītājs", "assets": "Faili", "assets_added_count": "Pievienoja {count, plural, one {# failu} other {# failus}}", "assets_added_to_album_count": "Pievienoja albumam {count, plural, one {# failu} other {# failus}}", @@ -326,6 +380,7 @@ "automatic_endpoint_switching_title": "Automātiska URL pārslēgšana", "autoplay_slideshow": "Automātiska slaidrādes atskaņošana", "back": "Atpakaļ", + "background_backup_running_error": "Pašlaik darbojas dublēšana fonā, nevar uzsākt manuālu dublēšanu", "background_options": "Fona opcijas", "backup": "Dublēšana", "backup_album_selection_page_albums_device": "Albumi ierīcē ({count})", @@ -374,7 +429,7 @@ "backup_controller_page_remainder": "Atlikums", "backup_controller_page_remainder_sub": "Atlikušie fotoattēli un videoklipi, kurus dublēt no atlases", "backup_controller_page_server_storage": "Servera krātuve", - "backup_controller_page_start_backup": "Sākt Dublēšanu", + "backup_controller_page_start_backup": "Sākt dublēšanu", "backup_controller_page_status_off": "Automātiskā priekšplāna dublēšana ir izslēgta", "backup_controller_page_status_on": "Automātiskā priekšplāna dublēšana ir ieslēgta", "backup_controller_page_storage_format": "{used} no {total} tiek izmantots", @@ -384,6 +439,7 @@ "backup_controller_page_turn_on": "Ieslēgt priekšplāna dublēšanu", "backup_controller_page_uploading_file_info": "Faila informācijas augšupielāde", "backup_err_only_album": "Nevar noņemt vienīgo albumu", + "backup_error_sync_failed": "Sinhronizācija neizdevās. Nevar apstrādāt rezerves kopiju.", "backup_info_card_assets": "faili", "backup_manual_cancelled": "Atcelts", "backup_manual_in_progress": "Augšupielāde jau notiek. Mēģiniet pēc kāda laika atkārtoti", @@ -391,20 +447,22 @@ "backup_manual_title": "Augšupielādes statuss", "backup_options_page_title": "Dublēšanas iestatījumi", "backup_settings_subtitle": "Pārvaldīt augšupielādes iestatījumus", - "backward": "Atpakaļejoši", + "backward": "Atpakaļejoša", "biometric_auth_enabled": "Ieslēgta biometriskā autentifikācija", "biometric_locked_out": "Biometriskā autentifikācija tev ir bloķēta", "biometric_no_options": "Nav pieejamas biometriskās autentifikācijas iespējas", "biometric_not_available": "Biometriskā autentifikācija šajā ierīcē nav pieejama", "birthdate_saved": "Dzimšanas datums veiksmīgi saglabāts", "birthdate_set_description": "Dzimšanas datums tiek izmantots, lai aprēķinātu šīs personas vecumu fotogrāfijas uzņemšanas brīdī.", + "blurred_background": "Izpludināts fons", "bugs_and_feature_requests": "Kļūdas un funkciju pieprasījumi", "build": "Būvējums", "build_image": "Būvējuma attēls", + "buy": "Iegādāties Immich", "cache_settings_clear_cache_button": "Iztīrīt kešatmiņu", "cache_settings_clear_cache_button_title": "Iztīra aplikācijas kešatmiņu. Tas būtiski ietekmēs lietotnes veiktspēju, līdz kešatmiņa būs pārbūvēta.", "cache_settings_duplicated_assets_clear_button": "NOTĪRĪT", - "cache_settings_duplicated_assets_subtitle": "Fotoattēli un videoklipi, kurus lietotne ir iekļāvusi melnajā sarakstā", + "cache_settings_duplicated_assets_subtitle": "Fotoattēli un videoklipi, kurus lietotne ir iekļāvusi ignorējamo sarakstā", "cache_settings_duplicated_assets_title": "Dublicētie faili ({count})", "cache_settings_statistics_album": "Bibliotēkas sīktēli", "cache_settings_statistics_full": "Pilni attēli", @@ -413,13 +471,17 @@ "cache_settings_statistics_title": "Kešatmiņas lietojums", "cache_settings_subtitle": "Kontrolēt Immich mobilās lietotnes kešdarbi", "cache_settings_tile_subtitle": "Kontrolēt lokālās krātuves uzvedību", - "cache_settings_tile_title": "Lokālā Krātuve", + "cache_settings_tile_title": "Lokālā krātuve", "cache_settings_title": "Kešdarbes iestatījumi", "camera": "Fotokamera", + "camera_brand": "Fotokameras zīmols", + "camera_model": "Fotokameras modelis", "cancel": "Atcelt", + "cancel_search": "Atcelt meklēšanu", "canceled": "Atcelts", "canceling": "Atceļ", "cannot_merge_people": "Nevar apvienot personas", + "cannot_undo_this_action": "Šo darbību nevar atcelt!", "cast": "Pārraidīt", "cast_description": "Konfigurēt pieejamos pārraides galamērķus", "change_date": "Mainīt datumu", @@ -430,6 +492,7 @@ "change_name": "Mainīt nosaukumu", "change_name_successfully": "Vārds veiksmīgi nomainīts", "change_password": "Nomainīt paroli", + "change_password_description": "Vai nu šī ir pirmā reize, kad pieslēdzaties sistēmai, vai arī ir iesniegts pieprasījums mainīt paroli. Lūdzu, ievadiet jauno paroli zemāk.", "change_password_form_confirm_password": "Apstiprināt Paroli", "change_password_form_description": "Sveiki {name},\n\nŠī ir pirmā reize, kad pierakstāties sistēmā, vai arī ir iesniegts pieprasījums mainīt paroli. Lūdzu, zemāk ievadiet jauno paroli.", "change_password_form_new_password": "Jauna Parole", @@ -443,7 +506,9 @@ "city": "Pilsēta", "clear": "Notīrīt", "clear_all": "Notīrīt visu", + "clear_all_recent_searches": "Notīrīt visas pēdējās meklēšanas", "clear_file_cache": "Notīrīt failu kešatmiņu", + "clear_message": "Notīrīt paziņojumu", "clear_value": "Notīrīt vērtību", "client_cert_dialog_msg_confirm": "Labi", "client_cert_enter_password": "Ievadi paroli", @@ -460,10 +525,14 @@ "color": "Krāsa", "color_theme": "Krāsu tēma", "comment_deleted": "Komentārs dzēsts", + "comment_options": "Komentāru iespējas", + "comments_and_likes": "Komentāri un tīkšķi", + "comments_are_disabled": "Komentāri ir atslēgti", "common_create_new_album": "Izveidot jaunu albumu", "common_server_error": "Lūdzu, pārbaudiet tīkla savienojumu, pārliecinieties, vai serveris ir sasniedzams un aplikācijas/servera versijas ir saderīgas.", "completed": "Pabeigts", "confirm": "Apstiprināt", + "confirm_admin_password": "Administratora paroles apstiprinājums", "confirm_new_pin_code": "Apstiprināt jauno PIN kodu", "confirm_password": "Apstiprināt paroli", "confirm_tag_face": "Vai vēlies atzīmēt šo seju kā {name}?", @@ -473,12 +542,15 @@ "control_bottom_app_bar_create_new_album": "Izveidot jaunu albumu", "control_bottom_app_bar_delete_from_immich": "Dzēst no Immich", "control_bottom_app_bar_delete_from_local": "Dzēst no ierīces", - "control_bottom_app_bar_edit_location": "Rediģēt Atrašanās Vietu", - "control_bottom_app_bar_edit_time": "Rediģēt Datumu un Laiku", - "control_bottom_app_bar_share_to": "Kopīgot Uz", + "control_bottom_app_bar_edit_location": "Rediģēt atrašanās vietu", + "control_bottom_app_bar_edit_time": "Rediģēt datumu un laiku", + "control_bottom_app_bar_share_to": "Kopīgot uz", "control_bottom_app_bar_trash_from_immich": "Pārvietot uz Atkritni", "copy_error": "Kopēšanas kļūda", + "copy_to_clipboard": "Kopēt starpliktuvē", "country": "Valsts", + "cover": "Aizpildīts ekrāns", + "covers": "Vāciņi", "create": "Izveidot", "create_album": "Izveidot albumu", "create_album_page_untitled": "Bez nosaukuma", @@ -487,6 +559,7 @@ "create_link_to_share": "Izveidot kopīgošanas saiti", "create_new": "IZVEIDOT JAUNU", "create_new_person": "Izveidot jaunu personu", + "create_new_person_hint": "Piesaistīt izvēlētos failus jaunai personai", "create_new_user": "Izveidot jaunu lietotāju", "create_shared_album_page_share_add_assets": "PIEVIENOT AKTĪVUS", "create_shared_album_page_share_select_photos": "Fotoattēlu Izvēle", @@ -499,6 +572,7 @@ "custom_locale_description": "Formatēt datumus un skaitļus atbilstoši valodai un reģionam", "custom_url": "Pielāgots URL", "daily_title_text_date_year": "E, MMM dd, gggg", + "dark_theme": "Pārslēgt tumšo tēmu", "date_after": "Datums pēc", "date_and_time": "Datums un Laiks", "date_before": "Datums pirms", @@ -527,7 +601,7 @@ "delete_library": "Dzēst bibliotēku", "delete_link": "Dzēst saiti", "delete_local_action_prompt": "{count} dzēsti lokāli", - "delete_local_dialog_ok_backed_up_only": "Dzēst tikai Dublētos", + "delete_local_dialog_ok_backed_up_only": "Dzēst tikai dublētos", "delete_local_dialog_ok_force": "Tā pat dzēst", "delete_others": "Dzēst citus", "delete_shared_link": "Dzēst Kopīgošanas saiti", @@ -540,8 +614,11 @@ "details": "INFORMĀCIJA", "direction": "Secība", "discord": "Discord", + "discover": "Atklāt", + "discovered_devices": "Atrastās ierīces", "display_order": "Attēlošanas secība", "display_original_photos": "Rādīt oriģinālās fotogrāfijas", + "do_not_show_again": "Vairs nerādīt šo ziņojumu", "documentation": "Dokumentācija", "done": "Gatavs", "download": "Lejupielādēt", @@ -552,6 +629,8 @@ "download_error": "Lejupielādes kļūda", "download_failed": "Lejupielāde neizdevās", "download_finished": "Lejupielāde pabeigta", + "download_include_embedded_motion_videos": "Iegultie videoklipi", + "download_include_embedded_motion_videos_description": "Iekļaut video, kas iebūvēti kustīgos fotoattēlos, kā atsevišķu failu", "download_notfound": "Lejupielāde nav atrasta", "download_paused": "Lejupielāde nopauzēta", "download_settings": "Lejupielāde", @@ -572,6 +651,7 @@ "edit_birthday": "Labot dzimšanas dienu", "edit_date": "Labot datumu", "edit_date_and_time": "Labot datumu un laiku", + "edit_date_and_time_action_prompt": "{count} datums un laiks labots", "edit_description": "Labot aprakstu", "edit_description_prompt": "Lūdzu, izvēlies jaunu aprakstu:", "edit_faces": "Labot sejas", @@ -603,9 +683,14 @@ "enter_your_pin_code_subtitle": "Ievadi savu PIN kodu, lai piekļūtu slēgtajai mapei", "error": "Kļūda", "error_change_sort_album": "Neizdevās nomainīt albuma kārtošanas secību", + "error_loading_image": "Kļūda, ielādējot attēlu", "error_loading_partners": "Kļūda, ielādējot partnerus: {error}", "error_saving_image": "Kļūda: {error}", + "error_title": "Kļūda - kaut kas nogāja greizi", "errors": { + "cannot_navigate_next_asset": "Nevar pāriet uz nākamo resursu", + "cannot_navigate_previous_asset": "Nevar pāriet uz iepriekšējo resursu", + "cant_apply_changes": "Nevar piemērot izmaiņas", "cant_get_faces": "Nevar iegūt sejas", "cant_search_people": "Neizdevās veikt peronu meklēšanu", "exclusion_pattern_already_exists": "Šāds izslēgšanas paraugs jau pastāv.", @@ -628,7 +713,12 @@ "profile_picture_transparent_pixels": "Profila attēlos nevar būt caurspīdīgi pikseļi. Lūdzu, palielini un/vai pārvieto attēlu.", "something_went_wrong": "Kaut kas nogāja greizi", "unable_to_change_description": "Neizdevās nomainīt aprakstu", + "unable_to_create_admin_account": "Nevar izveidot administratora kontu", + "unable_to_create_api_key": "Nevar izveidot jaunu API atslēgu", + "unable_to_create_library": "Nevar izveidot bibliotēku", "unable_to_create_user": "Neizdevās izveidot lietotāju", + "unable_to_delete_album": "Nevar izdzēst albumu", + "unable_to_delete_asset": "Nevar izdzēst failu", "unable_to_delete_user": "Neizdevās dzēst lietotāju", "unable_to_empty_trash": "Neizdevās iztukšot atkritni", "unable_to_hide_person": "Neizdevās paslēpt personu", @@ -636,7 +726,8 @@ "unable_to_save_date_of_birth": "Neizdevās saglabāt dzimšanas datumu", "unable_to_scan_libraries": "Bibliotēku skenēšana neizdevās", "unable_to_scan_library": "Bibliotēkas skenēšana neizdevās", - "unable_to_trash_asset": "Neizdevās pārvietot failu uz atkritni" + "unable_to_trash_asset": "Neizdevās pārvietot failu uz atkritni", + "unable_to_update_album_cover": "Nevar atjaunināt albuma vāciņu" }, "exif": "Exif", "exif_bottom_sheet_description": "Pievienot Aprakstu...", @@ -653,12 +744,14 @@ "expired": "Derīguma termiņš beidzās", "explore": "Izpētīt", "export": "Eksportēt", + "export_as_json": "Eksportēt kā JSON", "export_database": "Eksportēt datubāzi", "export_database_description": "Eksportēt SQLite datubāzi", "extension": "Paplašinājums", "external": "Ārējs", + "external_libraries": "Ārējas bibliotēkas", "external_network": "Ārējs tīkls", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network_sheet_info": "Kad nav pieejams izvēlētais Wi-Fi tīkls, aplikācija pieslēgsies serverim lietojot pirmo strādājošo URL no saraksta, sākot ar augšējo", "face_unassigned": "Nepiešķirts", "failed": "Neizdevās", "failed_to_authenticate": "Neizdevās autentificēties", @@ -699,13 +792,17 @@ "group_owner": "Grupēt pēc īpašnieka", "group_places_by": "Grupēt vietas pēc...", "group_year": "Grupēt pēc gada", - "haptic_feedback_switch": "Iestatīt haptisku reakciju", + "haptic_feedback_switch": "Iespējot haptisku reakciju", "haptic_feedback_title": "Haptiska Reakcija", - "has_quota": "Ir kvota", + "has_quota": "Kvota", "hash_asset": "Veidot faila jaucējvērtību", - "hashed_assets": "Faili ar izveidotām jaucējvērtībām", + "hashed_assets": "Faili ar jaucējvērtībām", "hashing": "Veido jaucējvērtības", "header_settings_field_validator_msg": "Vērtība nevar būt tukša", + "header_settings_header_name_input": "Galvenes lauks", + "header_settings_header_value_input": "Galvenes vērtība", + "headers_settings_tile_subtitle": "Norādiet starpniekservera galvenes, kuras lietotnei jānosūta ar katru tīkla pieprasījumu", + "headers_settings_tile_title": "Pielāgotas starpniekservera galvenes", "hide_all_people": "Paslēpt visas personas", "hide_gallery": "Paslēpt galeriju", "hide_named_person": "Paslēpt personu {name}", @@ -713,20 +810,20 @@ "hide_person": "Paslēpt personu", "hide_unnamed_people": "Paslēpt nenosauktas personas", "home_page_add_to_album_conflicts": "Pievienoja {added} failus albumam {album}. {failed} faili jau ir albumā.", - "home_page_add_to_album_err_local": "Albumiem vēl nevar pievienot lokālos aktīvus, notiek izlaišana", + "home_page_add_to_album_err_local": "Albumiem vēl nevar pievienot lokālos failus, izlaiž", "home_page_add_to_album_success": "Pievienoja {added} aktīvus albumam {album}.", "home_page_album_err_partner": "Pagaidām nevar pievienot partnera aktīvus albumam, notiek izlaišana", - "home_page_archive_err_local": "Vēl nevar arhivēt lokālos aktīvus, notiek izlaišana", + "home_page_archive_err_local": "Vēl nevar arhivēt lokālos aktīvus, izlaiž", "home_page_archive_err_partner": "Nevarēja arhivēt partnera aktīvus, notiek izlaišana", "home_page_building_timeline": "Tiek izveidota laika skala", "home_page_delete_err_partner": "Nevarēja dzēst partnera aktīvus, notiek izlaišana", - "home_page_delete_remote_err_local": "Lokālie faili dzēšanai attālinātajā izvēlē, tiek izlaists", - "home_page_favorite_err_local": "Vēl nevar pievienot izlasei vietējos failus, izlaiž", + "home_page_delete_remote_err_local": "Lokālie faili dzēšanai attālinātajā izvēlē, izlaiž", + "home_page_favorite_err_local": "Vēl nevar pievienot izlasei lokālos failus, izlaiž", "home_page_favorite_err_partner": "Pagaidām nevar ievietot izlasē partnera failus, izlaiž", "home_page_first_time_notice": "Ja šī ir pirmā reize, kad izmanto lietotni, lūdzu, izvēlies dublējamo albumu, lai laika skalā varētu aizpildīt fotoattēlus un videoklipus", - "home_page_locked_error_local": "Nevar pārvietot vietējos failus uz slēgto mapi, izlaiž", + "home_page_locked_error_local": "Nevar pārvietot lokālos failus uz slēgto mapi, izlaiž", "home_page_locked_error_partner": "Nevar pārvietot partneru failus uz slēgto mapi, izlaiž", - "home_page_share_err_local": "Caur saiti nevarēja kopīgot lokālos aktīvus, notiek izlaišana", + "home_page_share_err_local": "Caur saiti nevarēja kopīgot lokālos aktīvus, izlaiž", "home_page_upload_err_limit": "Vienlaikus var augšupielādēt ne vairāk kā 30 aktīvus, notiek izlaišana", "hour": "Stunda", "hours": "Stundas", @@ -736,7 +833,7 @@ "ignore_icloud_photos_description": "iCloud uzglabātās fotogrāfijas netiks augšupielādētas Immich serverī", "image": "Attēls", "image_saved_successfully": "Attēls saglabāts", - "image_viewer_page_state_provider_download_started": "Lejupielāde Uzsākta", + "image_viewer_page_state_provider_download_started": "Lejupielāde uzsākta", "image_viewer_page_state_provider_download_success": "Lejupielāde izdevās", "image_viewer_page_state_provider_share_error": "Kopīgošanas Kļūda", "immich_logo": "Immich logo", @@ -747,6 +844,7 @@ "in_archive": "Arhīvā", "include_archived": "Iekļaut arhivētos", "include_shared_albums": "Iekļaut koplietotos albumus", + "include_shared_partner_assets": "Iekļaut partneru koplietotos failus", "info": "Informācija", "interval": { "day_at_onepm": "Katru dienu 13.00", @@ -761,6 +859,7 @@ "ios_debug_info_last_sync_at": "Pēdējā sinhronizācija {dateTime}", "ios_debug_info_no_processes_queued": "Nav ierindotu fona procesu", "ios_debug_info_processing_ran_at": "Apstrāde notika {dateTime}", + "items_count": "{count, plural, one {# vienums} other {# vienumi}}", "jobs": "Uzdevumi", "keep": "Paturēt", "keep_all": "Paturēt visus", @@ -794,7 +893,12 @@ "linked_oauth_account": "Piesaistītais OAuth konts", "list": "Saraksts", "loading": "Ielādē", + "local": "Lokāli", + "local_asset_cast_failed": "Nav iespējams pārraidīt resursu, kas nav augšupielādēts serverī", + "local_assets": "Lokālie faili", + "local_media_summary": "Lokālo mediju kopsavilkums", "local_network": "Lokālais tīkls", + "local_network_sheet_info": "Izmantojot norādīto Wi-Fi tīklu, lietotne veidos savienojumu ar serveri, izmantojot šo URL", "location_permission": "Atrašanās vietas atļauja", "location_permission_content": "Lai izmantotu automātiskās pārslēgšanās funkciju, Immich ir nepieciešama precīzas atrašanās vietas atļauja, lai varētu nolasīt pašreizējā Wi-Fi tīkla nosaukumu", "location_picker_choose_on_map": "Izvēlēties uz kartes", @@ -849,11 +953,11 @@ "map_marker_with_image": "Kartes marķieris ar attēlu", "map_no_location_permission_content": "Atrašanās vietas atļauja ir nepieciešama, lai parādītu jūsu pašreizējās atrašanās vietas aktīvus. Vai vēlaties to atļaut tagad?", "map_no_location_permission_title": "Atrašanās vietas Atļaujas liegtas", - "map_settings": "Kartes Iestatījumi", + "map_settings": "Kartes iestatījumi", "map_settings_dark_mode": "Tumšais režīms", "map_settings_date_range_option_day": "Pēdējās 24 stundas", "map_settings_date_range_option_days": "Pēdējās {days} dienas", - "map_settings_date_range_option_year": "Pēdējo gadu", + "map_settings_date_range_option_year": "Pēdējais gads", "map_settings_date_range_option_years": "Pēdējie {years} gadi", "map_settings_dialog_title": "Kartes Iestatījumi", "map_settings_include_show_archived": "Iekļaut Arhivētos", @@ -862,7 +966,7 @@ "map_settings_theme_settings": "Kartes Dizains", "map_zoom_to_see_photos": "Attāliniet, lai redzētu fotoattēlus", "matches": "Atbilstības", - "media_type": "Multivides veids", + "media_type": "Faila veids", "memories": "Atmiņas", "memories_all_caught_up": "Šobrīd, tas arī viss", "memories_check_back_tomorrow": "Atgriezies rīt, lai skatītu vairāk atmiņu", @@ -899,6 +1003,8 @@ "network_requirement_photos_upload": "Izmantot mobilo datu pārraidi, lai dublētu fotoattēlus", "network_requirement_videos_upload": "Izmantot mobilo datu pārraidi, lai dublētu video", "network_requirements": "Tīkla prasības", + "network_requirements_updated": "Tīkla prasības ir mainījušās, atiestata dublēšanas rindu", + "networking_settings": "Tīkla iestatījumi", "networking_subtitle": "Pārvaldīt servera galapunktu iestatījumus", "never": "nekad", "new_album": "Jauns albums", @@ -906,6 +1012,7 @@ "new_password": "Jaunā parole", "new_person": "Jauna persona", "new_pin_code": "Jaunais PIN kods", + "new_timeline": "Jaunā laikjosla", "new_user_created": "Izveidots jauns lietotājs", "new_version_available": "PIEEJAMA JAUNA VERSIJA", "next": "Nākamais", @@ -917,8 +1024,13 @@ "no_archived_assets_message": "Arhivē fotoattēlus un videoklipus, lai paslēptu tos no Fotoattēli skata", "no_assets_message": "NOKLIKŠĶINIET, LAI AUGŠUPIELĀDĒTU SAVU PIRMO FOTOATTĒLU", "no_assets_to_show": "Nav uzrādāmo aktīvu", + "no_cast_devices_found": "Nav atrasta neviena pārraides ierīce", + "no_checksum_local": "Nav pieejama kontrolsumma - nevar iegūt lokālos failus", + "no_checksum_remote": "Nav pieejama kontrolsumma - nevar iegūt attālo failu", "no_duplicates_found": "Dublikāti netika atrasti.", "no_exif_info_available": "Nav pieejama exif informācija", + "no_explore_results_message": "Augšupielādē vairāk fotogrāfiju, lai iepazītu savu kolekciju.", + "no_local_assets_found": "Ar šo kontrolsummu nav atrasts neviens lokālais fails", "no_name": "Nav nosaukuma", "no_notifications": "Nav paziņojumu", "no_places": "Nav atrašanās vietu", @@ -926,12 +1038,13 @@ "no_results_description": "Izmēģiniet sinonīmu vai vispārīgāku atslēgvārdu", "not_in_any_album": "Nav nevienā albumā", "not_selected": "Nav izvēlēts", + "note_apply_storage_label_to_previously_uploaded assets": "Piezīme: Lai piemērotu glabātuves nosaukumu iepriekš augšupielādētiem failiem, izpildiet", "notes": "Piezīmes", "nothing_here_yet": "Šeit vēl nekā nav", "notification_permission_dialog_content": "Lai iespējotu paziņojumus, atveriet Iestatījumi un atlasiet Atļaut.", "notification_permission_list_tile_content": "Piešķirt atļauju, lai iespējotu paziņojumus.", - "notification_permission_list_tile_enable_button": "Iespējot Paziņojumus", - "notification_permission_list_tile_title": "Paziņojumu Atļaujas", + "notification_permission_list_tile_enable_button": "Iespējot paziņojumus", + "notification_permission_list_tile_title": "Paziņojumu atļaujas", "notification_toggle_setting_description": "Ieslēgt e-pasta paziņojumus", "notifications": "Paziņojumi", "notifications_setting_description": "Paziņojumu pārvaldība", @@ -972,6 +1085,7 @@ "partner_page_select_partner": "Izvēlēties partneri", "partner_page_shared_to_title": "Kopīgots uz", "partner_page_stop_sharing_content": "{partner} vairs nevarēs piekļūt jūsu fotoattēliem.", + "partner_sharing": "Koplietošana ar partneriem", "partners": "Partneri", "password": "Parole", "password_does_not_match": "Parole nesakrīt", @@ -1069,8 +1183,10 @@ "refreshes_every_file": "Vēlreiz nolasa esošos un jaunos failus", "refreshing_faces": "Atsvaidzina sejas", "refreshing_metadata": "Atsvaidzina metadatus", + "remote": "Attāli", "remove": "Noņemt", "remove_assets_title": "Izņemt failus?", + "remove_custom_date_range": "Novākt pielāgoto datuma intervālu", "remove_deleted_assets": "Izņemt dzēstos failus", "remove_from_album": "Noņemt no albuma", "remove_from_album_action_prompt": "No albuma izņemti {count} faili", @@ -1085,6 +1201,8 @@ "removed_from_archive": "Noņēma no arhīva", "removed_from_favorites": "Noņēma no izlases", "removed_from_favorites_count": "{count, plural, other {Izņēma #}} no izlases", + "removed_memory": "Noņēma atmiņu", + "removed_photo_from_memory": "Noņēma fotogrāfiju no atmiņas", "rename": "Pārsaukt", "repair": "Remonts", "replace_with_upload": "Aizstāt ar augšupielādi", @@ -1095,6 +1213,7 @@ "reset_password": "Atiestatīt paroli", "reset_people_visibility": "Atiestatīt personu redzamību", "reset_pin_code": "Atiestatīt PIN kodu", + "reset_sqlite": "Atiestatīt SQLite datubāzi", "reset_to_default": "Atiestatīt noklusējuma iestatījumus", "resolve_duplicates": "Atrisināt dublēšanās gadījumus", "resolved_all_duplicates": "Visi dublikāti ir atrisināti", @@ -1143,6 +1262,7 @@ "search_for_existing_person": "Meklēt esošu personu", "search_no_people": "Nav personu", "search_no_people_named": "Nav personas ar vārdu \"{name}\"", + "search_options": "Meklēšanas iespējas", "search_page_categories": "Kategorijas", "search_page_motion_photos": "Kustību Fotoattēli", "search_page_no_objects": "Informācija par Objektiem nav pieejama", @@ -1154,11 +1274,15 @@ "search_page_your_activity": "Jūsu aktivitāte", "search_page_your_map": "Jūsu Karte", "search_people": "Meklēt personas", + "search_rating": "Meklēt pēc vērtējuma...", "search_result_page_new_search_hint": "Jauns Meklējums", + "search_settings": "Meklēt iestatījumos", + "search_state": "Meklēt pēc štata...", "search_suggestion_list_smart_search_hint_1": "Viedā meklēšana pēc noklusējuma ir iespējota, lai meklētu metadatos, izmanto sintaksi ", "search_suggestion_list_smart_search_hint_2": "m:jūsu-meklēšanas-frāze", "search_type": "Meklēšanas veids", - "search_your_photos": "Meklēt Jūsu fotoattēlus", + "search_your_photos": "Meklēt fotoattēlos", + "searching_locales": "Meklē lokalizācijas...", "second": "Sekunde", "see_all_people": "Skatīt visas personas", "select_album_cover": "Izvēlieties albuma vāciņu", @@ -1180,6 +1304,8 @@ "server_privacy": "Servera privātums", "server_stats": "Servera statistika", "server_version": "Servera versija", + "set_as_album_cover": "Iestatīt kā albuma vāciņu", + "set_as_profile_picture": "Iestatīt kā profila attēlu", "set_date_of_birth": "Iestatīt dzimšanas datumu", "setting_image_viewer_help": "Detaļu skatītājs vispirms ielādē mazo sīktēlu, pēc tam ielādē vidēja lieluma priekšskatījumu (ja iespējots), visbeidzot ielādē oriģinālu (ja iespējots).", "setting_image_viewer_original_subtitle": "Iespējot sākotnējā pilnas izšķirtspējas attēla (liels!) ielādi. Atspējot, lai samazinātu datu lietojumu (gan tīklā, gan ierīces kešatmiņā).", @@ -1194,19 +1320,20 @@ "setting_notifications_notify_minutes": "{count} minūtes", "setting_notifications_notify_never": "nekad", "setting_notifications_notify_seconds": "{count} sekundes", - "setting_notifications_single_progress_subtitle": "Detalizēta augšupielādes progresa informācija par katru aktīvu", + "setting_notifications_single_progress_subtitle": "Detalizēta augšupielādes progresa informācija par katru failu", "setting_notifications_single_progress_title": "Rādīt fona dublējuma detalizēto progresu", "setting_notifications_subtitle": "Paziņojumu preferenču pielāgošana", "setting_notifications_total_progress_subtitle": "Kopējais augšupielādes progress (pabeigti/kopējie faili)", "setting_notifications_total_progress_title": "Rādīt fona dublējuma kopējo progresu", "setting_video_viewer_looping_title": "Cikliski", "setting_video_viewer_original_video_subtitle": "Straumējot video no servera, izmantot oriģinālu, pat ja ir pieejama pārkodēšana. Tas var izraisīt buferēšanu. Lokāli pieejamie video tiek atskaņoti oriģinālajā kvalitātē, neatkarīgi no šīs iestatījuma.", + "setting_video_viewer_original_video_title": "Vienmēr izmantot oriģinālo video", "settings": "Iestatījumi", "settings_require_restart": "Lūdzu, restartējiet Immich, lai lietotu šo iestatījumu", "setup_pin_code": "Uzstādīt PIN kodu", "share": "Kopīgot", "share_add_photos": "Pievienot fotoattēlus", - "share_assets_selected": "{count} izvēlēti", + "share_assets_selected": "{count} atlasīti", "share_dialog_preparing": "Notiek sagatavošana...", "shared": "Kopīgots", "shared_album_activities_input_disable": "Komentāri atslēgti", @@ -1287,7 +1414,7 @@ "slideshow_settings": "Slīdrādes iestatījumi", "sort_albums_by": "Kārtot albumus pēc...", "sort_created": "Izveides datums", - "sort_items": "Vienību skaits", + "sort_items": "Vienumu skaits", "sort_modified": "Izmaiņu datums", "sort_newest": "Jaunākā fotogrāfija", "sort_oldest": "Vecākā fotogrāfija", @@ -1296,24 +1423,29 @@ "sort_title": "Nosaukums", "source": "Pirmkods", "stack": "Apvienot kaudzē", + "start": "Sākt", "start_date": "Sākuma datums", + "start_date_before_end_date": "Sākuma datumam jābūt pirms beigu datuma", "state": "Štats", "status": "Statuss", "stop_photo_sharing": "Beigt kopīgot jūsu fotogrāfijas?", "stop_photo_sharing_description": "{partner} vairs nevarēs piekļūt tavām fotogrāfijām.", "stop_sharing_photos_with_user": "Pārtraukt dalīties ar fotogrāfijām ar šo lietotāju", "storage": "Vieta krātuvē", + "storage_label": "Glabātuves nosaukums", "storage_usage": "{used} no {available} izmantoti", "submit": "Iesniegt", "suggestions": "Ieteikumi", "sunrise_on_the_beach": "Saullēkts pludmalē", "support": "Atbalsts", "support_and_feedback": "Atbalsts un atsauksmes", + "support_third_party_description": "Tavu Immich instalāciju ir sagatavojusi trešā puse. Problēmas, ar kurām sastopies, var būt saistītas ar šo pakotni, tāpēc lūdzu vispirms ziņo par tām, izmantojot zemāk norādītās saites.", "sync": "Sinhronizēt", + "sync_local": "Sinhronizēt lokāli", "sync_status": "Sinhronizācijas statuss", "sync_status_subtitle": "Skatīt un pārvaldīt sinhronizācijas sistēmu", "theme": "Dizains", - "theme_setting_asset_list_storage_indicator_title": "Rādīt krātuves indikatoru uz aktīvu elementiem", + "theme_setting_asset_list_storage_indicator_title": "Rādīt krātuves indikatoru uz attēliem režga skatā", "theme_setting_asset_list_tiles_per_row_title": "Failu skaits rindā ({count})", "theme_setting_colorful_interface_subtitle": "Piemērot pamatkrāsu fona virsmām.", "theme_setting_colorful_interface_title": "Krāsaina saskarne", @@ -1345,7 +1477,7 @@ "trash_emptied": "Atkritne iztukšota", "trash_no_results_message": "Šeit parādīsies uz atkritni pārvietotās fotogrāfijas un video.", "trash_page_delete_all": "Dzēst Visu", - "trash_page_empty_trash_dialog_content": "Vai vēlaties iztukšot savus izmestos aktīvus? Tie tiks neatgriezeniski izņemti no Immich", + "trash_page_empty_trash_dialog_content": "Vai vēlaties iztukšot savus izmestos failus? Tie tiks neatgriezeniski izņemti no Immich", "trash_page_info": "Atkritnes vienumi tiks neatgriezeniski dzēsti pēc {days} dienām", "trash_page_no_assets": "Atkritnē nav aktīvu", "trash_page_restore_all": "Atjaunot Visu", @@ -1365,6 +1497,7 @@ "unlimited": "Neierobežots", "unnamed_album": "Albums bez nosaukuma", "unsaved_change": "Nesaglabāta izmaiņa", + "unselect_all": "Atcelt visu atlasi", "unstack": "At-Stekot", "updated_at": "Atjaunināts", "updated_password": "Parole ir atjaunināta", @@ -1405,6 +1538,7 @@ "version_history": "Versiju vēsture", "version_history_item": "{version} uzstādīta {date}", "video": "Videoklips", + "video_hover_setting_description": "Atskaņot video sīktēlu, kad peles kursors atrodas virs objekta. Pat ja funkcija ir atspējota, atskaņošanu var sākt, uzvirzot kursoru uz atskaņošanas ikonas.", "videos": "Videoklipi", "view": "Apskatīt", "view_album": "Skatīt Albumu", diff --git a/i18n/mk.json b/i18n/mk.json index 86694ce346..8430ae117e 100644 --- a/i18n/mk.json +++ b/i18n/mk.json @@ -15,6 +15,7 @@ "add_a_name": "Додади име", "add_a_title": "Додади наслов", "add_birthday": "Додади роденден", + "add_endpoint": "Додади крајна точка", "add_exclusion_pattern": "Додади шаблон за исклучување", "add_import_path": "Додади патека за импортирање", "add_location": "Додади локација", @@ -22,9 +23,13 @@ "add_partner": "Додади партнер", "add_path": "Додади патека", "add_photos": "Додади слики", + "add_tag": "Додади ознака", "add_to": "Додади во…", "add_to_album": "Додади во албум", "add_to_album_bottom_sheet_added": "Додадено во {album}", + "add_to_album_bottom_sheet_already_exists": "Веќе во {album}", + "add_to_album_bottom_sheet_some_local_assets": "Некои локални ресурси не можеа да се додадат во албумот", + "add_to_album_toggle": "Промени ја селекцијата за {album}", "add_to_albums": "Додади во албуми", "add_to_albums_count": "Додади во албуми ({count})", "add_to_shared_album": "Додади во споделен албум", @@ -44,8 +49,15 @@ "backup_database": "Креирај резервна копија од базата на податоци", "backup_database_enable_description": "Овозможи резервни копии од базата на податоци", "backup_keep_last_amount": "Количина на претходни резервни копии за чување", - "backup_settings": "Поставки за резервни копии", - "backup_settings_description": "Управувај со поставки за резервни копии на базата на податоци", + "backup_onboarding_1_description": "надворешна копија во облакот или на друга физичка локација.", + "backup_onboarding_2_description": "локални копии на различни уреди. Ова ги вклучува и основните фјалови и резервна копија од истите фајлови локално.", + "backup_onboarding_3_description": "сите копии од твоите податоци, вклучувајќи и оргиналните фајлови. Ова вклучува и 1 надворешна копија и 2 локални копии.", + "backup_onboarding_description": "3-2-1 стратегија за резервна копија е препорачано за да ги заштити твоите податоци. Потребно е да чуваш резервни копии од твоите прикачени фотографии/видеа како и базата за податоци на Immich за целосно решение за зачувување на резервна копија", + "backup_onboarding_footer": "Повеќе информации околу правење резервни копии за Immich, ве молам да се референцирате на документацијата", + "backup_onboarding_parts_title": "3-2-1 резервна копија вклучува:", + "backup_onboarding_title": "Резервни копии", + "backup_settings": "Поставки извезување база на податоци", + "backup_settings_description": "Управувај со поставки за извезување на базата на податоци", "cleared_jobs": "Исчистени задачи за: {job}", "config_set_by_file": "Конгигурацијата е моментално поставена од конфигурациска датотека", "confirm_delete_library": "Дали сте сигурни дека сакате да ја избришете библиотеката {library}?", @@ -53,19 +65,40 @@ "confirm_email_below": "За да потврдите, внесете \"{email}\" доле", "confirm_reprocess_all_faces": "Дали сте сигурни дека сакате да се обработат одново сите лица? Ова ќе ги избрише и сите именувани луѓе.", "confirm_user_password_reset": "Дали сте сигурни дека сакате да се поништи лозинката на {user}?", + "confirm_user_pin_code_reset": "Дали сигурно сакаш да го смените ПИН кодот за {user}", "create_job": "Создади задача", "cron_expression": "Cron израз", "cron_expression_description": "Подеси го интервалот на скенирање користејќи го cron форматот. За повеќе информации погледнете на пр. Crontab Guru", "cron_expression_presets": "Предефинирани Cron изрази", "disable_login": "Оневозможи најава", "duplicate_detection_job_description": "Пушти машинско учење на средствата за да се откријат слични слики. Се потпира на Smart Search", + "external_library_management": "Менаџмент на Надворешна Библиотека", + "face_detection": "Детекција на лице", "force_delete_user_warning": "ПРЕДУПРЕДУВАЊЕ: Ова веднаш ќе го отстрани корисникот и сите средства. Оваа акција не може да се поништи и датотеките нема да може да се вратат назад.", "image_format": "Формат", + "image_format_description": "WebP создава помали фајлви отколку JPEG, но е по спор при енкодирање.", + "image_fullsize_enabled": "Овозможи целосна-големина на генерирање на слика", + "image_fullsize_quality_description": "Целосна-големина на слика со квалитет од 1-100. Повисокто е подобро, но создава поголеми фајлови.", + "image_fullsize_title": "Поставки за Целосна-големина на Слика", + "image_prefer_embedded_preview": "Претпочитан вграден преглед", + "image_preview_title": "Поставки за Преглед", "image_quality": "Квалитет", "image_resolution": "Резолуција", "image_settings": "Поставки за слики", + "job_concurrency": "{job} конкурентност", + "job_created": "Креирана задача", + "job_not_concurrency_safe": "Оваа задача не е конкуретно-безбедна.", + "job_settings": "Поставки за задача", + "job_settings_description": "Управувај со конкурентност на задачи", + "job_status": "Статус на задачи", + "library_created": "Креирана библиотека: {library}", + "library_deleted": "Библиотеката е избришана", + "library_import_path_description": "Предложи папка за внес. Оваа папка, вклучува и под папки, ќе биде скенирана за слики и видеа.", "library_scanning": "Периодично скенирање", + "library_scanning_description": "Подеси периодично скениранје на библиотеката", + "library_scanning_enable_description": "Овозможи периодично скениранје на библиотеката", "library_settings": "Екстерна библиотека", + "library_settings_description": "Управувај со подесувањата за надворешната библиотека", "logging_enable_description": "Вклучи евидентирање", "logging_settings": "Евидентирање", "map_dark_style": "Темен стил", diff --git a/i18n/ml.json b/i18n/ml.json index 1ef0b46e66..0c85d53bd3 100644 --- a/i18n/ml.json +++ b/i18n/ml.json @@ -1,87 +1,2135 @@ { - "about": "വിഷയത്തെക്കുറിച്ച്", + "about": "കുറിച്ച്", "account": "അക്കൗണ്ട്", - "account_settings": "അക്കൗണ്ട് സെറ്റിംഗ്സ്", + "account_settings": "അക്കൗണ്ട് ക്രമീകരണങ്ങൾ", "acknowledge": "അംഗീകരിക്കുക", - "action": "ആക്ഷന്‍", - "action_common_update": "പുതുക്കുക", + "action": "പ്രവർത്തനം", + "action_common_update": "അപ്ഡേറ്റ് ചെയ്യുക", "actions": "പ്രവർത്തികൾ", - "active": "സജീവമായവ", - "activity": "പ്രവർത്തനങ്ങൾ", + "active": "സജീവം", + "activity": "പ്രവർത്തനം", + "activity_changed": "പ്രവർത്തനം {enabled, select, true {പ്രവർത്തനക്ഷമമാക്കി} other {നിർജ്ജീവമാക്കി}}", "add": "ചേർക്കുക", - "add_a_description": "ഒരു വിവരണം ചേർക്കുക", - "add_a_location": "ഒരു സ്ഥലം ചേർക്കുക", - "add_a_name": "ഒരു പേര് ചേർക്കുക", - "add_a_title": "ഒരു ശീർഷകം ചേർക്കുക", + "add_a_description": "വിവരണം ചേർക്കുക", + "add_a_location": "സ്ഥാനം ചേർക്കുക", + "add_a_name": "പേര് ചേർക്കുക", + "add_a_title": "ശീർഷകം ചേർക്കുക", "add_birthday": "ജന്മദിനം ചേർക്കുക", - "add_endpoint": "എൻഡ്പോയിന്റ് ചേർക്കുക", - "add_exclusion_pattern": "ഒഴിവാക്കാനുള്ള മാതൃക ചേർക്കുക", - "add_import_path": "ഇറക്കുമതി ചെയ്യുക", - "add_location": "സ്ഥലനാമം ചേര്‍ക്കുക", - "add_more_users": "കൂടുതല്‍ ഉപയോക്താക്കളെ ചേര്‍ക്കുക", - "add_partner": "പങ്കാളിയെ ചേര്‍ക്കുക", - "add_path": "പാത ചേര്‍ക്കുക", - "add_photos": "ചിത്രങ്ങള്‍ ചേര്‍ക്കുക", - "add_tag": "ടാഗ് ചേര്‍ക്കുക", - "add_to": "ചേര്‍ക്കുക…", - "add_to_album": "ആല്‍ബത്തിലേക്ക് ചേര്‍ക്കുക", - "add_to_album_bottom_sheet_added": "{album} - ലേക്ക് ചേര്‍ത്തു", - "add_to_album_bottom_sheet_already_exists": "{album} ആൽബത്തിൽ ഇപ്പോള്‍ തന്നെ ഉണ്ട്", - "add_to_albums": "ആൽബങ്ങളിൽ ചേർക്കുക", - "add_to_albums_count": "ആൽബങ്ങളിൽ ചേർക്കുക ({count})", + "add_endpoint": "എൻഡ്‌പോയിന്റ് ചേർക്കുക", + "add_exclusion_pattern": "ഒഴിവാക്കൽ പാറ്റേൺ ചേർക്കുക", + "add_import_path": "ഇമ്പോർട്ട് പാത്ത് ചേർക്കുക", + "add_location": "സ്ഥാനം ചേർക്കുക", + "add_more_users": "കൂടുതൽ ഉപയോക്താക്കളെ ചേർക്കുക", + "add_partner": "പങ്കാളിയെ ചേർക്കുക", + "add_path": "പാത്ത് ചേർക്കുക", + "add_photos": "ഫോട്ടോകൾ ചേർക്കുക", + "add_tag": "ടാഗ് ചേർക്കുക", + "add_to": "...ലേക്ക് ചേർക്കുക", + "add_to_album": "ആൽബത്തിലേക്ക് ചേർക്കുക", + "add_to_album_bottom_sheet_added": "{album} എന്നതിലേക്ക് ചേർത്തു", + "add_to_album_bottom_sheet_already_exists": "{album}-ൽ ഇതിനകം തന്നെയുണ്ട്", + "add_to_album_bottom_sheet_some_local_assets": "ചില പ്രാദേശിക അസറ്റുകൾ ആൽബത്തിലേക്ക് ചേർക്കാൻ കഴിഞ്ഞില്ല", + "add_to_album_toggle": "{album}-നുള്ള തിരഞ്ഞെടുപ്പ് ടോഗിൾ ചെയ്യുക", + "add_to_albums": "ആൽബങ്ങളിലേക്ക് ചേർക്കുക", + "add_to_albums_count": "ആൽബങ്ങളിലേക്ക് ചേർക്കുക ({count})", "add_to_shared_album": "പങ്കിട്ട ആൽബത്തിലേക്ക് ചേർക്കുക", - "add_url": "URL ചേര്‍ക്കുക", - "added_to_archive": "ചരിത്രരേഖയായി (ആര്‍ക്കൈവ്) ചേര്‍ത്തിരിക്കുന്നു", - "added_to_favorites": "ഇഷ്ടപ്പെട്ടവയിലേക്ക് ചേര്‍ത്തു", - "added_to_favorites_count": "{count, number} ഇഷ്ടപ്പെട്ടവയിലേക്ക് ചേര്‍ത്തു", + "add_url": "URL ചേർക്കുക", + "added_to_archive": "ആർക്കൈവിലേക്ക് ചേർത്തു", + "added_to_favorites": "പ്രിയപ്പെട്ടവയിലേക്ക് ചേർത്തു", + "added_to_favorites_count": "{count, number} എണ്ണം പ്രിയപ്പെട്ടവയിലേക്ക് ചേർത്തു", "admin": { - "add_exclusion_pattern_description": "ഒഴിവാക്കൽ ചിഹ്നങ്ങള്‍ ചേർക്കുക. *, **, ? എന്നിവ ഉപയോഗിച്ചുള്ള ഗ്ലോബിംഗ് പിന്തുണയ്ക്കുന്നു. \"Raw\" എന്ന് പേരുള്ള ഏതെങ്കിലും ഡയറക്ടറിയിലെ എല്ലാ ഫയലുകളും അവഗണിക്കാൻ, \"**/Raw/**\" ഉപയോഗിക്കുക. \".tif\" ൽ അവസാനിക്കുന്ന എല്ലാ ഫയലുകളും അവഗണിക്കാൻ, \"**/*.tif\" ഉപയോഗിക്കുക. ഒരു പരിപൂർണ്ണമായ പാത അവഗണിക്കാൻ, \"/path/to/ignore/**\" ഉപയോഗിക്കുക.", - "admin_user": "ഭരണാധികാരി", - "asset_offline_description": "ഈ പുറത്തുള്ള ശേഖരത്തിലെ വസ്തുക്കള്‍ ഇനി ഡിസ്കിൽ കാണുന്നില്ല, അവയെ ട്രാഷിലേക്ക് നീക്കിയിരിക്കുന്നു. ഫയൽ ലൈബ്രറിക്കുള്ളിൽ നിന്ന് നീക്കിയിട്ടുണ്ടെങ്കിൽ, പുതിയ അനുബന്ധ വസ്തുവിനായി നിങ്ങളുടെ സമയക്രമം (ടൈംലൈന്‍) പരിശോധിക്കുക. ഈ വസ്തു പുനഃസ്ഥാപിക്കാൻ, താഴെയുള്ള ഫയൽ പാത്ത് ഇമ്മിച്ചിന് എത്തിപ്പെടാന്‍ കഴിയുമെന്ന് ഉറപ്പാക്കുകയും ശേഖരം പുനഃപരിശോധിക്കുകയും (സ്കാൻ) ചെയ്യുക.", - "authentication_settings": "ആധികാരികതാ സജ്ജീകരണങ്ങൾ", - "authentication_settings_description": "പാസ്സ്‌വേര്‍ഡ്‌, OAuth തുടങ്ങിയ സജ്ജീകരണങ്ങള്‍", - "authentication_settings_disable_all": "എല്ലാ പ്രവേശന (ലോഗിൻ) രീതികളും പ്രവർത്തനരഹിതമാക്കണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ? പ്രവേശനങ്ങള്‍ പൂർണ്ണമായും പ്രവർത്തനരഹിതമാക്കപ്പെടും.", - "background_task_job": "പശ്ചാത്തല പ്രവര്‍ത്തികള്‍", - "backup_database": "ഡാറ്റാബേസ് ഡംമ്പ് ഉണ്ടാക്കുക", - "backup_database_enable_description": "ഡാറ്റാബേസ് ഡമ്പുകൾ പ്രാപ്തമാക്കുക", - "backup_keep_last_amount": "പഴയ ഡാറ്റാബേസ് ഡമ്പുകൾ എത്രയെണ്ണം സൂക്ഷിക്കണം", - "backup_settings": "ഡാറ്റാബേസ് ഡമ്പ് ക്രമീകരണങ്ങള്‍", - "backup_settings_description": "ഡാറ്റാബേസ് ഡമ്പ് ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക.", - "cleared_jobs": "{job} - ന്‍റെ ജോലികള്‍ മായ്ച്ചിരിക്കുന്നു", - "config_set_by_file": "ക്രമീകരണങ്ങള്‍ ഇപ്പോള്‍ ഒരു ക്രമീകരണ ഫയല്‍ വഴിയാണ് നിശ്ചയിക്കുന്നത്", - "confirm_delete_library": "{library} മായ്ച്ചു കളയണം എന്നുറപ്പാണോ?", - "confirm_delete_library_assets": "ഈ ശേഖരം ഇല്ലാതാക്കണം എന്ന് ഉറപ്പാണോ? ഇത് ഇമ്മിച്ചിൽ നിന്ന് {count, plural, one {# contained asset} other {all # contained assets}} ഇല്ലാതാക്കും, ഇത് പഴയപടിയാക്കാൻ കഴിയില്ല. ഫയലുകൾ ഡിസ്കിൽ തന്നെ തുടരും.", - "confirm_email_below": "തീര്‍ച്ചപ്പെടുത്താന്‍ {email} താഴെ കൊടുക്കുക", - "confirm_reprocess_all_faces": "എല്ലാ മുഖങ്ങളും വീണ്ടും കണ്ടെത്തണം എന്ന് ഉറപ്പാണോ? ഇത് ഇതിനകം പേരു ചേര്‍ത്ത മുഖങ്ങളെയും ആളുകളെയും മായ്ക്കും.", - "confirm_user_password_reset": "{user} എന്ന ഉപയോക്താവിന്‍റെ പാസ്സ്‌വേര്‍ഡ്‌ പുനഃക്രമീകരിക്കണം എന്നുറപ്പാണോ?", - "confirm_user_pin_code_reset": "{user} എന്ന ഉപയോക്താവിന്‍റെ PIN പുനഃക്രമീകരിക്കണം എന്നുറപ്പാണോ?", - "create_job": "ജോലി സൃഷ്ടിക്കുക", - "cron_expression": "ക്രോണ്‍ (cron) പ്രയോഗശൈലി", - "cron_expression_description": "ക്രോൺ ഫോർമാറ്റ് ഉപയോഗിച്ച് സ്കാനിംഗ് ഇടവേള സജ്ജമാക്കുക. കൂടുതൽ വിവരങ്ങൾക്ക് Crontab Guru സന്ദര്‍ശിക്കുക", - "cron_expression_presets": "മുന്‍കൂട്ടി തയ്യാര്‍ ചെയ്ത ക്രോണ്‍ പ്രവര്‍ത്തനശൈലികള്‍", - "disable_login": "ലോഗിന്‍ തടയുക", - "duplicate_detection_job_description": "സമാനമായ ചിത്രങ്ങൾ കണ്ടെത്താൻ വസ്തുവഹകളില്‍ യന്ത്രപഠനം പ്രവർത്തിപ്പിക്കുക. ഇത് സ്മാർട്ട് സര്‍ച്ചിനെ ആശ്രയിക്കുന്നു", - "exclusion_pattern_description": "നിങ്ങളുടെ ലൈബ്രറി സ്കാൻ ചെയ്യുമ്പോൾ ഫയലുകളും ഫോൾഡറുകളും അവഗണിക്കാൻ ഒഴിവാക്കല്‍ മാതൃകകള്‍ നിങ്ങളെ അനുവദിക്കുന്നു. RAW ഫയലുകൾ പോലുള്ള നിങ്ങൾക്ക് ഇറക്കുമതി ചെയ്യാൻ താൽപ്പര്യമില്ലാത്ത ഫയലുകൾ അടങ്ങിയ ഫോൾഡറുകൾ ഉണ്ടെങ്കിൽ ഇത് ഉപയോഗപ്രദമാണ്.", - "external_library_management": "ബാഹ്യമായശേഖരങ്ങളുടെ നിയന്ത്രണം", - "face_detection": "മുഖങ്ങള്‍ കണ്ടെത്തുക", - "face_detection_description": "യന്ത്രപഠനം ഉപയോഗിച്ച് വസ്തുക്കളിലെ മുഖങ്ങൾ കണ്ടെത്തുക. വീഡിയോകൾക്ക്, തംബ്‌നെയിൽ മാത്രമേ പരിഗണിക്കൂ. \"Refresh\" എല്ലാ വസ്തുക്കളും (വീണ്ടും) പ്രോസസ്സ് ചെയ്യുന്നു. കൂടാതെ \"Reset\" നിലവിലുള്ള എല്ലാ മുഖളും വിവരങ്ങളും മായ്‌ക്കുന്നു. \"Missing\" ഇതുവരെ ക്രമീകരിക്കാത്ത വസ്തുക്കളെ വരിയിലേക്ക് നിർത്തുന്നു. മുഖം തിരിച്ചറിയൽ പൂർത്തിയായ ശേഷം കണ്ടെത്തിയ മുഖങ്ങൾ മുഖം തിരിച്ചറിയലിനായി ക്യൂവിൽ നിർത്തും, അവയെ നിലവിലുള്ളതോ പുതിയതോ ആയ ആളുകളിലേക്ക് ഗ്രൂപ്പുചെയ്യും.", - "facial_recognition_job_description": "കണ്ടെത്തിയ മുഖങ്ങളെ ആളുകളുടെ കൂട്ടം ആക്കുക. മുഖം കണ്ടെത്തല്‍ ഘട്ടത്തിനു ശേഷമേ ഇത് ഉണ്ടാകൂ. \"Reset\" വീണ്ടും കൂട്ടങ്ങളെ ഉണ്ടാക്കും. \"Missing\" ആളെ നിയോഗിക്കാത്ത മുഖങ്ങളെ വരിയിലേക്ക് ചേര്‍ക്കുന്നു.", - "failed_job_command": "{job} എന്ന ജോലിക്ക് വേണ്ടിയുള്ള ആജ്ഞ {command} പരാജയപ്പെട്ടിരിക്കുന്നു", - "force_delete_user_warning": "മുന്നറിയിപ്പ്: ഇത് ഉപയോക്താവിനെയും എല്ലാ വസ്തുക്കളേയും ഉടനടി നീക്കം ചെയ്യും. ഇത് പഴയപടിയാക്കാനോ ഫയലുകൾ വീണ്ടെടുക്കാനോ കഴിയില്ല.", - "image_format": "ഘടന", - "image_format_description": "WebP ഉണ്ടാക്കാന്‍ സമയം എടുക്കും എങ്കിലും JPEG ഫയലുകളെക്കാള്‍ ചെറുതായിരിക്കും.", - "image_fullsize_description": "അധികവിവരങ്ങള്‍ ഒഴിവാക്കിയ ചിത്രം, വലുതാക്കി കാണിക്കുമ്പോള്‍ ഉപയോഗിക്കപ്പെടുന്നു", - "image_fullsize_enabled": "പൂര്‍ണ വലുപ്പത്തില്‍ ഉള്ള ചിത്രങ്ങള്‍ ഉണ്ടാക്കാന്‍പ്രാപ്തമാക്കുക", - "image_fullsize_quality_description": "1 മുതൽ 100 വരെയുള്ള പൂർണ്ണ വലുപ്പത്തിലുള്ള ഇമേജ് നിലവാരം. ഉയർന്നതാണ് നല്ലത്, പക്ഷേ വലിയ ഫയലുകൾ ഉത്പാദിപ്പിക്കുന്നു.", - "image_fullsize_title": "പൂർണ്ണ വലുപ്പത്തിലുള്ള ഇമേജ് ക്രമീകരണങ്ങൾ", - "image_quality": "ഗുണനിലവാരം", + "add_exclusion_pattern_description": "ഒഴിവാക്കൽ പാറ്റേണുകൾ ചേർക്കുക. *, **, ? എന്നിവ ഉപയോഗിച്ചുള്ള ഗ്ലോബിംഗ് പിന്തുണയ്ക്കുന്നു. \"Raw\" എന്ന് പേരുള്ള ഏതെങ്കിലും ഡയറക്ടറിയിലെ എല്ലാ ഫയലുകളും ഒഴിവാക്കാൻ, \"**/Raw/**\" ഉപയോഗിക്കുക. \".tif\"-ൽ അവസാനിക്കുന്ന എല്ലാ ഫയലുകളും ഒഴിവാക്കാൻ, \"**/*.tif\" ഉപയോഗിക്കുക. ഒരു കേവല പാത്ത് ഒഴിവാക്കാൻ, \"/path/to/ignore/**\" ഉപയോഗിക്കുക.", + "admin_user": "അഡ്മിൻ ഉപയോക്താവ്", + "asset_offline_description": "ഈ എക്സ്റ്റേണൽ ലൈബ്രറി അസറ്റ് ഇപ്പോൾ ഡിസ്കിൽ ലഭ്യമല്ല, അത് ട്രാഷിലേക്ക് മാറ്റിയിരിക്കുന്നു. ഫയൽ ലൈബ്രറിക്കുള്ളിൽ നീക്കിയിട്ടുണ്ടെങ്കിൽ, പുതിയ അനുബന്ധ അസറ്റിനായി നിങ്ങളുടെ ടൈംലൈൻ പരിശോധിക്കുക. ഈ അസറ്റ് പുനഃസ്ഥാപിക്കാൻ, താഴെയുള്ള ഫയൽ പാത Immich-ന് ആക്സസ് ചെയ്യാൻ കഴിയുമെന്ന് ഉറപ്പാക്കുകയും ലൈബ്രറി സ്കാൻ ചെയ്യുകയും ചെയ്യുക.", + "authentication_settings": "പ്രാമാണീകരണ ക്രമീകരണങ്ങൾ", + "authentication_settings_description": "പാസ്‌വേഡ്, OAuth, മറ്റ് പ്രാമാണീകരണ ക്രമീകരണങ്ങൾ എന്നിവ കൈകാര്യം ചെയ്യുക", + "authentication_settings_disable_all": "എല്ലാ ലോഗിൻ രീതികളും പ്രവർത്തനരഹിതമാക്കണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ? ലോഗിൻ പൂർണ്ണമായും പ്രവർത്തനരഹിതമാകും.", + "authentication_settings_reenable": "വീണ്ടും പ്രവർത്തനക്ഷമമാക്കാൻ, ഒരു സെർവർ കമാൻഡ് ഉപയോഗിക്കുക.", + "background_task_job": "പശ്ചാത്തല ജോലികൾ", + "backup_database": "ഡാറ്റാബേസ് ഡംപ് ഉണ്ടാക്കുക", + "backup_database_enable_description": "ഡാറ്റാബേസ് ഡംപുകൾ പ്രവർത്തനക്ഷമമാക്കുക", + "backup_keep_last_amount": "സൂക്ഷിക്കേണ്ട പഴയ ഡംപുകളുടെ എണ്ണം", + "backup_onboarding_1_description": "ക്ലൗഡിലോ മറ്റൊരു ഭൗതിക സ്ഥാനത്തോ ഉള്ള ഓഫ്‌സൈറ്റ് പകർപ്പ്.", + "backup_onboarding_2_description": "വിവിധ ഉപകരണങ്ങളിലെ പ്രാദേശിക പകർപ്പുകൾ. ഇതിൽ പ്രധാന ഫയലുകളും ആ ഫയലുകളുടെ പ്രാദേശിക ബാക്കപ്പും ഉൾപ്പെടുന്നു.", + "backup_onboarding_3_description": "യഥാർത്ഥ ഫയലുകൾ ഉൾപ്പെടെ നിങ്ങളുടെ ഡാറ്റയുടെ ആകെ പകർപ്പുകൾ. ഇതിൽ 1 ഓഫ്‌സൈറ്റ് പകർപ്പും 2 പ്രാദേശിക പകർപ്പുകളും ഉൾപ്പെടുന്നു.", + "backup_onboarding_description": "നിങ്ങളുടെ ഡാറ്റ പരിരക്ഷിക്കാൻ 3-2-1 ബാക്കപ്പ് തന്ത്രം ശുപാർശ ചെയ്യുന്നു. സമഗ്രമായ ഒരു ബാക്കപ്പ് പരിഹാരത്തിനായി, നിങ്ങൾ അപ്‌ലോഡ് ചെയ്ത ഫോട്ടോകളുടെ/വീഡിയോകളുടെ പകർപ്പുകളും Immich ഡാറ്റാബേസും സൂക്ഷിക്കേണ്ടതാണ്.", + "backup_onboarding_footer": "Immich ബാക്കപ്പ് ചെയ്യുന്നതിനെക്കുറിച്ചുള്ള കൂടുതൽ വിവരങ്ങൾക്കായി, ദയവായി ഡോക്യുമെന്റേഷൻ പരിശോധിക്കുക.", + "backup_onboarding_parts_title": "ഒരു 3-2-1 ബാക്കപ്പിൽ ഉൾപ്പെടുന്നവ:", + "backup_onboarding_title": "ബാക്കപ്പുകൾ", + "backup_settings": "ഡാറ്റാബേസ് ഡംപ് ക്രമീകരണങ്ങൾ", + "backup_settings_description": "ഡാറ്റാബേസ് ഡംപ് ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക.", + "cleared_jobs": "{job}-നുള്ള ജോലികൾ ക്ലിയർ ചെയ്തു", + "config_set_by_file": "കോൺഫിഗറേഷൻ നിലവിൽ ഒരു കോൺഫിഗറേഷൻ ഫയൽ വഴിയാണ് സജ്ജീകരിച്ചിരിക്കുന്നത്", + "confirm_delete_library": "{library} ലൈബ്രറി ഇല്ലാതാക്കണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?", + "confirm_delete_library_assets": "ഈ ലൈബ്രറി ഇല്ലാതാക്കണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ? ഇത് Immich-ൽ നിന്ന് {count, plural, one {അതിലുള്ള ഒരു അസറ്റ്} other {അതിലുള്ള എല്ലാ # അസറ്റുകളും}} ഇല്ലാതാക്കും, ഇത് പഴയപടിയാക്കാൻ കഴിയില്ല. ഫയലുകൾ ഡിസ്കിൽ തന്നെ തുടരും.", + "confirm_email_below": "സ്ഥിരീകരിക്കുന്നതിന്, താഴെ \"{email}\" എന്ന് ടൈപ്പ് ചെയ്യുക", + "confirm_reprocess_all_faces": "എല്ലാ മുഖങ്ങളും വീണ്ടും പ്രോസസ്സ് ചെയ്യണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ? ഇത് പേരുള്ള ആളുകളെയും നീക്കം ചെയ്യും.", + "confirm_user_password_reset": "{user}-ന്റെ പാസ്‌വേഡ് റീസെറ്റ് ചെയ്യണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?", + "confirm_user_pin_code_reset": "{user}-ന്റെ പിൻ കോഡ് റീസെറ്റ് ചെയ്യണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?", + "create_job": "ജോലി ഉണ്ടാക്കുക", + "cron_expression": "ക്രോൺ എക്സ്പ്രഷൻ", + "cron_expression_description": "ക്രോൺ ഫോർമാറ്റ് ഉപയോഗിച്ച് സ്കാനിംഗ് ഇടവേള സജ്ജീകരിക്കുക. കൂടുതൽ വിവരങ്ങൾക്കായി ദയവായി ക്രോൺടാബ് ഗുരു പോലുള്ളവ പരിശോധിക്കുക", + "cron_expression_presets": "ക്രോൺ എക്സ്പ്രഷൻ പ്രീസെറ്റുകൾ", + "disable_login": "ലോഗിൻ പ്രവർത്തനരഹിതമാക്കുക", + "duplicate_detection_job_description": "സമാന ചിത്രങ്ങൾ കണ്ടെത്താൻ അസറ്റുകളിൽ മെഷീൻ ലേണിംഗ് പ്രവർത്തിപ്പിക്കുക. ഇത് സ്മാർട്ട് സെർച്ചിനെ ആശ്രയിച്ചിരിക്കുന്നു", + "exclusion_pattern_description": "നിങ്ങളുടെ ലൈബ്രറി സ്കാൻ ചെയ്യുമ്പോൾ ഫയലുകളും ഫോൾഡറുകളും ഒഴിവാക്കാൻ എക്സ്ക്ലൂഷൻ പാറ്റേണുകൾ നിങ്ങളെ സഹായിക്കുന്നു. നിങ്ങൾ ഇമ്പോർട്ട് ചെയ്യാൻ ആഗ്രഹിക്കാത്ത ഫയലുകൾ അടങ്ങിയ ഫോൾഡറുകൾ (ഉദാഹരണത്തിന് RAW ഫയലുകൾ) ഉണ്ടെങ്കിൽ ഇത് ഉപയോഗപ്രദമാണ്.", + "external_library_management": "എക്സ്റ്റേണൽ ലൈബ്രറി മാനേജ്മെന്റ്", + "face_detection": "മുഖം തിരിച്ചറിയൽ", + "face_detection_description": "മെഷീൻ ലേണിംഗ് ഉപയോഗിച്ച് അസറ്റുകളിലെ മുഖങ്ങൾ കണ്ടെത്തുക. വീഡിയോകൾക്കായി, തംബ്നെയിൽ മാത്രമേ പരിഗണിക്കൂ. \"റിഫ്രഷ്\" എല്ലാ അസറ്റുകളും വീണ്ടും പ്രോസസ്സ് ചെയ്യുന്നു. \"റീസെറ്റ്\" നിലവിലുള്ള എല്ലാ മുഖ ഡാറ്റയും നീക്കംചെയ്യുന്നു. \"മിസ്സിംഗ്\" ഇതുവരെ പ്രോസസ്സ് ചെയ്യാത്ത അസറ്റുകളെ ക്യൂവിലാക്കുന്നു. മുഖം തിരിച്ചറിയൽ പൂർത്തിയായ ശേഷം, കണ്ടെത്തിയ മുഖങ്ങൾ ഫേഷ്യൽ റെക്കഗ്നിഷനായി ക്യൂ ചെയ്യപ്പെടും, അവയെ നിലവിലുള്ളതോ പുതിയതോ ആയ ആളുകളായി തരംതിരിക്കും.", + "facial_recognition_job_description": "കണ്ടെത്തിയ മുഖങ്ങളെ ആളുകളായി ഗ്രൂപ്പ് ചെയ്യുക. മുഖം കണ്ടെത്തൽ പൂർത്തിയായതിന് ശേഷമാണ് ഈ ഘട്ടം പ്രവർത്തിക്കുന്നത്. \"റീസെറ്റ്\" എല്ലാ മുഖങ്ങളെയും വീണ്ടും ക്ലസ്റ്റർ ചെയ്യുന്നു. \"മിസ്സിംഗ്\" ഒരു വ്യക്തിയെയും അസൈൻ ചെയ്യാത്ത മുഖങ്ങളെ ക്യൂവിലാക്കുന്നു.", + "failed_job_command": "{job} എന്ന ജോലിക്കുള്ള കമാൻഡ് {command} പരാജയപ്പെട്ടു", + "force_delete_user_warning": "മുന്നറിയിപ്പ്: ഇത് ഉപയോക്താവിനെയും എല്ലാ അസറ്റുകളെയും ഉടനടി നീക്കം ചെയ്യും. ഇത് പഴയപടിയാക്കാൻ കഴിയില്ല, ഫയലുകൾ വീണ്ടെടുക്കാനും സാധിക്കില്ല.", + "image_format": "ഫോർമാറ്റ്", + "image_format_description": "WebP, JPEG-നെക്കാൾ ചെറിയ ഫയലുകൾ നിർമ്മിക്കുന്നു, പക്ഷേ എൻകോഡ് ചെയ്യാൻ വേഗത കുറവാണ്.", + "image_fullsize_description": "മെറ്റാഡാറ്റ നീക്കംചെയ്ത പൂർണ്ണ വലുപ്പത്തിലുള്ള ചിത്രം, സൂം ചെയ്യുമ്പോൾ ഉപയോഗിക്കുന്നു", + "image_fullsize_enabled": "പൂർണ്ണ വലുപ്പത്തിലുള്ള ചിത്ര നിർമ്മാണം പ്രവർത്തനക്ഷമമാക്കുക", + "image_fullsize_enabled_description": "വെബ്-ഫ്രണ്ട്ലി അല്ലാത്ത ഫോർമാറ്റുകൾക്കായി പൂർണ്ണ വലുപ്പത്തിലുള്ള ചിത്രം നിർമ്മിക്കുക. \"എംബഡഡ് പ്രിവ്യൂവിന് മുൻഗണന നൽകുക\" പ്രവർത്തനക്ഷമമാക്കുമ്പോൾ, എംബഡഡ് പ്രിവ്യൂകൾ പരിവർത്തനം കൂടാതെ നേരിട്ട് ഉപയോഗിക്കുന്നു. JPEG പോലുള്ള വെബ്-ഫ്രണ്ട്ലി ഫോർമാറ്റുകളെ ഇത് ബാധിക്കില്ല.", + "image_fullsize_quality_description": "പൂർണ്ണ വലുപ്പത്തിലുള്ള ചിത്രത്തിന്റെ ഗുണമേന്മ 1-100 വരെ. ഉയർന്ന മൂല്യം മികച്ചതാണ്, പക്ഷേ വലിയ ഫയലുകൾ ഉണ്ടാക്കുന്നു.", + "image_fullsize_title": "പൂർണ്ണ വലുപ്പ ചിത്ര ക്രമീകരണങ്ങൾ", + "image_prefer_embedded_preview": "എംബഡഡ് പ്രിവ്യൂവിന് മുൻഗണന നൽകുക", + "image_prefer_embedded_preview_setting_description": "ലഭ്യമാകുമ്പോൾ, റോ (RAW) ഫോട്ടോകളിലെ എംബഡഡ് പ്രിവ്യൂകൾ ഇമേജ് പ്രോസസ്സിംഗിനുള്ള ഇൻപുട്ടായി ഉപയോഗിക്കുക. ഇത് ചില ചിത്രങ്ങൾക്ക് കൂടുതൽ കൃത്യമായ നിറങ്ങൾ നൽകിയേക്കാം, പക്ഷേ പ്രിവ്യൂവിൻ്റെ ഗുണനിലവാരം ക്യാമറയെ ആശ്രയിച്ചിരിക്കും, കൂടാതെ ചിത്രത്തിൽ കൂടുതൽ കംപ്രഷൻ ആർട്ടിഫാക്റ്റുകൾ ഉണ്ടാകാം.", + "image_prefer_wide_gamut": "വൈഡ് ഗാമറ്റിന് മുൻഗണന നൽകുക", + "image_prefer_wide_gamut_setting_description": "തംബ്നെയിലുകൾക്കായി ഡിസ്പ്ലേ P3 ഉപയോഗിക്കുക. ഇത് വൈഡ് കളർസ്പേസുകളുള്ള ചിത്രങ്ങളുടെ മിഴിവ് നന്നായി സംരക്ഷിക്കുന്നു, പക്ഷേ പഴയ ബ്രൗസർ പതിപ്പുള്ള പഴയ ഉപകരണങ്ങളിൽ ചിത്രങ്ങൾ വ്യത്യസ്തമായി കാണപ്പെട്ടേക്കാം. കളർ ഷിഫ്റ്റുകൾ ഒഴിവാക്കാൻ sRGB ചിത്രങ്ങൾ sRGB ആയി തന്നെ നിലനിർത്തുന്നു.", + "image_preview_description": "മെറ്റാഡാറ്റ നീക്കംചെയ്ത ഇടത്തരം വലുപ്പമുള്ള ചിത്രം, ഒരൊറ്റ അസറ്റ് കാണുമ്പോഴും മെഷീൻ ലേണിംഗിനും ഉപയോഗിക്കുന്നു", + "image_preview_quality_description": "പ്രിവ്യൂ ഗുണമേന്മ 1-100 വരെ. ഉയർന്ന മൂല്യം മികച്ചതാണ്, പക്ഷേ വലിയ ഫയലുകൾ ഉണ്ടാക്കുകയും ആപ്പിന്റെ പ്രതികരണശേഷി കുറയ്ക്കുകയും ചെയ്യും. കുറഞ്ഞ മൂല്യം സജ്ജീകരിക്കുന്നത് മെഷീൻ ലേണിംഗ് ഗുണമേന്മയെ ബാധിച്ചേക്കാം.", + "image_preview_title": "പ്രിവ്യൂ ക്രമീകരണങ്ങൾ", + "image_quality": "ഗുണമേന്മ", + "image_resolution": "റെസല്യൂഷൻ", + "image_resolution_description": "ഉയർന്ന റെസല്യൂഷനുകൾക്ക് കൂടുതൽ വിശദാംശങ്ങൾ സംരക്ഷിക്കാൻ കഴിയും, പക്ഷേ എൻകോഡ് ചെയ്യാൻ കൂടുതൽ സമയമെടുക്കും, വലിയ ഫയൽ വലുപ്പമുണ്ടാകും, കൂടാതെ ആപ്പിന്റെ പ്രതികരണശേഷി കുറയ്ക്കുകയും ചെയ്യും.", + "image_settings": "ചിത്ര ക്രമീകരണങ്ങൾ", + "image_settings_description": "നിർമ്മിച്ച ചിത്രങ്ങളുടെ ഗുണമേന്മയും റെസല്യൂഷനും കൈകാര്യം ചെയ്യുക", + "image_thumbnail_description": "മെറ്റാഡാറ്റ നീക്കംചെയ്ത ചെറിയ തംബ്നെയിൽ, പ്രധാന ടൈംലൈൻ പോലുള്ള ഫോട്ടോകളുടെ ഗ്രൂപ്പുകൾ കാണുമ്പോൾ ഉപയോഗിക്കുന്നു", + "image_thumbnail_quality_description": "തംബ്നെയിലിന്റെ ഗുണമേന്മ 1-100 വരെ. ഉയർന്ന മൂല്യം മികച്ചതാണ്, പക്ഷേ വലിയ ഫയലുകൾ ഉണ്ടാക്കുകയും ആപ്പിന്റെ പ്രതികരണശേഷി കുറയ്ക്കുകയും ചെയ്യും.", + "image_thumbnail_title": "തംബ്നെയിൽ ക്രമീകരണങ്ങൾ", + "job_concurrency": "{job} കോൺകറൻസി", "job_created": "ജോലി സൃഷ്ടിച്ചു", - "job_status": "ജോലി നില" + "job_not_concurrency_safe": "ഈ ജോലി കോൺകറൻസി-സേഫ് അല്ല.", + "job_settings": "ജോലി ക്രമീകരണങ്ങൾ", + "job_settings_description": "ജോലി കോൺകറൻസി കൈകാര്യം ചെയ്യുക", + "job_status": "ജോലിയുടെ നില", + "jobs_delayed": "{jobCount, plural, one {# ജോലി വൈകി} other {# ജോലികൾ വൈകി}}", + "jobs_failed": "{jobCount, plural, one {# ജോലി പരാജയപ്പെട്ടു} other {# ജോലികൾ പരാജയപ്പെട്ടു}}", + "library_created": "{library} എന്ന ലൈബ്രറി സൃഷ്ടിച്ചു", + "library_deleted": "ലൈബ്രറി ഇല്ലാതാക്കി", + "library_import_path_description": "ഇമ്പോർട്ടുചെയ്യാൻ ഒരു ഫോൾഡർ വ്യക്തമാക്കുക. സബ്ഫോൾഡറുകൾ ഉൾപ്പെടെ ഈ ഫോൾഡർ ചിത്രങ്ങൾക്കും വീഡിയോകൾക്കുമായി സ്കാൻ ചെയ്യും.", + "library_scanning": "ആനുകാലിക സ്കാനിംഗ്", + "library_scanning_description": "ആനുകാലിക ലൈബ്രറി സ്കാനിംഗ് കോൺഫിഗർ ചെയ്യുക", + "library_scanning_enable_description": "ആനുകാലിക ലൈബ്രറി സ്കാനിംഗ് പ്രവർത്തനക്ഷമമാക്കുക", + "library_settings": "എക്സ്റ്റേണൽ ലൈബ്രറി", + "library_settings_description": "എക്സ്റ്റേണൽ ലൈബ്രറി ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക", + "library_tasks_description": "പുതിയതോ മാറ്റം വരുത്തിയതോ ആയ അസറ്റുകൾക്കായി എക്സ്റ്റേണൽ ലൈബ്രറികൾ സ്കാൻ ചെയ്യുക", + "library_watching_enable_description": "ഫയൽ മാറ്റങ്ങൾക്കായി എക്സ്റ്റേണൽ ലൈബ്രറികൾ നിരീക്ഷിക്കുക", + "library_watching_settings": "ലൈബ്രറി നിരീക്ഷിക്കൽ (പരീക്ഷണാടിസ്ഥാനത്തിൽ)", + "library_watching_settings_description": "മാറ്റം വന്ന ഫയലുകൾക്കായി യാന്ത്രികമായി നിരീക്ഷിക്കുക", + "logging_enable_description": "ലോഗിംഗ് പ്രവർത്തനക്ഷമമാക്കുക", + "logging_level_description": "പ്രവർത്തനക്ഷമമാക്കുമ്പോൾ, ഏത് ലോഗ് ലെവൽ ഉപയോഗിക്കണം.", + "logging_settings": "ലോഗിംഗ്", + "machine_learning_availability_checks": "ലഭ്യത പരിശോധനകൾ", + "machine_learning_availability_checks_description": "ലഭ്യമായ മെഷീൻ ലേണിംഗ് സെർവറുകൾ യാന്ത്രികമായി കണ്ടെത്തുകയും മുൻഗണന നൽകുകയും ചെയ്യുക", + "machine_learning_availability_checks_enabled": "ലഭ്യത പരിശോധനകൾ പ്രവർത്തനക്ഷമമാക്കുക", + "machine_learning_availability_checks_interval": "പരിശോധന ഇടവേള", + "machine_learning_availability_checks_interval_description": "ലഭ്യത പരിശോധനകൾക്കിടയിലുള്ള ഇടവേള (മില്ലിസെക്കൻഡിൽ)", + "machine_learning_availability_checks_timeout": "അഭ്യർത്ഥനയുടെ സമയപരിധി", + "machine_learning_availability_checks_timeout_description": "ലഭ്യത പരിശോധനകൾക്കുള്ള സമയപരിധി (മില്ലിസെക്കൻഡിൽ)", + "machine_learning_clip_model": "CLIP മോഡൽ", + "machine_learning_clip_model_description": "ഇവിടെ ലിസ്റ്റ് ചെയ്തിട്ടുള്ള ഒരു CLIP മോഡലിന്റെ പേര്. ഒരു മോഡൽ മാറ്റുമ്പോൾ എല്ലാ ചിത്രങ്ങൾക്കുമായി 'സ്മാർട്ട് സെർച്ച്' ജോലി വീണ്ടും പ്രവർത്തിപ്പിക്കേണ്ടതുണ്ടെന്ന് ഓർമ്മിക്കുക.", + "machine_learning_duplicate_detection": "ഡ്യൂപ്ലിക്കേറ്റ് കണ്ടെത്തൽ", + "machine_learning_duplicate_detection_enabled": "ഡ്യൂപ്ലിക്കേറ്റ് കണ്ടെത്തൽ പ്രവർത്തനക്ഷമമാക്കുക", + "machine_learning_duplicate_detection_enabled_description": "പ്രവർത്തനരഹിതമാക്കിയാലും, തികച്ചും സമാനമായ അസറ്റുകളിലെ ഡ്യൂപ്ലിക്കേറ്റുകൾ ഒഴിവാക്കപ്പെടും.", + "machine_learning_duplicate_detection_setting_description": "സമാനമായ ഡ്യൂപ്ലിക്കേറ്റുകൾ കണ്ടെത്താൻ CLIP എംബെഡ്ഡിംഗ്‌സ് ഉപയോഗിക്കുക", + "machine_learning_enabled": "മെഷീൻ ലേണിംഗ് പ്രവർത്തനക്ഷമമാക്കുക", + "machine_learning_enabled_description": "പ്രവർത്തനരഹിതമാക്കിയാൽ, താഴെയുള്ള ക്രമീകരണങ്ങൾ പരിഗണിക്കാതെ എല്ലാ ML ഫീച്ചറുകളും പ്രവർത്തനരഹിതമാകും.", + "machine_learning_facial_recognition": "മുഖം തിരിച്ചറിയൽ", + "machine_learning_facial_recognition_description": "ചിത്രങ്ങളിലെ മുഖങ്ങൾ കണ്ടെത്തുക, തിരിച്ചറിയുക, ഗ്രൂപ്പ് ചെയ്യുക", + "machine_learning_facial_recognition_model": "മുഖം തിരിച്ചറിയൽ മോഡൽ", + "machine_learning_facial_recognition_model_description": "മോഡലുകൾ വലുപ്പത്തിന്റെ അവരോഹണ ക്രമത്തിലാണ് ലിസ്റ്റ് ചെയ്തിരിക്കുന്നത്. വലിയ മോഡലുകൾ വേഗത കുറഞ്ഞതും കൂടുതൽ മെമ്മറി ഉപയോഗിക്കുന്നവയുമാണ്, പക്ഷേ മികച്ച ഫലങ്ങൾ നൽകുന്നു. ഒരു മോഡൽ മാറ്റുമ്പോൾ എല്ലാ ചിത്രങ്ങൾക്കുമായി 'മുഖം കണ്ടെത്തൽ' ജോലി വീണ്ടും പ്രവർത്തിപ്പിക്കേണ്ടതുണ്ടെന്ന് ഓർമ്മിക്കുക.", + "machine_learning_facial_recognition_setting": "മുഖം തിരിച്ചറിയൽ പ്രവർത്തനക്ഷമമാക്കുക", + "machine_learning_facial_recognition_setting_description": "പ്രവർത്തനരഹിതമാക്കിയാൽ, മുഖം തിരിച്ചറിയലിനായി ചിത്രങ്ങൾ എൻകോഡ് ചെയ്യപ്പെടില്ല, കൂടാതെ എക്സ്പ്ലോർ പേജിലെ 'ആളുകൾ' വിഭാഗത്തിൽ അവ ദൃശ്യമാകുകയുമില്ല.", + "machine_learning_max_detection_distance": "പരമാവധി കണ്ടെത്തൽ ദൂരം", + "machine_learning_max_detection_distance_description": "രണ്ട് ചിത്രങ്ങളെ ഡ്യൂപ്ലിക്കേറ്റുകളായി കണക്കാക്കുന്നതിനുള്ള പരമാവധി ദൂരം, 0.001-0.1 വരെ. ഉയർന്ന മൂല്യങ്ങൾ കൂടുതൽ ഡ്യൂപ്ലിക്കേറ്റുകളെ കണ്ടെത്തും, പക്ഷേ തെറ്റായ ഫലങ്ങളിലേക്ക് നയിച്ചേക്കാം.", + "machine_learning_max_recognition_distance": "പരമാവധി തിരിച്ചറിയൽ ദൂരം", + "machine_learning_max_recognition_distance_description": "രണ്ട് മുഖങ്ങളെ ഒരേ വ്യക്തിയായി കണക്കാക്കുന്നതിനുള്ള പരമാവധി ദൂരം, 0-2 വരെ. ഇത് കുറയ്ക്കുന്നത് രണ്ട് പേരെ ഒരേ വ്യക്തിയായി ലേബൽ ചെയ്യുന്നത് തടയാൻ സഹായിക്കും, അതേസമയം ഇത് കൂട്ടുന്നത് ഒരേ വ്യക്തിയെ രണ്ട് വ്യത്യസ്ത ആളുകളായി ലേബൽ ചെയ്യുന്നത് തടയാനും സഹായിക്കും. ഒരാളെ രണ്ടായി വിഭജിക്കുന്നതിനേക്കാൾ എളുപ്പമാണ് രണ്ടുപേരെ ഒന്നാക്കുന്നത് എന്നത് ശ്രദ്ധിക്കുക, അതിനാൽ സാധ്യമാകുമ്പോൾ കുറഞ്ഞ പരിധി തിരഞ്ഞെടുക്കുന്നതാണ് ഉചിതം.", + "machine_learning_min_detection_score": "കുറഞ്ഞ കണ്ടെത്തൽ സ്കോർ", + "machine_learning_min_detection_score_description": "ഒരു മുഖം കണ്ടെത്തുന്നതിനുള്ള ഏറ്റവും കുറഞ്ഞ കോൺഫിഡൻസ് സ്കോർ 0-1 വരെ. കുറഞ്ഞ മൂല്യങ്ങൾ കൂടുതൽ മുഖങ്ങളെ കണ്ടെത്തും, പക്ഷേ തെറ്റായ ഫലങ്ങളിലേക്ക് നയിച്ചേക്കാം.", + "machine_learning_min_recognized_faces": "തിരിച്ചറിഞ്ഞ മുഖങ്ങളുടെ കുറഞ്ഞ എണ്ണം", + "machine_learning_min_recognized_faces_description": "ഒരു വ്യക്തിയെ സൃഷ്ടിക്കുന്നതിന് ആവശ്യമായ തിരിച്ചറിഞ്ഞ മുഖങ്ങളുടെ ഏറ്റവും കുറഞ്ഞ എണ്ണം. ഇത് വർദ്ധിപ്പിക്കുന്നത് മുഖം തിരിച്ചറിയൽ കൂടുതൽ കൃത്യമാക്കുന്നു, എന്നാൽ ഒരു മുഖം ഒരു വ്യക്തിക്ക് നൽകാതിരിക്കാനുള്ള സാധ്യതയും വർദ്ധിപ്പിക്കുന്നു.", + "machine_learning_settings": "മെഷീൻ ലേണിംഗ് ക്രമീകരണങ്ങൾ", + "machine_learning_settings_description": "മെഷീൻ ലേണിംഗ് ഫീച്ചറുകളും ക്രമീകരണങ്ങളും കൈകാര്യം ചെയ്യുക", + "machine_learning_smart_search": "സ്മാർട്ട് സെർച്ച്", + "machine_learning_smart_search_description": "CLIP എംബെഡ്ഡിംഗ്‌സ് ഉപയോഗിച്ച് ചിത്രങ്ങൾക്കായി അർത്ഥപൂർവ്വം തിരയുക", + "machine_learning_smart_search_enabled": "സ്മാർട്ട് സെർച്ച് പ്രവർത്തനക്ഷമമാക്കുക", + "machine_learning_smart_search_enabled_description": "പ്രവർത്തനരഹിതമാക്കിയാൽ, സ്മാർട്ട് സെർച്ചിനായി ചിത്രങ്ങൾ എൻകോഡ് ചെയ്യപ്പെടില്ല.", + "machine_learning_url_description": "മെഷീൻ ലേണിംഗ് സെർവറിന്റെ URL. ഒന്നിൽ കൂടുതൽ URL-കൾ നൽകിയിട്ടുണ്ടെങ്കിൽ, ഓരോ സെർവറും ഒന്നിനു പുറകെ ഒന്നായി വിജയകരമായി പ്രതികരിക്കുന്നതുവരെ ശ്രമിക്കും. പ്രതികരിക്കാത്ത സെർവറുകൾ ഓൺലൈനിൽ തിരികെ വരുന്നതുവരെ താൽക്കാലികമായി അവഗണിക്കപ്പെടും.", + "manage_concurrency": "കോൺകറൻസി കൈകാര്യം ചെയ്യുക", + "manage_log_settings": "ലോഗ് ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക", + "map_dark_style": "ഡാർക്ക് സ്റ്റൈൽ", + "map_enable_description": "മാപ്പ് ഫീച്ചറുകൾ പ്രവർത്തനക്ഷമമാക്കുക", + "map_gps_settings": "മാപ്പ് & GPS ക്രമീകരണങ്ങൾ", + "map_gps_settings_description": "മാപ്പ് & GPS (റിവേഴ്സ് ജിയോകോഡിംഗ്) ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക", + "map_implications": "മാപ്പ് ഫീച്ചർ ഒരു ബാഹ്യ ടൈൽ സേവനത്തെ (tiles.immich.cloud) ആശ്രയിച്ചിരിക്കുന്നു", + "map_light_style": "ലൈറ്റ് സ്റ്റൈൽ", + "map_manage_reverse_geocoding_settings": "റിവേഴ്സ് ജിയോകോഡിംഗ് ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക", + "map_reverse_geocoding": "റിവേഴ്സ് ജിയോകോഡിംഗ്", + "map_reverse_geocoding_enable_description": "റിവേഴ്സ് ജിയോകോഡിംഗ് പ്രവർത്തനക്ഷമമാക്കുക", + "map_reverse_geocoding_settings": "റിവേഴ്സ് ജിയോകോഡിംഗ് ക്രമീകരണങ്ങൾ", + "map_settings": "മാപ്പ്", + "map_settings_description": "മാപ്പ് ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക", + "map_style_description": "ഒരു style.json മാപ്പ് തീമിലേക്കുള്ള URL", + "memory_cleanup_job": "മെമ്മറി ക്ലീനപ്പ്", + "memory_generate_job": "മെമ്മറി ജനറേഷൻ", + "metadata_extraction_job": "മെറ്റാഡാറ്റ എക്‌സ്‌ട്രാക്റ്റുചെയ്യുക", + "metadata_extraction_job_description": "ഓരോ അസറ്റിൽ നിന്നും GPS, മുഖങ്ങൾ, റെസല്യൂഷൻ തുടങ്ങിയ മെറ്റാഡാറ്റ വിവരങ്ങൾ എക്‌സ്‌ട്രാക്റ്റുചെയ്യുക", + "metadata_faces_import_setting": "മുഖം ഇമ്പോർട്ട് പ്രവർത്തനക്ഷമമാക്കുക", + "metadata_faces_import_setting_description": "ചിത്രത്തിന്റെ EXIF ഡാറ്റയിൽ നിന്നും സൈഡ്‌കാർ ഫയലുകളിൽ നിന്നും മുഖങ്ങൾ ഇമ്പോർട്ടുചെയ്യുക", + "metadata_settings": "മെറ്റാഡാറ്റ ക്രമീകരണങ്ങൾ", + "metadata_settings_description": "മെറ്റാഡാറ്റ ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക", + "migration_job": "മൈഗ്രേഷൻ", + "migration_job_description": "അസറ്റുകളുടെയും മുഖങ്ങളുടെയും തംബ്നെയിലുകൾ ഏറ്റവും പുതിയ ഫോൾഡർ ഘടനയിലേക്ക് മൈഗ്രേറ്റ് ചെയ്യുക", + "nightly_tasks_cluster_faces_setting_description": "പുതുതായി കണ്ടെത്തിയ മുഖങ്ങളിൽ ഫേഷ്യൽ റെക്കഗ്നിഷൻ പ്രവർത്തിപ്പിക്കുക", + "nightly_tasks_cluster_new_faces_setting": "പുതിയ മുഖങ്ങളെ ക്ലസ്റ്റർ ചെയ്യുക", + "nightly_tasks_database_cleanup_setting": "ഡാറ്റാബേസ് ക്ലീനപ്പ് ടാസ്ക്കുകൾ", + "nightly_tasks_database_cleanup_setting_description": "ഡാറ്റാബേസിൽ നിന്ന് പഴയതും കാലഹരണപ്പെട്ടതുമായ ഡാറ്റ വൃത്തിയാക്കുക", + "nightly_tasks_generate_memories_setting": "മെമ്മറികൾ നിർമ്മിക്കുക", + "nightly_tasks_generate_memories_setting_description": "അസറ്റുകളിൽ നിന്ന് പുതിയ മെമ്മറികൾ സൃഷ്ടിക്കുക", + "nightly_tasks_missing_thumbnails_setting": "നഷ്ടപ്പെട്ട തംബ്നെയിലുകൾ നിർമ്മിക്കുക", + "nightly_tasks_missing_thumbnails_setting_description": "തംബ്നെയിലുകൾ ഇല്ലാത്ത അസറ്റുകളെ തംബ്നെയിൽ നിർമ്മാണത്തിനായി ക്യൂ ചെയ്യുക", + "nightly_tasks_settings": "രാത്രിയിലെ ടാസ്ക് ക്രമീകരണങ്ങൾ", + "nightly_tasks_settings_description": "രാത്രിയിലെ ടാസ്ക്കുകൾ കൈകാര്യം ചെയ്യുക", + "nightly_tasks_start_time_setting": "തുടങ്ങുന്ന സമയം", + "nightly_tasks_start_time_setting_description": "സെർവർ രാത്രിയിലെ ടാസ്ക്കുകൾ പ്രവർത്തിപ്പിക്കാൻ തുടങ്ങുന്ന സമയം", + "nightly_tasks_sync_quota_usage_setting": "ക്വാട്ട ഉപയോഗം സിങ്ക് ചെയ്യുക", + "nightly_tasks_sync_quota_usage_setting_description": "നിലവിലെ ഉപയോഗത്തെ അടിസ്ഥാനമാക്കി ഉപയോക്താവിന്റെ സ്റ്റോറേജ് ക്വാട്ട അപ്ഡേറ്റ് ചെയ്യുക", + "no_paths_added": "പാത്തുകൾ ഒന്നും ചേർത്തിട്ടില്ല", + "no_pattern_added": "പാറ്റേൺ ഒന്നും ചേർത്തിട്ടില്ല", + "note_apply_storage_label_previous_assets": "കുറിപ്പ്: മുമ്പ് അപ്‌ലോഡ് ചെയ്ത അസറ്റുകളിൽ സ്റ്റോറേജ് ലേബൽ പ്രയോഗിക്കാൻ, ഇത് പ്രവർത്തിപ്പിക്കുക", + "note_cannot_be_changed_later": "കുറിപ്പ്: ഇത് പിന്നീട് മാറ്റാൻ കഴിയില്ല!", + "notification_email_from_address": "അയയ്ക്കുന്നയാളുടെ വിലാസം", + "notification_email_from_address_description": "അയയ്ക്കുന്നയാളുടെ ഇമെയിൽ വിലാസം, ഉദാഹരണത്തിന്: \"Immich Photo Server \". നിങ്ങൾക്ക് ഇമെയിലുകൾ അയയ്ക്കാൻ അനുവാദമുള്ള ഒരു വിലാസം ഉപയോഗിക്കുന്നുവെന്ന് ഉറപ്പാക്കുക.", + "notification_email_host_description": "ഇമെയിൽ സെർവറിന്റെ ഹോസ്റ്റ് (ഉദാ. smtp.immich.app)", + "notification_email_ignore_certificate_errors": "സർട്ടിഫിക്കറ്റ് പിശകുകൾ അവഗണിക്കുക", + "notification_email_ignore_certificate_errors_description": "TLS സർട്ടിഫിക്കറ്റ് സാധുതാ പിശകുകൾ അവഗണിക്കുക (ശുപാർശ ചെയ്യുന്നില്ല)", + "notification_email_password_description": "ഇമെയിൽ സെർവറുമായി പ്രാമാണീകരിക്കുമ്പോൾ ഉപയോഗിക്കേണ്ട പാസ്‌വേഡ്", + "notification_email_port_description": "ഇമെയിൽ സെർവറിന്റെ പോർട്ട് (ഉദാ. 25, 465, അല്ലെങ്കിൽ 587)", + "notification_email_sent_test_email_button": "പരിശോധനാ ഇമെയിൽ അയച്ച് സേവ് ചെയ്യുക", + "notification_email_setting_description": "ഇമെയിൽ അറിയിപ്പുകൾ അയക്കുന്നതിനുള്ള ക്രമീകരണങ്ങൾ", + "notification_email_test_email": "പരിശോധനാ ഇമെയിൽ അയക്കുക", + "notification_email_test_email_failed": "പരിശോധനാ ഇമെയിൽ അയക്കുന്നതിൽ പരാജയപ്പെട്ടു, നിങ്ങളുടെ മൂല്യങ്ങൾ പരിശോധിക്കുക", + "notification_email_test_email_sent": "{email} എന്ന വിലാസത്തിലേക്ക് ഒരു പരിശോധനാ ഇമെയിൽ അയച്ചിട്ടുണ്ട്. ദയവായി നിങ്ങളുടെ ഇൻബോക്സ് പരിശോധിക്കുക.", + "notification_email_username_description": "ഇമെയിൽ സെർവറുമായി പ്രാമാണീകരിക്കുമ്പോൾ ഉപയോഗിക്കേണ്ട ഉപയോക്തൃനാമം", + "notification_enable_email_notifications": "ഇമെയിൽ അറിയിപ്പുകൾ പ്രവർത്തനക്ഷമമാക്കുക", + "notification_settings": "അറിയിപ്പ് ക്രമീകരണങ്ങൾ", + "notification_settings_description": "ഇമെയിൽ ഉൾപ്പെടെയുള്ള അറിയിപ്പ് ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക", + "oauth_auto_launch": "യാന്ത്രികമായി തുടങ്ങുക", + "oauth_auto_launch_description": "ലോഗിൻ പേജിലേക്ക് പോകുമ്പോൾ OAuth ലോഗിൻ ഫ്ലോ യാന്ത്രികമായി ആരംഭിക്കുക", + "oauth_auto_register": "യാന്ത്രികമായി രജിസ്റ്റർ ചെയ്യുക", + "oauth_auto_register_description": "OAuth ഉപയോഗിച്ച് സൈൻ ഇൻ ചെയ്ത ശേഷം പുതിയ ഉപയോക്താക്കളെ യാന്ത്രികമായി രജിസ്റ്റർ ചെയ്യുക", + "oauth_button_text": "ബട്ടണിലെ വാചകം", + "oauth_client_secret_description": "OAuth ദാതാവ് PKCE (പ്രൂഫ് കീ ഫോർ കോഡ് എക്സ്ചേഞ്ച്) പിന്തുണയ്ക്കുന്നില്ലെങ്കിൽ ആവശ്യമാണ്", + "oauth_enable_description": "OAuth ഉപയോഗിച്ച് ലോഗിൻ ചെയ്യുക", + "oauth_mobile_redirect_uri": "മൊബൈൽ റീഡയറക്ട് URI", + "oauth_mobile_redirect_uri_override": "മൊബൈൽ റീഡയറക്ട് URI ഓവർറൈഡ്", + "oauth_mobile_redirect_uri_override_description": "OAuth ദാതാവ് \"{callback}\" പോലുള്ള ഒരു മൊബൈൽ URI അനുവദിക്കാത്തപ്പോൾ പ്രവർത്തനക്ഷമമാക്കുക", + "oauth_role_claim": "റോൾ ക്ലെയിം", + "oauth_role_claim_description": "ഈ ക്ലെയിമിന്റെ സാന്നിധ്യത്തെ അടിസ്ഥാനമാക്കി യാന്ത്രികമായി അഡ്മിൻ ആക്സസ് നൽകുക. ക്ലെയിമിന് 'user' അല്ലെങ്കിൽ 'admin' എന്ന് ഉണ്ടാകാം.", + "oauth_settings": "OAuth", + "oauth_settings_description": "OAuth ലോഗിൻ ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക", + "oauth_settings_more_details": "ഈ ഫീച്ചറിനെക്കുറിച്ചുള്ള കൂടുതൽ വിവരങ്ങൾക്ക്, ഡോക്സ് പരിശോധിക്കുക.", + "oauth_storage_label_claim": "സ്റ്റോറേജ് ലേബൽ ക്ലെയിം", + "oauth_storage_label_claim_description": "ഉപയോക്താവിന്റെ സ്റ്റോറേജ് ലേബൽ ഈ ക്ലെയിമിന്റെ മൂല്യത്തിലേക്ക് യാന്ത്രികമായി സജ്ജമാക്കുക.", + "oauth_storage_quota_claim": "സ്റ്റോറേജ് ക്വാട്ട ക്ലെയിം", + "oauth_storage_quota_claim_description": "ഉപയോക്താവിന്റെ സ്റ്റോറേജ് ക്വാട്ട ഈ ക്ലെയിമിന്റെ മൂല്യത്തിലേക്ക് യാന്ത്രികമായി സജ്ജമാക്കുക.", + "oauth_storage_quota_default": "ഡിഫോൾട്ട് സ്റ്റോറേജ് ക്വാട്ട (GiB)", + "oauth_storage_quota_default_description": "ക്ലെയിം ഒന്നും നൽകാത്തപ്പോൾ ഉപയോഗിക്കേണ്ട ക്വാട്ട (GiB-യിൽ).", + "oauth_timeout": "അഭ്യർത്ഥനയുടെ സമയപരിധി", + "oauth_timeout_description": "അഭ്യർത്ഥനകൾക്കുള്ള സമയപരിധി (മില്ലിസെക്കൻഡിൽ)", + "password_enable_description": "ഇമെയിലും പാസ്‌വേഡും ഉപയോഗിച്ച് ലോഗിൻ ചെയ്യുക", + "password_settings": "പാസ്‌വേഡ് ലോഗിൻ", + "password_settings_description": "പാസ്‌വേഡ് ലോഗിൻ ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക", + "paths_validated_successfully": "എല്ലാ പാത്തുകളും വിജയകരമായി സാധൂകരിച്ചു", + "person_cleanup_job": "വ്യക്തി ക്ലീനപ്പ്", + "quota_size_gib": "ക്വാട്ട വലുപ്പം (GiB)", + "refreshing_all_libraries": "എല്ലാ ലൈബ്രറികളും പുതുക്കുന്നു", + "registration": "അഡ്മിൻ രജിസ്ട്രേഷൻ", + "registration_description": "നിങ്ങളാണ് സിസ്റ്റത്തിലെ ആദ്യത്തെ ഉപയോക്താവ് എന്നതിനാൽ, നിങ്ങളെ അഡ്മിൻ ആയി നിയമിക്കും. ഭരണപരമായ ജോലികൾക്ക് നിങ്ങൾ ഉത്തരവാദിയായിരിക്കും, കൂടാതെ അധിക ഉപയോക്താക്കളെ സൃഷ്ടിക്കുന്നത് നിങ്ങളായിരിക്കും.", + "require_password_change_on_login": "ആദ്യ ലോഗിൻ ചെയ്യുമ്പോൾ പാസ്‌വേഡ് മാറ്റാൻ ഉപയോക്താവിനോട് ആവശ്യപ്പെടുക", + "reset_settings_to_default": "ക്രമീകരണങ്ങൾ ഡിഫോൾട്ടിലേക്ക് പുനഃസജ്ജമാക്കുക", + "reset_settings_to_recent_saved": "അടുത്തിടെ സേവ് ചെയ്ത ക്രമീകരണങ്ങളിലേക്ക് പുനഃസജ്ജമാക്കുക", + "scanning_library": "ലൈബ്രറി സ്കാൻ ചെയ്യുന്നു", + "search_jobs": "ജോലികൾക്കായി തിരയുക…", + "send_welcome_email": "സ്വാഗത ഇമെയിൽ അയക്കുക", + "server_external_domain_settings": "ബാഹ്യ ഡൊമെയ്ൻ", + "server_external_domain_settings_description": "പൊതുവായി പങ്കിട്ട ലിങ്കുകൾക്കുള്ള ഡൊമെയ്ൻ, http(s):// ഉൾപ്പെടെ", + "server_public_users": "പൊതു ഉപയോക്താക്കൾ", + "server_public_users_description": "പങ്കിട്ട ആൽബങ്ങളിലേക്ക് ഒരു ഉപയോക്താവിനെ ചേർക്കുമ്പോൾ എല്ലാ ഉപയോക്താക്കളെയും (പേരും ഇമെയിലും) ലിസ്റ്റ് ചെയ്യും. പ്രവർത്തനരഹിതമാക്കുമ്പോൾ, ഉപയോക്തൃ ലിസ്റ്റ് അഡ്മിൻ ഉപയോക്താക്കൾക്ക് മാത്രമേ ലഭ്യമാകൂ.", + "server_settings": "സെർവർ ക്രമീകരണങ്ങൾ", + "server_settings_description": "സെർവർ ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക", + "server_welcome_message": "സ്വാഗത സന്ദേശം", + "server_welcome_message_description": "ലോഗിൻ പേജിൽ പ്രദർശിപ്പിക്കുന്ന ഒരു സന്ദേശം.", + "sidecar_job": "സൈഡ്‌കാർ മെറ്റാഡാറ്റ", + "sidecar_job_description": "ഫയൽസിസ്റ്റത്തിൽ നിന്ന് സൈഡ്‌കാർ മെറ്റാഡാറ്റ കണ്ടെത്തുകയോ സിൻക്രൊണൈസ് ചെയ്യുകയോ ചെയ്യുക", + "slideshow_duration_description": "ഓരോ ചിത്രവും പ്രദർശിപ്പിക്കാനുള്ള സെക്കൻഡുകളുടെ എണ്ണം", + "smart_search_job_description": "സ്മാർട്ട് സെർച്ചിനെ പിന്തുണയ്ക്കുന്നതിനായി അസറ്റുകളിൽ മെഷീൻ ലേണിംഗ് പ്രവർത്തിപ്പിക്കുക", + "storage_template_date_time_description": "ഡേറ്റ്ടൈം വിവരങ്ങൾക്കായി അസറ്റിന്റെ ക്രിയേഷൻ ടൈംസ്റ്റാമ്പ് ഉപയോഗിക്കുന്നു", + "storage_template_date_time_sample": "സാമ്പിൾ സമയം {date}", + "storage_template_enable_description": "സ്റ്റോറേജ് ടെംപ്ലേറ്റ് എഞ്ചിൻ പ്രവർത്തനക്ഷമമാക്കുക", + "storage_template_hash_verification_enabled": "ഹാഷ് വെരിഫിക്കേഷൻ പ്രവർത്തനക്ഷമമാക്കി", + "storage_template_hash_verification_enabled_description": "ഹാഷ് വെരിഫിക്കേഷൻ പ്രവർത്തനക്ഷമമാക്കുന്നു, പ്രത്യാഘാതങ്ങളെക്കുറിച്ച് ഉറപ്പില്ലെങ്കിൽ ഇത് പ്രവർത്തനരഹിതമാക്കരുത്", + "storage_template_migration": "സ്റ്റോറേജ് ടെംപ്ലേറ്റ് മൈഗ്രേഷൻ", + "storage_template_migration_description": "മുമ്പ് അപ്‌ലോഡ് ചെയ്ത അസറ്റുകളിലേക്ക് നിലവിലെ {template} പ്രയോഗിക്കുക", + "storage_template_migration_info": "സ്റ്റോറേജ് ടെംപ്ലേറ്റ് എല്ലാ എക്സ്റ്റൻഷനുകളെയും ചെറിയക്ഷരത്തിലേക്ക് മാറ്റും. ടെംപ്ലേറ്റിലെ മാറ്റങ്ങൾ പുതിയ അസറ്റുകൾക്ക് മാത്രമേ ബാധകമാകൂ. മുമ്പ് അപ്‌ലോഡ് ചെയ്ത അസറ്റുകളിൽ ടെംപ്ലേറ്റ് മുൻകാല പ്രാബല്യത്തോടെ പ്രയോഗിക്കാൻ, {job} പ്രവർത്തിപ്പിക്കുക.", + "storage_template_migration_job": "സ്റ്റോറേജ് ടെംപ്ലേറ്റ് മൈഗ്രേഷൻ ജോലി", + "storage_template_more_details": "ഈ ഫീച്ചറിനെക്കുറിച്ചുള്ള കൂടുതൽ വിവരങ്ങൾക്ക്, സ്റ്റോറേജ് ടെംപ്ലേറ്റ്-ഉം അതിൻ്റെ പ്രത്യാഘാതങ്ങളും പരിശോധിക്കുക", + "storage_template_onboarding_description_v2": "പ്രവർത്തനക്ഷമമാക്കുമ്പോൾ, ഉപയോക്താവ് നിർവചിച്ച ടെംപ്ലേറ്റ് അടിസ്ഥാനമാക്കി ഈ ഫീച്ചർ ഫയലുകൾ യാന്ത്രികമായി ഓർഗനൈസ് ചെയ്യും. കൂടുതൽ വിവരങ്ങൾക്ക്, ദയവായി ഡോക്യുമെന്റേഷൻ കാണുക.", + "storage_template_path_length": "ഏകദേശ പാത്ത് ദൈർഘ്യ പരിധി: {length, number}/{limit, number}", + "storage_template_settings": "സ്റ്റോറേജ് ടെംപ്ലേറ്റ്", + "storage_template_settings_description": "അപ്‌ലോഡ് ചെയ്യുന്ന അസറ്റിന്റെ ഫോൾഡർ ഘടനയും ഫയൽ നാമവും കൈകാര്യം ചെയ്യുക", + "storage_template_user_label": "{label} എന്നത് ഉപയോക്താവിന്റെ സ്റ്റോറേജ് ലേബലാണ്", + "system_settings": "സിസ്റ്റം ക്രമീകരണങ്ങൾ", + "tag_cleanup_job": "ടാഗ് ക്ലീനപ്പ്", + "template_email_available_tags": "നിങ്ങളുടെ ടെംപ്ലേറ്റിൽ ഇനിപ്പറയുന്ന വേരിയബിളുകൾ ഉപയോഗിക്കാം: {tags}", + "template_email_if_empty": "ടെംപ്ലേറ്റ് ശൂന്യമാണെങ്കിൽ, ഡിഫോൾട്ട് ഇമെയിൽ ഉപയോഗിക്കും.", + "template_email_invite_album": "ആൽബം ക്ഷണിക്കാനുള്ള ടെംപ്ലേറ്റ്", + "template_email_preview": "പ്രിവ്യൂ", + "template_email_settings": "ഇമെയിൽ ടെംപ്ലേറ്റുകൾ", + "template_email_update_album": "ആൽബം അപ്ഡേറ്റ് ടെംപ്ലേറ്റ്", + "template_email_welcome": "സ്വാഗത ഇമെയിൽ ടെംപ്ലേറ്റ്", + "template_settings": "അറിയിപ്പ് ടെംപ്ലേറ്റുകൾ", + "template_settings_description": "അറിയിപ്പുകൾക്കായി ഇഷ്ടാനുസൃത ടെംപ്ലേറ്റുകൾ കൈകാര്യം ചെയ്യുക", + "theme_custom_css_settings": "കസ്റ്റം CSS", + "theme_custom_css_settings_description": "കാസ്കേഡിംഗ് സ്റ്റൈൽ ഷീറ്റുകൾ (CSS) Immich-ന്റെ ഡിസൈൻ ഇഷ്ടാനുസൃതമാക്കാൻ അനുവദിക്കുന്നു.", + "theme_settings": "തീം ക്രമീകരണങ്ങൾ", + "theme_settings_description": "Immich വെബ് ഇന്റർഫേസിന്റെ കസ്റ്റമൈസേഷൻ കൈകാര്യം ചെയ്യുക", + "thumbnail_generation_job": "തംബ്നെയിലുകൾ നിർമ്മിക്കുക", + "thumbnail_generation_job_description": "ഓരോ അസറ്റിനും വലുതും ചെറുതും മങ്ങിയതുമായ തംബ്നെയിലുകളും ഓരോ വ്യക്തിക്കും തംബ്നെയിലുകളും നിർമ്മിക്കുക", + "transcoding_acceleration_api": "ആക്സിലറേഷൻ API", + "transcoding_acceleration_api_description": "ട്രാൻസ്‌കോഡിംഗ് ത്വരിതപ്പെടുത്തുന്നതിന് നിങ്ങളുടെ ഉപകരണവുമായി സംവദിക്കുന്ന API. ഈ ക്രമീകരണം 'ബെസ്റ്റ് എഫേർട്ട്' ആണ്: പരാജയപ്പെട്ടാൽ സോഫ്റ്റ്‌വെയർ ട്രാൻസ്‌കോഡിംഗിലേക്ക് മാറും. നിങ്ങളുടെ ഹാർഡ്‌വെയർ അനുസരിച്ച് VP9 പ്രവർത്തിക്കുകയോ പ്രവർത്തിക്കാതിരിക്കുകയോ ചെയ്യാം.", + "transcoding_acceleration_nvenc": "NVENC (NVIDIA GPU ആവശ്യമാണ്)", + "transcoding_acceleration_qsv": "ക്വിക്ക് സിങ്ക് (7-ാം തലമുറ ഇന്റൽ സിപിയു അല്ലെങ്കിൽ അതിനുശേഷമുള്ളത് ആവശ്യമാണ്)", + "transcoding_acceleration_rkmpp": "RKMPP (Rockchip SOC-കളിൽ മാത്രം)", + "transcoding_acceleration_vaapi": "VAAPI", + "transcoding_accepted_audio_codecs": "അംഗീകൃത ഓഡിയോ കോഡെക്കുകൾ", + "transcoding_accepted_audio_codecs_description": "ട്രാൻസ്‌കോഡ് ചെയ്യേണ്ടാത്ത ഓഡിയോ കോഡെക്കുകൾ തിരഞ്ഞെടുക്കുക. ചില ട്രാൻസ്‌കോഡ് നയങ്ങൾക്ക് വേണ്ടി മാത്രം ഉപയോഗിക്കുന്നു.", + "transcoding_accepted_containers": "അംഗീകൃത കണ്ടെയ്‌നറുകൾ", + "transcoding_accepted_containers_description": "MP4-ലേക്ക് റീമക്സ് ചെയ്യേണ്ടാത്ത കണ്ടെയ്‌നർ ഫോർമാറ്റുകൾ തിരഞ്ഞെടുക്കുക. ചില ട്രാൻസ്‌കോഡ് നയങ്ങൾക്ക് വേണ്ടി മാത്രം ഉപയോഗിക്കുന്നു.", + "transcoding_accepted_video_codecs": "അംഗീകൃത വീഡിയോ കോഡെക്കുകൾ", + "transcoding_accepted_video_codecs_description": "ട്രാൻസ്‌കോഡ് ചെയ്യേണ്ടാത്ത വീഡിയോ കോഡെക്കുകൾ തിരഞ്ഞെടുക്കുക. ചില ട്രാൻസ്‌കോഡ് നയങ്ങൾക്ക് വേണ്ടി മാത്രം ഉപയോഗിക്കുന്നു.", + "transcoding_advanced_options_description": "മിക്ക ഉപയോക്താക്കളും മാറ്റം വരുത്തേണ്ടതില്ലാത്ത ഓപ്ഷനുകൾ", + "transcoding_audio_codec": "ഓഡിയോ കോഡെക്", + "transcoding_audio_codec_description": "Opus ഏറ്റവും ഉയർന്ന നിലവാരമുള്ള ഓപ്ഷനാണ്, പക്ഷേ പഴയ ഉപകരണങ്ങളുമായോ സോഫ്റ്റ്‌വെയറുമായോ ഇതിന് കുറഞ്ഞ അനുയോജ്യതയേ ഉള്ളൂ.", + "transcoding_bitrate_description": "പരമാവധി ബിറ്റ്റേറ്റിനേക്കാൾ ഉയർന്നതോ അംഗീകൃത ഫോർമാറ്റിൽ അല്ലാത്തതോ ആയ വീഡിയോകൾ", + "transcoding_codecs_learn_more": "ഇവിടെ ഉപയോഗിച്ചിരിക്കുന്ന പദങ്ങളെക്കുറിച്ച് കൂടുതലറിയാൻ, H.264 കോഡെക്, HEVC കോഡെക്, VP9 കോഡെക് എന്നിവയുടെ FFmpeg ഡോക്യുമെന്റേഷൻ പരിശോധിക്കുക.", + "transcoding_constant_quality_mode": "സ്ഥിരമായ ഗുണമേന്മ മോഡ്", + "transcoding_constant_quality_mode_description": "CQP-യെക്കാൾ മികച്ചതാണ് ICQ, എന്നാൽ ചില ഹാർഡ്‌വെയർ ആക്സിലറേഷൻ ഉപകരണങ്ങൾ ഈ മോഡിനെ പിന്തുണയ്ക്കുന്നില്ല. ഗുണമേന്മ അടിസ്ഥാനമാക്കിയുള്ള എൻകോഡിംഗ് ഉപയോഗിക്കുമ്പോൾ ഈ ഓപ്ഷൻ സജ്ജീകരിക്കുന്നത് നിർദ്ദിഷ്ട മോഡിന് മുൻഗണന നൽകും. ICQ പിന്തുണയ്ക്കാത്തതിനാൽ NVENC ഇത് അവഗണിക്കുന്നു.", + "transcoding_constant_rate_factor": "കോൺസ്റ്റന്റ് റേറ്റ് ഫാക്ടർ (-crf)", + "transcoding_constant_rate_factor_description": "വീഡിയോയുടെ ഗുണമേന്മ നില. സാധാരണ മൂല്യങ്ങൾ H.264-ന് 23, HEVC-ക്ക് 28, VP9-ന് 31, AV1-ന് 35 എന്നിങ്ങനെയാണ്. കുറഞ്ഞ മൂല്യം മികച്ചതാണ്, പക്ഷേ വലിയ ഫയലുകൾ ഉണ്ടാക്കുന്നു.", + "transcoding_disabled_description": "ഒരു വീഡിയോയും ട്രാൻസ്‌കോഡ് ചെയ്യരുത്, ഇത് ചില ക്ലയിന്റുകളിൽ പ്ലേബാക്ക് തടസ്സപ്പെടുത്തിയേക്കാം", + "transcoding_encoding_options": "എൻകോഡിംഗ് ഓപ്ഷനുകൾ", + "transcoding_encoding_options_description": "എൻകോഡ് ചെയ്ത വീഡിയോകൾക്കായി കോഡെക്കുകൾ, റെസല്യൂഷൻ, ഗുണമേന്മ, മറ്റ് ഓപ്ഷനുകൾ എന്നിവ സജ്ജമാക്കുക", + "transcoding_hardware_acceleration": "ഹാർഡ്‌വെയർ ആക്സിലറേഷൻ", + "transcoding_hardware_acceleration_description": "പരീക്ഷണാടിസ്ഥാനത്തിലുള്ളത്: വേഗതയേറിയ ട്രാൻസ്‌കോഡിംഗ്, എന്നാൽ ഒരേ ബിറ്റ്റേറ്റിൽ ഗുണമേന്മ കുറച്ചേക്കാം", + "transcoding_hardware_decoding": "ഹാർഡ്‌വെയർ ഡീകോഡിംഗ്", + "transcoding_hardware_decoding_setting_description": "എൻകോഡിംഗ് മാത്രം ത്വരിതപ്പെടുത്തുന്നതിന് പകരം എൻഡ്-ടു-എൻഡ് ആക്സിലറേഷൻ പ്രവർത്തനക്ഷമമാക്കുന്നു. എല്ലാ വീഡിയോകളിലും പ്രവർത്തിച്ചേക്കില്ല.", + "transcoding_max_b_frames": "പരമാവധി ബി-ഫ്രെയിമുകൾ", + "transcoding_max_b_frames_description": "ഉയർന്ന മൂല്യങ്ങൾ കംപ്രഷൻ കാര്യക്ഷമത മെച്ചപ്പെടുത്തുന്നു, പക്ഷേ എൻകോഡിംഗ് വേഗത കുറയ്ക്കുന്നു. പഴയ ഉപകരണങ്ങളിലെ ഹാർഡ്‌വെയർ ആക്സിലറേഷനുമായി പൊരുത്തപ്പെടണമെന്നില്ല. 0 ബി-ഫ്രെയിമുകൾ പ്രവർത്തനരഹിതമാക്കുന്നു, അതേസമയം -1 ഈ മൂല്യം യാന്ത്രികമായി സജ്ജീകരിക്കുന്നു.", + "transcoding_max_bitrate": "പരമാവധി ബിറ്റ്റേറ്റ്", + "transcoding_max_bitrate_description": "ഒരു പരമാവധി ബിറ്റ്റേറ്റ് സജ്ജീകരിക്കുന്നത് ഗുണമേന്മയിൽ ചെറിയൊരു വിട്ടുവീഴ്ചയോടെ ഫയൽ വലുപ്പങ്ങൾ കൂടുതൽ പ്രവചനാതീതമാക്കും. 720p-ൽ, സാധാരണ മൂല്യങ്ങൾ VP9 അല്ലെങ്കിൽ HEVC-ക്ക് 2600 kbit/s, അല്ലെങ്കിൽ H.264-ന് 4500 kbit/s ആണ്. 0 ആയി സജ്ജീകരിച്ചാൽ പ്രവർത്തനരഹിതമാകും.", + "transcoding_max_keyframe_interval": "പരമാവധി കീഫ്രെയിം ഇടവേള", + "transcoding_max_keyframe_interval_description": "കീഫ്രെയിമുകൾക്കിടയിലുള്ള പരമാവധി ഫ്രെയിം ദൂരം സജ്ജമാക്കുന്നു. കുറഞ്ഞ മൂല്യങ്ങൾ കംപ്രഷൻ കാര്യക്ഷമത കുറയ്ക്കുന്നു, പക്ഷേ സീക്ക് സമയം മെച്ചപ്പെടുത്തുകയും വേഗതയേറിയ ചലനമുള്ള രംഗങ്ങളിൽ ഗുണമേന്മ മെച്ചപ്പെടുത്തുകയും ചെയ്തേക്കാം. 0 ഈ മൂല്യം യാന്ത്രികമായി സജ്ജീകരിക്കുന്നു.", + "transcoding_optimal_description": "ലക്ഷ്യമിട്ട റെസല്യൂഷനേക്കാൾ ഉയർന്നതോ അംഗീകൃത ഫോർമാറ്റിൽ അല്ലാത്തതോ ആയ വീഡിയോകൾ", + "transcoding_policy": "ട്രാൻസ്‌കോഡ് നയം", + "transcoding_policy_description": "ഒരു വീഡിയോ എപ്പോൾ ട്രാൻസ്‌കോഡ് ചെയ്യണമെന്ന് സജ്ജമാക്കുക", + "transcoding_preferred_hardware_device": "തിരഞ്ഞെടുക്കുന്ന ഹാർഡ്‌വെയർ ഉപകരണം", + "transcoding_preferred_hardware_device_description": "VAAPI, QSV എന്നിവയ്ക്ക് മാത്രം ബാധകം. ഹാർഡ്‌വെയർ ട്രാൻസ്‌കോഡിംഗിനായി ഉപയോഗിക്കുന്ന dri നോഡ് സജ്ജമാക്കുന്നു.", + "transcoding_preset_preset": "പ്രീസെറ്റ് (-preset)", + "transcoding_preset_preset_description": "കംപ്രഷൻ വേഗത. വേഗത കുറഞ്ഞ പ്രീസെറ്റുകൾ ചെറിയ ഫയലുകൾ നിർമ്മിക്കുന്നു, ഒരു നിശ്ചിത ബിറ്റ്റേറ്റ് ലക്ഷ്യമിടുമ്പോൾ ഗുണമേന്മ വർദ്ധിപ്പിക്കുന്നു. 'faster'-നേക്കാൾ ഉയർന്ന വേഗത VP9 അവഗണിക്കുന്നു.", + "transcoding_reference_frames": "റഫറൻസ് ഫ്രെയിമുകൾ", + "transcoding_reference_frames_description": "നൽകിയിരിക്കുന്ന ഒരു ഫ്രെയിം കംപ്രസ് ചെയ്യുമ്പോൾ റഫറൻസ് ചെയ്യേണ്ട ഫ്രെയിമുകളുടെ എണ്ണം. ഉയർന്ന മൂല്യങ്ങൾ കംപ്രഷൻ കാര്യക്ഷമത മെച്ചപ്പെടുത്തുന്നു, പക്ഷേ എൻകോഡിംഗ് വേഗത കുറയ്ക്കുന്നു. 0 ഈ മൂല്യം യാന്ത്രികമായി സജ്ജീകരിക്കുന്നു.", + "transcoding_required_description": "അംഗീകൃത ഫോർമാറ്റിൽ അല്ലാത്ത വീഡിയോകൾ മാത്രം", + "transcoding_settings": "വീഡിയോ ട്രാൻസ്കോഡിംഗ് ക്രമീകരണങ്ങൾ", + "transcoding_settings_description": "ഏതൊക്കെ വീഡിയോകളാണ് ട്രാൻസ്‌കോഡ് ചെയ്യേണ്ടതെന്നും അവ എങ്ങനെ പ്രോസസ്സ് ചെയ്യണമെന്നും കൈകാര്യം ചെയ്യുക", + "transcoding_target_resolution": "ലക്ഷ്യമിടുന്ന റെസല്യൂഷൻ", + "transcoding_target_resolution_description": "ഉയർന്ന റെസല്യൂഷനുകൾക്ക് കൂടുതൽ വിശദാംശങ്ങൾ സംരക്ഷിക്കാൻ കഴിയും, പക്ഷേ എൻകോഡ് ചെയ്യാൻ കൂടുതൽ സമയമെടുക്കും, വലിയ ഫയൽ വലുപ്പമുണ്ടാകും, കൂടാതെ ആപ്പിന്റെ പ്രതികരണശേഷി കുറയ്ക്കുകയും ചെയ്യും.", + "transcoding_temporal_aq": "ടെമ്പറൽ AQ", + "transcoding_temporal_aq_description": "NVENC-ക്ക് മാത്രം ബാധകം. ഉയർന്ന വിശദാംശങ്ങളും കുറഞ്ഞ ചലനവുമുള്ള രംഗങ്ങളുടെ ഗുണമേന്മ വർദ്ധിപ്പിക്കുന്നു. പഴയ ഉപകരണങ്ങളുമായി പൊരുത്തപ്പെടണമെന്നില്ല.", + "transcoding_threads": "ത്രെഡുകൾ", + "transcoding_threads_description": "ഉയർന്ന മൂല്യങ്ങൾ വേഗതയേറിയ എൻകോഡിംഗിലേക്ക് നയിക്കുന്നു, പക്ഷേ സെർവറിന് മറ്റ് ജോലികൾ പ്രോസസ്സ് ചെയ്യാൻ കുറച്ച് ഇടം നൽകുന്നു. ഈ മൂല്യം സിപിയു കോറുകളുടെ എണ്ണത്തേക്കാൾ കൂടുതലാകരുത്. 0 ആയി സജ്ജീകരിച്ചാൽ ഉപയോഗം പരമാവധിയാക്കുന്നു.", + "transcoding_tone_mapping": "ടോൺ-മാപ്പിംഗ്", + "transcoding_tone_mapping_description": "HDR വീഡിയോകൾ SDR-ലേക്ക് പരിവർത്തനം ചെയ്യുമ്പോൾ അവയുടെ രൂപം നിലനിർത്താൻ ശ്രമിക്കുന്നു. ഓരോ അൽഗോരിതവും നിറം, വിശദാംശങ്ങൾ, തെളിച്ചം എന്നിവയ്ക്കായി വ്യത്യസ്ത വിട്ടുവീഴ്ചകൾ ചെയ്യുന്നു. Hable വിശദാംശങ്ങൾ സംരക്ഷിക്കുന്നു, Mobius നിറം സംരക്ഷിക്കുന്നു, Reinhard തെളിച്ചം സംരക്ഷിക്കുന്നു.", + "transcoding_transcode_policy": "ട്രാൻസ്‌കോഡ് നയം", + "transcoding_transcode_policy_description": "ഒരു വീഡിയോ എപ്പോൾ ട്രാൻസ്‌കോഡ് ചെയ്യണം എന്നതിനെക്കുറിച്ചുള്ള നയം. HDR വീഡിയോകൾ എല്ലായ്പ്പോഴും ട്രാൻസ്‌കോഡ് ചെയ്യപ്പെടും (ട്രാൻസ്‌കോഡിംഗ് പ്രവർത്തനരഹിതമാക്കിയിട്ടില്ലെങ്കിൽ).", + "transcoding_two_pass_encoding": "ടു-പാസ് എൻകോഡിംഗ്", + "transcoding_two_pass_encoding_setting_description": "മെച്ചപ്പെട്ട എൻകോഡ് ചെയ്ത വീഡിയോകൾ നിർമ്മിക്കുന്നതിന് രണ്ട് പാസുകളിലായി ട്രാൻസ്‌കോഡ് ചെയ്യുക. പരമാവധി ബിറ്റ്റേറ്റ് പ്രവർത്തനക്ഷമമാക്കുമ്പോൾ (H.264, HEVC എന്നിവയ്‌ക്കൊപ്പം പ്രവർത്തിക്കാൻ ഇത് ആവശ്യമാണ്), ഈ മോഡ് പരമാവധി ബിറ്റ്റേറ്റിനെ അടിസ്ഥാനമാക്കി ഒരു ബിറ്റ്റേറ്റ് ശ്രേണി ഉപയോഗിക്കുകയും CRF അവഗണിക്കുകയും ചെയ്യുന്നു. VP9-നായി, പരമാവധി ബിറ്റ്റേറ്റ് പ്രവർത്തനരഹിതമാക്കിയിട്ടുണ്ടെങ്കിൽ CRF ഉപയോഗിക്കാം.", + "transcoding_video_codec": "വീഡിയോ കോഡെക്", + "transcoding_video_codec_description": "VP9-ന് ഉയർന്ന കാര്യക്ഷമതയും വെബ് അനുയോജ്യതയുമുണ്ട്, പക്ഷേ ട്രാൻസ്‌കോഡ് ചെയ്യാൻ കൂടുതൽ സമയമെടുക്കും. HEVC സമാനമായി പ്രവർത്തിക്കുന്നു, പക്ഷേ വെബ് അനുയോജ്യത കുറവാണ്. H.264 വ്യാപകമായി അനുയോജ്യവും വേഗത്തിൽ ട്രാൻസ്‌കോഡ് ചെയ്യാൻ കഴിയുന്നതുമാണ്, പക്ഷേ വളരെ വലിയ ഫയലുകൾ നിർമ്മിക്കുന്നു. AV1 ഏറ്റവും കാര്യക്ഷമമായ കോഡെക്കാണ്, പക്ഷേ പഴയ ഉപകരണങ്ങളിൽ ഇതിന് പിന്തുണയില്ല.", + "trash_enabled_description": "ട്രാഷ് ഫീച്ചറുകൾ പ്രവർത്തനക്ഷമമാക്കുക", + "trash_number_of_days": "ദിവസങ്ങളുടെ എണ്ണം", + "trash_number_of_days_description": "അസറ്റുകൾ ശാശ്വതമായി നീക്കം ചെയ്യുന്നതിനുമുമ്പ് ട്രാഷിൽ സൂക്ഷിക്കേണ്ട ദിവസങ്ങളുടെ എണ്ണം", + "trash_settings": "ട്രാഷ് ക്രമീകരണങ്ങൾ", + "trash_settings_description": "ട്രാഷ് ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക", + "unlink_all_oauth_accounts": "എല്ലാ OAuth അക്കൗണ്ടുകളും അൺലിങ്ക് ചെയ്യുക", + "unlink_all_oauth_accounts_description": "ഒരു പുതിയ ദാതാവിലേക്ക് മാറുന്നതിന് മുമ്പ് എല്ലാ OAuth അക്കൗണ്ടുകളും അൺലിങ്ക് ചെയ്യാൻ ഓർമ്മിക്കുക.", + "unlink_all_oauth_accounts_prompt": "എല്ലാ OAuth അക്കൗണ്ടുകളും അൺലിങ്ക് ചെയ്യണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ? ഇത് ഓരോ ഉപയോക്താവിന്റെയും OAuth ഐഡി റീസെറ്റ് ചെയ്യും, ഇത് പഴയപടിയാക്കാൻ കഴിയില്ല.", + "user_cleanup_job": "ഉപയോക്തൃ ക്ലീനപ്പ്", + "user_delete_delay": "{user}-ന്റെ അക്കൗണ്ടും അസറ്റുകളും {delay, plural, one {# ദിവസത്തിനുള്ളിൽ} other {# ദിവസങ്ങൾക്കുള്ളിൽ}} ശാശ്വതമായി ഇല്ലാതാക്കാൻ ഷെഡ്യൂൾ ചെയ്യും.", + "user_delete_delay_settings": "ഇല്ലാതാക്കാനുള്ള കാലതാമസം", + "user_delete_delay_settings_description": "ഒരു ഉപയോക്താവിന്റെ അക്കൗണ്ടും അസറ്റുകളും ശാശ്വതമായി ഇല്ലാതാക്കാൻ നീക്കം ചെയ്തതിന് ശേഷമുള്ള ദിവസങ്ങളുടെ എണ്ണം. ഇല്ലാതാക്കാൻ തയ്യാറായ ഉപയോക്താക്കളെ പരിശോധിക്കാൻ ഉപയോക്താവിനെ ഇല്ലാതാക്കാനുള്ള ജോലി അർദ്ധരാത്രിയിൽ പ്രവർത്തിക്കുന്നു. ഈ ക്രമീകരണത്തിലെ മാറ്റങ്ങൾ അടുത്ത എക്സിക്യൂഷനിൽ വിലയിരുത്തപ്പെടും.", + "user_delete_immediately": "{user}-ന്റെ അക്കൗണ്ടും അസറ്റുകളും ഉടനടി ശാശ്വതമായി ഇല്ലാതാക്കുന്നതിനായി ക്യൂ ചെയ്യും.", + "user_delete_immediately_checkbox": "ഉപയോക്താവിനെയും അസറ്റുകളെയും ഉടനടി ഇല്ലാതാക്കാൻ ക്യൂ ചെയ്യുക", + "user_details": "ഉപയോക്തൃ വിശദാംശങ്ങൾ", + "user_management": "ഉപയോക്തൃ മാനേജ്മെന്റ്", + "user_password_has_been_reset": "ഉപയോക്താവിന്റെ പാസ്‌വേഡ് റീസെറ്റ് ചെയ്തു:", + "user_password_reset_description": "ദയവായി ഉപയോക്താവിന് താൽക്കാലിക പാസ്‌വേഡ് നൽകുക, അടുത്ത ലോഗിൻ ചെയ്യുമ്പോൾ പാസ്‌വേഡ് മാറ്റേണ്ടിവരുമെന്ന് അവരെ അറിയിക്കുക.", + "user_restore_description": "{user}-ന്റെ അക്കൗണ്ട് പുനഃസ്ഥാപിക്കും.", + "user_restore_scheduled_removal": "ഉപയോക്താവിനെ പുനഃസ്ഥാപിക്കുക - {date, date, long}-ന് നീക്കംചെയ്യാൻ ഷെഡ്യൂൾ ചെയ്‌തിരിക്കുന്നു", + "user_settings": "ഉപയോക്താവിന്റെ ക്രമീകരണങ്ങൾ", + "user_settings_description": "ഉപയോക്തൃ ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക", + "user_successfully_removed": "{email} എന്ന ഉപയോക്താവിനെ വിജയകരമായി നീക്കംചെയ്തു.", + "version_check_enabled_description": "പതിപ്പ് പരിശോധന പ്രവർത്തനക്ഷമമാക്കുക", + "version_check_implications": "പതിപ്പ് പരിശോധന ഫീച്ചർ github.com-മായി ആനുകാലിക ആശയവിനിമയത്തെ ആശ്രയിച്ചിരിക്കുന്നു", + "version_check_settings": "പതിപ്പ് പരിശോധന", + "version_check_settings_description": "പുതിയ പതിപ്പിന്റെ അറിയിപ്പ് പ്രവർത്തനക്ഷമമാക്കുക/പ്രവർത്തനരഹിതമാക്കുക", + "video_conversion_job": "വീഡിയോകൾ ട്രാൻസ്‌കോഡ് ചെയ്യുക", + "video_conversion_job_description": "ബ്രൗസറുകളുമായും ഉപകരണങ്ങളുമായും കൂടുതൽ അനുയോജ്യതയ്ക്കായി വീഡിയോകൾ ട്രാൻസ്‌കോഡ് ചെയ്യുക" }, + "admin_email": "അഡ്മിൻ ഇമെയിൽ", + "admin_password": "അഡ്മിൻ പാസ്‌വേഡ്", + "administration": "അഡ്മിനിസ്ട്രേഷൻ", + "advanced": "വിപുലമായത്", + "advanced_settings_enable_alternate_media_filter_subtitle": "ഇതര മാനദണ്ഡങ്ങളെ അടിസ്ഥാനമാക്കി സിങ്ക് സമയത്ത് മീഡിയ ഫിൽട്ടർ ചെയ്യാൻ ഈ ഓപ്ഷൻ ഉപയോഗിക്കുക. എല്ലാ ആൽബങ്ങളും കണ്ടെത്തുന്നതിൽ ആപ്പിന് പ്രശ്നങ്ങളുണ്ടെങ്കിൽ മാത്രം ഇത് പരീക്ഷിക്കുക.", + "advanced_settings_enable_alternate_media_filter_title": "[പരീക്ഷണാടിസ്ഥാനത്തിൽ] ഇതര ഉപകരണ ആൽബം സിങ്ക് ഫിൽട്ടർ ഉപയോഗിക്കുക", + "advanced_settings_log_level_title": "ലോഗ് ലെവൽ: {level}", + "advanced_settings_prefer_remote_subtitle": "ചില ഉപകരണങ്ങളിൽ പ്രാദേശിക അസറ്റുകളിൽ നിന്ന് തംബ്നെയിലുകൾ ലോഡുചെയ്യാൻ വളരെ വേഗത കുറവാണ്. പകരം റിമോട്ട് ചിത്രങ്ങൾ ലോഡുചെയ്യാൻ ഈ ക്രമീകരണം സജീവമാക്കുക.", + "advanced_settings_prefer_remote_title": "റിമോട്ട് ചിത്രങ്ങൾക്ക് മുൻഗണന നൽകുക", + "advanced_settings_proxy_headers_subtitle": "ഓരോ നെറ്റ്‌വർക്ക് അഭ്യർത്ഥനയ്‌ക്കൊപ്പവും Immich അയയ്‌ക്കേണ്ട പ്രോക്സി ഹെഡറുകൾ നിർവചിക്കുക", + "advanced_settings_proxy_headers_title": "പ്രോക്സി ഹെഡറുകൾ", + "advanced_settings_readonly_mode_subtitle": "ഫോട്ടോകൾ മാത്രം കാണാൻ കഴിയുന്ന റീഡ്-ഓൺലി മോഡ് പ്രവർത്തനക്ഷമമാക്കുന്നു. ഒന്നിലധികം ചിത്രങ്ങൾ തിരഞ്ഞെടുക്കൽ, പങ്കിടൽ, കാസ്റ്റിംഗ്, ഇല്ലാതാക്കൽ എന്നിവയെല്ലാം പ്രവർത്തനരഹിതമാകും. പ്രധാന സ്ക്രീനിലെ ഉപയോക്തൃ അവതാർ വഴി റീഡ്-ഓൺലി പ്രവർത്തനക്ഷമമാക്കുക/പ്രവർത്തനരഹിതമാക്കുക.", + "advanced_settings_readonly_mode_title": "റീഡ്-ഓൺലി മോഡ്", + "advanced_settings_self_signed_ssl_subtitle": "സെർവർ എൻഡ്‌പോയിന്റിനായുള്ള SSL സർട്ടിഫിക്കറ്റ് പരിശോധന ഒഴിവാക്കുന്നു. സ്വയം ഒപ്പിട്ട സർട്ടിഫിക്കറ്റുകൾക്ക് ആവശ്യമാണ്.", + "advanced_settings_self_signed_ssl_title": "സ്വയം ഒപ്പിട്ട SSL സർട്ടിഫിക്കറ്റുകൾ അനുവദിക്കുക", + "advanced_settings_sync_remote_deletions_subtitle": "വെബിൽ ആ പ്രവർത്തനം നടത്തുമ്പോൾ ഈ ഉപകരണത്തിലെ ഒരു അസറ്റ് യാന്ത്രികമായി ഇല്ലാതാക്കുകയോ പുനഃസ്ഥാപിക്കുകയോ ചെയ്യുക", + "advanced_settings_sync_remote_deletions_title": "റിമോട്ട് ഇല്ലാതാക്കലുകൾ സിങ്ക് ചെയ്യുക [പരീക്ഷണാടിസ്ഥാനത്തിൽ]", + "advanced_settings_tile_subtitle": "വിപുലമായ ഉപയോക്തൃ ക്രമീകരണങ്ങൾ", + "advanced_settings_troubleshooting_subtitle": "ട്രബിൾഷൂട്ടിംഗിനായി അധിക ഫീച്ചറുകൾ പ്രവർത്തനക്ഷമമാക്കുക", + "advanced_settings_troubleshooting_title": "ട്രബിൾഷൂട്ടിംഗ്", + "age_months": "പ്രായം {months, plural, one {# മാസം} other {# മാസം}}", + "age_year_months": "പ്രായം 1 വർഷം, {months, plural, one {# മാസം} other {# മാസം}}", + "age_years": "{years, plural, other {പ്രായം #}}", + "album_added": "ആൽബം ചേർത്തു", + "album_added_notification_setting_description": "നിങ്ങളെ ഒരു പങ്കിട്ട ആൽബത്തിലേക്ക് ചേർക്കുമ്പോൾ ഒരു ഇമെയിൽ അറിയിപ്പ് സ്വീകരിക്കുക", + "album_cover_updated": "ആൽബം കവർ അപ്ഡേറ്റ് ചെയ്തു", + "album_delete_confirmation": "നിങ്ങൾക്ക് {album} ആൽബം ഇല്ലാതാക്കണമെന്ന് ഉറപ്പാണോ?", + "album_delete_confirmation_description": "ഈ ആൽബം പങ്കിട്ടതാണെങ്കിൽ, മറ്റ് ഉപയോക്താക്കൾക്ക് ഇനി ഇത് ആക്‌സസ് ചെയ്യാൻ കഴിയില്ല.", + "album_deleted": "ആൽബം ഇല്ലാതാക്കി", + "album_info_card_backup_album_excluded": "ഒഴിവാക്കി", + "album_info_card_backup_album_included": "ഉൾപ്പെടുത്തി", + "album_info_updated": "ആൽബം വിവരങ്ങൾ അപ്ഡേറ്റ് ചെയ്തു", + "album_leave": "ആൽബം വിടുകയാണോ?", + "album_leave_confirmation": "{album} വിട്ടുപോകണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?", + "album_name": "ആൽബത്തിന്റെ പേര്", + "album_options": "ആൽബം ഓപ്ഷനുകൾ", + "album_remove_user": "ഉപയോക്താവിനെ നീക്കം ചെയ്യണോ?", + "album_remove_user_confirmation": "{user}-നെ നീക്കം ചെയ്യണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?", + "album_search_not_found": "നിങ്ങളുടെ തിരയലുമായി പൊരുത്തപ്പെടുന്ന ആൽബങ്ങളൊന്നും കണ്ടെത്തിയില്ല", + "album_share_no_users": "നിങ്ങൾ ഈ ആൽബം എല്ലാ ഉപയോക്താക്കളുമായും പങ്കിട്ടു, അല്ലെങ്കിൽ നിങ്ങൾക്ക് പങ്കിടാൻ ഉപയോക്താക്കളാരും ഇല്ലെന്നു തോന്നുന്നു.", + "album_summary": "ആൽബത്തിന്റെ സംഗ്രഹം", + "album_updated": "ആൽബം അപ്ഡേറ്റ് ചെയ്തു", + "album_updated_setting_description": "ഒരു പങ്കിട്ട ആൽബത്തിൽ പുതിയ അസറ്റുകൾ ഉണ്ടാകുമ്പോൾ ഒരു ഇമെയിൽ അറിയിപ്പ് സ്വീകരിക്കുക", + "album_user_left": "{album} വിട്ടുപോയി", + "album_user_removed": "{user}-നെ നീക്കം ചെയ്തു", + "album_viewer_appbar_delete_confirm": "നിങ്ങളുടെ അക്കൗണ്ടിൽ നിന്ന് ഈ ആൽബം ഇല്ലാതാക്കണമെന്ന് ഉറപ്പാണോ?", + "album_viewer_appbar_share_err_delete": "ആൽബം ഇല്ലാതാക്കുന്നതിൽ പരാജയപ്പെട്ടു", + "album_viewer_appbar_share_err_leave": "ആൽബം വിടുന്നതിൽ പരാജയപ്പെട്ടു", + "album_viewer_appbar_share_err_remove": "ആൽബത്തിൽ നിന്ന് അസറ്റുകൾ നീക്കം ചെയ്യുന്നതിൽ പ്രശ്നങ്ങളുണ്ട്", + "album_viewer_appbar_share_err_title": "ആൽബത്തിന്റെ ശീർഷകം മാറ്റുന്നതിൽ പരാജയപ്പെട്ടു", + "album_viewer_appbar_share_leave": "ആൽബം വിടുക", + "album_viewer_appbar_share_to": "ഇതിലേക്ക് പങ്കിടുക", + "album_viewer_page_share_add_users": "ഉപയോക്താക്കളെ ചേർക്കുക", + "album_with_link_access": "ലിങ്കുള്ള ആർക്കും ഈ ആൽബത്തിലെ ഫോട്ടോകളും ആളുകളെയും കാണാൻ അനുവദിക്കുക.", + "albums": "ആൽബങ്ങൾ", + "albums_count": "{count, plural, one {{count, number} ആൽബം} other {{count, number} ആൽബങ്ങൾ}}", + "albums_default_sort_order": "ഡിഫോൾട്ട് ആൽബം സോർട്ട് ഓർഡർ", + "albums_default_sort_order_description": "പുതിയ ആൽബങ്ങൾ സൃഷ്ടിക്കുമ്പോൾ പ്രാരംഭ അസറ്റ് സോർട്ട് ഓർഡർ.", + "albums_feature_description": "മറ്റ് ഉപയോക്താക്കളുമായി പങ്കിടാൻ കഴിയുന്ന അസറ്റുകളുടെ ശേഖരം.", + "albums_on_device_count": "ഉപകരണത്തിലെ ആൽബങ്ങൾ ({count})", + "all": "എല്ലാം", + "all_albums": "എല്ലാ ആൽബങ്ങളും", + "all_people": "എല്ലാ ആളുകളും", + "all_videos": "എല്ലാ വീഡിയോകളും", + "allow_dark_mode": "ഡാർക്ക് മോഡ് അനുവദിക്കുക", + "allow_edits": "എഡിറ്റുകൾ അനുവദിക്കുക", + "allow_public_user_to_download": "പൊതു ഉപയോക്താവിനെ ഡൗൺലോഡ് ചെയ്യാൻ അനുവദിക്കുക", + "allow_public_user_to_upload": "പൊതു ഉപയോക്താവിനെ അപ്‌ലോഡ് ചെയ്യാൻ അനുവദിക്കുക", + "alt_text_qr_code": "QR കോഡ് ചിത്രം", + "anti_clockwise": "അപ്രദക്ഷിണമായി", + "api_key": "API കീ", + "api_key_description": "ഈ മൂല്യം ഒരു തവണ മാത്രമേ കാണിക്കൂ. വിൻഡോ അടയ്ക്കുന്നതിന് മുമ്പ് ഇത് പകർത്തുന്നത് ഉറപ്പാക്കുക.", + "api_key_empty": "നിങ്ങളുടെ API കീയുടെ പേര് ശൂന്യമാകരുത്", + "api_keys": "API കീകൾ", + "app_bar_signout_dialog_content": "സൈൻ ഔട്ട് ചെയ്യണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?", + "app_bar_signout_dialog_ok": "അതെ", + "app_bar_signout_dialog_title": "സൈൻ ഔട്ട്", + "app_settings": "ആപ്പ് ക്രമീകരണങ്ങൾ", + "appears_in": "ഇതിൽ കാണപ്പെടുന്നു", + "apply_count": "പ്രയോഗിക്കുക ({count, number})", + "archive": "ആർക്കൈവ്", + "archive_action_prompt": "{count} എണ്ണം ആർക്കൈവിലേക്ക് ചേർത്തു", + "archive_or_unarchive_photo": "ഫോട്ടോ ആർക്കൈവ് ചെയ്യുക അല്ലെങ്കിൽ അൺആർക്കൈവ് ചെയ്യുക", + "archive_page_no_archived_assets": "ആർക്കൈവുചെയ്‌ത അസറ്റുകളൊന്നും കണ്ടെത്തിയില്ല", + "archive_page_title": "ആർക്കൈവ് ({count})", + "archive_size": "ആർക്കൈവ് വലുപ്പം", + "archive_size_description": "ഡൗൺലോഡുകൾക്കായി ആർക്കൈവ് വലുപ്പം കോൺഫിഗർ ചെയ്യുക (GiB-യിൽ)", + "archived": "ആർക്കൈവുചെയ്‌തു", + "archived_count": "{count, plural, other {ആർക്കൈവുചെയ്‌തവ #}}", + "are_these_the_same_person": "ഇവർ ഒരേ വ്യക്തിയാണോ?", + "are_you_sure_to_do_this": "ഇത് ചെയ്യണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?", + "asset_action_delete_err_read_only": "വായിക്കാൻ മാത്രമുള്ള അസറ്റ്(കൾ) ഇല്ലാതാക്കാൻ കഴിയില്ല, ഒഴിവാക്കുന്നു", + "asset_action_share_err_offline": "ഓഫ്‌ലൈൻ അസറ്റ്(കൾ) ലഭ്യമാക്കാൻ കഴിയില്ല, ഒഴിവാക്കുന്നു", + "asset_added_to_album": "ആൽബത്തിലേക്ക് ചേർത്തു", + "asset_adding_to_album": "ആൽബത്തിലേക്ക് ചേർത്തുകൊണ്ടിരിക്കുന്നു…", + "asset_description_updated": "അസറ്റിന്റെ വിവരണം അപ്ഡേറ്റ് ചെയ്തു", + "asset_filename_is_offline": "{filename} എന്ന അസറ്റ് ഓഫ്‌ലൈനാണ്", + "asset_has_unassigned_faces": "അസറ്റിൽ അസൈൻ ചെയ്യാത്ത മുഖങ്ങളുണ്ട്", + "asset_hashing": "ഹാഷിംഗ്…", + "asset_list_group_by_sub_title": "ഇതനുസരിച്ച് ഗ്രൂപ്പ് ചെയ്യുക", + "asset_list_layout_settings_dynamic_layout_title": "ഡൈനാമിക് ലേഔട്ട്", + "asset_list_layout_settings_group_automatically": "യാന്ത്രികം", + "asset_list_layout_settings_group_by": "അസറ്റുകളെ ഇതനുസരിച്ച് ഗ്രൂപ്പ് ചെയ്യുക", + "asset_list_layout_settings_group_by_month_day": "മാസം + ദിവസം", + "asset_list_layout_sub_title": "ലേഔട്ട്", + "asset_list_settings_subtitle": "ഫോട്ടോ ഗ്രിഡ് ലേഔട്ട് ക്രമീകരണങ്ങൾ", + "asset_list_settings_title": "ഫോട്ടോ ഗ്രിഡ്", + "asset_offline": "അസറ്റ് ഓഫ്‌ലൈൻ", + "asset_offline_description": "ഈ എക്സ്റ്റേണൽ അസറ്റ് ഇപ്പോൾ ഡിസ്കിൽ ലഭ്യമല്ല. സഹായത്തിനായി നിങ്ങളുടെ Immich അഡ്മിനിസ്ട്രേറ്ററുമായി ബന്ധപ്പെടുക.", + "asset_restored_successfully": "അസറ്റ് വിജയകരമായി പുനഃസ്ഥാപിച്ചു", + "asset_skipped": "ഒഴിവാക്കി", + "asset_skipped_in_trash": "ട്രാഷിൽ", + "asset_trashed": "അസറ്റ് ട്രാഷ് ചെയ്തു", + "asset_troubleshoot": "അസറ്റ് ട്രബിൾഷൂട്ട്", + "asset_uploaded": "അപ്‌ലോഡ് ചെയ്തു", + "asset_uploading": "അപ്‌ലോഡ് ചെയ്യുന്നു…", + "asset_viewer_settings_subtitle": "നിങ്ങളുടെ ഗാലറി വ്യൂവർ ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക", + "asset_viewer_settings_title": "അസറ്റ് വ്യൂവർ", + "assets": "അസറ്റുകൾ", + "assets_added_count": "{count, plural, one {# അസറ്റ് ചേർത്തു} other {# അസറ്റുകൾ ചേർത്തു}}", + "assets_added_to_album_count": "ആൽബത്തിലേക്ക് {count, plural, one {# അസറ്റ് ചേർത്തു} other {# അസറ്റുകൾ ചേർത്തു}}", + "assets_added_to_albums_count": "{albumTotal, plural, one {# ആൽബത്തിലേക്ക്} other {# ആൽബങ്ങളിലേക്ക്}} {assetTotal, plural, one {# അസറ്റ് ചേർത്തു} other {# അസറ്റുകൾ ചേർത്തു}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {അസറ്റ്} other {അസറ്റുകൾ}} ആൽബത്തിലേക്ക് ചേർക്കാൻ കഴിയില്ല", + "assets_cannot_be_added_to_albums": "{count, plural, one {അസറ്റ്} other {അസറ്റുകൾ}} ഒരു ആൽബത്തിലേക്കും ചേർക്കാൻ കഴിയില്ല", + "assets_count": "{count, plural, one {# അസറ്റ്} other {# അസറ്റുകൾ}}", + "assets_deleted_permanently": "{count} അസറ്റ്(കൾ) ശാശ്വതമായി ഇല്ലാതാക്കി", + "assets_deleted_permanently_from_server": "{count} അസറ്റ്(കൾ) Immich സെർവറിൽ നിന്ന് ശാശ്വതമായി ഇല്ലാതാക്കി", + "assets_downloaded_failed": "{count, plural, one {# ഫയൽ ഡൗൺലോഡ് ചെയ്തു - {error} ഫയൽ പരാജയപ്പെട്ടു} other {# ഫയലുകൾ ഡൗൺലോഡ് ചെയ്തു - {error} ഫയലുകൾ പരാജയപ്പെട്ടു}}", + "assets_downloaded_successfully": "{count, plural, one {# ഫയൽ വിജയകരമായി ഡൗൺലോഡ് ചെയ്തു} other {# ഫയലുകൾ വിജയകരമായി ഡൗൺലോഡ് ചെയ്തു}}", + "assets_moved_to_trash_count": "{count, plural, one {# അസറ്റ് ട്രാഷിലേക്ക് മാറ്റി} other {# അസറ്റുകൾ ട്രാഷിലേക്ക് മാറ്റി}}", + "assets_permanently_deleted_count": "{count, plural, one {# അസറ്റ് ശാശ്വതമായി ഇല്ലാതാക്കി} other {# അസറ്റുകൾ ശാശ്വതമായി ഇല്ലാതാക്കി}}", + "assets_removed_count": "{count, plural, one {# അസറ്റ് നീക്കം ചെയ്തു} other {# അസറ്റുകൾ നീക്കം ചെയ്തു}}", + "assets_removed_permanently_from_device": "{count} അസറ്റ്(കൾ) നിങ്ങളുടെ ഉപകരണത്തിൽ നിന്ന് ശാശ്വതമായി നീക്കം ചെയ്തു", + "assets_restore_confirmation": "ട്രാഷ് ചെയ്ത എല്ലാ അസറ്റുകളും പുനഃസ്ഥാപിക്കണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ? ഈ പ്രവർത്തനം പഴയപടിയാക്കാൻ കഴിയില്ല! ഓഫ്‌ലൈൻ അസറ്റുകളൊന്നും ഈ രീതിയിൽ പുനഃസ്ഥാപിക്കാൻ കഴിയില്ലെന്ന കാര്യം ശ്രദ്ധിക്കുക.", + "assets_restored_count": "{count, plural, one {# അസറ്റ് പുനഃസ്ഥാപിച്ചു} other {# അസറ്റുകൾ പുനഃസ്ഥാപിച്ചു}}", + "assets_restored_successfully": "{count} അസറ്റ്(കൾ) വിജയകരമായി പുനഃസ്ഥാപിച്ചു", + "assets_trashed": "{count} അസറ്റ്(കൾ) ട്രാഷ് ചെയ്തു", + "assets_trashed_count": "{count, plural, one {# അസറ്റ് ട്രാഷ് ചെയ്തു} other {# അസറ്റുകൾ ട്രാഷ് ചെയ്തു}}", + "assets_trashed_from_server": "{count} അസറ്റ്(കൾ) Immich സെർവറിൽ നിന്ന് ട്രാഷ് ചെയ്തു", + "assets_were_part_of_album_count": "{count, plural, one {അസറ്റ് ഇതിനകം} other {അസറ്റുകൾ ഇതിനകം}} ആൽബത്തിന്റെ ഭാഗമായിരുന്നു", + "assets_were_part_of_albums_count": "{count, plural, one {അസറ്റ് ഇതിനകം} other {അസറ്റുകൾ ഇതിനകം}} ആൽബങ്ങളുടെ ഭാഗമായിരുന്നു", + "authorized_devices": "അംഗീകൃത ഉപകരണങ്ങൾ", + "automatic_endpoint_switching_subtitle": "ലഭ്യമാകുമ്പോൾ നിശ്ചിത വൈ-ഫൈ വഴി പ്രാദേശികമായി കണക്റ്റുചെയ്യുക, മറ്റ് സ്ഥലങ്ങളിൽ ഇതര കണക്ഷനുകൾ ഉപയോഗിക്കുക", + "automatic_endpoint_switching_title": "യാന്ത്രിക URL സ്വിച്ചിംഗ്", + "autoplay_slideshow": "സ്ലൈഡ്‌ഷോ യാന്ത്രികമായി പ്ലേ ചെയ്യുക", + "back": "തിരികെ", + "back_close_deselect": "പുറകോട്ട്, അടയ്ക്കുക, അല്ലെങ്കിൽ തിരഞ്ഞെടുത്തത് മാറ്റുക", + "background_backup_running_error": "പശ്ചാത്തല ബാക്കപ്പ് ഇപ്പോൾ പ്രവർത്തിക്കുന്നു, മാനുവൽ ബാക്കപ്പ് ആരംഭിക്കാൻ കഴിയില്ല", + "background_location_permission": "പശ്ചാത്തല ലൊക്കേഷൻ അനുമതി", + "background_location_permission_content": "പശ്ചാത്തലത്തിൽ പ്രവർത്തിക്കുമ്പോൾ നെറ്റ്‌വർക്കുകൾ മാറുന്നതിന്, Immich-ന് എപ്പോഴും കൃത്യമായ ലൊക്കേഷൻ ആക്‌സസ് ഉണ്ടായിരിക്കണം, അതുവഴി ആപ്പിന് വൈ-ഫൈ നെറ്റ്‌വർക്കിന്റെ പേര് വായിക്കാൻ കഴിയും", + "background_options": "പശ്ചാത്തല ഓപ്ഷനുകൾ", + "backup": "ബാക്കപ്പ്", + "backup_album_selection_page_albums_device": "ഉപകരണത്തിലെ ആൽബങ്ങൾ ({count})", + "backup_album_selection_page_albums_tap": "ഉൾപ്പെടുത്താൻ ടാപ്പുചെയ്യുക, ഒഴിവാക്കാൻ ഡബിൾ ടാപ്പുചെയ്യുക", + "backup_album_selection_page_assets_scatter": "അസറ്റുകൾ ഒന്നിലധികം ആൽബങ്ങളിലായി വ്യാപിച്ചുകിടക്കാം. അതിനാൽ, ബാക്കപ്പ് പ്രക്രിയയിൽ ആൽബങ്ങൾ ഉൾപ്പെടുത്തുകയോ ഒഴിവാക്കുകയോ ചെയ്യാം.", + "backup_album_selection_page_select_albums": "ആൽബങ്ങൾ തിരഞ്ഞെടുക്കുക", + "backup_album_selection_page_selection_info": "തിരഞ്ഞെടുക്കൽ വിവരം", + "backup_album_selection_page_total_assets": "ആകെ സവിശേഷമായ അസറ്റുകൾ", + "backup_albums_sync": "ബാക്കപ്പ് ആൽബം സിൻക്രൊണൈസേഷൻ", + "backup_all": "എല്ലാം ബാക്കപ്പ് ചെയ്യുക", + "backup_background_service_backup_failed_message": "അസറ്റുകൾ ബാക്കപ്പ് ചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു. വീണ്ടും ശ്രമിക്കുന്നു…", + "backup_background_service_connection_failed_message": "സെർവറിലേക്ക് കണക്റ്റുചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു. വീണ്ടും ശ്രമിക്കുന്നു…", + "backup_background_service_current_upload_notification": "{filename} അപ്‌ലോഡ് ചെയ്യുന്നു", + "backup_background_service_default_notification": "പുതിയ അസറ്റുകൾക്കായി പരിശോധിക്കുന്നു…", + "backup_background_service_error_title": "ബാക്കപ്പ് പിശക്", + "backup_background_service_in_progress_notification": "നിങ്ങളുടെ അസറ്റുകൾ ബാക്കപ്പ് ചെയ്യുന്നു…", + "backup_background_service_upload_failure_notification": "{filename} അപ്‌ലോഡ് ചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു", + "backup_controller_page_albums": "ബാക്കപ്പ് ആൽബങ്ങൾ", + "backup_controller_page_background_app_refresh_disabled_content": "പശ്ചാത്തല ബാക്കപ്പ് ഉപയോഗിക്കുന്നതിന്, ക്രമീകരണങ്ങൾ > പൊതുവായത് > പശ്ചാത്തല ആപ്പ് റിഫ്രഷ് എന്നതിൽ പശ്ചാത്തല ആപ്പ് റിഫ്രഷ് പ്രവർത്തനക്ഷമമാക്കുക.", + "backup_controller_page_background_app_refresh_disabled_title": "പശ്ചാത്തല ആപ്പ് റിഫ്രഷ് പ്രവർത്തനരഹിതമാക്കി", + "backup_controller_page_background_app_refresh_enable_button_text": "ക്രമീകരണങ്ങളിലേക്ക് പോകുക", + "backup_controller_page_background_battery_info_link": "എങ്ങനെയെന്ന് കാണിക്കുക", + "backup_controller_page_background_battery_info_message": "മികച്ച പശ്ചാത്തല ബാക്കപ്പ് അനുഭവത്തിനായി, Immich-ന്റെ പശ്ചാത്തല പ്രവർത്തനം നിയന്ത്രിക്കുന്ന ഏതെങ്കിലും ബാറ്ററി ഒപ്റ്റിമൈസേഷനുകൾ പ്രവർത്തനരഹിതമാക്കുക.\n\nഇത് ഉപകരണത്തെ ആശ്രയിച്ചിരിക്കുന്നതിനാൽ, നിങ്ങളുടെ ഉപകരണ നിർമ്മാതാവിനുള്ള ആവശ്യമായ വിവരങ്ങൾ ദയവായി കണ്ടെത്തുക.", + "backup_controller_page_background_battery_info_ok": "ശരി", + "backup_controller_page_background_battery_info_title": "ബാറ്ററി ഒപ്റ്റിമൈസേഷനുകൾ", + "backup_controller_page_background_charging": "ചാർജ് ചെയ്യുമ്പോൾ മാത്രം", + "backup_controller_page_background_configure_error": "പശ്ചാത്തല സേവനം കോൺഫിഗർ ചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു", + "backup_controller_page_background_delay": "പുതിയ അസറ്റുകളുടെ ബാക്കപ്പ് വൈകിപ്പിക്കുക: {duration}", + "backup_controller_page_background_description": "ആപ്പ് തുറക്കാതെ തന്നെ പുതിയ അസറ്റുകൾ യാന്ത്രികമായി ബാക്കപ്പ് ചെയ്യാൻ പശ്ചാത്തല സേവനം ഓണാക്കുക", + "backup_controller_page_background_is_off": "യാന്ത്രിക പശ്ചാത്തല ബാക്കപ്പ് ഓഫാണ്", + "backup_controller_page_background_is_on": "യാന്ത്രിക പശ്ചാത്തല ബാക്കപ്പ് ഓണാണ്", + "backup_controller_page_background_turn_off": "പശ്ചാത്തല സേവനം ഓഫാക്കുക", + "backup_controller_page_background_turn_on": "പശ്ചാത്തല സേവനം ഓണാക്കുക", + "backup_controller_page_background_wifi": "വൈ-ഫൈയിൽ മാത്രം", + "backup_controller_page_backup": "ബാക്കപ്പ്", + "backup_controller_page_backup_selected": "തിരഞ്ഞെടുത്തവ: ", + "backup_controller_page_backup_sub": "ബാക്കപ്പ് ചെയ്ത ഫോട്ടോകളും വീഡിയോകളും", + "backup_controller_page_created": "സൃഷ്ടിച്ചത്: {date}", + "backup_controller_page_desc_backup": "ആപ്പ് തുറക്കുമ്പോൾ പുതിയ അസറ്റുകൾ യാന്ത്രികമായി സെർവറിലേക്ക് അപ്‌ലോഡ് ചെയ്യാൻ ഫോർഗ്രൗണ്ട് ബാക്കപ്പ് ഓണാക്കുക.", + "backup_controller_page_excluded": "ഒഴിവാക്കിയവ: ", + "backup_controller_page_failed": "പരാജയപ്പെട്ടു ({count})", + "backup_controller_page_filename": "ഫയലിന്റെ പേര്: {filename}[{size}]", + "backup_controller_page_id": "ഐഡി: {id}", + "backup_controller_page_info": "ബാക്കപ്പ് വിവരങ്ങൾ", + "backup_controller_page_none_selected": "ഒന്നും തിരഞ്ഞെടുത്തിട്ടില്ല", + "backup_controller_page_remainder": "ശേഷിക്കുന്നത്", + "backup_controller_page_remainder_sub": "തിരഞ്ഞെടുത്തവയിൽ നിന്ന് ബാക്കപ്പ് ചെയ്യാനുള്ള ശേഷിക്കുന്ന ഫോട്ടോകളും വീഡിയോകളും", + "backup_controller_page_server_storage": "സെർവർ സ്റ്റോറേജ്", + "backup_controller_page_start_backup": "ബാക്കപ്പ് ആരംഭിക്കുക", + "backup_controller_page_status_off": "യാന്ത്രിക ഫോർഗ്രൗണ്ട് ബാക്കപ്പ് ഓഫാണ്", + "backup_controller_page_status_on": "യാന്ത്രിക ഫോർഗ്രൗണ്ട് ബാക്കപ്പ് ഓണാണ്", + "backup_controller_page_storage_format": "{total}-ൽ {used} ഉപയോഗിച്ചു", + "backup_controller_page_to_backup": "ബാക്കപ്പ് ചെയ്യേണ്ട ആൽബങ്ങൾ", + "backup_controller_page_total_sub": "തിരഞ്ഞെടുത്ത ആൽബങ്ങളിൽ നിന്നുള്ള എല്ലാ സവിശേഷമായ ഫോട്ടോകളും വീഡിയോകളും", + "backup_controller_page_turn_off": "ഫോർഗ്രൗണ്ട് ബാക്കപ്പ് ഓഫാക്കുക", + "backup_controller_page_turn_on": "ഫോർഗ്രൗണ്ട് ബാക്കപ്പ് ഓണാക്കുക", + "backup_controller_page_uploading_file_info": "ഫയൽ വിവരങ്ങൾ അപ്‌ലോഡ് ചെയ്യുന്നു", + "backup_err_only_album": "ഒരേയൊരു ആൽബം നീക്കം ചെയ്യാൻ കഴിയില്ല", + "backup_error_sync_failed": "സിങ്ക് പരാജയപ്പെട്ടു. ബാക്കപ്പ് പ്രോസസ്സ് ചെയ്യാൻ കഴിയില്ല.", + "backup_info_card_assets": "അസറ്റുകൾ", + "backup_manual_cancelled": "റദ്ദാക്കി", + "backup_manual_in_progress": "അപ്‌ലോഡ് ഇതിനകം പുരോഗമിക്കുന്നു. കുറച്ച് സമയത്തിന് ശേഷം ശ്രമിക്കുക", + "backup_manual_success": "വിജയം", + "backup_manual_title": "അപ്‌ലോഡ് നില", + "backup_options": "ബാക്കപ്പ് ഓപ്ഷനുകൾ", + "backup_options_page_title": "ബാക്കപ്പ് ഓപ്ഷനുകൾ", + "backup_setting_subtitle": "പശ്ചാത്തല, മുൻനിര അപ്‌ലോഡ് ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക", + "backup_settings_subtitle": "അപ്‌ലോഡ് ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക", + "backward": "പിന്നോട്ട്", + "biometric_auth_enabled": "ബയോമെട്രിക് പ്രാമാണീകരണം പ്രവർത്തനക്ഷമമാക്കി", + "biometric_locked_out": "ബയോമെട്രിക് പ്രാമാണീകരണത്തിൽ നിന്ന് നിങ്ങളെ ലോക്ക് ഔട്ട് ചെയ്തിരിക്കുന്നു", + "biometric_no_options": "ബയോമെട്രിക് ഓപ്ഷനുകളൊന്നും ലഭ്യമല്ല", + "biometric_not_available": "ഈ ഉപകരണത്തിൽ ബയോമെട്രിക് പ്രാമാണീകരണം ലഭ്യമല്ല", + "birthdate_saved": "ജനനത്തീയതി വിജയകരമായി സേവ് ചെയ്തു", + "birthdate_set_description": "ഒരു ഫോട്ടോ എടുക്കുമ്പോഴുള്ള ഈ വ്യക്തിയുടെ പ്രായം കണക്കാക്കാൻ ജനനത്തീയതി ഉപയോഗിക്കുന്നു.", + "blurred_background": "മങ്ങിയ പശ്ചാത്തലം", + "bugs_and_feature_requests": "ബഗുകളും ഫീച്ചർ അഭ്യർത്ഥനകളും", + "build": "ബിൽഡ്", + "build_image": "ഇമേജ് നിർമ്മിക്കുക", + "bulk_delete_duplicates_confirmation": "നിങ്ങൾക്ക് {count, plural, one {# ഡ്യൂപ്ലിക്കേറ്റ് അസറ്റ്} other {# ഡ്യൂപ്ലിക്കേറ്റ് അസറ്റുകൾ}} ബൾക്കായി ഇല്ലാതാക്കണമെന്ന് ഉറപ്പാണോ? ഇത് ഓരോ ഗ്രൂപ്പിലെയും ഏറ്റവും വലിയ അസറ്റ് നിലനിർത്തുകയും മറ്റ് എല്ലാ ഡ്യൂപ്ലിക്കേറ്റുകളും ശാശ്വതമായി ഇല്ലാതാക്കുകയും ചെയ്യും. ഈ പ്രവർത്തനം പഴയപടിയാക്കാൻ കഴിയില്ല!", + "bulk_keep_duplicates_confirmation": "നിങ്ങൾക്ക് {count, plural, one {# ഡ്യൂപ്ലിക്കേറ്റ് അസറ്റ്} other {# ഡ്യൂപ്ലിക്കേറ്റ് അസറ്റുകൾ}} നിലനിർത്തണമെന്ന് ഉറപ്പാണോ? ഇത് ഒന്നും ഇല്ലാതാക്കാതെ എല്ലാ ഡ്യൂപ്ലിക്കേറ്റ് ഗ്രൂപ്പുകളെയും പരിഹരിക്കും.", + "bulk_trash_duplicates_confirmation": "നിങ്ങൾക്ക് {count, plural, one {# ഡ്യൂപ്ലിക്കേറ്റ് അസറ്റ്} other {# ഡ്യൂപ്ലിക്കേറ്റ് അസറ്റുകൾ}} ബൾക്കായി ട്രാഷ് ചെയ്യണമെന്ന് ഉറപ്പാണോ? ഇത് ഓരോ ഗ്രൂപ്പിലെയും ഏറ്റവും വലിയ അസറ്റ് നിലനിർത്തുകയും മറ്റ് എല്ലാ ഡ്യൂപ്ലിക്കേറ്റുകളും ട്രാഷ് ചെയ്യുകയും ചെയ്യും.", + "buy": "Immich വാങ്ങുക", + "cache_settings_clear_cache_button": "കാഷെ ക്ലിയർ ചെയ്യുക", + "cache_settings_clear_cache_button_title": "ആപ്പിന്റെ കാഷെ ക്ലിയർ ചെയ്യുന്നു. കാഷെ പുനർനിർമ്മിക്കുന്നതുവരെ ഇത് ആപ്പിന്റെ പ്രകടനത്തെ സാരമായി ബാധിക്കും.", + "cache_settings_duplicated_assets_clear_button": "ക്ലിയർ ചെയ്യുക", + "cache_settings_duplicated_assets_subtitle": "ആപ്പ് അവഗണിച്ച ഫോട്ടോകളും വീഡിയോകളും", + "cache_settings_duplicated_assets_title": "ഡ്യൂപ്ലിക്കേറ്റ് അസറ്റുകൾ ({count})", + "cache_settings_statistics_album": "ലൈബ്രറി തംബ്നെയിലുകൾ", + "cache_settings_statistics_full": "പൂർണ്ണ ചിത്രങ്ങൾ", + "cache_settings_statistics_shared": "പങ്കിട്ട ആൽബം തംബ്നെയിലുകൾ", + "cache_settings_statistics_thumbnail": "തംബ്നെയിലുകൾ", + "cache_settings_statistics_title": "കാഷെ ഉപയോഗം", + "cache_settings_subtitle": "Immich മൊബൈൽ ആപ്ലിക്കേഷന്റെ കാഷിംഗ് സ്വഭാവം നിയന്ത്രിക്കുക", + "cache_settings_tile_subtitle": "പ്രാദേശിക സ്റ്റോറേജ് സ്വഭാവം നിയന്ത്രിക്കുക", + "cache_settings_tile_title": "പ്രാദേശിക സ്റ്റോറേജ്", + "cache_settings_title": "കാഷിംഗ് ക്രമീകരണങ്ങൾ", + "camera": "ക്യാമറ", + "camera_brand": "ക്യാമറ ബ്രാൻഡ്", + "camera_model": "ക്യാമറ മോഡൽ", + "cancel": "റദ്ദാക്കുക", + "cancel_search": "തിരയൽ റദ്ദാക്കുക", + "canceled": "റദ്ദാക്കി", + "canceling": "റദ്ദാക്കുന്നു", + "cannot_merge_people": "ആളുകളെ ലയിപ്പിക്കാൻ കഴിയില്ല", + "cannot_undo_this_action": "നിങ്ങൾക്ക് ഈ പ്രവർത്തനം പഴയപടിയാക്കാൻ കഴിയില്ല!", + "cannot_update_the_description": "വിവരണം അപ്ഡേറ്റ് ചെയ്യാൻ കഴിയില്ല", + "cast": "കാസ്റ്റ്", + "cast_description": "ലഭ്യമായ കാസ്റ്റ് ഡെസ്റ്റിനേഷനുകൾ കോൺഫിഗർ ചെയ്യുക", + "change_date": "തീയതി മാറ്റുക", + "change_description": "വിവരണം മാറ്റുക", + "change_display_order": "പ്രദർശന ക്രമം മാറ്റുക", + "change_expiration_time": "കാലഹരണപ്പെടുന്ന സമയം മാറ്റുക", + "change_location": "സ്ഥാനം മാറ്റുക", + "change_name": "പേര് മാറ്റുക", + "change_name_successfully": "പേര് വിജയകരമായി മാറ്റി", + "change_password": "പാസ്‌വേഡ് മാറ്റുക", + "change_password_description": "ഇത് നിങ്ങൾ ആദ്യമായി സിസ്റ്റത്തിൽ സൈൻ ഇൻ ചെയ്യുന്നതുകൊണ്ടോ അല്ലെങ്കിൽ നിങ്ങളുടെ പാസ്‌വേഡ് മാറ്റാൻ ഒരു അഭ്യർത്ഥന നടത്തിയതുകൊണ്ടോ ആകാം. ദയവായി താഴെ പുതിയ പാസ്‌വേഡ് നൽകുക.", + "change_password_form_confirm_password": "പാസ്‌വേഡ് സ്ഥിരീകരിക്കുക", + "change_password_form_description": "നമസ്കാരം {name},\n\nഇത് നിങ്ങൾ ആദ്യമായി സിസ്റ്റത്തിൽ സൈൻ ഇൻ ചെയ്യുന്നതുകൊണ്ടോ അല്ലെങ്കിൽ നിങ്ങളുടെ പാസ്‌വേഡ് മാറ്റാൻ ഒരു അഭ്യർത്ഥന നടത്തിയതുകൊണ്ടോ ആകാം. ദയവായി താഴെ പുതിയ പാസ്‌വേഡ് നൽകുക.", + "change_password_form_new_password": "പുതിയ പാസ്‌വേഡ്", + "change_password_form_password_mismatch": "പാസ്‌വേഡുകൾ പൊരുത്തപ്പെടുന്നില്ല", + "change_password_form_reenter_new_password": "പുതിയ പാസ്‌വേഡ് വീണ്ടും നൽകുക", + "change_pin_code": "പിൻ കോഡ് മാറ്റുക", + "change_your_password": "നിങ്ങളുടെ പാസ്‌വേഡ് മാറ്റുക", + "changed_visibility_successfully": "ദൃശ്യത വിജയകരമായി മാറ്റി", + "charging": "ചാർജ്ജ് ചെയ്യുന്നു", + "charging_requirement_mobile_backup": "പശ്ചാത്തല ബാക്കപ്പിന് ഉപകരണം ചാർജ് ചെയ്യേണ്ടതുണ്ട്", + "check_corrupt_asset_backup": "കേടായ അസറ്റ് ബാക്കപ്പുകൾ പരിശോധിക്കുക", + "check_corrupt_asset_backup_button": "പരിശോധന നടത്തുക", + "check_corrupt_asset_backup_description": "എല്ലാ അസറ്റുകളും ബാക്കപ്പ് ചെയ്ത ശേഷം, വൈ-ഫൈയിൽ മാത്രം ഈ പരിശോധന നടത്തുക. നടപടിക്രമത്തിന് കുറച്ച് മിനിറ്റുകൾ എടുത്തേക്കാം.", + "check_logs": "ലോഗുകൾ പരിശോധിക്കുക", + "choose_matching_people_to_merge": "ലയിപ്പിക്കാൻ പൊരുത്തപ്പെടുന്ന ആളുകളെ തിരഞ്ഞെടുക്കുക", + "city": "നഗരം", + "clear": "ക്ലിയർ ചെയ്യുക", + "clear_all": "എല്ലാം ക്ലിയർ ചെയ്യുക", + "clear_all_recent_searches": "അടുത്തിടെ നടത്തിയ എല്ലാ തിരയലുകളും ക്ലിയർ ചെയ്യുക", + "clear_file_cache": "ഫയൽ കാഷെ ക്ലിയർ ചെയ്യുക", + "clear_message": "സന്ദേശം ക്ലിയർ ചെയ്യുക", + "clear_value": "മൂല്യം ക്ലിയർ ചെയ്യുക", + "client_cert_dialog_msg_confirm": "ശരി", + "client_cert_enter_password": "പാസ്‌വേഡ് നൽകുക", + "client_cert_import": "ഇമ്പോർട്ട് ചെയ്യുക", + "client_cert_import_success_msg": "ക്ലയിന്റ് സർട്ടിഫിക്കറ്റ് ഇമ്പോർട്ടുചെയ്‌തു", + "client_cert_invalid_msg": "അസാധുവായ സർട്ടിഫിക്കറ്റ് ഫയൽ അല്ലെങ്കിൽ തെറ്റായ പാസ്‌വേഡ്", + "client_cert_remove_msg": "ക്ലയിന്റ് സർട്ടിഫിക്കറ്റ് നീക്കംചെയ്‌തു", + "client_cert_subtitle": "PKCS12 (.p12, .pfx) ഫോർമാറ്റ് മാത്രം പിന്തുണയ്ക്കുന്നു. ലോഗിൻ ചെയ്യുന്നതിന് മുമ്പ് മാത്രമേ സർട്ടിഫിക്കറ്റ് ഇമ്പോർട്ട്/നീക്കംചെയ്യൽ ലഭ്യമാകൂ", + "client_cert_title": "SSL ക്ലയിന്റ് സർട്ടിഫിക്കറ്റ്", + "clockwise": "പ്രദക്ഷിണമായി", + "close": "അടയ്ക്കുക", + "collapse": "ചുരുക്കുക", + "collapse_all": "എല്ലാം ചുരുക്കുക", + "color": "നിറം", + "color_theme": "കളർ തീം", + "comment_deleted": "അഭിപ്രായം ഇല്ലാതാക്കി", + "comment_options": "അഭിപ്രായത്തിനുള്ള ഓപ്ഷനുകൾ", + "comments_and_likes": "അഭിപ്രായങ്ങളും ലൈക്കുകളും", + "comments_are_disabled": "അഭിപ്രായങ്ങൾ പ്രവർത്തനരഹിതമാക്കി", + "common_create_new_album": "പുതിയ ആൽബം ഉണ്ടാക്കുക", + "common_server_error": "നിങ്ങളുടെ നെറ്റ്‌വർക്ക് കണക്ഷൻ പരിശോധിക്കുക, സെർവർ ലഭ്യമാണെന്നും ആപ്പ്/സെർവർ പതിപ്പുകൾ അനുയോജ്യമാണെന്നും ഉറപ്പാക്കുക.", + "completed": "പൂർത്തിയായി", + "confirm": "സ്ഥിരീകരിക്കുക", + "confirm_admin_password": "അഡ്മിൻ പാസ്‌വേഡ് സ്ഥിരീകരിക്കുക", + "confirm_delete_face": "{name} എന്ന മുഖം അസറ്റിൽ നിന്ന് ഇല്ലാതാക്കണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?", + "confirm_delete_shared_link": "ഈ പങ്കിട്ട ലിങ്ക് ഇല്ലാതാക്കണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?", + "confirm_keep_this_delete_others": "ഈ അസറ്റ് ഒഴികെ സ്റ്റാക്കിലെ മറ്റെല്ലാ അസറ്റുകളും ഇല്ലാതാക്കപ്പെടും. തുടരണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?", + "confirm_new_pin_code": "പുതിയ പിൻ കോഡ് സ്ഥിരീകരിക്കുക", + "confirm_password": "പാസ്‌വേഡ് സ്ഥിരീകരിക്കുക", + "confirm_tag_face": "ഈ മുഖം {name} എന്ന് ടാഗ് ചെയ്യണോ?", + "confirm_tag_face_unnamed": "ഈ മുഖം ടാഗ് ചെയ്യണോ?", + "connected_device": "ബന്ധിപ്പിച്ച ഉപകരണം", + "connected_to": "ഇതുമായി ബന്ധിപ്പിച്ചു", + "contain": "ഉൾക്കൊള്ളുക", + "context": "സന്ദർഭം", + "continue": "തുടരുക", + "control_bottom_app_bar_create_new_album": "പുതിയ ആൽബം ഉണ്ടാക്കുക", + "control_bottom_app_bar_delete_from_immich": "Immich-ൽ നിന്ന് ഇല്ലാതാക്കുക", + "control_bottom_app_bar_delete_from_local": "ഉപകരണത്തിൽ നിന്ന് ഇല്ലാതാക്കുക", + "control_bottom_app_bar_edit_location": "സ്ഥാനം എഡിറ്റുചെയ്യുക", + "control_bottom_app_bar_edit_time": "തീയതിയും സമയവും എഡിറ്റുചെയ്യുക", + "control_bottom_app_bar_share_link": "ലിങ്ക് പങ്കിടുക", + "control_bottom_app_bar_share_to": "ഇതിലേക്ക് പങ്കിടുക", + "control_bottom_app_bar_trash_from_immich": "ട്രാഷിലേക്ക് മാറ്റുക", + "copied_image_to_clipboard": "ചിത്രം ക്ലിപ്പ്ബോർഡിലേക്ക് പകർത്തി.", + "copied_to_clipboard": "ക്ലിപ്പ്ബോർഡിലേക്ക് പകർത്തി!", + "copy_error": "പകർത്തുന്നതിൽ പിശക്", + "copy_file_path": "ഫയൽ പാത്ത് പകർത്തുക", + "copy_image": "ചിത്രം പകർത്തുക", + "copy_link": "ലിങ്ക് പകർത്തുക", + "copy_link_to_clipboard": "ലിങ്ക് ക്ലിപ്പ്ബോർഡിലേക്ക് പകർത്തുക", + "copy_password": "പാസ്‌വേഡ് പകർത്തുക", + "copy_to_clipboard": "ക്ലിപ്പ്ബോർഡിലേക്ക് പകർത്തുക", + "country": "രാജ്യം", + "cover": "കവർ", + "covers": "കവറുകൾ", + "create": "സൃഷ്ടിക്കുക", + "create_album": "ആൽബം ഉണ്ടാക്കുക", + "create_album_page_untitled": "പേരില്ലാത്തത്", + "create_library": "ലൈബ്രറി ഉണ്ടാക്കുക", + "create_link": "ലിങ്ക് ഉണ്ടാക്കുക", + "create_link_to_share": "പങ്കിടാൻ ലിങ്ക് ഉണ്ടാക്കുക", + "create_link_to_share_description": "തിരഞ്ഞെടുത്ത ഫോട്ടോ(കൾ) കാണാൻ ലിങ്കുള്ള ആർക്കും അനുവാദം നൽകുക", + "create_new": "പുതിയത് ഉണ്ടാക്കുക", + "create_new_person": "പുതിയ വ്യക്തിയെ ഉണ്ടാക്കുക", + "create_new_person_hint": "തിരഞ്ഞെടുത്ത അസറ്റുകൾ ഒരു പുതിയ വ്യക്തിക്ക് നൽകുക", + "create_new_user": "പുതിയ ഉപയോക്താവിനെ ഉണ്ടാക്കുക", + "create_shared_album_page_share_add_assets": "അസറ്റുകൾ ചേർക്കുക", + "create_shared_album_page_share_select_photos": "ഫോട്ടോകൾ തിരഞ്ഞെടുക്കുക", + "create_shared_link": "പങ്കിട്ട ലിങ്ക് ഉണ്ടാക്കുക", + "create_tag": "ടാഗ് ഉണ്ടാക്കുക", + "create_tag_description": "ഒരു പുതിയ ടാഗ് ഉണ്ടാക്കുക. നെസ്റ്റഡ് ടാഗുകൾക്കായി, ഫോർവേഡ് സ്ലാഷുകൾ ഉൾപ്പെടെ ടാഗിന്റെ പൂർണ്ണ പാത നൽകുക.", + "create_user": "ഉപയോക്താവിനെ ഉണ്ടാക്കുക", + "created": "സൃഷ്ടിച്ചത്", + "created_at": "സൃഷ്ടിച്ചത്", + "creating_linked_albums": "ബന്ധിപ്പിച്ച ആൽബങ്ങൾ ഉണ്ടാക്കുന്നു...", + "crop": "ക്രോപ്പ് ചെയ്യുക", + "curated_object_page_title": "വസ്തുക്കൾ", + "current_device": "നിലവിലെ ഉപകരണം", + "current_pin_code": "നിലവിലെ പിൻ കോഡ്", + "current_server_address": "നിലവിലെ സെർവർ വിലാസം", + "custom_locale": "കസ്റ്റം ലൊക്കേൽ", + "custom_locale_description": "ഭാഷയെയും പ്രദേശത്തെയും അടിസ്ഥാനമാക്കി തീയതികളും അക്കങ്ങളും ഫോർമാറ്റ് ചെയ്യുക", + "custom_url": "കസ്റ്റം URL", + "daily_title_text_date": "E, MMM dd", + "daily_title_text_date_year": "E, MMM dd, yyyy", + "dark": "ഇരുണ്ടത്", + "dark_theme": "ഡാർക്ക് തീം ടോഗിൾ ചെയ്യുക", + "date_after": "ഇതിനു ശേഷമുള്ള തീയതി", + "date_and_time": "തീയതിയും സമയവും", + "date_before": "ഇതിനു മുമ്പുള്ള തീയതി", + "date_format": "E, LLL d, y • h:mm a", + "date_of_birth_saved": "ജനനത്തീയതി വിജയകരമായി സേവ് ചെയ്തു", + "date_range": "തീയതി പരിധി", + "day": "ദിവസം", + "days": "ദിവസങ്ങൾ", + "deduplicate_all": "എല്ലാ ഡ്യൂപ്ലിക്കേറ്റുകളും ഒഴിവാക്കുക", + "deduplication_criteria_1": "ചിത്രത്തിന്റെ വലുപ്പം (ബൈറ്റുകളിൽ)", + "deduplication_criteria_2": "EXIF ഡാറ്റയുടെ എണ്ണം", + "deduplication_info": "ഡ്യൂപ്ലിക്കേഷൻ ഒഴിവാക്കൽ വിവരം", + "deduplication_info_description": "അസറ്റുകൾ യാന്ത്രികമായി മുൻകൂട്ടി തിരഞ്ഞെടുക്കുന്നതിനും ഡ്യൂപ്ലിക്കേറ്റുകൾ ബൾക്കായി നീക്കം ചെയ്യുന്നതിനും, ഞങ്ങൾ ഇവ പരിഗണിക്കുന്നു:", + "default_locale": "ഡിഫോൾട്ട് ലൊക്കേൽ", + "default_locale_description": "നിങ്ങളുടെ ബ്രൗസർ ലൊക്കേലിനെ അടിസ്ഥാനമാക്കി തീയതികളും അക്കങ്ങളും ഫോർമാറ്റ് ചെയ്യുക", + "delete": "ഇല്ലാതാക്കുക", + "delete_action_confirmation_message": "ഈ അസറ്റ് ഇല്ലാതാക്കണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ? ഈ പ്രവർത്തനം അസറ്റിനെ സെർവറിന്റെ ട്രാഷിലേക്ക് മാറ്റും, കൂടാതെ ഇത് പ്രാദേശികമായി ഇല്ലാതാക്കണോ എന്ന് ചോദിക്കുകയും ചെയ്യും", + "delete_action_prompt": "{count} എണ്ണം ഇല്ലാതാക്കി", + "delete_album": "ആൽബം ഇല്ലാതാക്കുക", + "delete_api_key_prompt": "ഈ API കീ ഇല്ലാതാക്കണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?", + "delete_dialog_alert": "ഈ ഇനങ്ങൾ Immich-ൽ നിന്നും നിങ്ങളുടെ ഉപകരണത്തിൽ നിന്നും ശാശ്വതമായി ഇല്ലാതാക്കപ്പെടും", + "delete_dialog_alert_local": "ഈ ഇനങ്ങൾ നിങ്ങളുടെ ഉപകരണത്തിൽ നിന്ന് ശാശ്വതമായി നീക്കം ചെയ്യപ്പെടും, പക്ഷേ Immich സെർവറിൽ തുടർന്നും ലഭ്യമാകും", + "delete_dialog_alert_local_non_backed_up": "ചില ഇനങ്ങൾ Immich-ലേക്ക് ബാക്കപ്പ് ചെയ്തിട്ടില്ല, അവ നിങ്ങളുടെ ഉപകരണത്തിൽ നിന്ന് ശാശ്വതമായി നീക്കം ചെയ്യപ്പെടും", + "delete_dialog_alert_remote": "ഈ ഇനങ്ങൾ Immich സെർവറിൽ നിന്ന് ശാശ്വതമായി ഇല്ലാതാക്കപ്പെടും", + "delete_dialog_ok_force": "എന്തായാലും ഇല്ലാതാക്കുക", + "delete_dialog_title": "ശാശ്വതമായി ഇല്ലാതാക്കുക", + "delete_duplicates_confirmation": "ഈ ഡ്യൂപ്ലിക്കേറ്റുകൾ ശാശ്വതമായി ഇല്ലാതാക്കണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?", + "delete_face": "മുഖം ഇല്ലാതാക്കുക", + "delete_key": "കീ ഇല്ലാതാക്കുക", + "delete_library": "ലൈബ്രറി ഇല്ലാതാക്കുക", + "delete_link": "ലിങ്ക് ഇല്ലാതാക്കുക", + "delete_local_action_prompt": "{count} എണ്ണം പ്രാദേശികമായി ഇല്ലാതാക്കി", + "delete_local_dialog_ok_backed_up_only": "ബാക്കപ്പ് ചെയ്തവ മാത്രം ഇല്ലാതാക്കുക", + "delete_local_dialog_ok_force": "എന്തായാലും ഇല്ലാതാക്കുക", + "delete_others": "മറ്റുള്ളവ ഇല്ലാതാക്കുക", + "delete_permanently": "ശാശ്വതമായി ഇല്ലാതാക്കുക", + "delete_permanently_action_prompt": "{count} എണ്ണം ശാശ്വതമായി ഇല്ലാതാക്കി", + "delete_shared_link": "പങ്കിട്ട ലിങ്ക് ഇല്ലാതാക്കുക", + "delete_shared_link_dialog_title": "പങ്കിട്ട ലിങ്ക് ഇല്ലാതാക്കുക", + "delete_tag": "ടാഗ് ഇല്ലാതാക്കുക", + "delete_tag_confirmation_prompt": "{tagName} എന്ന ടാഗ് ഇല്ലാതാക്കണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?", + "delete_user": "ഉപയോക്താവിനെ ഇല്ലാതാക്കുക", + "deleted_shared_link": "പങ്കിട്ട ലിങ്ക് ഇല്ലാതാക്കി", + "deletes_missing_assets": "ഡിസ്കിൽ നിന്ന് കാണാതായ അസറ്റുകൾ ഇല്ലാതാക്കുന്നു", + "description": "വിവരണം", + "description_input_hint_text": "വിവരണം ചേർക്കുക...", + "description_input_submit_error": "വിവരണം അപ്ഡേറ്റ് ചെയ്യുന്നതിൽ പിശക്, കൂടുതൽ വിവരങ്ങൾക്ക് ലോഗ് പരിശോധിക്കുക", + "deselect_all": "എല്ലാം തിരഞ്ഞെടുത്തത് മാറ്റുക", + "details": "വിശദാംശങ്ങൾ", + "direction": "ദിശ", + "disabled": "പ്രവർത്തനരഹിതം", + "disallow_edits": "എഡിറ്റുകൾ അനുവദിക്കരുത്", + "discord": "ഡിസ്കോർഡ്", + "discover": "കണ്ടെത്തുക", + "discovered_devices": "കണ്ടെത്തിയ ഉപകരണങ്ങൾ", + "dismiss_all_errors": "എല്ലാ പിശകുകളും തള്ളിക്കളയുക", + "dismiss_error": "പിശക് തള്ളിക്കളയുക", + "display_options": "പ്രദർശന ഓപ്ഷനുകൾ", + "display_order": "പ്രദർശന ക്രമം", + "display_original_photos": "യഥാർത്ഥ ഫോട്ടോകൾ പ്രദർശിപ്പിക്കുക", + "display_original_photos_setting_description": "യഥാർത്ഥ അസറ്റ് വെബ്-അനുയോജ്യമാകുമ്പോൾ, തംബ്നെയിലുകൾക്ക് പകരം യഥാർത്ഥ ഫോട്ടോ പ്രദർശിപ്പിക്കാൻ മുൻഗണന നൽകുക. ഇത് ഫോട്ടോ പ്രദർശന വേഗത കുറയ്ക്കാൻ ഇടയാക്കും.", + "do_not_show_again": "ഈ സന്ദേശം വീണ്ടും കാണിക്കരുത്", + "documentation": "ഡോക്യുമെന്റേഷൻ", + "done": "പൂർത്തിയായി", + "download": "ഡൗൺലോഡ്", + "download_action_prompt": "{count} അസറ്റുകൾ ഡൗൺലോഡ് ചെയ്യുന്നു", + "download_canceled": "ഡൗൺലോഡ് റദ്ദാക്കി", + "download_complete": "ഡൗൺലോഡ് പൂർത്തിയായി", + "download_enqueue": "ഡൗൺലോഡ് ക്യൂവിൽ ചേർത്തു", + "download_error": "ഡൗൺലോഡ് പിശക്", + "download_failed": "ഡൗൺലോഡ് പരാജയപ്പെട്ടു", + "download_finished": "ഡൗൺലോഡ് കഴിഞ്ഞു", + "download_include_embedded_motion_videos": "ഉൾച്ചേർത്ത വീഡിയോകൾ", + "download_include_embedded_motion_videos_description": "ചലിക്കുന്ന ഫോട്ടോകളിൽ ഉൾച്ചേർത്ത വീഡിയോകൾ ഒരു പ്രത്യേക ഫയലായി ഉൾപ്പെടുത്തുക", + "download_notfound": "ഡൗൺലോഡ് കണ്ടെത്തിയില്ല", + "download_paused": "ഡൗൺലോഡ് താൽക്കാലികമായി നിർത്തി", + "download_settings": "ഡൗൺലോഡ് ക്രമീകരണങ്ങൾ", + "download_settings_description": "അസറ്റ് ഡൗൺലോഡുമായി ബന്ധപ്പെട്ട ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക", + "download_started": "ഡൗൺലോഡ് ആരംഭിച്ചു", + "download_sucess": "ഡൗൺലോഡ് വിജയിച്ചു", + "download_sucess_android": "മീഡിയ DCIM/Immich എന്നതിലേക്ക് ഡൗൺലോഡ് ചെയ്തിരിക്കുന്നു", + "download_waiting_to_retry": "വീണ്ടും ശ്രമിക്കാൻ കാത്തിരിക്കുന്നു", + "downloading": "ഡൗൺലോഡ് ചെയ്യുന്നു", + "downloading_asset_filename": "{filename} എന്ന അസറ്റ് ഡൗൺലോഡ് ചെയ്യുന്നു", + "downloading_media": "മീഡിയ ഡൗൺലോഡ് ചെയ്യുന്നു", + "drop_files_to_upload": "അപ്‌ലോഡ് ചെയ്യാൻ ഫയലുകൾ എവിടെയും ഡ്രോപ്പ് ചെയ്യുക", + "duplicates": "ഡ്യൂപ്ലിക്കേറ്റുകൾ", + "duplicates_description": "ഏതാണ് ഡ്യൂപ്ലിക്കേറ്റ് എന്ന് സൂചിപ്പിച്ച് ഓരോ ഗ്രൂപ്പും പരിഹരിക്കുക", + "duration": "ദൈർഘ്യം", + "edit": "തിരുത്തുക", + "edit_album": "ആൽബം എഡിറ്റുചെയ്യുക", + "edit_avatar": "അവതാർ എഡിറ്റുചെയ്യുക", + "edit_birthday": "ജന്മദിനം എഡിറ്റുചെയ്യുക", + "edit_date": "തീയതി എഡിറ്റുചെയ്യുക", + "edit_date_and_time": "തീയതിയും സമയവും എഡിറ്റുചെയ്യുക", + "edit_date_and_time_action_prompt": "{count} എണ്ണത്തിന്റെ തീയതിയും സമയവും എഡിറ്റുചെയ്തു", + "edit_date_and_time_by_offset": "ഓഫ്സെറ്റ് ഉപയോഗിച്ച് തീയതി മാറ്റുക", + "edit_date_and_time_by_offset_interval": "പുതിയ തീയതി പരിധി: {from}-{to}", + "edit_description": "വിവരണം എഡിറ്റുചെയ്യുക", + "edit_description_prompt": "ദയവായി ഒരു പുതിയ വിവരണം തിരഞ്ഞെടുക്കുക:", + "edit_exclusion_pattern": "ഒഴിവാക്കൽ പാറ്റേൺ എഡിറ്റുചെയ്യുക", + "edit_faces": "മുഖങ്ങൾ എഡിറ്റുചെയ്യുക", + "edit_import_path": "ഇമ്പോർട്ട് പാത്ത് എഡിറ്റുചെയ്യുക", + "edit_import_paths": "ഇമ്പോർട്ട് പാത്തുകൾ എഡിറ്റുചെയ്യുക", + "edit_key": "കീ എഡിറ്റുചെയ്യുക", + "edit_link": "ലിങ്ക് എഡിറ്റുചെയ്യുക", + "edit_location": "സ്ഥാനം എഡിറ്റുചെയ്യുക", + "edit_location_action_prompt": "{count} എണ്ണത്തിന്റെ സ്ഥാനം എഡിറ്റുചെയ്തു", + "edit_location_dialog_title": "സ്ഥാനം", + "edit_name": "പേര് എഡിറ്റുചെയ്യുക", + "edit_people": "ആളുകളെ എഡിറ്റുചെയ്യുക", + "edit_tag": "ടാഗ് എഡിറ്റുചെയ്യുക", + "edit_title": "ശീർഷകം എഡിറ്റുചെയ്യുക", + "edit_user": "ഉപയോക്താവിനെ എഡിറ്റുചെയ്യുക", + "edited": "എഡിറ്റുചെയ്തു", + "editor": "എഡിറ്റർ", + "editor_close_without_save_prompt": "മാറ്റങ്ങൾ സേവ് ചെയ്യില്ല", + "editor_close_without_save_title": "എഡിറ്റർ അടയ്ക്കണോ?", + "editor_crop_tool_h2_aspect_ratios": "വീക്ഷണാനുപാതം", + "editor_crop_tool_h2_rotation": "റൊട്ടേഷൻ", + "email": "ഇമെയിൽ", + "email_notifications": "ഇമെയിൽ അറിയിപ്പുകൾ", + "empty_folder": "ഈ ഫോൾഡർ ശൂന്യമാണ്", + "empty_trash": "ട്രാഷ് ശൂന്യമാക്കുക", + "empty_trash_confirmation": "ട്രാഷ് ശൂന്യമാക്കണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ? ഇത് ട്രാഷിലെ എല്ലാ അസറ്റുകളും Immich-ൽ നിന്ന് ശാശ്വതമായി നീക്കംചെയ്യും.\nനിങ്ങൾക്ക് ഈ പ്രവർത്തനം പഴയപടിയാക്കാൻ കഴിയില്ല!", + "enable": "പ്രവർത്തനക്ഷമമാക്കുക", + "enable_backup": "ബാക്കപ്പ് പ്രവർത്തനക്ഷമമാക്കുക", + "enable_biometric_auth_description": "ബയോമെട്രിക് പ്രാമാണീകരണം പ്രവർത്തനക്ഷമമാക്കാൻ നിങ്ങളുടെ പിൻ കോഡ് നൽകുക", + "enabled": "പ്രവർത്തനക്ഷമമാക്കി", + "end_date": "അവസാന തീയതി", + "enqueued": "ക്യൂവിൽ ചേർത്തു", + "enter_wifi_name": "വൈ-ഫൈയുടെ പേര് നൽകുക", + "enter_your_pin_code": "നിങ്ങളുടെ പിൻ കോഡ് നൽകുക", + "enter_your_pin_code_subtitle": "ലോക്ക് ചെയ്ത ഫോൾഡർ ആക്‌സസ് ചെയ്യാൻ നിങ്ങളുടെ പിൻ കോഡ് നൽകുക", + "error": "പിശക്", + "error_change_sort_album": "ആൽബത്തിന്റെ സോർട്ട് ഓർഡർ മാറ്റുന്നതിൽ പരാജയപ്പെട്ടു", + "error_delete_face": "അസറ്റിൽ നിന്ന് മുഖം ഇല്ലാതാക്കുന്നതിൽ പിശക്", + "error_getting_places": "സ്ഥലങ്ങൾ ലഭിക്കുന്നതിൽ പിശക്", + "error_loading_image": "ചിത്രം ലോഡുചെയ്യുന്നതിൽ പിശക്", + "error_loading_partners": "പങ്കാളികളെ ലോഡുചെയ്യുന്നതിൽ പിശക്: {error}", + "error_saving_image": "പിശക്: {error}", + "error_tag_face_bounding_box": "മുഖം ടാഗ് ചെയ്യുന്നതിൽ പിശക് - ബൗണ്ടിംഗ് ബോക്സ് കോർഡിനേറ്റുകൾ ലഭിക്കുന്നില്ല", + "error_title": "പിശക് - എന്തോ കുഴപ്പം സംഭവിച്ചു", + "errors": { + "cannot_navigate_next_asset": "അടുത്ത അസറ്റിലേക്ക് പോകാൻ കഴിയില്ല", + "cannot_navigate_previous_asset": "മുമ്പത്തെ അസറ്റിലേക്ക് പോകാൻ കഴിയില്ല", + "cant_apply_changes": "മാറ്റങ്ങൾ പ്രയോഗിക്കാൻ കഴിയില്ല", + "cant_change_activity": "പ്രവർത്തനം {enabled, select, true {പ്രവർത്തനരഹിതമാക്കാൻ} other {പ്രവർത്തനക്ഷമമാക്കാൻ}} കഴിയില്ല", + "cant_change_asset_favorite": "അസറ്റിന്റെ പ്രിയപ്പെട്ടവ മാറ്റാൻ കഴിയില്ല", + "cant_change_metadata_assets_count": "{count, plural, one {# അസറ്റിന്റെ} other {# അസറ്റുകളുടെ}} മെറ്റാഡാറ്റ മാറ്റാൻ കഴിയില്ല", + "cant_get_faces": "മുഖങ്ങൾ ലഭിക്കുന്നില്ല", + "cant_get_number_of_comments": "അഭിപ്രായങ്ങളുടെ എണ്ണം ലഭിക്കുന്നില്ല", + "cant_search_people": "ആളുകളെ തിരയാൻ കഴിയില്ല", + "cant_search_places": "സ്ഥലങ്ങൾ തിരയാൻ കഴിയില്ല", + "error_adding_assets_to_album": "ആൽബത്തിലേക്ക് അസറ്റുകൾ ചേർക്കുന്നതിൽ പിശക്", + "error_adding_users_to_album": "ആൽബത്തിലേക്ക് ഉപയോക്താക്കളെ ചേർക്കുന്നതിൽ പിശക്", + "error_deleting_shared_user": "പങ്കിട്ട ഉപയോക്താവിനെ ഇല്ലാതാക്കുന്നതിൽ പിശക്", + "error_downloading": "{filename} ഡൗൺലോഡ് ചെയ്യുന്നതിൽ പിശക്", + "error_hiding_buy_button": "വാങ്ങാനുള്ള ബട്ടൺ മറയ്ക്കുന്നതിൽ പിശക്", + "error_removing_assets_from_album": "ആൽബത്തിൽ നിന്ന് അസറ്റുകൾ നീക്കം ചെയ്യുന്നതിൽ പിശക്, കൂടുതൽ വിവരങ്ങൾക്ക് കൺസോൾ പരിശോധിക്കുക", + "error_selecting_all_assets": "എല്ലാ അസറ്റുകളും തിരഞ്ഞെടുക്കുന്നതിൽ പിശക്", + "exclusion_pattern_already_exists": "ഈ ഒഴിവാക്കൽ പാറ്റേൺ ഇതിനകം നിലവിലുണ്ട്.", + "failed_to_create_album": "ആൽബം ഉണ്ടാക്കുന്നതിൽ പരാജയപ്പെട്ടു", + "failed_to_create_shared_link": "പങ്കിട്ട ലിങ്ക് ഉണ്ടാക്കുന്നതിൽ പരാജയപ്പെട്ടു", + "failed_to_edit_shared_link": "പങ്കിട്ട ലിങ്ക് എഡിറ്റുചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു", + "failed_to_get_people": "ആളുകളെ ലഭിക്കുന്നതിൽ പരാജയപ്പെട്ടു", + "failed_to_keep_this_delete_others": "ഈ അസറ്റ് നിലനിർത്തി മറ്റ് അസറ്റുകൾ ഇല്ലാതാക്കുന്നതിൽ പരാജയപ്പെട്ടു", + "failed_to_load_asset": "അസറ്റ് ലോഡുചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു", + "failed_to_load_assets": "അസറ്റുകൾ ലോഡുചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു", + "failed_to_load_notifications": "അറിയിപ്പുകൾ ലോഡുചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു", + "failed_to_load_people": "ആളുകളെ ലോഡുചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു", + "failed_to_remove_product_key": "പ്രൊഡക്റ്റ് കീ നീക്കം ചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു", + "failed_to_reset_pin_code": "പിൻ കോഡ് റീസെറ്റ് ചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു", + "failed_to_stack_assets": "അസറ്റുകൾ സ്റ്റാക്ക് ചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു", + "failed_to_unstack_assets": "അസറ്റുകൾ അൺ-സ്റ്റാക്ക് ചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു", + "failed_to_update_notification_status": "അറിയിപ്പിന്റെ നില അപ്ഡേറ്റ് ചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു", + "import_path_already_exists": "ഈ ഇമ്പോർട്ട് പാത്ത് ഇതിനകം നിലവിലുണ്ട്.", + "incorrect_email_or_password": "തെറ്റായ ഇമെയിൽ അല്ലെങ്കിൽ പാസ്‌വേഡ്", + "paths_validation_failed": "{paths, plural, one {# പാത്ത്} other {# പാത്തുകൾ}} സാധൂകരണത്തിൽ പരാജയപ്പെട്ടു", + "profile_picture_transparent_pixels": "പ്രൊഫൈൽ ചിത്രങ്ങൾക്ക് സുതാര്യമായ പിക്സലുകൾ ഉണ്ടാകരുത്. ദയവായി സൂം ഇൻ ചെയ്യുക കൂടാതെ/അല്ലെങ്കിൽ ചിത്രം നീക്കുക.", + "quota_higher_than_disk_size": "ഡിസ്ക് വലുപ്പത്തേക്കാൾ ഉയർന്ന ക്വാട്ട നിങ്ങൾ സജ്ജമാക്കിയിരിക്കുന്നു", + "something_went_wrong": "എന്തോ കുഴപ്പം സംഭവിച്ചു", + "unable_to_add_album_users": "ആൽബത്തിലേക്ക് ഉപയോക്താക്കളെ ചേർക്കാൻ കഴിയില്ല", + "unable_to_add_assets_to_shared_link": "പങ്കിട്ട ലിങ്കിലേക്ക് അസറ്റുകൾ ചേർക്കാൻ കഴിയില്ല", + "unable_to_add_comment": "അഭിപ്രായം ചേർക്കാൻ കഴിയില്ല", + "unable_to_add_exclusion_pattern": "ഒഴിവാക്കൽ പാറ്റേൺ ചേർക്കാൻ കഴിയില്ല", + "unable_to_add_import_path": "ഇമ്പോർട്ട് പാത്ത് ചേർക്കാൻ കഴിയില്ല", + "unable_to_add_partners": "പങ്കാളികളെ ചേർക്കാൻ കഴിയില്ല", + "unable_to_add_remove_archive": "ആർക്കൈവിൽ {archived, select, true {നിന്ന് അസറ്റ് നീക്കംചെയ്യാൻ} other {ലേക്ക് അസറ്റ് ചേർക്കാൻ}} കഴിയില്ല", + "unable_to_add_remove_favorites": "പ്രിയപ്പെട്ടവയിലേക്ക് {favorite, select, true {അസറ്റ് ചേർക്കാൻ} other {നിന്ന് അസറ്റ് നീക്കംചെയ്യാൻ}} കഴിയില്ല", + "unable_to_archive_unarchive": "{archived, select, true {ആർക്കൈവ് ചെയ്യാൻ} other {അൺആർക്കൈവ് ചെയ്യാൻ}} കഴിയില്ല", + "unable_to_change_album_user_role": "ആൽബം ഉപയോക്താവിന്റെ റോൾ മാറ്റാൻ കഴിയില്ല", + "unable_to_change_date": "തീയതി മാറ്റാൻ കഴിയില്ല", + "unable_to_change_description": "വിവരണം മാറ്റാൻ കഴിയില്ല", + "unable_to_change_favorite": "അസറ്റിന്റെ പ്രിയപ്പെട്ടവ മാറ്റാൻ കഴിയില്ല", + "unable_to_change_location": "സ്ഥാനം മാറ്റാൻ കഴിയില്ല", + "unable_to_change_password": "പാസ്‌വേഡ് മാറ്റാൻ കഴിയില്ല", + "unable_to_change_visibility": "{count, plural, one {# വ്യക്തിയുടെ} other {# ആളുകളുടെ}} ദൃശ്യത മാറ്റാൻ കഴിയില്ല", + "unable_to_complete_oauth_login": "OAuth ലോഗിൻ പൂർത്തിയാക്കാൻ കഴിയില്ല", + "unable_to_connect": "ബന്ധിപ്പിക്കാൻ കഴിയില്ല", + "unable_to_copy_to_clipboard": "ക്ലിപ്പ്ബോർഡിലേക്ക് പകർത്താൻ കഴിയില്ല, നിങ്ങൾ https വഴിയാണ് പേജ് ആക്‌സസ് ചെയ്യുന്നതെന്ന് ഉറപ്പാക്കുക", + "unable_to_create_admin_account": "അഡ്മിൻ അക്കൗണ്ട് ഉണ്ടാക്കാൻ കഴിയില്ല", + "unable_to_create_api_key": "പുതിയ API കീ ഉണ്ടാക്കാൻ കഴിയില്ല", + "unable_to_create_library": "ലൈബ്രറി ഉണ്ടാക്കാൻ കഴിയില്ല", + "unable_to_create_user": "ഉപയോക്താവിനെ ഉണ്ടാക്കാൻ കഴിയില്ല", + "unable_to_delete_album": "ആൽബം ഇല്ലാതാക്കാൻ കഴിയില്ല", + "unable_to_delete_asset": "അസറ്റ് ഇല്ലാതാക്കാൻ കഴിയില്ല", + "unable_to_delete_assets": "അസറ്റുകൾ ഇല്ലാതാക്കുന്നതിൽ പിശക്", + "unable_to_delete_exclusion_pattern": "ഒഴിവാക്കൽ പാറ്റേൺ ഇല്ലാതാക്കാൻ കഴിയില്ല", + "unable_to_delete_import_path": "ഇമ്പോർട്ട് പാത്ത് ഇല്ലാതാക്കാൻ കഴിയില്ല", + "unable_to_delete_shared_link": "പങ്കിട്ട ലിങ്ക് ഇല്ലാതാക്കാൻ കഴിയില്ല", + "unable_to_delete_user": "ഉപയോക്താവിനെ ഇല്ലാതാക്കാൻ കഴിയില്ല", + "unable_to_download_files": "ഫയലുകൾ ഡൗൺലോഡ് ചെയ്യാൻ കഴിയില്ല", + "unable_to_edit_exclusion_pattern": "ഒഴിവാക്കൽ പാറ്റേൺ എഡിറ്റുചെയ്യാൻ കഴിയില്ല", + "unable_to_edit_import_path": "ഇമ്പോർട്ട് പാത്ത് എഡിറ്റുചെയ്യാൻ കഴിയില്ല", + "unable_to_empty_trash": "ട്രാഷ് ശൂന്യമാക്കാൻ കഴിയില്ല", + "unable_to_enter_fullscreen": "ഫുൾസ്ക്രീനിൽ പ്രവേശിക്കാൻ കഴിയില്ല", + "unable_to_exit_fullscreen": "ഫുൾസ്ക്രീനിൽ നിന്ന് പുറത്തുകടക്കാൻ കഴിയില്ല", + "unable_to_get_comments_number": "അഭിപ്രായങ്ങളുടെ എണ്ണം ലഭിക്കുന്നില്ല", + "unable_to_get_shared_link": "പങ്കിട്ട ലിങ്ക് ലഭിക്കുന്നതിൽ പരാജയപ്പെട്ടു", + "unable_to_hide_person": "വ്യക്തിയെ മറയ്ക്കാൻ കഴിയില്ല", + "unable_to_link_motion_video": "ചലിക്കുന്ന വീഡിയോ ലിങ്ക് ചെയ്യാൻ കഴിയില്ല", + "unable_to_link_oauth_account": "OAuth അക്കൗണ്ട് ലിങ്ക് ചെയ്യാൻ കഴിയില്ല", + "unable_to_log_out_all_devices": "എല്ലാ ഉപകരണങ്ങളിൽ നിന്നും ലോഗ് ഔട്ട് ചെയ്യാൻ കഴിയില്ല", + "unable_to_log_out_device": "ഉപകരണത്തിൽ നിന്ന് ലോഗ് ഔട്ട് ചെയ്യാൻ കഴിയില്ല", + "unable_to_login_with_oauth": "OAuth ഉപയോഗിച്ച് ലോഗിൻ ചെയ്യാൻ കഴിയില്ല", + "unable_to_play_video": "വീഡിയോ പ്ലേ ചെയ്യാൻ കഴിയില്ല", + "unable_to_reassign_assets_existing_person": "{name, select, null {നിലവിലുള്ള ഒരു വ്യക്തിക്ക്} other {{name}-ന്}} അസറ്റുകൾ വീണ്ടും നൽകാൻ കഴിയില്ല", + "unable_to_reassign_assets_new_person": "ഒരു പുതിയ വ്യക്തിക്ക് അസറ്റുകൾ വീണ്ടും നൽകാൻ കഴിയില്ല", + "unable_to_refresh_user": "ഉപയോക്താവിനെ പുതുക്കാൻ കഴിയില്ല", + "unable_to_remove_album_users": "ആൽബത്തിൽ നിന്ന് ഉപയോക്താക്കളെ നീക്കംചെയ്യാൻ കഴിയില്ല", + "unable_to_remove_api_key": "API കീ നീക്കംചെയ്യാൻ കഴിയില്ല", + "unable_to_remove_assets_from_shared_link": "പങ്കിട്ട ലിങ്കിൽ നിന്ന് അസറ്റുകൾ നീക്കംചെയ്യാൻ കഴിയില്ല", + "unable_to_remove_library": "ലൈബ്രറി നീക്കംചെയ്യാൻ കഴിയില്ല", + "unable_to_remove_partner": "പങ്കാളിയെ നീക്കംചെയ്യാൻ കഴിയില്ല", + "unable_to_remove_reaction": "പ്രതികരണം നീക്കംചെയ്യാൻ കഴിയില്ല", + "unable_to_reset_password": "പാസ്‌വേഡ് റീസെറ്റ് ചെയ്യാൻ കഴിയില്ല", + "unable_to_reset_pin_code": "പിൻ കോഡ് റീസെറ്റ് ചെയ്യാൻ കഴിയില്ല", + "unable_to_resolve_duplicate": "ഡ്യൂപ്ലിക്കേറ്റ് പരിഹരിക്കാൻ കഴിയില്ല", + "unable_to_restore_assets": "അസറ്റുകൾ പുനഃസ്ഥാപിക്കാൻ കഴിയില്ല", + "unable_to_restore_trash": "ട്രാഷ് പുനഃസ്ഥാപിക്കാൻ കഴിയില്ല", + "unable_to_restore_user": "ഉപയോക്താവിനെ പുനഃസ്ഥാപിക്കാൻ കഴിയില്ല", + "unable_to_save_album": "ആൽബം സേവ് ചെയ്യാൻ കഴിയില്ല", + "unable_to_save_api_key": "API കീ സേവ് ചെയ്യാൻ കഴിയില്ല", + "unable_to_save_date_of_birth": "ജനനത്തീയതി സേവ് ചെയ്യാൻ കഴിയില്ല", + "unable_to_save_name": "പേര് സേവ് ചെയ്യാൻ കഴിയില്ല", + "unable_to_save_profile": "പ്രൊഫൈൽ സേവ് ചെയ്യാൻ കഴിയില്ല", + "unable_to_save_settings": "ക്രമീകരണങ്ങൾ സേവ് ചെയ്യാൻ കഴിയില്ല", + "unable_to_scan_libraries": "ലൈബ്രറികൾ സ്കാൻ ചെയ്യാൻ കഴിയില്ല", + "unable_to_scan_library": "ലൈബ്രറി സ്കാൻ ചെയ്യാൻ കഴിയില്ല", + "unable_to_set_feature_photo": "ഫീച്ചർ ഫോട്ടോ സജ്ജമാക്കാൻ കഴിയില്ല", + "unable_to_set_profile_picture": "പ്രൊഫൈൽ ചിത്രം സജ്ജമാക്കാൻ കഴിയില്ല", + "unable_to_submit_job": "ജോലി സമർപ്പിക്കാൻ കഴിയില്ല", + "unable_to_trash_asset": "അസറ്റ് ട്രാഷ് ചെയ്യാൻ കഴിയില്ല", + "unable_to_unlink_account": "അക്കൗണ്ട് അൺലിങ്ക് ചെയ്യാൻ കഴിയില്ല", + "unable_to_unlink_motion_video": "ചലിക്കുന്ന വീഡിയോ അൺലിങ്ക് ചെയ്യാൻ കഴിയില്ല", + "unable_to_update_album_cover": "ആൽബം കവർ അപ്ഡേറ്റ് ചെയ്യാൻ കഴിയില്ല", + "unable_to_update_album_info": "ആൽബം വിവരങ്ങൾ അപ്ഡേറ്റ് ചെയ്യാൻ കഴിയില്ല", + "unable_to_update_library": "ലൈബ്രറി അപ്ഡേറ്റ് ചെയ്യാൻ കഴിയില്ല", + "unable_to_update_location": "സ്ഥാനം അപ്ഡേറ്റ് ചെയ്യാൻ കഴിയില്ല", + "unable_to_update_settings": "ക്രമീകരണങ്ങൾ അപ്ഡേറ്റ് ചെയ്യാൻ കഴിയില്ല", + "unable_to_update_timeline_display_status": "ടൈംലൈൻ പ്രദർശന നില അപ്ഡേറ്റ് ചെയ്യാൻ കഴിയില്ല", + "unable_to_update_user": "ഉപയോക്താവിനെ അപ്ഡേറ്റ് ചെയ്യാൻ കഴിയില്ല", + "unable_to_upload_file": "ഫയൽ അപ്‌ലോഡ് ചെയ്യാൻ കഴിയില്ല" + }, + "exif": "Exif", + "exif_bottom_sheet_description": "വിവരണം ചേർക്കുക...", + "exif_bottom_sheet_description_error": "വിവരണം അപ്ഡേറ്റ് ചെയ്യുന്നതിൽ പിശക്", + "exif_bottom_sheet_details": "വിശദാംശങ്ങൾ", + "exif_bottom_sheet_location": "സ്ഥാനം", + "exif_bottom_sheet_people": "ആളുകൾ", + "exif_bottom_sheet_person_add_person": "പേര് ചേർക്കുക", + "exit_slideshow": "സ്ലൈഡ്‌ഷോയിൽ നിന്ന് പുറത്തുകടക്കുക", + "expand_all": "എല്ലാം വികസിപ്പിക്കുക", + "experimental_settings_new_asset_list_subtitle": "പുരോഗതിയിലാണ്", + "experimental_settings_new_asset_list_title": "പരീക്ഷണാടിസ്ഥാനത്തിലുള്ള ഫോട്ടോ ഗ്രിഡ് പ്രവർത്തനക്ഷമമാക്കുക", + "experimental_settings_subtitle": "നിങ്ങളുടെ സ്വന്തം ഉത്തരവാദിത്വത്തിൽ ഉപയോഗിക്കുക!", + "experimental_settings_title": "പരീക്ഷണാടിസ്ഥാനത്തിലുള്ളത്", + "expire_after": "ഇതിന് ശേഷം കാലഹരണപ്പെടും", + "expired": "കാലഹരണപ്പെട്ടു", + "expires_date": "{date}-ന് കാലഹരണപ്പെടും", + "explore": "പര്യവേക്ഷണം ചെയ്യുക", + "explorer": "എക്സ്പ്ലോറർ", + "export": "കയറ്റുമതി ചെയ്യുക", + "export_as_json": "JSON ആയി കയറ്റുമതി ചെയ്യുക", + "export_database": "ഡാറ്റാബേസ് കയറ്റുമതി ചെയ്യുക", + "export_database_description": "SQLite ഡാറ്റാബേസ് കയറ്റുമതി ചെയ്യുക", + "extension": "എക്സ്റ്റൻഷൻ", + "external": "ബാഹ്യം", + "external_libraries": "ബാഹ്യ ലൈബ്രറികൾ", + "external_network": "ബാഹ്യ നെറ്റ്‌വർക്ക്", + "external_network_sheet_info": "തിരഞ്ഞെടുത്ത വൈ-ഫൈ നെറ്റ്‌വർക്കിൽ അല്ലാത്തപ്പോൾ, താഴെ നൽകിയിട്ടുള്ള URL-കളിൽ ആദ്യം ലഭ്യമാകുന്ന ഒന്നിലൂടെ, മുകളിൽ നിന്ന് താഴേക്കുള്ള ക്രമത്തിൽ, ആപ്പ് സെർവറുമായി ബന്ധിപ്പിക്കും", + "face_unassigned": "അസൈൻ ചെയ്തിട്ടില്ല", + "failed": "പരാജയപ്പെട്ടു", + "failed_to_authenticate": "പ്രാമാണീകരിക്കുന്നതിൽ പരാജയപ്പെട്ടു", + "failed_to_load_assets": "അസറ്റുകൾ ലോഡുചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു", + "failed_to_load_folder": "ഫോൾഡർ ലോഡുചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു", + "favorite": "പ്രിയം", + "favorite_action_prompt": "{count} എണ്ണം പ്രിയപ്പെട്ടവയിലേക്ക് ചേർത്തു", + "favorite_or_unfavorite_photo": "ഫോട്ടോ പ്രിയപ്പെട്ടതാക്കുക അല്ലെങ്കിൽ മാറ്റുക", + "favorites": "പ്രിയങ്ങൾ", + "favorites_page_no_favorites": "പ്രിയപ്പെട്ട അസറ്റുകളൊന്നും കണ്ടെത്തിയില്ല", + "feature_photo_updated": "ഫീച്ചർ ഫോട്ടോ അപ്ഡേറ്റ് ചെയ്തു", + "features": "ഫീച്ചറുകൾ", + "features_in_development": "വികസിപ്പിച്ചുകൊണ്ടിരിക്കുന്ന ഫീച്ചറുകൾ", + "features_setting_description": "ആപ്പ് ഫീച്ചറുകൾ കൈകാര്യം ചെയ്യുക", + "file_name": "ഫയലിന്റെ പേര്", + "file_name_or_extension": "ഫയലിന്റെ പേര് അല്ലെങ്കിൽ എക്സ്റ്റൻഷൻ", + "filename": "ഫയൽനാമം", + "filetype": "ഫയൽ തരം", + "filter": "ഫിൽട്ടർ ചെയ്യുക", + "filter_people": "ആളുകളെ ഫിൽട്ടർ ചെയ്യുക", + "filter_places": "സ്ഥലങ്ങൾ ഫിൽട്ടർ ചെയ്യുക", + "find_them_fast": "തിരയലിലൂടെ പേര് ഉപയോഗിച്ച് അവരെ വേഗത്തിൽ കണ്ടെത്തുക", + "first": "ആദ്യം", + "fix_incorrect_match": "തെറ്റായ പൊരുത്തം ശരിയാക്കുക", + "folder": "ഫോൾഡർ", + "folder_not_found": "ഫോൾഡർ കണ്ടെത്തിയില്ല", + "folders": "ഫോൾഡറുകൾ", + "folders_feature_description": "ഫയൽ സിസ്റ്റത്തിലെ ഫോട്ടോകൾക്കും വീഡിയോകൾക്കുമായി ഫോൾഡർ കാഴ്‌ച ബ്രൗസുചെയ്യുന്നു", + "forgot_pin_code_question": "നിങ്ങളുടെ പിൻ മറന്നോ?", + "forward": "മുന്നോട്ട്", + "gcast_enabled": "ഗൂഗിൾ കാസ്റ്റ്", + "gcast_enabled_description": "പ്രവർത്തിക്കുന്നതിനായി ഈ ഫീച്ചർ ഗൂഗിളിൽ നിന്ന് ബാഹ്യ ഉറവിടങ്ങൾ ലോഡുചെയ്യുന്നു.", + "general": "പൊതുവായത്", + "geolocation_instruction_location": "ലൊക്കേഷൻ ഉപയോഗിക്കുന്നതിന് GPS കോർഡിനേറ്റുകളുള്ള ഒരു അസറ്റിൽ ക്ലിക്കുചെയ്യുക, അല്ലെങ്കിൽ മാപ്പിൽ നിന്ന് നേരിട്ട് ഒരു സ്ഥലം തിരഞ്ഞെടുക്കുക", + "get_help": "സഹായം നേടുക", + "get_wifiname_error": "വൈ-ഫൈയുടെ പേര് ലഭിച്ചില്ല. നിങ്ങൾ ആവശ്യമായ അനുമതികൾ നൽകിയിട്ടുണ്ടെന്നും ഒരു വൈ-ഫൈ നെറ്റ്‌വർക്കിലേക്ക് കണക്റ്റുചെയ്‌തിട്ടുണ്ടെന്നും ഉറപ്പാക്കുക", + "getting_started": "ആരംഭിക്കുന്നു", + "go_back": "പിന്നോട്ട് പോകുക", + "go_to_folder": "ഫോൾഡറിലേക്ക് പോകുക", + "go_to_search": "തിരയലിലേക്ക് പോകുക", + "gps": "ജിപിഎസ്", + "gps_missing": "GPS ഇല്ല", + "grant_permission": "അനുമതി നൽകുക", + "group_albums_by": "ആൽബങ്ങളെ ഇതനുസരിച്ച് ഗ്രൂപ്പ് ചെയ്യുക...", + "group_country": "രാജ്യം അനുസരിച്ച് ഗ്രൂപ്പ് ചെയ്യുക", + "group_no": "ഗ്രൂപ്പിംഗ് ഇല്ല", + "group_owner": "ഉടമ അനുസരിച്ച് ഗ്രൂപ്പ് ചെയ്യുക", + "group_places_by": "സ്ഥലങ്ങളെ ഇതനുസരിച്ച് ഗ്രൂപ്പ് ചെയ്യുക...", + "group_year": "വർഷം അനുസരിച്ച് ഗ്രൂപ്പ് ചെയ്യുക", + "haptic_feedback_switch": "ഹാപ്റ്റിക് ഫീഡ്‌ബാക്ക് പ്രവർത്തനക്ഷമമാക്കുക", + "haptic_feedback_title": "ഹാപ്റ്റിക് ഫീഡ്‌ബാക്ക്", + "has_quota": "ക്വാട്ടയുണ്ട്", + "hash_asset": "അസറ്റ് ഹാഷ് ചെയ്യുക", + "hashed_assets": "ഹാഷ് ചെയ്ത അസറ്റുകൾ", + "hashing": "ഹാഷിംഗ്", + "header_settings_add_header_tip": "ഹെഡർ ചേർക്കുക", + "header_settings_field_validator_msg": "മൂല്യം ശൂന്യമാകരുത്", + "header_settings_header_name_input": "ഹെഡറിന്റെ പേര്", + "header_settings_header_value_input": "ഹെഡറിന്റെ മൂല്യം", + "headers_settings_tile_subtitle": "ഓരോ നെറ്റ്‌വർക്ക് അഭ്യർത്ഥനയ്‌ക്കൊപ്പവും ആപ്പ് അയയ്‌ക്കേണ്ട പ്രോക്സി ഹെഡറുകൾ നിർവചിക്കുക", + "headers_settings_tile_title": "കസ്റ്റം പ്രോക്സി ഹെഡറുകൾ", + "hi_user": "നമസ്കാരം {name} ({email})", + "hide_all_people": "എല്ലാ ആളുകളെയും മറയ്ക്കുക", + "hide_gallery": "ഗാലറി മറയ്ക്കുക", + "hide_named_person": "{name} എന്ന വ്യക്തിയെ മറയ്ക്കുക", + "hide_password": "പാസ്‌വേഡ് മറയ്ക്കുക", + "hide_person": "വ്യക്തിയെ മറയ്ക്കുക", + "hide_unnamed_people": "പേരില്ലാത്ത ആളുകളെ മറയ്ക്കുക", + "home_page_add_to_album_conflicts": "{album} എന്ന ആൽബത്തിലേക്ക് {added} അസറ്റുകൾ ചേർത്തു. {failed} അസറ്റുകൾ ഇതിനകം ആൽബത്തിലുണ്ട്.", + "home_page_add_to_album_err_local": "പ്രാദേശിക അസറ്റുകൾ ഇപ്പോൾ ആൽബങ്ങളിലേക്ക് ചേർക്കാൻ കഴിയില്ല, ഒഴിവാക്കുന്നു", + "home_page_add_to_album_success": "{album} എന്ന ആൽബത്തിലേക്ക് {added} അസറ്റുകൾ ചേർത്തു.", + "home_page_album_err_partner": "പങ്കാളിയുടെ അസറ്റുകൾ ഇപ്പോൾ ഒരു ആൽബത്തിലേക്ക് ചേർക്കാൻ കഴിയില്ല, ഒഴിവാക്കുന്നു", + "home_page_archive_err_local": "പ്രാദേശിക അസറ്റുകൾ ഇപ്പോൾ ആർക്കൈവ് ചെയ്യാൻ കഴിയില്ല, ഒഴിവാക്കുന്നു", + "home_page_archive_err_partner": "പങ്കാളിയുടെ അസറ്റുകൾ ആർക്കൈവ് ചെയ്യാൻ കഴിയില്ല, ഒഴിവാക്കുന്നു", + "home_page_building_timeline": "ടൈംലൈൻ നിർമ്മിക്കുന്നു", + "home_page_delete_err_partner": "പങ്കാളിയുടെ അസറ്റുകൾ ഇല്ലാതാക്കാൻ കഴിയില്ല, ഒഴിവാക്കുന്നു", + "home_page_delete_remote_err_local": "റിമോട്ട് ഇല്ലാതാക്കൽ തിരഞ്ഞെടുപ്പിൽ പ്രാദേശിക അസറ്റുകൾ ഉണ്ട്, ഒഴിവാക്കുന്നു", + "home_page_favorite_err_local": "പ്രാദേശിക അസറ്റുകൾ ഇപ്പോൾ പ്രിയപ്പെട്ടതാക്കാൻ കഴിയില്ല, ഒഴിവാക്കുന്നു", + "home_page_favorite_err_partner": "പങ്കാളിയുടെ അസറ്റുകൾ ഇപ്പോൾ പ്രിയപ്പെട്ടതാക്കാൻ കഴിയില്ല, ഒഴിവാക്കുന്നു", + "home_page_first_time_notice": "നിങ്ങൾ ആദ്യമായിട്ടാണ് ഈ ആപ്പ് ഉപയോഗിക്കുന്നതെങ്കിൽ, ഒരു ബാക്കപ്പ് ആൽബം തിരഞ്ഞെടുക്കുന്നത് ഉറപ്പാക്കുക, അതുവഴി ടൈംലൈനിൽ ഫോട്ടോകളും വീഡിയോകളും ചേർക്കാൻ കഴിയും", + "home_page_locked_error_local": "പ്രാദേശിക അസറ്റുകൾ ലോക്ക് ചെയ്ത ഫോൾഡറിലേക്ക് മാറ്റാൻ കഴിയില്ല, ഒഴിവാക്കുന്നു", + "home_page_locked_error_partner": "പങ്കാളിയുടെ അസറ്റുകൾ ലോക്ക് ചെയ്ത ഫോൾഡറിലേക്ക് മാറ്റാൻ കഴിയില്ല, ഒഴിവാക്കുന്നു", + "home_page_share_err_local": "പ്രാദേശിക അസറ്റുകൾ ലിങ്ക് വഴി പങ്കിടാൻ കഴിയില്ല, ഒഴിവാക്കുന്നു", + "home_page_upload_err_limit": "ഒരു സമയം പരമാവധി 30 അസറ്റുകൾ മാത്രമേ അപ്‌ലോഡ് ചെയ്യാൻ കഴിയൂ, ഒഴിവാക്കുന്നു", + "host": "ഹോസ്റ്റ്", + "hour": "മണിക്കൂർ", + "hours": "മണിക്കൂറുകൾ", + "id": "ഐഡി", + "idle": "നിഷ്‌ക്രിയം", + "ignore_icloud_photos": "iCloud ഫോട്ടോകൾ അവഗണിക്കുക", + "ignore_icloud_photos_description": "iCloud-ൽ സംഭരിച്ചിരിക്കുന്ന ഫോട്ടോകൾ Immich സെർവറിലേക്ക് അപ്‌ലോഡ് ചെയ്യില്ല", + "image": "ചിത്രം", + "image_alt_text_date": "{date}-ന് എടുത്ത {isVideo, select, true {വീഡിയോ} other {ചിത്രം}}", + "image_alt_text_date_1_person": "{date}-ന് {person1}-നൊപ്പം എടുത്ത {isVideo, select, true {വീഡിയോ} other {ചിത്രം}}", + "image_alt_text_date_2_people": "{date}-ന് {person1}, {person2} എന്നിവർക്കൊപ്പം എടുത്ത {isVideo, select, true {വീഡിയോ} other {ചിത്രം}}", + "image_alt_text_date_3_people": "{date}-ന് {person1}, {person2}, {person3} എന്നിവർക്കൊപ്പം എടുത്ത {isVideo, select, true {വീഡിയോ} other {ചിത്രം}}", + "image_alt_text_date_4_or_more_people": "{date}-ന് {person1}, {person2}, കൂടാതെ മറ്റ് {additionalCount, number} പേർക്കുമൊപ്പം എടുത്ത {isVideo, select, true {വീഡിയോ} other {ചിത്രം}}", + "image_alt_text_date_place": "{date}-ന് {city}, {country} എന്നിവിടങ്ങളിൽ വെച്ച് എടുത്ത {isVideo, select, true {വീഡിയോ} other {ചിത്രം}}", + "image_alt_text_date_place_1_person": "{date}-ന് {city}, {country} എന്നിവിടങ്ങളിൽ വെച്ച് {person1}-നൊപ്പം എടുത്ത {isVideo, select, true {വീഡിയോ} other {ചിത്രം}}", + "image_alt_text_date_place_2_people": "{date}-ന് {city}, {country} എന്നിവിടങ്ങളിൽ വെച്ച് {person1}, {person2} എന്നിവർക്കൊപ്പം എടുത്ത {isVideo, select, true {വീഡിയോ} other {ചിത്രം}}", + "image_alt_text_date_place_3_people": "{date}-ന് {city}, {country} എന്നിവിടങ്ങളിൽ വെച്ച് {person1}, {person2}, {person3} എന്നിവർക്കൊപ്പം എടുത്ത {isVideo, select, true {വീഡിയോ} other {ചിത്രം}}", + "image_alt_text_date_place_4_or_more_people": "{date}-ന് {city}, {country} എന്നിവിടങ്ങളിൽ വെച്ച് {person1}, {person2}, കൂടാതെ മറ്റ് {additionalCount, number} പേർക്കുമൊപ്പം എടുത്ത {isVideo, select, true {വീഡിയോ} other {ചിത്രം}}", + "image_saved_successfully": "ചിത്രം സേവ് ചെയ്തു", + "image_viewer_page_state_provider_download_started": "ഡൗൺലോഡ് ആരംഭിച്ചു", + "image_viewer_page_state_provider_download_success": "ഡൗൺലോഡ് വിജയിച്ചു", + "image_viewer_page_state_provider_share_error": "പങ്കിടുന്നതിൽ പിശക്", + "immich_logo": "Immich ലോഗോ", + "immich_web_interface": "Immich വെബ് ഇന്റർഫേസ്", + "import_from_json": "JSON-ൽ നിന്ന് ഇമ്പോർട്ടുചെയ്യുക", + "import_path": "ഇമ്പോർട്ട് പാത്ത്", + "in_albums": "{count, plural, one {# ആൽബത്തിൽ} other {# ആൽബങ്ങളിൽ}}", + "in_archive": "ആർക്കൈവിൽ", + "include_archived": "ആർക്കൈവുചെയ്‌തവ ഉൾപ്പെടുത്തുക", + "include_shared_albums": "പങ്കിട്ട ആൽബങ്ങൾ ഉൾപ്പെടുത്തുക", + "include_shared_partner_assets": "പങ്കിട്ട പങ്കാളിയുടെ അസറ്റുകൾ ഉൾപ്പെടുത്തുക", + "individual_share": "വ്യക്തിഗത പങ്കിടൽ", + "individual_shares": "വ്യക്തിഗത പങ്കിടലുകൾ", + "info": "വിവരങ്ങൾ", + "interval": { + "day_at_onepm": "എല്ലാ ദിവസവും ഉച്ചയ്ക്ക് 1 മണിക്ക്", + "hours": "{hours, plural, one {ഓരോ മണിക്കൂറിലും} other {ഓരോ {hours, number} മണിക്കൂറിലും}}", + "night_at_midnight": "എല്ലാ രാത്രിയും അർദ്ധരാത്രിക്ക്", + "night_at_twoam": "എല്ലാ രാത്രിയും പുലർച്ചെ 2 മണിക്ക്" + }, + "invalid_date": "അസാധുവായ തീയതി", + "invalid_date_format": "അസാധുവായ തീയതി ഫോർമാറ്റ്", + "invite_people": "ആളുകളെ ക്ഷണിക്കുക", + "invite_to_album": "ആൽബത്തിലേക്ക് ക്ഷണിക്കുക", + "ios_debug_info_fetch_ran_at": "ഫെച്ച് പ്രവർത്തിച്ചത് {dateTime}", + "ios_debug_info_last_sync_at": "അവസാന സിങ്ക് {dateTime}", + "ios_debug_info_no_processes_queued": "പശ്ചാത്തല പ്രോസസ്സുകളൊന്നും ക്യൂവിൽ ഇല്ല", + "ios_debug_info_no_sync_yet": "പശ്ചാത്തല സിങ്ക് ജോലി ഇതുവരെ പ്രവർത്തിച്ചിട്ടില്ല", + "ios_debug_info_processes_queued": "{count, plural, one {{count} പശ്ചാത്തല പ്രോസസ്സ് ക്യൂവിലുണ്ട്} other {{count} പശ്ചാത്തല പ്രോസസ്സുകൾ ക്യൂവിലുണ്ട്}}", + "ios_debug_info_processing_ran_at": "പ്രോസസ്സിംഗ് പ്രവർത്തിച്ചത് {dateTime}", + "items_count": "{count, plural, one {# ഇനം} other {# ഇനങ്ങൾ}}", + "jobs": "ജോലികൾ", + "keep": "സൂക്ഷിക്കുക", + "keep_all": "എല്ലാം സൂക്ഷിക്കുക", + "keep_this_delete_others": "ഇത് സൂക്ഷിച്ച് മറ്റുള്ളവ ഇല്ലാതാക്കുക", + "kept_this_deleted_others": "ഈ അസറ്റ് സൂക്ഷിക്കുകയും {count, plural, one {# അസറ്റ്} other {# അസറ്റുകൾ}} ഇല്ലാതാക്കുകയും ചെയ്തു", + "keyboard_shortcuts": "കീബോർഡ് കുറുക്കുവഴികൾ", + "language": "ഭാഷ", + "language_no_results_subtitle": "നിങ്ങളുടെ തിരയൽ പദം ക്രമീകരിച്ച് ശ്രമിക്കുക", + "language_no_results_title": "ഭാഷകളൊന്നും കണ്ടെത്തിയില്ല", + "language_search_hint": "ഭാഷകൾക്കായി തിരയുക...", + "language_setting_description": "നിങ്ങൾക്കിഷ്ടപ്പെട്ട ഭാഷ തിരഞ്ഞെടുക്കുക", + "large_files": "വലിയ ഫയലുകൾ", + "last": "അവസാനത്തേത്", + "last_seen": "അവസാനം കണ്ടത്", + "latest_version": "ഏറ്റവും പുതിയ പതിപ്പ്", + "latitude": "അക്ഷാംശം", + "leave": "വിടുക", + "leave_album": "ആൽബം വിടുക", + "lens_model": "ലെൻസ് മോഡൽ", + "let_others_respond": "മറ്റുള്ളവരെ പ്രതികരിക്കാൻ അനുവദിക്കുക", + "level": "തലം", + "library": "ലൈബ്രറി", + "library_options": "ലൈബ്രറി ഓപ്ഷനുകൾ", + "library_page_device_albums": "ഉപകരണത്തിലെ ആൽബങ്ങൾ", + "library_page_new_album": "പുതിയ ആൽബം", + "library_page_sort_asset_count": "അസറ്റുകളുടെ എണ്ണം", + "library_page_sort_created": "സൃഷ്ടിച്ച തീയതി", + "library_page_sort_last_modified": "അവസാനം മാറ്റം വരുത്തിയത്", + "library_page_sort_title": "ആൽബത്തിന്റെ ശീർഷകം", + "licenses": "ലൈസൻസുകൾ", + "light": "ലൈറ്റ്", + "like": "ഇഷ്ടപ്പെട്ടു", + "like_deleted": "ഇഷ്ടം നീക്കംചെയ്തു", + "link_motion_video": "ചലിക്കുന്ന വീഡിയോ ലിങ്ക് ചെയ്യുക", + "link_to_oauth": "OAuth-ലേക്ക് ലിങ്ക് ചെയ്യുക", + "linked_oauth_account": "ലിങ്ക് ചെയ്ത OAuth അക്കൗണ്ട്", + "list": "ലിസ്റ്റ്", + "loading": "ലോഡിംഗ്", + "loading_search_results_failed": "തിരയൽ ഫലങ്ങൾ ലോഡുചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു", + "local": "പ്രാദേശികം", + "local_asset_cast_failed": "സെർവറിലേക്ക് അപ്‌ലോഡ് ചെയ്യാത്ത ഒരു അസറ്റ് കാസ്റ്റ് ചെയ്യാൻ കഴിയില്ല", + "local_assets": "പ്രാദേശിക അസറ്റുകൾ", + "local_media_summary": "പ്രാദേശിക മീഡിയ സംഗ്രഹം", + "local_network": "പ്രാദേശിക നെറ്റ്‌വർക്ക്", + "local_network_sheet_info": "നിർദ്ദിഷ്‌ട വൈ-ഫൈ നെറ്റ്‌വർക്ക് ഉപയോഗിക്കുമ്പോൾ ഈ URL വഴി ആപ്പ് സെർവറുമായി ബന്ധിപ്പിക്കും", + "location_permission": "ലൊക്കേഷൻ അനുമതി", + "location_permission_content": "യാന്ത്രിക-സ്വിച്ചിംഗ് ഫീച്ചർ ഉപയോഗിക്കുന്നതിന്, Immich-ന് കൃത്യമായ ലൊക്കേഷൻ അനുമതി ആവശ്യമാണ്, അതുവഴി നിലവിലെ വൈ-ഫൈ നെറ്റ്‌വർക്കിന്റെ പേര് വായിക്കാൻ കഴിയും", + "location_picker_choose_on_map": "മാപ്പിൽ തിരഞ്ഞെടുക്കുക", + "location_picker_latitude_error": "സാധുവായ അക്ഷാംശം നൽകുക", + "location_picker_latitude_hint": "നിങ്ങളുടെ അക്ഷാംശം ഇവിടെ നൽകുക", + "location_picker_longitude_error": "സാധുവായ രേഖാംശം നൽകുക", + "location_picker_longitude_hint": "നിങ്ങളുടെ രേഖാംശം ഇവിടെ നൽകുക", + "lock": "ലോക്ക് ചെയ്യുക", + "locked_folder": "ലോക്ക് ചെയ്ത ഫോൾഡർ", + "log_detail_title": "ലോഗ് വിശദാംശം", + "log_out": "ലോഗ് ഔട്ട്", + "log_out_all_devices": "എല്ലാ ഉപകരണങ്ങളിൽ നിന്നും ലോഗ് ഔട്ട് ചെയ്യുക", + "logged_in_as": "{user} ആയി ലോഗിൻ ചെയ്തു", + "logged_out_all_devices": "എല്ലാ ഉപകരണങ്ങളിൽ നിന്നും ലോഗ് ഔട്ട് ചെയ്തു", + "logged_out_device": "ഉപകരണത്തിൽ നിന്ന് ലോഗ് ഔട്ട് ചെയ്തു", + "login": "ലോഗിൻ", + "login_disabled": "ലോഗിൻ പ്രവർത്തനരഹിതമാക്കി", + "login_form_api_exception": "API എക്സെപ്ഷൻ. ദയവായി സെർവർ URL പരിശോധിച്ച് വീണ്ടും ശ്രമിക്കുക.", + "login_form_back_button_text": "തിരികെ", + "login_form_email_hint": "youremail@email.com", + "login_form_endpoint_hint": "http://your-server-ip:port", + "login_form_endpoint_url": "സെർവർ എൻഡ്‌പോയിന്റ് URL", + "login_form_err_http": "ദയവായി http:// അല്ലെങ്കിൽ https:// എന്ന് വ്യക്തമാക്കുക", + "login_form_err_invalid_email": "അസാധുവായ ഇമെയിൽ", + "login_form_err_invalid_url": "അസാധുവായ URL", + "login_form_err_leading_whitespace": "തുടക്കത്തിലെ ശൂന്യസ്ഥലം", + "login_form_err_trailing_whitespace": "അവസാനത്തെ ശൂന്യസ്ഥലം", + "login_form_failed_get_oauth_server_config": "OAuth ഉപയോഗിച്ച് ലോഗിൻ ചെയ്യുന്നതിൽ പിശക്, സെർവർ URL പരിശോധിക്കുക", + "login_form_failed_get_oauth_server_disable": "ഈ സെർവറിൽ OAuth ഫീച്ചർ ലഭ്യമല്ല", + "login_form_failed_login": "നിങ്ങളെ ലോഗിൻ ചെയ്യുന്നതിൽ പിശക്, സെർവർ URL, ഇമെയിൽ, പാസ്‌വേഡ് എന്നിവ പരിശോധിക്കുക", + "login_form_handshake_exception": "സെർവറുമായി ഒരു ഹാൻഡ്‌ഷേക്ക് എക്സെപ്ഷൻ ഉണ്ടായി. നിങ്ങൾ ഒരു സ്വയം ഒപ്പിട്ട സർട്ടിഫിക്കറ്റാണ് ഉപയോഗിക്കുന്നതെങ്കിൽ ക്രമീകരണങ്ങളിൽ സ്വയം ഒപ്പിട്ട സർട്ടിഫിക്കറ്റ് പിന്തുണ പ്രവർത്തനക്ഷമമാക്കുക.", + "login_form_password_hint": "പാസ്‌വേഡ്", + "login_form_save_login": "ലോഗിൻ ആയി തുടരുക", + "login_form_server_empty": "ഒരു സെർവർ URL നൽകുക.", + "login_form_server_error": "സെർവറിലേക്ക് കണക്റ്റുചെയ്യാൻ കഴിഞ്ഞില്ല.", + "login_has_been_disabled": "ലോഗിൻ പ്രവർത്തനരഹിതമാക്കി.", + "login_password_changed_error": "നിങ്ങളുടെ പാസ്‌വേഡ് അപ്ഡേറ്റ് ചെയ്യുന്നതിൽ ഒരു പിശകുണ്ടായി", + "login_password_changed_success": "പാസ്‌വേഡ് വിജയകരമായി അപ്ഡേറ്റ് ചെയ്തു", + "logout_all_device_confirmation": "എല്ലാ ഉപകരണങ്ങളിൽ നിന്നും ലോഗ് ഔട്ട് ചെയ്യണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?", + "logout_this_device_confirmation": "ഈ ഉപകരണത്തിൽ നിന്ന് ലോഗ് ഔട്ട് ചെയ്യണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?", + "logs": "ലോഗുകൾ", + "longitude": "രേഖാംശം", + "look": "കാഴ്ച", + "loop_videos": "വീഡിയോകൾ ലൂപ്പ് ചെയ്യുക", + "loop_videos_description": "വിശദാംശ വ്യൂവറിൽ ഒരു വീഡിയോ യാന്ത്രികമായി ലൂപ്പ് ചെയ്യാൻ പ്രവർത്തനക്ഷമമാക്കുക.", + "main_branch_warning": "നിങ്ങൾ ഒരു ഡെവലപ്‌മെന്റ് പതിപ്പാണ് ഉപയോഗിക്കുന്നത്; ഒരു റിലീസ് പതിപ്പ് ഉപയോഗിക്കാൻ ഞങ്ങൾ ശക്തമായി ശുപാർശ ചെയ്യുന്നു!", + "main_menu": "പ്രധാന മെനു", + "make": "നിർമ്മാതാവ്", + "manage_geolocation": "സ്ഥാനം കൈകാര്യം ചെയ്യുക", + "manage_shared_links": "പങ്കിട്ട ലിങ്കുകൾ കൈകാര്യം ചെയ്യുക", + "manage_sharing_with_partners": "പങ്കാളികളുമായുള്ള പങ്കിടൽ കൈകാര്യം ചെയ്യുക", + "manage_the_app_settings": "ആപ്പ് ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക", + "manage_your_account": "നിങ്ങളുടെ അക്കൗണ്ട് കൈകാര്യം ചെയ്യുക", + "manage_your_api_keys": "നിങ്ങളുടെ API കീകൾ കൈകാര്യം ചെയ്യുക", + "manage_your_devices": "നിങ്ങൾ ലോഗിൻ ചെയ്ത ഉപകരണങ്ങൾ കൈകാര്യം ചെയ്യുക", + "manage_your_oauth_connection": "നിങ്ങളുടെ OAuth കണക്ഷൻ കൈകാര്യം ചെയ്യുക", + "map": "മാപ്പ്", + "map_assets_in_bounds": "{count, plural, =0 {ഈ പ്രദേശത്ത് ഫോട്ടോകളൊന്നുമില്ല} one {# ഫോട്ടോ} other {# ഫോട്ടോകൾ}}", + "map_cannot_get_user_location": "ഉപയോക്താവിന്റെ സ്ഥാനം ലഭിക്കുന്നില്ല", + "map_location_dialog_yes": "അതെ", + "map_location_picker_page_use_location": "ഈ സ്ഥലം ഉപയോഗിക്കുക", + "map_location_service_disabled_content": "നിങ്ങളുടെ നിലവിലെ സ്ഥാനത്ത് നിന്നുള്ള അസറ്റുകൾ പ്രദർശിപ്പിക്കുന്നതിന് ലൊക്കേഷൻ സേവനം പ്രവർത്തനക്ഷമമാക്കേണ്ടതുണ്ട്. ഇപ്പോൾ പ്രവർത്തനക്ഷമമാക്കണോ?", + "map_location_service_disabled_title": "ലൊക്കേഷൻ സേവനം പ്രവർത്തനരഹിതമാക്കി", + "map_marker_for_images": "{city}, {country} എന്നിവിടങ്ങളിൽ എടുത്ത ചിത്രങ്ങൾക്കുള്ള മാപ്പ് മാർക്കർ", + "map_marker_with_image": "ചിത്രത്തോടുകൂടിയ മാപ്പ് മാർക്കർ", + "map_no_location_permission_content": "നിങ്ങളുടെ നിലവിലെ സ്ഥാനത്ത് നിന്നുള്ള അസറ്റുകൾ പ്രദർശിപ്പിക്കുന്നതിന് ലൊക്കേഷൻ അനുമതി ആവശ്യമാണ്. ഇപ്പോൾ അനുവദിക്കണോ?", + "map_no_location_permission_title": "ലൊക്കേഷൻ അനുമതി നിഷേധിച്ചു", + "map_settings": "മാപ്പ് ക്രമീകരണങ്ങൾ", + "map_settings_dark_mode": "ഡാർക്ക് മോഡ്", + "map_settings_date_range_option_day": "കഴിഞ്ഞ 24 മണിക്കൂർ", + "map_settings_date_range_option_days": "കഴിഞ്ഞ {days} ദിവസങ്ങൾ", + "map_settings_date_range_option_year": "കഴിഞ്ഞ വർഷം", + "map_settings_date_range_option_years": "കഴിഞ്ഞ {years} വർഷങ്ങൾ", + "map_settings_dialog_title": "മാപ്പ് ക്രമീകരണങ്ങൾ", + "map_settings_include_show_archived": "ആർക്കൈവുചെയ്‌തവ ഉൾപ്പെടുത്തുക", + "map_settings_include_show_partners": "പങ്കാളികളെ ഉൾപ്പെടുത്തുക", + "map_settings_only_show_favorites": "പ്രിയപ്പെട്ടവ മാത്രം കാണിക്കുക", + "map_settings_theme_settings": "മാപ്പ് തീം", + "map_zoom_to_see_photos": "ഫോട്ടോകൾ കാണാൻ സൂം ഔട്ട് ചെയ്യുക", + "mark_all_as_read": "എല്ലാം വായിച്ചതായി അടയാളപ്പെടുത്തുക", + "mark_as_read": "വായിച്ചതായി അടയാളപ്പെടുത്തുക", + "marked_all_as_read": "എല്ലാം വായിച്ചതായി അടയാളപ്പെടുത്തി", + "matches": "ചേർച്ചകൾ", + "matching_assets": "ചേർച്ചയുള്ള അസറ്റുകൾ", + "media_type": "മീഡിയ തരം", + "memories": "ഓർമ്മകൾ", + "memories_all_caught_up": "എല്ലാം കണ്ടുതീർത്തു", + "memories_check_back_tomorrow": "കൂടുതൽ ഓർമ്മകൾക്കായി നാളെ വീണ്ടും പരിശോധിക്കുക", + "memories_setting_description": "നിങ്ങളുടെ ഓർമ്മകളിൽ നിങ്ങൾ കാണുന്നത് കൈകാര്യം ചെയ്യുക", + "memories_start_over": "വീണ്ടും തുടങ്ങുക", + "memories_swipe_to_close": "അടയ്ക്കാൻ മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക", + "memory": "മെമ്മറി", + "memory_lane_title": "ഓർമ്മകളുടെ പാത {title}", + "menu": "മെനു", + "merge": "ലയിപ്പിക്കുക", + "merge_people": "ആളുകളെ ലയിപ്പിക്കുക", + "merge_people_limit": "ഒരു സമയം 5 മുഖങ്ങൾ വരെ മാത്രമേ ലയിപ്പിക്കാൻ കഴിയൂ", + "merge_people_prompt": "ഈ ആളുകളെ ലയിപ്പിക്കണോ? ഈ പ്രവർത്തനം പഴയപടിയാക്കാൻ കഴിയില്ല.", + "merge_people_successfully": "ആളുകളെ വിജയകരമായി ലയിപ്പിച്ചു", + "merged_people_count": "{count, plural, one {# വ്യക്തിയെ ലയിപ്പിച്ചു} other {# ആളുകളെ ലയിപ്പിച്ചു}}", + "minimize": "ചെറുതാക്കുക", + "minute": "മിനിറ്റ്", + "minutes": "മിനിറ്റുകൾ", + "missing": "കാണാനില്ല", + "model": "മോഡൽ", + "month": "മാസം", + "monthly_title_text_date_format": "MMMM y", + "more": "കൂടുതൽ", + "move": "നീക്കുക", + "move_off_locked_folder": "ലോക്ക് ചെയ്ത ഫോൾഡറിൽ നിന്ന് മാറ്റുക", + "move_to_lock_folder_action_prompt": "{count} എണ്ണം ലോക്ക് ചെയ്ത ഫോൾഡറിലേക്ക് ചേർത്തു", + "move_to_locked_folder": "ലോക്ക് ചെയ്ത ഫോൾഡറിലേക്ക് മാറ്റുക", + "move_to_locked_folder_confirmation": "ഈ ഫോട്ടോകളും വീഡിയോയും എല്ലാ ആൽബങ്ങളിൽ നിന്നും നീക്കം ചെയ്യപ്പെടും, ലോക്ക് ചെയ്ത ഫോൾഡറിൽ നിന്ന് മാത്രമേ കാണാൻ കഴിയൂ", + "moved_to_archive": "{count, plural, one {# അസറ്റ്} other {# അസറ്റുകൾ}} ആർക്കൈവിലേക്ക് മാറ്റി", + "moved_to_library": "{count, plural, one {# അസറ്റ്} other {# അസറ്റുകൾ}} ലൈബ്രറിയിലേക്ക് മാറ്റി", + "moved_to_trash": "ട്രാഷിലേക്ക് മാറ്റി", + "multiselect_grid_edit_date_time_err_read_only": "വായിക്കാൻ മാത്രമുള്ള അസറ്റി(കളു)ടെ തീയതി എഡിറ്റുചെയ്യാൻ കഴിയില്ല, ഒഴിവാക്കുന്നു", + "multiselect_grid_edit_gps_err_read_only": "വായിക്കാൻ മാത്രമുള്ള അസറ്റി(കളു)ടെ സ്ഥാനം എഡിറ്റുചെയ്യാൻ കഴിയില്ല, ഒഴിവാക്കുന്നു", + "mute_memories": "ഓർമ്മകൾ നിശ്ശബ്ദമാക്കുക", + "my_albums": "എന്റെ ആൽബങ്ങൾ", + "name": "പേര്", + "name_or_nickname": "പേര് അല്ലെങ്കിൽ വിളിപ്പേര്", + "network_requirement_photos_upload": "ഫോട്ടോകൾ ബാക്കപ്പ് ചെയ്യാൻ സെല്ലുലാർ ഡാറ്റ ഉപയോഗിക്കുക", + "network_requirement_videos_upload": "വീഡിയോകൾ ബാക്കപ്പ് ചെയ്യാൻ സെല്ലുലാർ ഡാറ്റ ഉപയോഗിക്കുക", + "network_requirements": "നെറ്റ്‌വർക്ക് ആവശ്യകതകൾ", + "network_requirements_updated": "നെറ്റ്‌വർക്ക് ആവശ്യകതകൾ മാറി, ബാക്കപ്പ് ക്യൂ പുനഃസജ്ജമാക്കുന്നു", + "networking_settings": "നെറ്റ്‌വർക്കിംഗ്", + "networking_subtitle": "സെർവർ എൻഡ്‌പോയിന്റ് ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക", + "never": "ഒരിക്കലും ഇല്ല", + "new_album": "പുതിയ ആൽബം", + "new_api_key": "പുതിയ API കീ", + "new_password": "പുതിയ പാസ്‌വേഡ്", + "new_person": "പുതിയ വ്യക്തി", + "new_pin_code": "പുതിയ പിൻ കോഡ്", + "new_pin_code_subtitle": "നിങ്ങൾ ആദ്യമായിട്ടാണ് ലോക്ക് ചെയ്ത ഫോൾഡർ ആക്‌സസ് ചെയ്യുന്നത്. ഈ പേജ് സുരക്ഷിതമായി ആക്‌സസ് ചെയ്യാൻ ഒരു പിൻ കോഡ് ഉണ്ടാക്കുക", + "new_timeline": "പുതിയ ടൈംലൈൻ", + "new_user_created": "പുതിയ ഉപയോക്താവിനെ ഉണ്ടാക്കി", + "new_version_available": "പുതിയ പതിപ്പ് ലഭ്യമാണ്", + "newest_first": "ഏറ്റവും പുതിയത് ആദ്യം", + "next": "അടുത്തത്", + "next_memory": "അടുത്ത ഓർമ്മ", + "no": "ഇല്ല", + "no_albums_message": "നിങ്ങളുടെ ഫോട്ടോകളും വീഡിയോകളും ക്രമീകരിക്കാൻ ഒരു ആൽബം ഉണ്ടാക്കുക", + "no_albums_with_name_yet": "ഈ പേരിൽ നിങ്ങൾക്ക് ഇതുവരെ ആൽബങ്ങളൊന്നും ഇല്ലെന്ന് തോന്നുന്നു.", + "no_albums_yet": "നിങ്ങൾക്ക് ഇതുവരെ ആൽബങ്ങളൊന്നും ഇല്ലെന്ന് തോന്നുന്നു.", + "no_archived_assets_message": "നിങ്ങളുടെ ഫോട്ടോ കാഴ്‌ചയിൽ നിന്ന് മറയ്ക്കാൻ ഫോട്ടോകളും വീഡിയോകളും ആർക്കൈവ് ചെയ്യുക", + "no_assets_message": "നിങ്ങളുടെ ആദ്യ ഫോട്ടോ അപ്‌ലോഡ് ചെയ്യാൻ ക്ലിക്കുചെയ്യുക", + "no_assets_to_show": "കാണിക്കാൻ അസറ്റുകളൊന്നുമില്ല", + "no_cast_devices_found": "കാസ്റ്റ് ഉപകരണങ്ങളൊന്നും കണ്ടെത്തിയില്ല", + "no_checksum_local": "ചെക്ക്സം ലഭ്യമല്ല - പ്രാദേശിക അസറ്റുകൾ ലഭ്യമാക്കാൻ കഴിയില്ല", + "no_checksum_remote": "ചെക്ക്സം ലഭ്യമല്ല - റിമോട്ട് അസറ്റ് ലഭ്യമാക്കാൻ കഴിയില്ല", + "no_duplicates_found": "ഡ്യൂപ്ലിക്കേറ്റുകളൊന്നും കണ്ടെത്തിയില്ല.", + "no_exif_info_available": "Exif വിവരങ്ങൾ ലഭ്യമല്ല", + "no_explore_results_message": "നിങ്ങളുടെ ശേഖരം പര്യവേക്ഷണം ചെയ്യാൻ കൂടുതൽ ഫോട്ടോകൾ അപ്‌ലോഡ് ചെയ്യുക.", + "no_favorites_message": "നിങ്ങളുടെ മികച്ച ചിത്രങ്ങളും വീഡിയോകളും വേഗത്തിൽ കണ്ടെത്താൻ പ്രിയപ്പെട്ടവ ചേർക്കുക", + "no_libraries_message": "നിങ്ങളുടെ ഫോട്ടോകളും വീഡിയോകളും കാണുന്നതിന് ഒരു ബാഹ്യ ലൈബ്രറി ഉണ്ടാക്കുക", + "no_local_assets_found": "ഈ ചെക്ക്സം ഉപയോഗിച്ച് പ്രാദേശിക അസറ്റുകളൊന്നും കണ്ടെത്തിയില്ല", + "no_locked_photos_message": "ലോക്ക് ചെയ്‌ത ഫോൾഡറിലെ ഫോട്ടോകളും വീഡിയോകളും മറച്ചിരിക്കുന്നു, നിങ്ങൾ ലൈബ്രറി ബ്രൗസ് ചെയ്യുമ്പോഴോ തിരയുമ്പോഴോ അവ ദൃശ്യമാകില്ല.", + "no_name": "പേരില്ല", + "no_notifications": "അറിയിപ്പുകളൊന്നുമില്ല", + "no_people_found": "ചേർച്ചയുള്ള ആളുകളെ കണ്ടെത്തിയില്ല", + "no_places": "സ്ഥലങ്ങളില്ല", + "no_remote_assets_found": "ഈ ചെക്ക്സം ഉപയോഗിച്ച് റിമോട്ട് അസറ്റുകളൊന്നും കണ്ടെത്തിയില്ല", + "no_results": "ഫലങ്ങളൊന്നുമില്ല", + "no_results_description": "ഒരു പര്യായപദമോ കൂടുതൽ പൊതുവായ കീവേഡോ ഉപയോഗിച്ച് ശ്രമിക്കുക", + "no_shared_albums_message": "നിങ്ങളുടെ നെറ്റ്‌വർക്കിലെ ആളുകളുമായി ഫോട്ടോകളും വീഡിയോകളും പങ്കിടാൻ ഒരു ആൽബം ഉണ്ടാക്കുക", + "no_uploads_in_progress": "അപ്‌ലോഡുകളൊന്നും പുരോഗമിക്കുന്നില്ല", + "not_available": "ലഭ്യമല്ല", + "not_in_any_album": "ഒരു ആൽബത്തിലുമില്ല", + "not_selected": "തിരഞ്ഞെടുത്തിട്ടില്ല", + "note_apply_storage_label_to_previously_uploaded assets": "കുറിപ്പ്: മുമ്പ് അപ്‌ലോഡ് ചെയ്ത അസറ്റുകളിൽ സ്റ്റോറേജ് ലേബൽ പ്രയോഗിക്കാൻ, ഇത് പ്രവർത്തിപ്പിക്കുക", + "notes": "കുറിപ്പുകൾ", + "nothing_here_yet": "ഇവിടെ ഇതുവരെ ഒന്നുമില്ല", + "notification_permission_dialog_content": "അറിയിപ്പുകൾ പ്രവർത്തനക്ഷമമാക്കാൻ, ക്രമീകരണങ്ങളിലേക്ക് പോയി 'അനുവദിക്കുക' തിരഞ്ഞെടുക്കുക.", + "notification_permission_list_tile_content": "അറിയിപ്പുകൾ പ്രവർത്തനക്ഷമമാക്കാൻ അനുമതി നൽകുക.", + "notification_permission_list_tile_enable_button": "അറിയിപ്പുകൾ പ്രവർത്തനക്ഷമമാക്കുക", + "notification_permission_list_tile_title": "അറിയിപ്പ് അനുമതി", + "notification_toggle_setting_description": "ഇമെയിൽ അറിയിപ്പുകൾ പ്രവർത്തനക്ഷമമാക്കുക", + "notifications": "അറിയിപ്പുകൾ", + "notifications_setting_description": "അറിയിപ്പുകൾ കൈകാര്യം ചെയ്യുക", + "oauth": "OAuth", + "official_immich_resources": "Immich-ന്റെ ഔദ്യോഗിക ഉറവിടങ്ങൾ", + "offline": "ഓഫ്‌ലൈൻ", + "offset": "ഓഫ്സെറ്റ്", + "ok": "ശരി", + "oldest_first": "ഏറ്റവും പഴയത് ആദ്യം", + "on_this_device": "ഈ ഉപകരണത്തിൽ", + "onboarding": "ഓൺബോർഡിംഗ്", + "onboarding_locale_description": "നിങ്ങൾക്കിഷ്ടപ്പെട്ട ഭാഷ തിരഞ്ഞെടുക്കുക. ഇത് പിന്നീട് നിങ്ങളുടെ ക്രമീകരണങ്ങളിൽ മാറ്റാവുന്നതാണ്.", + "onboarding_privacy_description": "ഇനിപ്പറയുന്ന (ഓപ്ഷണൽ) ഫീച്ചറുകൾ ബാഹ്യ സേവനങ്ങളെ ആശ്രയിച്ചിരിക്കുന്നു, അവ എപ്പോൾ വേണമെങ്കിലും ക്രമീകരണങ്ങളിൽ പ്രവർത്തനരഹിതമാക്കാം.", + "onboarding_server_welcome_description": "ചില സാധാരണ ക്രമീകരണങ്ങൾ ഉപയോഗിച്ച് നിങ്ങളുടെ ഇൻസ്റ്റൻസ് സജ്ജമാക്കാം.", + "onboarding_theme_description": "നിങ്ങളുടെ ഇൻസ്റ്റൻസിനായി ഒരു കളർ തീം തിരഞ്ഞെടുക്കുക. ഇത് പിന്നീട് നിങ്ങളുടെ ക്രമീകരണങ്ങളിൽ മാറ്റാവുന്നതാണ്.", + "onboarding_user_welcome_description": "നമുക്ക് ആരംഭിക്കാം!", + "onboarding_welcome_user": "സ്വാഗതം, {user}", + "online": "ഓൺലൈൻ", + "only_favorites": "പ്രിയപ്പെട്ടവ മാത്രം", + "open": "തുറക്കുക", + "open_in_map_view": "മാപ്പ് കാഴ്‌ചയിൽ തുറക്കുക", + "open_in_openstreetmap": "OpenStreetMap-ൽ തുറക്കുക", + "open_the_search_filters": "തിരയൽ ഫിൽട്ടറുകൾ തുറക്കുക", + "options": "ഓപ്ഷനുകൾ", + "or": "അല്ലെങ്കിൽ", + "organize_into_albums": "ആൽബങ്ങളായി ക്രമീകരിക്കുക", + "organize_into_albums_description": "നിലവിലെ സിങ്ക് ക്രമീകരണങ്ങൾ ഉപയോഗിച്ച് നിലവിലുള്ള ഫോട്ടോകൾ ആൽബങ്ങളിലേക്ക് മാറ്റുക", + "organize_your_library": "നിങ്ങളുടെ ലൈബ്രറി ക്രമീകരിക്കുക", + "original": "യഥാർത്ഥം", + "other": "മറ്റുള്ളവ", + "other_devices": "മറ്റ് ഉപകരണങ്ങൾ", + "other_entities": "മറ്റ് എന്റിറ്റികൾ", + "other_variables": "മറ്റ് വേരിയബിളുകൾ", + "owned": "സ്വന്തമായുള്ളത്", + "owner": "ഉടമ", + "partner": "പങ്കാളി", + "partner_can_access": "{partner}-ക്ക് ആക്സസ് ചെയ്യാൻ കഴിയും", + "partner_can_access_assets": "ആർക്കൈവുചെയ്‌തതും ഇല്ലാതാക്കിയതുമായവ ഒഴികെ നിങ്ങളുടെ എല്ലാ ഫോട്ടോകളും വീഡിയോകളും", + "partner_can_access_location": "നിങ്ങളുടെ ഫോട്ടോകൾ എടുത്ത സ്ഥലം", + "partner_list_user_photos": "{user}-ന്റെ ഫോട്ടോകൾ", + "partner_list_view_all": "എല്ലാം കാണുക", + "partner_page_empty_message": "നിങ്ങളുടെ ഫോട്ടോകൾ ഇതുവരെ ഒരു പങ്കാളിയുമായും പങ്കിട്ടിട്ടില്ല.", + "partner_page_no_more_users": "ചേർക്കാൻ കൂടുതൽ ഉപയോക്താക്കളില്ല", + "partner_page_partner_add_failed": "പങ്കാളിയെ ചേർക്കുന്നതിൽ പരാജയപ്പെട്ടു", + "partner_page_select_partner": "പങ്കാളിയെ തിരഞ്ഞെടുക്കുക", + "partner_page_shared_to_title": "ഇതിലേക്ക് പങ്കിട്ടു", + "partner_page_stop_sharing_content": "{partner}-ക്ക് ഇനി നിങ്ങളുടെ ഫോട്ടോകൾ ആക്‌സസ് ചെയ്യാൻ കഴിയില്ല.", + "partner_sharing": "പങ്കാളിയുമായി പങ്കിടൽ", + "partners": "പങ്കാളികൾ", + "password": "പാസ്‌വേർഡ്", + "password_does_not_match": "പാസ്‌വേഡ് പൊരുത്തപ്പെടുന്നില്ല", + "password_required": "പാസ്‌വേഡ് ആവശ്യമാണ്", + "password_reset_success": "പാസ്‌വേഡ് റീസെറ്റ് വിജയിച്ചു", + "past_durations": { + "days": "കഴിഞ്ഞ {days, plural, one {ദിവസം} other {# ദിവസങ്ങൾ}}", + "hours": "കഴിഞ്ഞ {hours, plural, one {മണിക്കൂർ} other {# മണിക്കൂറുകൾ}}", + "years": "കഴിഞ്ഞ {years, plural, one {വർഷം} other {# വർഷങ്ങൾ}}" + }, + "path": "പാത്ത്", + "pattern": "പാറ്റേൺ", + "pause": "താൽക്കാലികമായി നിർത്തുക", + "pause_memories": "ഓർമ്മകൾ താൽക്കാലികമായി നിർത്തുക", + "paused": "താൽക്കാലികമായി നിർത്തി", + "pending": "കാത്തിരിക്കുന്നു", + "people": "ആളുകൾ", + "people_edits_count": "{count, plural, one {# വ്യക്തിയെ എഡിറ്റുചെയ്തു} other {# ആളുകളെ എഡിറ്റുചെയ്തു}}", + "people_feature_description": "ആളുകളനുസരിച്ച് ഗ്രൂപ്പ് ചെയ്ത ഫോട്ടോകളും വീഡിയോകളും ബ്രൗസുചെയ്യുന്നു", + "people_sidebar_description": "സൈഡ്‌ബാറിൽ 'ആളുകൾ' എന്നതിലേക്ക് ഒരു ലിങ്ക് പ്രദർശിപ്പിക്കുക", + "permanent_deletion_warning": "ശാശ്വതമായ ഇല്ലാതാക്കൽ മുന്നറിയിപ്പ്", + "permanent_deletion_warning_setting_description": "അസറ്റുകൾ ശാശ്വതമായി ഇല്ലാതാക്കുമ്പോൾ ഒരു മുന്നറിയിപ്പ് കാണിക്കുക", + "permanently_delete": "ശാശ്വതമായി ഇല്ലാതാക്കുക", + "permanently_delete_assets_count": "{count, plural, one {അസറ്റ്} other {അസറ്റുകൾ}} ശാശ്വതമായി ഇല്ലാതാക്കുക", + "permanently_delete_assets_prompt": "{count, plural, one {ഈ അസറ്റ് ശാശ്വതമായി ഇല്ലാതാക്കണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?} other {ഈ # അസറ്റുകൾ ശാശ്വതമായി ഇല്ലാതാക്കണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?}} ഇത് {count, plural, one {അതിനെ അതിന്റെ} other {അവയെ അവയുടെ}} ആൽബത്തിൽ(ങ്ങളിൽ) നിന്നും നീക്കം ചെയ്യും.", + "permanently_deleted_asset": "അസറ്റ് ശാശ്വതമായി ഇല്ലാതാക്കി", + "permanently_deleted_assets_count": "{count, plural, one {# അസറ്റ്} other {# അസറ്റുകൾ}} ശാശ്വതമായി ഇല്ലാതാക്കി", + "permission": "അനുമതി", + "permission_empty": "നിങ്ങളുടെ അനുമതി ശൂന്യമാകരുത്", + "permission_onboarding_back": "തിരികെ", + "permission_onboarding_continue_anyway": "എന്തായാലും തുടരുക", + "permission_onboarding_get_started": "തുടങ്ങാം", + "permission_onboarding_go_to_settings": "ക്രമീകരണങ്ങളിലേക്ക് പോകുക", + "permission_onboarding_permission_denied": "അനുമതി നിഷേധിച്ചു. Immich ഉപയോഗിക്കുന്നതിന്, ക്രമീകരണങ്ങളിൽ ഫോട്ടോ, വീഡിയോ അനുമതികൾ നൽകുക.", + "permission_onboarding_permission_granted": "അനുമതി ലഭിച്ചു! നിങ്ങൾ തയ്യാറാണ്.", + "permission_onboarding_permission_limited": "അനുമതി പരിമിതമാണ്. Immich-ന് നിങ്ങളുടെ മുഴുവൻ ഗാലറി ശേഖരവും ബാക്കപ്പ് ചെയ്യാനും കൈകാര്യം ചെയ്യാനും അനുവദിക്കുന്നതിന്, ക്രമീകരണങ്ങളിൽ ഫോട്ടോ, വീഡിയോ അനുമതികൾ നൽകുക.", + "permission_onboarding_request": "നിങ്ങളുടെ ഫോട്ടോകളും വീഡിയോകളും കാണുന്നതിന് Immich-ന് അനുമതി ആവശ്യമാണ്.", + "person": "വ്യക്തി", + "person_age_months": "{months, plural, one {# മാസം} other {# മാസം}} പ്രായം", + "person_age_year_months": "1 വർഷവും {months, plural, one {# മാസവും} other {# മാസവും}} പ്രായം", + "person_age_years": "{years, plural, other {# വയസ്സ്}}", + "person_birthdate": "{date}-ന് ജനിച്ചു", + "person_hidden": "{name}{hidden, select, true { (മറച്ചത്)} other {}}", + "photo_shared_all_users": "നിങ്ങൾ എല്ലാ ഉപയോക്താക്കളുമായും ഫോട്ടോകൾ പങ്കിട്ടു, അല്ലെങ്കിൽ നിങ്ങൾക്ക് പങ്കിടാൻ ഉപയോക്താക്കളാരും ഇല്ലെന്നു തോന്നുന്നു.", + "photos": "ഫോട്ടോകൾ", + "photos_and_videos": "ഫോട്ടോകളും വീഡിയോകളും", + "photos_count": "{count, plural, one {{count, number} ഫോട്ടോ} other {{count, number} ഫോട്ടോകൾ}}", + "photos_from_previous_years": "മുൻ വർഷങ്ങളിലെ ഫോട്ടോകൾ", + "pick_a_location": "ഒരു സ്ഥലം തിരഞ്ഞെടുക്കുക", + "pin_code_changed_successfully": "പിൻ കോഡ് വിജയകരമായി മാറ്റി", + "pin_code_reset_successfully": "പിൻ കോഡ് വിജയകരമായി റീസെറ്റ് ചെയ്തു", + "pin_code_setup_successfully": "പിൻ കോഡ് വിജയകരമായി സജ്ജീകരിച്ചു", + "pin_verification": "പിൻ കോഡ് പരിശോധന", + "place": "സ്ഥലം", + "places": "സ്ഥലങ്ങൾ", + "places_count": "{count, plural, one {{count, number} സ്ഥലം} other {{count, number} സ്ഥലങ്ങൾ}}", + "play": "പ്ലേ ചെയ്യുക", + "play_memories": "ഓർമ്മകൾ പ്ലേ ചെയ്യുക", + "play_motion_photo": "ചലിക്കുന്ന ഫോട്ടോ പ്ലേ ചെയ്യുക", + "play_or_pause_video": "വീഡിയോ പ്ലേ ചെയ്യുക അല്ലെങ്കിൽ താൽക്കാലികമായി നിർത്തുക", + "please_auth_to_access": "ആക്‌സസ് ചെയ്യുന്നതിന് ദയവായി പ്രാമാണീകരിക്കുക", + "port": "പോർട്ട്", + "preferences_settings_subtitle": "ആപ്പിന്റെ മുൻഗണനകൾ കൈകാര്യം ചെയ്യുക", + "preferences_settings_title": "മുൻഗണനകൾ", + "preparing": "തയ്യാറാക്കുന്നു", + "preset": "പ്രീസെറ്റ്", + "preview": "പ്രിവ്യൂ", + "previous": "മുമ്പത്തെ", + "previous_memory": "മുമ്പത്തെ ഓർമ്മ", + "previous_or_next_day": "ദിവസം മുന്നോട്ട്/പുറകോട്ട്", + "previous_or_next_month": "മാസം മുന്നോട്ട്/പുറകോട്ട്", + "previous_or_next_photo": "ഫോട്ടോ മുന്നോട്ട്/പുറകോട്ട്", + "previous_or_next_year": "വർഷം മുന്നോട്ട്/പുറകോട്ട്", + "primary": "പ്രധാനമായത്", + "privacy": "സ്വകാര്യത", + "profile": "പ്രൊഫൈൽ", + "profile_drawer_app_logs": "ലോഗുകൾ", + "profile_drawer_client_out_of_date_major": "മൊബൈൽ ആപ്പ് കാലഹരണപ്പെട്ടു. ദയവായി ഏറ്റവും പുതിയ പ്രധാന പതിപ്പിലേക്ക് അപ്ഡേറ്റ് ചെയ്യുക.", + "profile_drawer_client_out_of_date_minor": "മൊബൈൽ ആപ്പ് കാലഹരണപ്പെട്ടു. ദയവായി ഏറ്റവും പുതിയ മൈനർ പതിപ്പിലേക്ക് അപ്ഡേറ്റ് ചെയ്യുക.", + "profile_drawer_client_server_up_to_date": "ക്ലയിന്റും സെർവറും ഏറ്റവും പുതിയതാണ്", + "profile_drawer_github": "ഗിറ്റ്ഹബ്", + "profile_drawer_readonly_mode": "റീഡ്-ഓൺലി മോഡ് പ്രവർത്തനക്ഷമമാക്കി. പുറത്തുകടക്കാൻ ഉപയോക്തൃ അവതാർ ഐക്കണിൽ ദീർഘനേരം അമർത്തുക.", + "profile_drawer_server_out_of_date_major": "സെർവർ കാലഹരണപ്പെട്ടു. ദയവായി ഏറ്റവും പുതിയ പ്രധാന പതിപ്പിലേക്ക് അപ്ഡേറ്റ് ചെയ്യുക.", + "profile_drawer_server_out_of_date_minor": "സെർവർ കാലഹരണപ്പെട്ടു. ദയവായി ഏറ്റവും പുതിയ മൈനർ പതിപ്പിലേക്ക് അപ്ഡേറ്റ് ചെയ്യുക.", + "profile_image_of_user": "{user}-ന്റെ പ്രൊഫൈൽ ചിത്രം", + "profile_picture_set": "പ്രൊഫൈൽ ചിത്രം സജ്ജീകരിച്ചു.", + "public_album": "പൊതു ആൽബം", + "public_share": "പൊതു പങ്കിടൽ", + "purchase_account_info": "സഹായി", + "purchase_activated_subtitle": "Immich-നെയും ഓപ്പൺ സോഴ്‌സ് സോഫ്റ്റ്‌വെയറിനെയും പിന്തുണച്ചതിന് നന്ദി", + "purchase_activated_time": "{date}-ന് സജീവമാക്കി", + "purchase_activated_title": "നിങ്ങളുടെ കീ വിജയകരമായി സജീവമാക്കി", + "purchase_button_activate": "സജീവമാക്കുക", + "purchase_button_buy": "വാങ്ങുക", + "purchase_button_buy_immich": "Immich വാങ്ങുക", + "purchase_button_never_show_again": "ഇനി കാണിക്കരുത്", + "purchase_button_reminder": "30 ദിവസത്തിനുള്ളിൽ ഓർമ്മിപ്പിക്കുക", + "purchase_button_remove_key": "കീ നീക്കം ചെയ്യുക", + "purchase_button_select": "തിരഞ്ഞെടുക്കുക", + "purchase_failed_activation": "സജീവമാക്കുന്നതിൽ പരാജയപ്പെട്ടു! ശരിയായ പ്രൊഡക്റ്റ് കീക്കായി ദയവായി നിങ്ങളുടെ ഇമെയിൽ പരിശോധിക്കുക!", + "purchase_individual_description_1": "ഒരു വ്യക്തിക്ക്", + "purchase_individual_description_2": "സഹായിയുടെ നില", + "purchase_individual_title": "വ്യക്തിഗതം", + "purchase_input_suggestion": "പ്രൊഡക്റ്റ് കീ ഉണ്ടോ? താഴെ കീ നൽകുക", + "purchase_license_subtitle": "സേവനത്തിന്റെ തുടർ വികസനത്തെ പിന്തുണയ്ക്കാൻ Immich വാങ്ങുക", + "purchase_lifetime_description": "ആജീവനാന്ത വാങ്ങൽ", + "purchase_option_title": "വാങ്ങാനുള്ള ഓപ്ഷനുകൾ", + "purchase_panel_info_1": "Immich നിർമ്മിക്കാൻ ധാരാളം സമയവും പ്രയത്നവും ആവശ്യമാണ്, അത് കഴിയുന്നത്ര മികച്ചതാക്കാൻ മുഴുവൻ സമയ എഞ്ചിനീയർമാർ പ്രവർത്തിക്കുന്നു. ഓപ്പൺ സോഴ്‌സ് സോഫ്റ്റ്‌വെയറും ധാർമ്മികമായ ബിസിനസ്സ് രീതികളും ഡെവലപ്പർമാർക്ക് സുസ്ഥിരമായ വരുമാന മാർഗ്ഗമാക്കുക, ചൂഷണപരമായ ക്ലൗഡ് സേവനങ്ങൾക്ക് യഥാർത്ഥ ബദലുകളുള്ള സ്വകാര്യതയെ മാനിക്കുന്ന ഒരു ആവാസവ്യവസ്ഥ സൃഷ്ടിക്കുക എന്നതാണ് ഞങ്ങളുടെ ദൗത്യം.", + "purchase_panel_info_2": "പേവാളുകൾ ചേർക്കില്ലെന്ന് ഞങ്ങൾ പ്രതിജ്ഞാബദ്ധരായതിനാൽ, ഈ വാങ്ങൽ നിങ്ങൾക്ക് Immich-ൽ അധിക ഫീച്ചറുകളൊന്നും നൽകില്ല. Immich-ന്റെ തുടർ വികസനത്തെ പിന്തുണയ്ക്കാൻ നിങ്ങളെപ്പോലുള്ള ഉപയോക്താക്കളെയാണ് ഞങ്ങൾ ആശ്രയിക്കുന്നത്.", + "purchase_panel_title": "പദ്ധതിയെ പിന്തുണയ്ക്കുക", + "purchase_per_server": "ഓരോ സെർവറിനും", + "purchase_per_user": "ഓരോ ഉപയോക്താവിനും", + "purchase_remove_product_key": "പ്രൊഡക്റ്റ് കീ നീക്കം ചെയ്യുക", + "purchase_remove_product_key_prompt": "പ്രൊഡക്റ്റ് കീ നീക്കം ചെയ്യണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?", + "purchase_remove_server_product_key": "സെർവർ പ്രൊഡക്റ്റ് കീ നീക്കം ചെയ്യുക", + "purchase_remove_server_product_key_prompt": "സെർവർ പ്രൊഡക്റ്റ് കീ നീക്കം ചെയ്യണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?", + "purchase_server_description_1": "മുഴുവൻ സെർവറിനും", + "purchase_server_description_2": "സഹായിയുടെ നില", + "purchase_server_title": "സെർവർ", + "purchase_settings_server_activated": "സെർവർ പ്രൊഡക്റ്റ് കീ അഡ്മിൻ ആണ് കൈകാര്യം ചെയ്യുന്നത്", + "query_asset_id": "അസറ്റ് ഐഡി അന്വേഷിക്കുക", + "queue_status": "ക്യൂ ചെയ്യുന്നു {count}/{total}", + "rating": "സ്റ്റാർ റേറ്റിംഗ്", + "rating_clear": "റേറ്റിംഗ് ക്ലിയർ ചെയ്യുക", + "rating_count": "{count, plural, one {# സ്റ്റാർ} other {# സ്റ്റാറുകൾ}}", + "rating_description": "വിവര പാനലിൽ EXIF റേറ്റിംഗ് പ്രദർശിപ്പിക്കുക", + "reaction_options": "പ്രതികരണ ഓപ്ഷനുകൾ", + "read_changelog": "മാറ്റങ്ങളുടെ ലോഗ് വായിക്കുക", + "readonly_mode_disabled": "റീഡ്-ഓൺലി മോഡ് പ്രവർത്തനരഹിതമാക്കി", + "readonly_mode_enabled": "റീഡ്-ഓൺലി മോഡ് പ്രവർത്തനക്ഷമമാക്കി", + "ready_for_upload": "അപ്‌ലോഡിനായി തയ്യാറാണ്", + "reassign": "വീണ്ടും നൽകുക", + "reassigned_assets_to_existing_person": "{count, plural, one {# അസറ്റ്} other {# അസറ്റുകൾ}} {name, select, null {നിലവിലുള്ള ഒരു വ്യക്തിക്ക്} other {{name}-ന്}} വീണ്ടും നൽകി", + "reassigned_assets_to_new_person": "{count, plural, one {# അസറ്റ്} other {# അസറ്റുകൾ}} ഒരു പുതിയ വ്യക്തിക്ക് വീണ്ടും നൽകി", + "reassing_hint": "തിരഞ്ഞെടുത്ത അസറ്റുകൾ നിലവിലുള്ള ഒരു വ്യക്തിക്ക് നൽകുക", + "recent": "സമീപകാലം", + "recent-albums": "സമീപകാല ആൽബങ്ങൾ", + "recent_searches": "സമീപകാല തിരയലുകൾ", + "recently_added": "അടുത്തിടെ ചേർത്തത്", + "recently_added_page_title": "അടുത്തിടെ ചേർത്തത്", + "recently_taken": "അടുത്തിടെ എടുത്തത്", + "recently_taken_page_title": "അടുത്തിടെ എടുത്തത്", + "refresh": "പുതുക്കുക", + "refresh_encoded_videos": "എൻകോഡ് ചെയ്ത വീഡിയോകൾ പുതുക്കുക", + "refresh_faces": "മുഖങ്ങൾ പുതുക്കുക", + "refresh_metadata": "മെറ്റാഡാറ്റ പുതുക്കുക", + "refresh_thumbnails": "തംബ്നെയിലുകൾ പുതുക്കുക", + "refreshed": "പുതുക്കി", + "refreshes_every_file": "നിലവിലുള്ളതും പുതിയതുമായ എല്ലാ ഫയലുകളും വീണ്ടും വായിക്കുന്നു", + "refreshing_encoded_video": "എൻകോഡ് ചെയ്ത വീഡിയോ പുതുക്കുന്നു", + "refreshing_faces": "മുഖങ്ങൾ പുതുക്കുന്നു", + "refreshing_metadata": "മെറ്റാഡാറ്റ പുതുക്കുന്നു", + "regenerating_thumbnails": "തംബ്നെയിലുകൾ പുനർനിർമ്മിക്കുന്നു", + "remote": "റിമോട്ട്", + "remote_assets": "റിമോട്ട് അസറ്റുകൾ", + "remote_media_summary": "റിമോട്ട് മീഡിയ സംഗ്രഹം", + "remove": "നീക്കം ചെയ്യുക", + "remove_assets_album_confirmation": "ആൽബത്തിൽ നിന്ന് {count, plural, one {# അസറ്റ്} other {# അസറ്റുകൾ}} നീക്കം ചെയ്യണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?", + "remove_assets_shared_link_confirmation": "ഈ പങ്കിട്ട ലിങ്കിൽ നിന്ന് {count, plural, one {# അസറ്റ്} other {# അസറ്റുകൾ}} നീക്കം ചെയ്യണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?", + "remove_assets_title": "അസറ്റുകൾ നീക്കം ചെയ്യണോ?", + "remove_custom_date_range": "കസ്റ്റം തീയതി പരിധി നീക്കം ചെയ്യുക", + "remove_deleted_assets": "ഇല്ലാതാക്കിയ അസറ്റുകൾ നീക്കം ചെയ്യുക", + "remove_from_album": "ആൽബത്തിൽ നിന്ന് നീക്കം ചെയ്യുക", + "remove_from_album_action_prompt": "{count} എണ്ണം ആൽബത്തിൽ നിന്ന് നീക്കം ചെയ്തു", + "remove_from_favorites": "പ്രിയപ്പെട്ടവയിൽ നിന്ന് നീക്കം ചെയ്യുക", + "remove_from_lock_folder_action_prompt": "{count} എണ്ണം ലോക്ക് ചെയ്ത ഫോൾഡറിൽ നിന്ന് നീക്കം ചെയ്തു", + "remove_from_locked_folder": "ലോക്ക് ചെയ്ത ഫോൾഡറിൽ നിന്ന് നീക്കം ചെയ്യുക", + "remove_from_locked_folder_confirmation": "ഈ ഫോട്ടോകളും വീഡിയോകളും ലോക്ക് ചെയ്ത ഫോൾഡറിൽ നിന്ന് മാറ്റണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ? അവ നിങ്ങളുടെ ലൈബ്രറിയിൽ ദൃശ്യമാകും.", + "remove_from_shared_link": "പങ്കിട്ട ലിങ്കിൽ നിന്ന് നീക്കം ചെയ്യുക", + "remove_memory": "ഓർമ്മ നീക്കം ചെയ്യുക", + "remove_photo_from_memory": "ഈ ഓർമ്മയിൽ നിന്ന് ഫോട്ടോ നീക്കം ചെയ്യുക", + "remove_tag": "ടാഗ് നീക്കം ചെയ്യുക", + "remove_url": "URL നീക്കം ചെയ്യുക", + "remove_user": "ഉപയോക്താവിനെ നീക്കം ചെയ്യുക", + "removed_api_key": "API കീ നീക്കം ചെയ്തു: {name}", + "removed_from_archive": "ആർക്കൈവിൽ നിന്ന് നീക്കം ചെയ്തു", + "removed_from_favorites": "പ്രിയപ്പെട്ടവയിൽ നിന്ന് നീക്കം ചെയ്തു", + "removed_from_favorites_count": "{count, plural, other {# എണ്ണം}} പ്രിയപ്പെട്ടവയിൽ നിന്ന് നീക്കം ചെയ്തു", + "removed_memory": "ഓർമ്മ നീക്കം ചെയ്തു", + "removed_photo_from_memory": "ഓർമ്മയിൽ നിന്ന് ഫോട്ടോ നീക്കം ചെയ്തു", + "removed_tagged_assets": "{count, plural, one {# അസറ്റിൽ നിന്ന്} other {# അസറ്റുകളിൽ നിന്ന്}} ടാഗ് നീക്കം ചെയ്തു", + "rename": "പുനർനാമകരണം ചെയ്യുക", + "repair": "റിപ്പയർ ചെയ്യുക", + "repair_no_results_message": "ട്രാക്ക് ചെയ്യാത്തതും കാണാതായതുമായ ഫയലുകൾ ഇവിടെ കാണിക്കും", + "replace_with_upload": "അപ്‌ലോഡ് ഉപയോഗിച്ച് മാറ്റിസ്ഥാപിക്കുക", + "repository": "റെപ്പോസിറ്ററി", + "require_password": "പാസ്‌വേഡ് ആവശ്യമാണ്", + "require_user_to_change_password_on_first_login": "ആദ്യ ലോഗിൻ ചെയ്യുമ്പോൾ പാസ്‌വേഡ് മാറ്റാൻ ഉപയോക്താവിനോട് ആവശ്യപ്പെടുക", + "rescan": "വീണ്ടും സ്കാൻ ചെയ്യുക", + "reset": "റീസെറ്റ് ചെയ്യുക", + "reset_password": "പാസ്‌വേഡ് റീസെറ്റ് ചെയ്യുക", + "reset_people_visibility": "ആളുകളുടെ ദൃശ്യത പുനഃസജ്ജമാക്കുക", + "reset_pin_code": "പിൻ കോഡ് റീസെറ്റ് ചെയ്യുക", + "reset_pin_code_description": "നിങ്ങൾ പിൻ കോഡ് മറന്നെങ്കിൽ, അത് പുനഃസജ്ജമാക്കാൻ നിങ്ങൾക്ക് സെർവർ അഡ്മിനിസ്ട്രേറ്ററുമായി ബന്ധപ്പെടാം", + "reset_pin_code_success": "പിൻ കോഡ് വിജയകരമായി റീസെറ്റ് ചെയ്തു", + "reset_pin_code_with_password": "നിങ്ങളുടെ പാസ്‌വേഡ് ഉപയോഗിച്ച് നിങ്ങൾക്ക് എപ്പോഴും പിൻ കോഡ് റീസെറ്റ് ചെയ്യാം", + "reset_sqlite": "SQLite ഡാറ്റാബേസ് റീസെറ്റ് ചെയ്യുക", + "reset_sqlite_confirmation": "SQLite ഡാറ്റാബേസ് റീസെറ്റ് ചെയ്യണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ? ഡാറ്റ വീണ്ടും സിങ്ക് ചെയ്യുന്നതിന് നിങ്ങൾ ലോഗ് ഔട്ട് ചെയ്ത് വീണ്ടും ലോഗിൻ ചെയ്യേണ്ടിവരും", + "reset_sqlite_success": "SQLite ഡാറ്റാബേസ് വിജയകരമായി റീസെറ്റ് ചെയ്തു", + "reset_to_default": "ഡിഫോൾട്ടിലേക്ക് പുനഃസജ്ജമാക്കുക", + "resolve_duplicates": "ഡ്യൂപ്ലിക്കേറ്റുകൾ പരിഹരിക്കുക", + "resolved_all_duplicates": "എല്ലാ ഡ്യൂപ്ലിക്കേറ്റുകളും പരിഹരിച്ചു", + "restore": "പുനഃസ്ഥാപിക്കുക", + "restore_all": "എല്ലാം പുനഃസ്ഥാപിക്കുക", + "restore_trash_action_prompt": "{count} എണ്ണം ട്രാഷിൽ നിന്ന് പുനഃസ്ഥാപിച്ചു", + "restore_user": "ഉപയോക്താവിനെ പുനഃസ്ഥാപിക്കുക", + "restored_asset": "അസറ്റ് പുനഃസ്ഥാപിച്ചു", + "resume": "പുനരാരംഭിക്കുക", + "resume_paused_jobs": "{count, plural, one {താൽക്കാലികമായി നിർത്തിയ # ജോലി} other {താൽക്കാലികമായി നിർത്തിയ # ജോലികൾ}} പുനരാരംഭിക്കുക", + "retry_upload": "അപ്‌ലോഡ് വീണ്ടും ശ്രമിക്കുക", + "review_duplicates": "ഡ്യൂപ്ലിക്കേറ്റുകൾ അവലോകനം ചെയ്യുക", + "review_large_files": "വലിയ ഫയലുകൾ അവലോകനം ചെയ്യുക", + "role": "റോൾ", + "role_editor": "എഡിറ്റർ", + "role_viewer": "കാണുന്നയാൾ", + "running": "പ്രവർത്തിക്കുന്നു", + "save": "സേവ് ചെയ്യുക", + "save_to_gallery": "ഗാലറിയിലേക്ക് സേവ് ചെയ്യുക", + "saved_api_key": "API കീ സേവ് ചെയ്തു", + "saved_profile": "പ്രൊഫൈൽ സേവ് ചെയ്തു", + "saved_settings": "ക്രമീകരണങ്ങൾ സേവ് ചെയ്തു", + "say_something": "എന്തെങ്കിലും പറയൂ", + "scaffold_body_error_occurred": "പിശക് സംഭവിച്ചു", + "scan_all_libraries": "എല്ലാ ലൈബ്രറികളും സ്കാൻ ചെയ്യുക", + "scan_library": "സ്കാൻ ചെയ്യുക", + "scan_settings": "സ്കാൻ ക്രമീകരണങ്ങൾ", + "scanning_for_album": "ആൽബത്തിനായി സ്കാൻ ചെയ്യുന്നു...", + "search": "തിരയുക", + "search_albums": "ആൽബങ്ങൾക്കായി തിരയുക", + "search_by_context": "സന്ദർഭം അനുസരിച്ച് തിരയുക", + "search_by_description": "വിവരണം അനുസരിച്ച് തിരയുക", + "search_by_description_example": "സപ്പയിലെ ഹൈക്കിംഗ് ദിനം", + "search_by_filename": "ഫയൽ നാമം അല്ലെങ്കിൽ എക്സ്റ്റൻഷൻ ഉപയോഗിച്ച് തിരയുക", + "search_by_filename_example": "ഉദാ. IMG_1234.JPG അല്ലെങ്കിൽ PNG", + "search_camera_make": "ക്യാമറ നിർമ്മാതാവിനായി തിരയുക...", + "search_camera_model": "ക്യാമറ മോഡലിനായി തിരയുക...", + "search_city": "നഗരത്തിനായി തിരയുക...", + "search_country": "രാജ്യത്തിനായി തിരയുക...", + "search_filter_apply": "ഫിൽട്ടർ പ്രയോഗിക്കുക", + "search_filter_camera_title": "ക്യാമറ തരം തിരഞ്ഞെടുക്കുക", + "search_filter_date": "തീയതി", + "search_filter_date_interval": "{start} മുതൽ {end} വരെ", + "search_filter_date_title": "ഒരു തീയതി പരിധി തിരഞ്ഞെടുക്കുക", + "search_filter_display_option_not_in_album": "ആൽബത്തിൽ ഇല്ല", + "search_filter_display_options": "പ്രദർശന ഓപ്ഷനുകൾ", + "search_filter_filename": "ഫയൽ നാമം ഉപയോഗിച്ച് തിരയുക", + "search_filter_location": "സ്ഥാനം", + "search_filter_location_title": "സ്ഥാനം തിരഞ്ഞെടുക്കുക", + "search_filter_media_type": "മീഡിയ തരം", + "search_filter_media_type_title": "മീഡിയ തരം തിരഞ്ഞെടുക്കുക", + "search_filter_people_title": "ആളുകളെ തിരഞ്ഞെടുക്കുക", + "search_for": "ഇതിനായി തിരയുക", + "search_for_existing_person": "നിലവിലുള്ള വ്യക്തിക്കായി തിരയുക", + "search_no_more_result": "കൂടുതൽ ഫലങ്ങളില്ല", + "search_no_people": "ആളുകളില്ല", + "search_no_people_named": "\"{name}\" എന്ന് പേരുള്ള ആളുകളില്ല", + "search_no_result": "ഫലങ്ങളൊന്നും കണ്ടെത്തിയില്ല, മറ്റൊരു തിരയൽ പദമോ സംയോജനമോ ഉപയോഗിച്ച് ശ്രമിക്കുക", + "search_options": "തിരയൽ ഓപ്ഷനുകൾ", + "search_page_categories": "വിഭാഗങ്ങൾ", + "search_page_motion_photos": "ചലിക്കുന്ന ഫോട്ടോകൾ", + "search_page_no_objects": "വസ്തുക്കളുടെ വിവരങ്ങൾ ലഭ്യമല്ല", + "search_page_no_places": "സ്ഥലങ്ങളുടെ വിവരങ്ങൾ ലഭ്യമല്ല", + "search_page_screenshots": "സ്ക്രീൻഷോട്ടുകൾ", + "search_page_search_photos_videos": "നിങ്ങളുടെ ഫോട്ടോകൾക്കും വീഡിയോകൾക്കുമായി തിരയുക", + "search_page_selfies": "സെൽഫികൾ", + "search_page_things": "വസ്തുക്കൾ", + "search_page_view_all_button": "എല്ലാം കാണുക", + "search_page_your_activity": "നിങ്ങളുടെ പ്രവർത്തനം", + "search_page_your_map": "നിങ്ങളുടെ മാപ്പ്", + "search_people": "ആളുകളെ തിരയുക", + "search_places": "സ്ഥലങ്ങൾ തിരയുക", + "search_rating": "റേറ്റിംഗ് അനുസരിച്ച് തിരയുക...", + "search_result_page_new_search_hint": "പുതിയ തിരയൽ", + "search_settings": "തിരയൽ ക്രമീകരണങ്ങൾ", + "search_state": "സംസ്ഥാനം തിരയുക...", + "search_suggestion_list_smart_search_hint_1": "സ്മാർട്ട് സെർച്ച് ഡിഫോൾട്ടായി പ്രവർത്തനക്ഷമമാക്കിയിരിക്കുന്നു, മെറ്റാഡാറ്റയ്ക്കായി തിരയാൻ ഈ വാക്യഘടന ഉപയോഗിക്കുക ", + "search_suggestion_list_smart_search_hint_2": "m:നിങ്ങളുടെ-തിരയൽ-പദം", + "search_tags": "ടാഗുകൾക്കായി തിരയുക...", + "search_timezone": "സമയമേഖലയ്ക്കായി തിരയുക...", + "search_type": "തിരയൽ തരം", + "search_your_photos": "നിങ്ങളുടെ ഫോട്ടോകൾ തിരയുക", + "searching_locales": "ലൊക്കേലുകൾക്കായി തിരയുന്നു...", + "second": "സെക്കന്റ്", + "see_all_people": "എല്ലാ ആളുകളെയും കാണുക", + "select": "തിരഞ്ഞെടുക്കുക", + "select_album_cover": "ആൽബം കവർ തിരഞ്ഞെടുക്കുക", + "select_all": "എല്ലാം തിരഞ്ഞെടുക്കുക", + "select_all_duplicates": "എല്ലാ ഡ്യൂപ്ലിക്കേറ്റുകളും തിരഞ്ഞെടുക്കുക", + "select_all_in": "{group}-ലെ എല്ലാം തിരഞ്ഞെടുക്കുക", + "select_avatar_color": "അവതാർ നിറം തിരഞ്ഞെടുക്കുക", + "select_face": "മുഖം തിരഞ്ഞെടുക്കുക", + "select_featured_photo": "ഫീച്ചർ ചെയ്ത ഫോട്ടോ തിരഞ്ഞെടുക്കുക", + "select_from_computer": "കമ്പ്യൂട്ടറിൽ നിന്ന് തിരഞ്ഞെടുക്കുക", + "select_keep_all": "എല്ലാം സൂക്ഷിക്കാൻ തിരഞ്ഞെടുക്കുക", + "select_library_owner": "ലൈബ്രറി ഉടമയെ തിരഞ്ഞെടുക്കുക", + "select_new_face": "പുതിയ മുഖം തിരഞ്ഞെടുക്കുക", + "select_person_to_tag": "ടാഗ് ചെയ്യാൻ ഒരു വ്യക്തിയെ തിരഞ്ഞെടുക്കുക", + "select_photos": "ഫോട്ടോകൾ തിരഞ്ഞെടുക്കുക", + "select_trash_all": "എല്ലാം ട്രാഷ് ചെയ്യാൻ തിരഞ്ഞെടുക്കുക", + "select_user_for_sharing_page_err_album": "ആൽബം ഉണ്ടാക്കുന്നതിൽ പരാജയപ്പെട്ടു", + "selected": "തിരഞ്ഞെടുത്തു", + "selected_count": "{count, plural, other {# എണ്ണം തിരഞ്ഞെടുത്തു}}", + "selected_gps_coordinates": "തിരഞ്ഞെടുത്ത GPS കോർഡിനേറ്റുകൾ", + "send_message": "സന്ദേശം അയക്കുക", + "send_welcome_email": "സ്വാഗത ഇമെയിൽ അയക്കുക", + "server_endpoint": "സെർവർ എൻഡ്‌പോയിന്റ്", + "server_info_box_app_version": "ആപ്പ് പതിപ്പ്", + "server_info_box_server_url": "സെർവർ URL", + "server_offline": "സെർവർ ഓഫ്‌ലൈൻ", + "server_online": "സെർവർ ഓൺലൈൻ", + "server_privacy": "സെർവർ സ്വകാര്യത", + "server_stats": "സെർവർ സ്ഥിതിവിവരക്കണക്കുകൾ", + "server_version": "സെർവർ പതിപ്പ്", + "set": "സജ്ജമാക്കുക", + "set_as_album_cover": "ആൽബം കവറായി സജ്ജമാക്കുക", + "set_as_featured_photo": "ഫീച്ചർ ചെയ്ത ഫോട്ടോയായി സജ്ജമാക്കുക", + "set_as_profile_picture": "പ്രൊഫൈൽ ചിത്രമായി സജ്ജമാക്കുക", + "set_date_of_birth": "ജനനത്തീയതി സജ്ജമാക്കുക", + "set_profile_picture": "പ്രൊഫൈൽ ചിത്രം സജ്ജമാക്കുക", + "set_slideshow_to_fullscreen": "സ്ലൈഡ്‌ഷോ ഫുൾസ്ക്രീനായി സജ്ജമാക്കുക", + "set_stack_primary_asset": "പ്രധാന അസറ്റായി സജ്ജമാക്കുക", + "setting_image_viewer_help": "വിശദാംശ വ്യൂവർ ആദ്യം ചെറിയ തംബ്നെയിൽ ലോഡുചെയ്യുന്നു, തുടർന്ന് ഇടത്തരം പ്രിവ്യൂ (പ്രവർത്തനക്ഷമമാക്കിയിട്ടുണ്ടെങ്കിൽ), ഒടുവിൽ യഥാർത്ഥ ചിത്രം (പ്രവർത്തനക്ഷമമാക്കിയിട്ടുണ്ടെങ്കിൽ) ലോഡുചെയ്യുന്നു.", + "setting_image_viewer_original_subtitle": "യഥാർത്ഥ പൂർണ്ണ-റെസല്യൂഷൻ ചിത്രം (വലുത്!) ലോഡുചെയ്യാൻ പ്രവർത്തനക്ഷമമാക്കുക. ഡാറ്റ ഉപയോഗം (നെറ്റ്‌വർക്കും ഉപകരണ കാഷെയും) കുറയ്ക്കാൻ പ്രവർത്തനരഹിതമാക്കുക.", + "setting_image_viewer_original_title": "യഥാർത്ഥ ചിത്രം ലോഡുചെയ്യുക", + "setting_image_viewer_preview_subtitle": "ഇടത്തരം റെസല്യൂഷനുള്ള ചിത്രം ലോഡുചെയ്യാൻ പ്രവർത്തനക്ഷമമാക്കുക. ഒന്നുകിൽ യഥാർത്ഥ ചിത്രം നേരിട്ട് ലോഡുചെയ്യുന്നതിനോ അല്ലെങ്കിൽ തംബ്നെയിൽ മാത്രം ഉപയോഗിക്കുന്നതിനോ പ്രവർത്തനരഹിതമാക്കുക.", + "setting_image_viewer_preview_title": "പ്രിവ്യൂ ചിത്രം ലോഡുചെയ്യുക", + "setting_image_viewer_title": "ചിത്രങ്ങൾ", + "setting_languages_apply": "പ്രയോഗിക്കുക", + "setting_languages_subtitle": "ആപ്പിന്റെ ഭാഷ മാറ്റുക", + "setting_notifications_notify_failures_grace_period": "പശ്ചാത്തല ബാക്കപ്പ് പരാജയങ്ങൾ അറിയിക്കുക: {duration}", + "setting_notifications_notify_hours": "{count} മണിക്കൂർ", + "setting_notifications_notify_immediately": "ഉടനടി", + "setting_notifications_notify_minutes": "{count} മിനിറ്റ്", + "setting_notifications_notify_never": "ഒരിക്കലും", + "setting_notifications_notify_seconds": "{count} സെക്കൻഡ്", + "setting_notifications_single_progress_subtitle": "ഓരോ അസറ്റിന്റെയും വിശദമായ അപ്‌ലോഡ് പുരോഗതി വിവരം", + "setting_notifications_single_progress_title": "പശ്ചാത്തല ബാക്കപ്പിന്റെ വിശദമായ പുരോഗതി കാണിക്കുക", + "setting_notifications_subtitle": "നിങ്ങളുടെ അറിയിപ്പ് മുൻഗണനകൾ ക്രമീകരിക്കുക", + "setting_notifications_total_progress_subtitle": "മൊത്തത്തിലുള്ള അപ്‌ലോഡ് പുരോഗതി (ചെയ്തത്/ആകെ അസറ്റുകൾ)", + "setting_notifications_total_progress_title": "പശ്ചാത്തല ബാക്കപ്പിന്റെ മൊത്തം പുരോഗതി കാണിക്കുക", + "setting_video_viewer_looping_title": "ലൂപ്പിംഗ്", + "setting_video_viewer_original_video_subtitle": "സെർവറിൽ നിന്ന് ഒരു വീഡിയോ സ്ട്രീം ചെയ്യുമ്പോൾ, ഒരു ട്രാൻസ്‌കോഡ് ലഭ്യമാണെങ്കിലും യഥാർത്ഥ വീഡിയോ പ്ലേ ചെയ്യുക. ഇത് ബഫറിംഗിന് കാരണമായേക്കാം. പ്രാദേശികമായി ലഭ്യമായ വീഡിയോകൾ ഈ ക്രമീകരണം പരിഗണിക്കാതെ തന്നെ യഥാർത്ഥ ഗുണമേന്മയിൽ പ്ലേ ചെയ്യും.", + "setting_video_viewer_original_video_title": "യഥാർത്ഥ വീഡിയോ നിർബന്ധമാക്കുക", + "settings": "ക്രമീകരണങ്ങൾ", + "settings_require_restart": "ഈ ക്രമീകരണം പ്രയോഗിക്കാൻ ദയവായി Immich പുനരാരംഭിക്കുക", + "settings_saved": "ക്രമീകരണങ്ങൾ സേവ് ചെയ്തു", + "setup_pin_code": "ഒരു പിൻ കോഡ് സജ്ജീകരിക്കുക", + "share": "പങ്കിടുക", + "share_action_prompt": "{count} അസറ്റുകൾ പങ്കിട്ടു", + "share_add_photos": "ഫോട്ടോകൾ ചേർക്കുക", + "share_assets_selected": "{count} എണ്ണം തിരഞ്ഞെടുത്തു", + "share_dialog_preparing": "തയ്യാറാക്കുന്നു...", + "share_link": "ലിങ്ക് പങ്കിടുക", + "shared": "പങ്കിട്ടത്", + "shared_album_activities_input_disable": "അഭിപ്രായം പ്രവർത്തനരഹിതമാക്കി", + "shared_album_activity_remove_content": "ഈ പ്രവർത്തനം ഇല്ലാതാക്കണോ?", + "shared_album_activity_remove_title": "പ്രവർത്തനം ഇല്ലാതാക്കുക", + "shared_album_section_people_action_error": "ആൽബത്തിൽ നിന്ന് വിട്ടുപോകുന്നതിനോ/നീക്കംചെയ്യുന്നതിനോ പിശക്", + "shared_album_section_people_action_leave": "ആൽബത്തിൽ നിന്ന് ഉപയോക്താവിനെ നീക്കം ചെയ്യുക", + "shared_album_section_people_action_remove_user": "ആൽബത്തിൽ നിന്ന് ഉപയോക്താവിനെ നീക്കം ചെയ്യുക", + "shared_album_section_people_title": "ആളുകൾ", + "shared_by": "പങ്കിട്ടത്", + "shared_by_user": "{user} പങ്കിട്ടു", + "shared_by_you": "നിങ്ങൾ പങ്കിട്ടത്", + "shared_from_partner": "{partner}-ൽ നിന്നുള്ള ഫോട്ടോകൾ", + "shared_intent_upload_button_progress_text": "{current}/{total} അപ്‌ലോഡ് ചെയ്തു", + "shared_link_app_bar_title": "പങ്കിട്ട ലിങ്കുകൾ", + "shared_link_clipboard_copied_massage": "ക്ലിപ്പ്ബോർഡിലേക്ക് പകർത്തി", + "shared_link_clipboard_text": "ലിങ്ക്: {link}\nപാസ്‌വേഡ്: {password}", + "shared_link_create_error": "പങ്കിട്ട ലിങ്ക് ഉണ്ടാക്കുന്നതിൽ പിശക്", + "shared_link_custom_url_description": "ഒരു കസ്റ്റം URL ഉപയോഗിച്ച് ഈ പങ്കിട്ട ലിങ്ക് ആക്‌സസ് ചെയ്യുക", + "shared_link_edit_description_hint": "പങ്കിടലിന്റെ വിവരണം നൽകുക", + "shared_link_edit_expire_after_option_day": "1 ദിവസം", + "shared_link_edit_expire_after_option_days": "{count} ദിവസങ്ങൾ", + "shared_link_edit_expire_after_option_hour": "1 മണിക്കൂർ", + "shared_link_edit_expire_after_option_hours": "{count} മണിക്കൂറുകൾ", + "shared_link_edit_expire_after_option_minute": "1 മിനിറ്റ്", + "shared_link_edit_expire_after_option_minutes": "{count} മിനിറ്റുകൾ", + "shared_link_edit_expire_after_option_months": "{count} മാസങ്ങൾ", + "shared_link_edit_expire_after_option_year": "{count} വർഷം", + "shared_link_edit_password_hint": "പങ്കിടലിന്റെ പാസ്‌വേഡ് നൽകുക", + "shared_link_edit_submit_button": "ലിങ്ക് അപ്ഡേറ്റ് ചെയ്യുക", + "shared_link_error_server_url_fetch": "സെർവർ url ലഭ്യമാക്കാൻ കഴിയില്ല", + "shared_link_expires_day": "{count} ദിവസത്തിനുള്ളിൽ കാലഹരണപ്പെടും", + "shared_link_expires_days": "{count} ദിവസങ്ങൾക്കുള്ളിൽ കാലഹരണപ്പെടും", + "shared_link_expires_hour": "{count} മണിക്കൂറിനുള്ളിൽ കാലഹരണപ്പെടും", + "shared_link_expires_hours": "{count} മണിക്കൂറുകൾക്കുള്ളിൽ കാലഹരണപ്പെടും", + "shared_link_expires_minute": "{count} മിനിറ്റിനുള്ളിൽ കാലഹരണപ്പെടും", + "shared_link_expires_minutes": "{count} മിനിറ്റുകൾക്കുള്ളിൽ കാലഹരണപ്പെടും", + "shared_link_expires_never": "കാലഹരണപ്പെടില്ല ∞", + "shared_link_expires_second": "{count} സെക്കൻഡിനുള്ളിൽ കാലഹരണപ്പെടും", + "shared_link_expires_seconds": "{count} സെക്കൻഡുകൾക്കുള്ളിൽ കാലഹരണപ്പെടും", + "shared_link_individual_shared": "വ്യക്തിഗതമായി പങ്കിട്ടത്", + "shared_link_info_chip_metadata": "EXIF", + "shared_link_manage_links": "പങ്കിട്ട ലിങ്കുകൾ കൈകാര്യം ചെയ്യുക", + "shared_link_options": "പങ്കിട്ട ലിങ്ക് ഓപ്ഷനുകൾ", + "shared_link_password_description": "ഈ പങ്കിട്ട ലിങ്ക് ആക്‌സസ് ചെയ്യാൻ ഒരു പാസ്‌വേഡ് ആവശ്യമാണ്", + "shared_links": "പങ്കിട്ട ലിങ്കുകൾ", + "shared_links_description": "ഒരു ലിങ്ക് ഉപയോഗിച്ച് ഫോട്ടോകളും വീഡിയോകളും പങ്കിടുക", + "shared_photos_and_videos_count": "{assetCount, plural, other {പങ്കിട്ട # ഫോട്ടോകളും വീഡിയോകളും.}}", + "shared_with_me": "എനിക്കായി പങ്കിട്ടത്", + "shared_with_partner": "{partner}-മായി പങ്കിട്ടു", + "sharing": "പങ്കിടൽ", + "sharing_enter_password": "ഈ പേജ് കാണുന്നതിന് ദയവായി പാസ്‌വേഡ് നൽകുക.", + "sharing_page_album": "പങ്കിട്ട ആൽബങ്ങൾ", + "sharing_page_description": "നിങ്ങളുടെ നെറ്റ്‌വർക്കിലെ ആളുകളുമായി ഫോട്ടോകളും വീഡിയോകളും പങ്കിടാൻ പങ്കിട്ട ആൽബങ്ങൾ ഉണ്ടാക്കുക.", + "sharing_page_empty_list": "ശൂന്യമായ ലിസ്റ്റ്", + "sharing_sidebar_description": "സൈഡ്‌ബാറിൽ 'പങ്കിടൽ' എന്നതിലേക്ക് ഒരു ലിങ്ക് പ്രദർശിപ്പിക്കുക", + "sharing_silver_appbar_create_shared_album": "പുതിയ പങ്കിട്ട ആൽബം", + "sharing_silver_appbar_share_partner": "പങ്കാളിയുമായി പങ്കിടുക", + "shift_to_permanent_delete": "അസറ്റ് ശാശ്വതമായി ഇല്ലാതാക്കാൻ ⇧ അമർത്തുക", + "show_album_options": "ആൽബം ഓപ്ഷനുകൾ കാണിക്കുക", + "show_albums": "ആൽബങ്ങൾ കാണിക്കുക", + "show_all_people": "എല്ലാ ആളുകളെയും കാണിക്കുക", + "show_and_hide_people": "ആളുകളെ കാണിക്കുകയും മറയ്ക്കുകയും ചെയ്യുക", + "show_file_location": "ഫയലിന്റെ സ്ഥാനം കാണിക്കുക", + "show_gallery": "ഗാലറി കാണിക്കുക", + "show_hidden_people": "മറച്ച ആളുകളെ കാണിക്കുക", + "show_in_timeline": "ടൈംലൈനിൽ കാണിക്കുക", + "show_in_timeline_setting_description": "ഈ ഉപയോക്താവിൽ നിന്നുള്ള ഫോട്ടോകളും വീഡിയോകളും നിങ്ങളുടെ ടൈംലൈനിൽ കാണിക്കുക", + "show_keyboard_shortcuts": "കീബോർഡ് കുറുക്കുവഴികൾ കാണിക്കുക", + "show_metadata": "മെറ്റാഡാറ്റ കാണിക്കുക", + "show_or_hide_info": "വിവരങ്ങൾ കാണിക്കുക അല്ലെങ്കിൽ മറയ്ക്കുക", + "show_password": "പാസ്‌വേഡ് കാണിക്കുക", + "show_person_options": "വ്യക്തിയുടെ ഓപ്ഷനുകൾ കാണിക്കുക", + "show_progress_bar": "പുരോഗതി ബാർ കാണിക്കുക", + "show_search_options": "തിരയൽ ഓപ്ഷനുകൾ കാണിക്കുക", + "show_shared_links": "പങ്കിട്ട ലിങ്കുകൾ കാണിക്കുക", + "show_slideshow_transition": "സ്ലൈഡ്‌ഷോ സംക്രമണം കാണിക്കുക", + "show_supporter_badge": "സഹായിയുടെ ബാഡ്ജ്", + "show_supporter_badge_description": "ഒരു സഹായിയുടെ ബാഡ്ജ് കാണിക്കുക", + "show_text_search_menu": "ടെക്സ്റ്റ് തിരയൽ മെനു കാണിക്കുക", + "shuffle": "ഷഫിൾ ചെയ്യുക", + "sidebar": "സൈഡ്‌ബാർ", + "sidebar_display_description": "സൈഡ്‌ബാറിലെ കാഴ്‌ചയിലേക്ക് ഒരു ലിങ്ക് പ്രദർശിപ്പിക്കുക", + "sign_out": "സൈൻ ഔട്ട്", + "sign_up": "സൈൻ അപ്പ് ചെയ്യുക", + "size": "വലിപ്പം", + "skip_to_content": "ഉള്ളടക്കത്തിലേക്ക് പോകുക", + "skip_to_folders": "ഫോൾഡറുകളിലേക്ക് പോകുക", + "skip_to_tags": "ടാഗുകളിലേക്ക് പോകുക", + "slideshow": "സ്ലൈഡ്‌ഷോ", + "slideshow_settings": "സ്ലൈഡ്‌ഷോ ക്രമീകരണങ്ങൾ", + "sort_albums_by": "ആൽബങ്ങളെ ഇതനുസരിച്ച് ക്രമീകരിക്കുക...", + "sort_created": "സൃഷ്ടിച്ച തീയതി", + "sort_items": "ഇനങ്ങളുടെ എണ്ണം", + "sort_modified": "മാറ്റം വരുത്തിയ തീയതി", + "sort_newest": "ഏറ്റവും പുതിയ ഫോട്ടോ", + "sort_oldest": "ഏറ്റവും പഴയ ഫോട്ടോ", + "sort_people_by_similarity": "സാമ്യം അനുസരിച്ച് ആളുകളെ ക്രമീകരിക്കുക", + "sort_recent": "ഏറ്റവും പുതിയ ഫോട്ടോ", + "sort_title": "ശീർഷകം", + "source": "ഉറവിടം", + "stack": "സ്റ്റാക്ക്", + "stack_action_prompt": "{count} എണ്ണം സ്റ്റാക്ക് ചെയ്തു", + "stack_duplicates": "ഡ്യൂപ്ലിക്കേറ്റുകൾ സ്റ്റാക്ക് ചെയ്യുക", + "stack_select_one_photo": "സ്റ്റാക്കിനായി ഒരു പ്രധാന ഫോട്ടോ തിരഞ്ഞെടുക്കുക", + "stack_selected_photos": "തിരഞ്ഞെടുത്ത ഫോട്ടോകൾ സ്റ്റാക്ക് ചെയ്യുക", + "stacked_assets_count": "{count, plural, one {# അസറ്റ്} other {# അസറ്റുകൾ}} സ്റ്റാക്ക് ചെയ്തു", + "stacktrace": "സ്റ്റാക്ക്ട്രേസ്", + "start": "ആരംഭിക്കുക", + "start_date": "ആരംഭ തീയതി", + "start_date_before_end_date": "ആരംഭ തീയതി അവസാന തീയതിക്ക് മുമ്പായിരിക്കണം", + "state": "സംസ്ഥാനം", + "status": "നില", + "stop_casting": "കാസ്റ്റിംഗ് നിർത്തുക", + "stop_motion_photo": "ചലിക്കുന്ന ഫോട്ടോ നിർത്തുക", + "stop_photo_sharing": "നിങ്ങളുടെ ഫോട്ടോകൾ പങ്കിടുന്നത് നിർത്തണോ?", + "stop_photo_sharing_description": "{partner}-ക്ക് ഇനി നിങ്ങളുടെ ഫോട്ടോകൾ ആക്‌സസ് ചെയ്യാൻ കഴിയില്ല.", + "stop_sharing_photos_with_user": "ഈ ഉപയോക്താവുമായി നിങ്ങളുടെ ഫോട്ടോകൾ പങ്കിടുന്നത് നിർത്തുക", + "storage": "സംഭരണ സ്ഥലം", + "storage_label": "സ്റ്റോറേജ് ലേബൽ", + "storage_quota": "സ്റ്റോറേജ് ക്വാട്ട", + "storage_usage": "{available}-ൽ {used} ഉപയോഗിച്ചു", + "submit": "സമർപ്പിക്കുക", + "success": "വിജയം", + "suggestions": "നിർദ്ദേശങ്ങൾ", + "sunrise_on_the_beach": "ബീച്ചിലെ സൂര്യോദയം", + "support": "പിന്തുണ", + "support_and_feedback": "പിന്തുണയും അഭിപ്രായവും", + "support_third_party_description": "നിങ്ങളുടെ Immich ഇൻസ്റ്റാളേഷൻ ഒരു മൂന്നാം കക്ഷി പാക്കേജ് ചെയ്തതാണ്. നിങ്ങൾ അനുഭവിക്കുന്ന പ്രശ്നങ്ങൾ ആ പാക്കേജ് കാരണമാകാം, അതിനാൽ താഴെയുള്ള ലിങ്കുകൾ ഉപയോഗിച്ച് ആദ്യം അവരുമായി പ്രശ്നങ്ങൾ ഉന്നയിക്കുക.", + "swap_merge_direction": "ലയിപ്പിക്കൽ ദിശ മാറ്റുക", + "sync": "സിങ്ക്", + "sync_albums": "ആൽബങ്ങൾ സിങ്ക് ചെയ്യുക", + "sync_albums_manual_subtitle": "അപ്‌ലോഡ് ചെയ്ത എല്ലാ വീഡിയോകളും ഫോട്ടോകളും തിരഞ്ഞെടുത്ത ബാക്കപ്പ് ആൽബങ്ങളിലേക്ക് സിങ്ക് ചെയ്യുക", + "sync_local": "പ്രാദേശികമായി സിങ്ക് ചെയ്യുക", + "sync_remote": "റിമോട്ടായി സിങ്ക് ചെയ്യുക", + "sync_status": "സിങ്ക് നില", + "sync_status_subtitle": "സിങ്ക് സിസ്റ്റം കാണുക, കൈകാര്യം ചെയ്യുക", + "sync_upload_album_setting_subtitle": "നിങ്ങളുടെ ഫോട്ടോകളും വീഡിയോകളും Immich-ലെ തിരഞ്ഞെടുത്ത ആൽബങ്ങളിലേക്ക് ഉണ്ടാക്കി അപ്‌ലോഡ് ചെയ്യുക", + "tag": "ടാഗ്", + "tag_assets": "അസറ്റുകൾ ടാഗ് ചെയ്യുക", + "tag_created": "{tag} എന്ന ടാഗ് ഉണ്ടാക്കി", + "tag_feature_description": "യുക്തിസഹമായ ടാഗ് വിഷയങ്ങൾ അനുസരിച്ച് ഗ്രൂപ്പ് ചെയ്ത ഫോട്ടോകളും വീഡിയോകളും ബ്രൗസുചെയ്യുന്നു", + "tag_not_found_question": "ഒരു ടാഗ് കണ്ടെത്താൻ കഴിയുന്നില്ലേ? ഒരു പുതിയ ടാഗ് ഉണ്ടാക്കുക.", + "tag_people": "ആളുകളെ ടാഗ് ചെയ്യുക", + "tag_updated": "{tag} എന്ന ടാഗ് അപ്ഡേറ്റ് ചെയ്തു", + "tagged_assets": "{count, plural, one {# അസറ്റ്} other {# അസറ്റുകൾ}} ടാഗ് ചെയ്തു", + "tags": "ടാഗുകൾ", + "tap_to_run_job": "ജോലി പ്രവർത്തിപ്പിക്കാൻ ടാപ്പുചെയ്യുക", + "template": "ടെംപ്ലേറ്റ്", + "theme": "തീം", + "theme_selection": "തീം തിരഞ്ഞെടുക്കൽ", + "theme_selection_description": "നിങ്ങളുടെ ബ്രൗസറിന്റെ സിസ്റ്റം മുൻഗണന അനുസരിച്ച് തീം ലൈറ്റ് അല്ലെങ്കിൽ ഡാർക്ക് ആയി യാന്ത്രികമായി സജ്ജമാക്കുക", + "theme_setting_asset_list_storage_indicator_title": "അസറ്റ് ടൈലുകളിൽ സ്റ്റോറേജ് ഇൻഡിക്കേറ്റർ കാണിക്കുക", + "theme_setting_asset_list_tiles_per_row_title": "ഓരോ വരിയിലും അസറ്റുകളുടെ എണ്ണം ({count})", + "theme_setting_colorful_interface_subtitle": "പശ്ചാത്തല പ്രതലങ്ങളിൽ പ്രധാന നിറം പ്രയോഗിക്കുക.", + "theme_setting_colorful_interface_title": "വർണ്ണാഭമായ ഇന്റർഫേസ്", + "theme_setting_image_viewer_quality_subtitle": "വിശദാംശ ഇമേജ് വ്യൂവറിന്റെ ഗുണമേന്മ ക്രമീകരിക്കുക", + "theme_setting_image_viewer_quality_title": "ഇമേജ് വ്യൂവർ ഗുണമേന്മ", + "theme_setting_primary_color_subtitle": "പ്രധാന പ്രവർത്തനങ്ങൾക്കും ആക്‌സന്റുകൾക്കുമായി ഒരു നിറം തിരഞ്ഞെടുക്കുക.", + "theme_setting_primary_color_title": "പ്രധാന നിറം", + "theme_setting_system_primary_color_title": "സിസ്റ്റം നിറം ഉപയോഗിക്കുക", + "theme_setting_system_theme_switch": "യാന്ത്രികം (സിസ്റ്റം ക്രമീകരണം പിന്തുടരുക)", + "theme_setting_theme_subtitle": "ആപ്പിന്റെ തീം ക്രമീകരണം തിരഞ്ഞെടുക്കുക", + "theme_setting_three_stage_loading_subtitle": "മൂന്ന്-ഘട്ട ലോഡിംഗ് പ്രകടനം വർദ്ധിപ്പിച്ചേക്കാം, പക്ഷേ നെറ്റ്‌വർക്ക് ലോഡ് ഗണ്യമായി വർദ്ധിപ്പിക്കുന്നു", + "theme_setting_three_stage_loading_title": "മൂന്ന്-ഘട്ട ലോഡിംഗ് പ്രവർത്തനക്ഷമമാക്കുക", + "they_will_be_merged_together": "അവയെ ഒരുമിച്ച് ലയിപ്പിക്കും", + "third_party_resources": "മൂന്നാം കക്ഷി ഉറവിടങ്ങൾ", + "time_based_memories": "സമയം അടിസ്ഥാനമാക്കിയുള്ള ഓർമ്മകൾ", + "timeline": "ടൈംലൈൻ", + "timezone": "സമയമേഖല", + "to_archive": "ആർക്കൈവ് ചെയ്യുക", + "to_change_password": "പാസ്‌വേഡ് മാറ്റുക", + "to_favorite": "പ്രിയപ്പെട്ടതാക്കുക", + "to_login": "ലോഗിൻ", + "to_multi_select": "ഒന്നിലധികം തിരഞ്ഞെടുക്കാൻ", + "to_parent": "പാരന്റിലേക്ക് പോകുക", + "to_select": "തിരഞ്ഞെടുക്കാൻ", + "to_trash": "ട്രാഷ് ചെയ്യുക", + "toggle_settings": "ക്രമീകരണങ്ങൾ ടോഗിൾ ചെയ്യുക", + "total": "ആകെ", + "total_usage": "ആകെ ഉപയോഗം", + "trash": "ട്രാഷ്", + "trash_action_prompt": "{count} എണ്ണം ട്രാഷിലേക്ക് മാറ്റി", + "trash_all": "എല്ലാം ട്രാഷ് ചെയ്യുക", + "trash_count": "ട്രാഷ് ({count, number})", + "trash_delete_asset": "അസറ്റ് ട്രാഷ് ചെയ്യുക/ഇല്ലാതാക്കുക", + "trash_emptied": "ട്രാഷ് ശൂന്യമാക്കി", + "trash_no_results_message": "ട്രാഷ് ചെയ്ത ഫോട്ടോകളും വീഡിയോകളും ഇവിടെ കാണിക്കും.", + "trash_page_delete_all": "എല്ലാം ഇല്ലാതാക്കുക", + "trash_page_empty_trash_dialog_content": "നിങ്ങളുടെ ട്രാഷ് ചെയ്ത അസറ്റുകൾ ശൂന്യമാക്കണോ? ഈ ഇനങ്ങൾ Immich-ൽ നിന്ന് ശാശ്വതമായി നീക്കം ചെയ്യപ്പെടും", + "trash_page_info": "ട്രാഷ് ചെയ്ത ഇനങ്ങൾ {days} ദിവസങ്ങൾക്ക് ശേഷം ശാശ്വതമായി ഇല്ലാതാക്കപ്പെടും", + "trash_page_no_assets": "ട്രാഷ് ചെയ്ത അസറ്റുകളൊന്നുമില്ല", + "trash_page_restore_all": "എല്ലാം പുനഃസ്ഥാപിക്കുക", + "trash_page_select_assets_btn": "അസറ്റുകൾ തിരഞ്ഞെടുക്കുക", + "trash_page_title": "ട്രാഷ് ({count})", + "trashed_items_will_be_permanently_deleted_after": "ട്രാഷ് ചെയ്ത ഇനങ്ങൾ {days, plural, one {# ദിവസത്തിന്} other {# ദിവസങ്ങൾക്ക്}} ശേഷം ശാശ്വതമായി ഇല്ലാതാക്കപ്പെടും.", + "troubleshoot": "ട്രബിൾഷൂട്ട്", + "type": "തരം", + "unable_to_change_pin_code": "പിൻ കോഡ് മാറ്റാൻ കഴിയില്ല", + "unable_to_setup_pin_code": "പിൻ കോഡ് സജ്ജീകരിക്കാൻ കഴിയില്ല", + "unarchive": "അൺആർക്കൈവ് ചെയ്യുക", + "unarchive_action_prompt": "{count} എണ്ണം ആർക്കൈവിൽ നിന്ന് നീക്കം ചെയ്തു", + "unarchived_count": "{count, plural, other {അൺആർക്കൈവ് ചെയ്തവ #}}", + "undo": "തിരികെചെയ്യുക", + "unfavorite": "പ്രിയപ്പെട്ടതല്ലാതാക്കുക", + "unfavorite_action_prompt": "{count} എണ്ണം പ്രിയപ്പെട്ടവയിൽ നിന്ന് നീക്കം ചെയ്തു", + "unhide_person": "വ്യക്തിയെ മറവിൽനിന്ന് മാറ്റുക", + "unknown": "അജ്ഞാതം", + "unknown_country": "അജ്ഞാത രാജ്യം", + "unknown_year": "അജ്ഞാത വർഷം", + "unlimited": "പരിധിയില്ലാത്തത്", + "unlink_motion_video": "ചലിക്കുന്ന വീഡിയോ അൺലിങ്ക് ചെയ്യുക", + "unlink_oauth": "OAuth അൺലിങ്ക് ചെയ്യുക", + "unlinked_oauth_account": "അൺലിങ്ക് ചെയ്ത OAuth അക്കൗണ്ട്", + "unmute_memories": "ഓർമ്മകൾ ശബ്ദമുള്ളതാക്കുക", + "unnamed_album": "പേരില്ലാത്ത ആൽബം", + "unnamed_album_delete_confirmation": "ഈ ആൽബം ഇല്ലാതാക്കണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?", + "unnamed_share": "പേരില്ലാത്ത പങ്കിടൽ", + "unsaved_change": "സേവ് ചെയ്യാത്ത മാറ്റം", + "unselect_all": "എല്ലാം തിരഞ്ഞെടുത്തത് മാറ്റുക", + "unselect_all_duplicates": "എല്ലാ ഡ്യൂപ്ലിക്കേറ്റുകളും തിരഞ്ഞെടുത്തത് മാറ്റുക", + "unselect_all_in": "{group}-ലെ എല്ലാം തിരഞ്ഞെടുത്തത് മാറ്റുക", + "unstack": "അൺ-സ്റ്റാക്ക് ചെയ്യുക", + "unstack_action_prompt": "{count} എണ്ണം അൺ-സ്റ്റാക്ക് ചെയ്തു", + "unstacked_assets_count": "{count, plural, one {# അസറ്റ്} other {# അസറ്റുകൾ}} അൺ-സ്റ്റാക്ക് ചെയ്തു", + "untagged": "ടാഗ് ചെയ്തിട്ടില്ല", + "up_next": "അടുത്തത്", + "update_location_action_prompt": "തിരഞ്ഞെടുത്ത {count} അസറ്റുകളുടെ സ്ഥാനം ഇതുപയോഗിച്ച് അപ്ഡേറ്റ് ചെയ്യുക:", + "updated_at": "അപ്ഡേറ്റ് ചെയ്തത്", + "updated_password": "പാസ്‌വേഡ് അപ്ഡേറ്റ് ചെയ്തു", + "upload": "അപ്‌ലോഡ്", + "upload_action_prompt": "{count} എണ്ണം അപ്‌ലോഡിനായി ക്യൂവിൽ ചേർത്തു", + "upload_concurrency": "അപ്‌ലോഡ് കോൺകറൻസി", + "upload_details": "അപ്‌ലോഡ് വിശദാംശങ്ങൾ", + "upload_dialog_info": "തിരഞ്ഞെടുത്ത അസറ്റ്(കൾ) സെർവറിലേക്ക് ബാക്കപ്പ് ചെയ്യണോ?", + "upload_dialog_title": "അസറ്റ് അപ്‌ലോഡ് ചെയ്യുക", + "upload_errors": "അപ്‌ലോഡ് {count, plural, one {# പിശകോടെ} other {# പിശകുകളോടെ}} പൂർത്തിയായി, പുതിയ അപ്‌ലോഡ് അസറ്റുകൾ കാണുന്നതിന് പേജ് പുതുക്കുക.", + "upload_finished": "അപ്‌ലോഡ് കഴിഞ്ഞു", + "upload_progress": "ശേഷിക്കുന്നത് {remaining, number} - പ്രോസസ്സ് ചെയ്തത് {processed, number}/{total, number}", + "upload_skipped_duplicates": "{count, plural, one {# ഡ്യൂപ്ലിക്കേറ്റ് അസറ്റ്} other {# ഡ്യൂപ്ലിക്കേറ്റ് അസറ്റുകൾ}} ഒഴിവാക്കി", + "upload_status_duplicates": "ഡ്യൂപ്ലിക്കേറ്റുകൾ", + "upload_status_errors": "പിശകുകൾ", + "upload_status_uploaded": "അപ്‌ലോഡ് ചെയ്തു", + "upload_success": "അപ്‌ലോഡ് വിജയിച്ചു, പുതിയ അപ്‌ലോഡ് അസറ്റുകൾ കാണുന്നതിന് പേജ് പുതുക്കുക.", + "upload_to_immich": "Immich-ലേക്ക് അപ്‌ലോഡ് ചെയ്യുക ({count})", + "uploading": "അപ്‌ലോഡ് ചെയ്യുന്നു", + "uploading_media": "മീഡിയ അപ്‌ലോഡ് ചെയ്യുന്നു", + "url": "യുആർഎൽ", + "usage": "ഉപയോഗം", + "use_biometric": "ബയോമെട്രിക് ഉപയോഗിക്കുക", + "use_current_connection": "നിലവിലെ കണക്ഷൻ ഉപയോഗിക്കുക", + "use_custom_date_range": "പകരം കസ്റ്റം തീയതി പരിധി ഉപയോഗിക്കുക", + "user": "ഉപയോക്താവ്", + "user_has_been_deleted": "ഈ ഉപയോക്താവിനെ ഇല്ലാതാക്കി.", + "user_id": "ഉപയോക്തൃ ഐഡി", + "user_liked": "{user} {type, select, photo {ഈ ഫോട്ടോ} video {ഈ വീഡിയോ} asset {ഈ അസറ്റ്} other {ഇത്}} ഇഷ്ടപ്പെട്ടു", + "user_pin_code_settings": "പിൻ കോഡ്", + "user_pin_code_settings_description": "നിങ്ങളുടെ പിൻ കോഡ് കൈകാര്യം ചെയ്യുക", + "user_privacy": "ഉപയോക്തൃ സ്വകാര്യത", + "user_purchase_settings": "വാങ്ങൽ", + "user_purchase_settings_description": "നിങ്ങളുടെ വാങ്ങൽ കൈകാര്യം ചെയ്യുക", + "user_role_set": "{user}-നെ {role} ആയി സജ്ജമാക്കി", + "user_usage_detail": "ഉപയോക്തൃ ഉപയോഗ വിശദാംശം", + "user_usage_stats": "അക്കൗണ്ട് ഉപയോഗ സ്ഥിതിവിവരക്കണക്കുകൾ", + "user_usage_stats_description": "അക്കൗണ്ട് ഉപയോഗ സ്ഥിതിവിവരക്കണക്കുകൾ കാണുക", + "username": "ഉപയോക്തൃനാമം", + "users": "ഉപയോക്താക്കൾ", + "users_added_to_album_count": "{count, plural, one {# ഉപയോക്താവിനെ} other {# ഉപയോക്താക്കളെ}} ആൽബത്തിലേക്ക് ചേർത്തു", + "utilities": "യൂട്ടിലിറ്റികൾ", + "validate": "സാധൂകരിക്കുക", + "validate_endpoint_error": "ദയവായി സാധുവായ ഒരു URL നൽകുക", + "variables": "വേരിയബിളുകൾ", + "version": "പതിപ്പ്", + "version_announcement_closing": "നിങ്ങളുടെ സുഹൃത്ത്, അലക്സ്", + "version_announcement_message": "നമസ്കാരം! Immich-ന്റെ ഒരു പുതിയ പതിപ്പ് ലഭ്യമാണ്. നിങ്ങളുടെ സജ്ജീകരണം ഏറ്റവും പുതിയതാണെന്ന് ഉറപ്പാക്കാൻ ദയവായി റിലീസ് നോട്ടുകൾ വായിക്കാൻ കുറച്ച് സമയമെടുക്കുക. ഇത് തെറ്റായ കോൺഫിഗറേഷനുകൾ ഒഴിവാക്കാൻ സഹായിക്കും, പ്രത്യേകിച്ചും നിങ്ങൾ വാച്ച്ടവർ അല്ലെങ്കിൽ നിങ്ങളുടെ Immich ഇൻസ്റ്റൻസ് യാന്ത്രികമായി അപ്ഡേറ്റ് ചെയ്യുന്ന ഏതെങ്കിലും സംവിധാനം ഉപയോഗിക്കുന്നുണ്ടെങ്കിൽ.", + "version_history": "പതിപ്പ് ചരിത്രം", + "version_history_item": "{date}-ന് {version} ഇൻസ്റ്റാൾ ചെയ്തു", + "video": "വീഡിയോ", + "video_hover_setting": "ഹോവർ ചെയ്യുമ്പോൾ വീഡിയോ തംബ്നെയിൽ പ്ലേ ചെയ്യുക", + "video_hover_setting_description": "മൗസ് ഒരു ഇനത്തിന് മുകളിലൂടെ ഹോവർ ചെയ്യുമ്പോൾ വീഡിയോ തംബ്നെയിൽ പ്ലേ ചെയ്യുക. പ്രവർത്തനരഹിതമാക്കിയാലും, പ്ലേ ഐക്കണിന് മുകളിലൂടെ ഹോവർ ചെയ്ത് പ്ലേബാക്ക് ആരംഭിക്കാൻ കഴിയും.", + "videos": "വീഡിയോകൾ", + "videos_count": "{count, plural, one {# വീഡിയോ} other {# വീഡിയോകൾ}}", + "view": "കാണുക", + "view_album": "ആൽബം കാണുക", + "view_all": "എല്ലാം കാണുക", + "view_all_users": "എല്ലാ ഉപയോക്താക്കളെയും കാണുക", + "view_details": "വിശദാംശങ്ങൾ കാണുക", + "view_in_timeline": "ടൈംലൈനിൽ കാണുക", + "view_link": "ലിങ്ക് കാണുക", + "view_links": "ലിങ്കുകൾ കാണുക", + "view_name": "കാണുക", + "view_next_asset": "അടുത്ത അസറ്റ് കാണുക", + "view_previous_asset": "മുമ്പത്തെ അസറ്റ് കാണുക", + "view_qr_code": "QR കോഡ് കാണുക", + "view_similar_photos": "സമാനമായ ഫോട്ടോകൾ കാണുക", + "view_stack": "സ്റ്റാക്ക് കാണുക", + "view_user": "ഉപയോക്താവിനെ കാണുക", + "viewer_remove_from_stack": "സ്റ്റാക്കിൽ നിന്ന് നീക്കം ചെയ്യുക", + "viewer_stack_use_as_main_asset": "പ്രധാന അസറ്റായി ഉപയോഗിക്കുക", + "viewer_unstack": "അൺ-സ്റ്റാക്ക് ചെയ്യുക", + "visibility_changed": "{count, plural, one {# വ്യക്തിയുടെ} other {# ആളുകളുടെ}} ദൃശ്യത മാറ്റി", "waiting": "കാത്തിരിക്കുന്നു", "warning": "മുന്നറിയിപ്പ്", "week": "ആഴ്ച", "welcome": "സ്വാഗതം", + "welcome_to_immich": "Immich-ലേക്ക് സ്വാഗതം", + "wifi_name": "വൈ-ഫൈയുടെ പേര്", + "wrong_pin_code": "തെറ്റായ പിൻ കോഡ്", "year": "വർഷം", - "yes": "അതെ" + "years_ago": "{years, plural, one {# വർഷം} other {# വർഷങ്ങൾ}} മുമ്പ്", + "yes": "അതെ", + "you_dont_have_any_shared_links": "നിങ്ങൾക്ക് പങ്കിട്ട ലിങ്കുകളൊന്നുമില്ല", + "your_wifi_name": "നിങ്ങളുടെ വൈ-ഫൈയുടെ പേര്", + "zoom_image": "ചിത്രം വലുതാക്കുക", + "zoom_to_bounds": "പരിധികളിലേക്ക് സൂം ചെയ്യുക" } diff --git a/i18n/mr.json b/i18n/mr.json index dbc2e3d114..fb1b61e2af 100644 --- a/i18n/mr.json +++ b/i18n/mr.json @@ -357,6 +357,9 @@ "trash_number_of_days_description": "कायमस्वरीत्या काढून टाकण्यापूर्वी ट्रॅशमध्ये सामग्री किती दिवस ठेवायची ते क्रम", "trash_settings": "ट्रॅश सेटिंग्ज", "trash_settings_description": "ट्रॅश सेटिंग्ज व्यवस्थापित करा", + "unlink_all_oauth_accounts": "सर्व OAuth खात्यांची जोडणी तोडा", + "unlink_all_oauth_accounts_description": "नव्या सेवा-प्रदात्याकडे स्थलांतर करण्यापूर्वी सर्व OAuth खात्यांची जोडणी तोडायला विसरू नका.", + "unlink_all_oauth_accounts_prompt": "तुम्ही खरोखर सर्व OAuth खात्यांची जोडणी तोडू इच्छिता का? यामुळे प्रत्येक वापरकर्त्याचा OAuth ID रीसेट होईल आणि ही कृती पूर्वस्थितीत आणता येणार नाही.", "user_cleanup_job": "वापरकर्ता स्वच्छता", "user_delete_delay": "{user} यांचे खाते आणि मालमत्ता कायमची हटविण्यासाठी {delay, plural, one {# दिवस} other {# दिवस}} नंतर शेड्यूल केली जातील.", "user_delete_delay_settings": "हटविण्याची विलंबीत कालावधी", @@ -390,6 +393,8 @@ "advanced_settings_prefer_remote_title": "रिमोट प्रतिमा पसंत करा", "advanced_settings_proxy_headers_subtitle": "प्रत्येक नेटवर्क विनंतीसोबत Immich पाठवावयाचे प्रॉक्सी हेडर येथे परिभाषित करा", "advanced_settings_proxy_headers_title": "प्रॉक्सी हेडर", + "advanced_settings_readonly_mode_subtitle": "या मोडमध्ये फोटो फक्त पाहता येतात - अनेक फोटो निवडणे, शेअर करणे, कास्ट करणे आणि हटवणे अशा क्रिया निष्क्रिय राहतात. मुख्य स्क्रीनवरील वापरकर्ता अवतारातून हा मोड चालू किंवा बंद करा", + "advanced_settings_readonly_mode_title": "फक्त पाहण्याचा मोड", "advanced_settings_self_signed_ssl_subtitle": "सर्व्हर एंडपॉइंटसाठी SSL प्रमाणपत्र सत्यापन वगळते. स्वाक्षरीत प्रमाणपत्रांसाठी आवश्यक.", "advanced_settings_self_signed_ssl_title": "स्वतः स्वाक्षरीत SSL प्रमाणपत्रांना परवानगी द्या", "advanced_settings_sync_remote_deletions_subtitle": "वेबवर ही क्रिया केली गेल्यावर या उपकरणावर असलेले अॅसेट आपोआप हटवा किंवा पुनर्संचयित करा", @@ -417,6 +422,7 @@ "album_remove_user_confirmation": "आपण निश्चितच वापरकर्ता {user} काढून टाकणार आहात का?", "album_search_not_found": "तुमच्या शोधाशी जुळणारे कोणतेही अल्बम आढळले नाहीत", "album_share_no_users": "असा दिसते की हा अल्बम तुम्ही सर्व वापरकर्त्यांसोबत शेअर केला आहे किंवा शेअर करण्यासाठी कुठलाही वापरकर्ता उपलब्ध नाही.", + "album_summary": "अल्बम सारांश", "album_updated": "अल्बम अद्यतनित", "album_updated_setting_description": "शेअर केलेल्या अल्बममध्ये नवीन फाईल्स आल्यास ईमेल सूचनार्थ प्राप्त करा", "album_user_left": "सोडले: {album}", @@ -455,6 +461,7 @@ "app_bar_signout_dialog_title": "साइन आउट", "app_settings": "अ‍ॅप सेटिंग्ज", "appears_in": "दिसते (कुठे दिसते)", + "apply_count": "लागू करा ({count, number})", "archive": "आर्काइव्ह", "archive_action_prompt": "{count} आर्काइव्हमध्ये जोडले", "archive_or_unarchive_photo": "फोटो आर्काइव्ह करा किंवा अनआर्काइव्ह करा", @@ -487,6 +494,8 @@ "asset_restored_successfully": "साधन यशस्वीपणे पुनर्संचयित केले गेले", "asset_skipped": "वगळले", "asset_skipped_in_trash": "ट्रॅशमध्ये", + "asset_trashed": "मीडिया घटक कचरापेटीत हलवला", + "asset_troubleshoot": "मीडिया घटक समस्यानिवारण", "asset_uploaded": "अपलोड झाले", "asset_uploading": "अपलोड करत आहे…", "asset_viewer_settings_subtitle": "आपल्या गॅलरी व्ह्यूअरच्या सेटिंग्ज व्यवस्थापित करा", @@ -494,7 +503,9 @@ "assets": "साधने", "assets_added_count": "{count, plural, one {# साधन जोडले} other {# साधने जोडले}}", "assets_added_to_album_count": "{count, plural, one {# साधन अल्बममध्ये जोडले} other {# साधने अल्बममध्ये जोडले}}", + "assets_added_to_albums_count": "{albumTotal, plural, one {# अल्बममध्ये} other {# अल्बममध्ये}} {assetTotal, plural, one {# मीडिया घटक} other {# मीडिया घटक}} जोडले", "assets_cannot_be_added_to_album_count": "{count, plural, one {# साधन अल्बममध्ये जोडता येणार नाही} other {# साधने अल्बममध्ये जोडता येणार नाहीत}}", + "assets_cannot_be_added_to_albums": "{count, plural, one {# मीडिया घटक कोणत्याही अल्बममध्ये जोडता येत नाही} other {# मीडिया घटक कोणत्याही अल्बममध्ये जोडता येत नाहीत}}", "assets_count": "{count, plural, one {# साधन} other {# साधने}}", "assets_deleted_permanently": "{count} साधन(े) कायमचे हटविले", "assets_deleted_permanently_from_server": "Immich सर्व्हरवरून {count} साधन(े) कायमचे हटविले", @@ -502,6 +513,20 @@ "assets_downloaded_successfully": "{count, plural, one {एक फाईल यशस्वीरित्या डाउनलोड झाली} other {# फाईल्स यशस्वीरित्या डाउनलोड झाल्या}}", "assets_moved_to_trash_count": "{count, plural, one {एक फाईल ट्रॅशमध्ये हलवली} other {# फाईल्स ट्रॅशमध्ये हलवल्या}}", "assets_permanently_deleted_count": "{count, plural, one {एक फाईल कायमस्वरूपी हटवली} other {# फाईल्स कायमस्वरूपी हटवल्या}}", + "assets_removed_count": "{count, plural, one {# मीडिया घटक काढून टाकला} other {# मीडिया घटक काढून टाकले}}", + "assets_removed_permanently_from_device": "{count} मीडिया घटक तुमच्या डिव्हाइसवरून कायमचे काढले गेले", + "assets_restore_confirmation": "कचरापेटीतले सर्व मीडिया घटक पुनर्संचयित करायचे आहेत का? ही कृती पूर्ववत करता येणार नाही. लक्षात ठेवा - ऑफलाइन मीडिया घटक अशा प्रकारे पुनर्संचयित करता येत नाहीत.", + "assets_restored_count": "{count, plural, one {# मीडिया घटक पुनर्संचयित केला} other {# मीडिया घटक पुनर्संचयित केले}}", + "assets_restored_successfully": "{count} मीडिया घटक यशस्वीरित्या पुनर्संचयित झाले", + "assets_trashed": "{count} मीडिया घटक कचरापेटीत हलवले", + "assets_trashed_count": "{count, plural, one {# मीडिया घटक कचरापेटीत हलवला} other {# मीडिया घटक कचरापेटीत हलवले}}", + "assets_trashed_from_server": "{count} मीडिया घटक Immich सर्व्हरवरून कचरापेटीत हलवले", + "assets_were_part_of_album_count": "{count, plural, one {मीडिया घटक आधीच त्या अल्बमचा भाग होता} other {मीडिया घटक आधीच त्या अल्बमचा भाग होते}}", + "assets_were_part_of_albums_count": "{count, plural, one {मीडिया घटक आधीच अल्बम्सचा भाग होता} other {मीडिया घटक आधीच अल्बम्सचा भाग होते}}", + "authorized_devices": "अधिकृत उपकरणे", + "automatic_endpoint_switching_subtitle": "उपलब्ध असल्यास निश्‍चित Wi-Fi वर स्थानिकरित्या कनेक्ट करा आणि इतर ठिकाणी पर्यायी कनेक्शन वापरा", + "automatic_endpoint_switching_title": "स्वयंचलित URL स्विचिंग", + "autoplay_slideshow": "स्वयंचलित स्लाइडशो", "back": "मागे", "back_close_deselect": "मागे किंवा बंद करा / निवड रद्द करा", "background_location_permission": "बॅकग्राउंडमध्ये स्थान परवानगी द्या", @@ -562,6 +587,7 @@ "backup_controller_page_turn_on": "फोरग्राउंड बॅकअप चालू करा", "backup_controller_page_uploading_file_info": "फाईल माहिती अपलोड करत आहे", "backup_err_only_album": "अंतिम अल्बम काढता येणार नाही", + "backup_error_sync_failed": "समक्रमण अयशस्वी. बॅकअप प्रक्रिया करता येत नाही.", "backup_info_card_assets": "फाईल्स", "backup_manual_cancelled": "रद्द केले", "backup_manual_in_progress": "अपलोड आधीच चालू आहे. थोड्यावेळेनंतर पुन्हा प्रयत्न करा", @@ -629,6 +655,8 @@ "change_pin_code": "PIN कोड बदला", "change_your_password": "आपला संकेतशब्द बदला", "changed_visibility_successfully": "दृश्यमानता यशस्वीरित्या बदलली", + "charging": "चार्जिंग", + "charging_requirement_mobile_backup": "बॅकग्राउंड बॅकअपसाठी उपकरण चार्ज होत असणे आवश्यक आहे", "check_corrupt_asset_backup": "भ्रष्ट फाईल बॅकअप तपासा", "check_corrupt_asset_backup_button": "तपासणी करा", "check_corrupt_asset_backup_description": "फक्त Wi-Fi वर हा तपास चालवा आणि सर्व फाईल्स बॅकअप झाल्यावरच. प्रक्रिया काही मिनिटे लागू शकते.", @@ -715,6 +743,7 @@ "create_user": "वापरकर्ता तयार करा", "created": "तयार केले", "created_at": "निर्मिती तारीख", + "creating_linked_albums": "लिंक केलेले अल्बम तयार करत आहे...", "crop": "छाटणी करा", "curated_object_page_title": "गोष्टी", "current_device": "वर्तमान उपकरण", @@ -1032,6 +1061,7 @@ "filter_people": "लोक फिल्टर करा", "filter_places": "ठिकाणे फिल्टर करा", "find_them_fast": "नावाने पटकन शोधा", + "first": "प्रथम", "fix_incorrect_match": "चुकीची जुळणी दुरुस्त करा", "folder": "फोल्डर", "folder_not_found": "फोल्डर सापडला नाही", @@ -1042,18 +1072,72 @@ "gcast_enabled": "Google Cast", "gcast_enabled_description": "ही सुविधा चालण्यासाठी Google कडील बाह्य संसाधने लोड करते.", "general": "सामान्य", + "geolocation_instruction_location": "GPS निर्देशांक असलेल्या मीडिया घटकावर क्लिक करून त्याचे स्थान वापरा, किंवा थेट नकाशावरून स्थान निवडा", "get_help": "मदत घ्या", "get_wifiname_error": "Wi-Fi चे नाव मिळाले नाही. आवश्यक परवानग्या दिल्या आहेत आणि Wi-Fi नेटवर्कशी जोडले आहात याची खात्री करा", "getting_started": "सुरुवात करा", "go_back": "मागे जा", "go_to_folder": "फोल्डरकडे जा", "go_to_search": "शोधाकडे जा", + "gps": "जीपीएस", + "gps_missing": "GPS उपलब्ध नाही", "grant_permission": "परवानगी द्या", "group_albums_by": "अल्बम गटबद्ध करा: …", "group_country": "देशानुसार गट करा", "group_no": "गटबद्ध नाही", "group_owner": "मालकानुसार गट करा", "group_places_by": "स्थळे गटबद्ध करा: …", + "group_year": "वर्षानुसार गटबद्ध करा", + "haptic_feedback_switch": "हॅप्टिक फीडबॅक सक्षम करा", + "haptic_feedback_title": "हॅप्टिक फीडबॅक", + "has_quota": "कोटा आहे", + "hash_asset": "मीडिया घटकाचा हॅश तयार करा", + "hashed_assets": "हॅश केलेले मीडिया घटक", + "hashing": "हॅशिंग", + "header_settings_add_header_tip": "हेडर जोडा", + "header_settings_field_validator_msg": "मूल्य रिकामे असू शकत नाही", + "header_settings_header_name_input": "हेडरचे नाव", + "header_settings_header_value_input": "हेडरचे मूल्य", + "headers_settings_tile_subtitle": "प्रत्येक नेटवर्क विनंतीसोबत अ‍ॅपने पाठवायचे प्रॉक्सी हेडर्स परिभाषित करा", + "headers_settings_tile_title": "सानुकूल प्रॉक्सी हेडर्स", + "hi_user": "नमस्कार {name} ({email})", + "hide_all_people": "सर्व व्यक्ती लपवा", + "hide_gallery": "गॅलरी लपवा", + "hide_named_person": "व्यक्ती {name} लपवा", + "hide_password": "संकेतशब्द लपवा", + "hide_person": "व्यक्ती लपवा", + "hide_unnamed_people": "नाव नसलेल्या व्यक्ती लपवा", + "home_page_add_to_album_conflicts": "अल्बम {album} मध्ये {added} मीडिया घटक जोडले. {failed} मीडिया घटक आधीच त्या अल्बममध्ये आहेत.", + "home_page_add_to_album_err_local": "स्थानिक मीडिया घटक अजून अल्बममध्ये जोडता येत नाहीत, वगळत आहे", + "home_page_add_to_album_success": "अल्बम {album} मध्ये {added} मीडिया घटक जोडले.", + "home_page_album_err_partner": "भागीदारचे मीडिया घटक अजून अल्बममध्ये जोडता येत नाहीत, वगळत आहे", + "home_page_archive_err_local": "स्थानिक मीडिया घटक अजून संग्रहित करता येत नाहीत, वगळत आहे", + "home_page_archive_err_partner": "भागीदारचे मीडिया घटक संग्रहित करता येत नाहीत, वगळत आहे", + "home_page_building_timeline": "टाइमलाइन तयार करत आहे", + "home_page_delete_err_partner": "भागीदारचे मीडिया घटक हटवता येत नाहीत, वगळत आहे", + "home_page_delete_remote_err_local": "दूरस्थ हटवण्याच्या निवडीत स्थानिक मीडिया घटक आहेत, वगळत आहे", + "home_page_favorite_err_local": "स्थानिक मीडिया घटकांना अजून आवडीमध्ये जोडता येत नाही, वगळत आहे", + "home_page_favorite_err_partner": "भागीदारचे मीडिया घटक अजून आवडीमध्ये जोडता येत नाहीत, वगळत आहे", + "home_page_first_time_notice": "अ‍ॅप प्रथमच वापरत असाल तर टाइमलाइनमध्ये फोटो आणि व्हिडिओ भरण्यासाठी कृपया बॅकअप अल्बम निवडा", + "home_page_locked_error_local": "स्थानिक मीडिया घटक लॉक केलेल्या फोल्डरमध्ये हलवता येत नाहीत, वगळत आहे", + "home_page_locked_error_partner": "भागीदारचे मीडिया घटक लॉक केलेल्या फोल्डरमध्ये हलवता येत नाहीत, वगळत आहे", + "home_page_share_err_local": "स्थानिक मीडिया घटक लिंकद्वारे शेअर करता येत नाहीत, वगळत आहे", + "home_page_upload_err_limit": "एकावेळी कमाल 30 मीडिया घटकच अपलोड करता येतात, वगळत आहे", + "host": "होस्ट", + "hour": "तास", + "hours": "तास", + "id": "ID", + "idle": "निष्क्रिय", + "ignore_icloud_photos": "iCloud वरील फोटो दुर्लक्षित करा", + "ignore_icloud_photos_description": "iCloud वर साठवलेले फोटो Immich सर्व्हरवर अपलोड केले जाणार नाहीत", + "image": "फोटो", + "image_alt_text_date": "{isVideo, select, true {व्हिडिओ} other {फोटो}} {date} ला घेतले", + "image_alt_text_date_1_person": "{isVideo, select, true {व्हिडिओ} other {फोटो}} {person1} सोबत {date} ला घेतले", + "image_alt_text_date_2_people": "{isVideo, select, true {व्हिडिओ} other {फोटो}} {person1} आणि {person2} सोबत {date} ला घेतले", + "image_alt_text_date_3_people": "{isVideo, select, true {व्हिडिओ} other {फोटो}} {person1}, {person2} आणि {person3} सोबत {date} ला घेतले", + "image_alt_text_date_4_or_more_people": "{isVideo, select, true {व्हिडिओ} other {फोटो}} {person1}, {person2} आणि आणखी {additionalCount, number} जणांसोबत {date} ला घेतले", + "image_alt_text_date_place": "{isVideo, select, true {व्हिडिओ} other {फोटो}} {city}, {country} येथे {date} ला घेतले", + "image_alt_text_date_place_1_person": "{isVideo, select, true {व्हिडिओ} other {फोटो}} {city}, {country} येथे {person1} सोबत {date} ला घेतले", "notification_permission_dialog_content": "सूचना सक्षम करण्यासाठी सेटिंग्जमध्ये जा आणि अनुमती द्या.", "notification_permission_list_tile_content": "सूचना सक्षम करण्यासाठी परवानगी द्या.", "notification_permission_list_tile_enable_button": "सूचना सक्षम करा", @@ -1764,5 +1848,6 @@ "year": "वर्ष", "yes": "हो", "you_dont_have_any_shared_links": "आपल्याकडे कोणतेही सामायिक दुवे नाहीत", - "zoom_image": "प्रतिमा झूम करा" + "zoom_image": "प्रतिमा झूम करा", + "zoom_to_bounds": "सीमेपर्यंत झूम करा" } diff --git a/i18n/nb_NO.json b/i18n/nb_NO.json index 4162170885..75429712d5 100644 --- a/i18n/nb_NO.json +++ b/i18n/nb_NO.json @@ -23,23 +23,25 @@ "add_partner": "Legg til partner", "add_path": "Legg til sti", "add_photos": "Legg til bilder", - "add_tag": "Legg til tagg", + "add_tag": "Legg til merkelapp", "add_to": "Legg til i…", "add_to_album": "Legg til album", "add_to_album_bottom_sheet_added": "Lagt til i {album}", "add_to_album_bottom_sheet_already_exists": "Allerede i {album}", + "add_to_album_bottom_sheet_some_local_assets": "Noen lokale elementer kunne ikke legges til i albumet", "add_to_album_toggle": "Avhuking for {album}", "add_to_albums": "Legg til i album", "add_to_albums_count": "Legg til i albumer ({count})", "add_to_shared_album": "Legg til delt album", + "add_upload_to_stack": "Legg til opplasting i stakken", "add_url": "Legg til URL", - "added_to_archive": "Lagt til i arkiv", - "added_to_favorites": "Lagt til i favoritter", + "added_to_archive": "Lagt til i arkivet", + "added_to_favorites": "Lagt til favoritter", "added_to_favorites_count": "Lagt til {count, number} i favoritter", "admin": { "add_exclusion_pattern_description": "Legg til ekskluderingsmønstre. Globbing med *, ** og ? støttes. For å ignorere alle filer i en hvilken som helst mappe som heter \"Raw\", bruk \"**/Raw/**\". For å ignorere alle filer som slutter på \".tif\", bruk \"**/*.tif\". For å ignorere en absolutt filplassering, bruk \"/filsti/til/ignorer/**\".", "admin_user": "Administrasjonsbruker", - "asset_offline_description": "Dette eksterne biblioteksobjektet finnes ikke lenger på disk og har blitt flyttet til papirkurven. Hvis filen ble flyttet innad i biblioteket, se etter det tilsvarende objektet i tidslinjen din. For å gjenopprette objektet, vennligst sørg for at filstien under er tilgjengelig for Immich og skann biblioteket.", + "asset_offline_description": "Dette eksterne bibliotekselementet finnes ikke lenger på disk og har blitt flyttet til papirkurven. Hvis filen ble flyttet innad i biblioteket, se etter det tilsvarende elementet i tidslinjen din. For å gjenopprette elementet, vennligst sørg for at filstien under er tilgjengelig for Immich og skann biblioteket.", "authentication_settings": "Godkjenningsinnstillinger", "authentication_settings_description": "Administrer passord, OAuth, og andre innstillinger for autentisering", "authentication_settings_disable_all": "Er du sikker på at du ønsker å deaktivere alle innloggingsmetoder? Innlogging vil bli fullstendig deaktivert.", @@ -59,12 +61,12 @@ "backup_settings_description": "Håndter innstillinger for database-dump.", "cleared_jobs": "Ryddet opp jobber for: {job}", "config_set_by_file": "Konfigurasjonen er for øyeblikket satt av en konfigurasjonsfil", - "confirm_delete_library": "Er du sikker på at du vil slette biblioteket {library}?", - "confirm_delete_library_assets": "Er du sikker på at du vil slette dette biblioteket? Dette vil slette alt innhold ({count, plural, one {# objekt} other {# objekter}}) og tilhørende eiendeler fra Immich og kan ikke angres. Filene vil forbli på disken.", + "confirm_delete_library": "Vil du virkelig slette biblioteket {library}?", + "confirm_delete_library_assets": "Vil du virkelig slette dette biblioteket? Dette vil slette alt innhold ({count, plural, one {# element} other {# elementer}}) og tilhørende eiendeler fra Immich og kan ikke angres. Filene vil forbli på disken.", "confirm_email_below": "For å bekrefte, skriv inn \"{email}\" nedenfor", - "confirm_reprocess_all_faces": "Er du sikker på at du vil behandle alle ansikter på nytt? Dette vil også fjerne navngitte personer.", - "confirm_user_password_reset": "Er du sikker på at du vil tilbakestille passordet til {user}?", - "confirm_user_pin_code_reset": "Er du sikker på at du vil tilbakestille PIN-koden til {user} ?", + "confirm_reprocess_all_faces": "Vil du virkelig behandle alle ansikter på nytt? Dette vil også fjerne navngitte personer.", + "confirm_user_password_reset": "Vil du virkelig tilbakestille passordet til {user}?", + "confirm_user_pin_code_reset": "Vil du virkelig tilbakestille PIN-koden til {user} ?", "create_job": "Lag jobb", "cron_expression": "Cron uttrykk", "cron_expression_description": "Still inn skanneintervallet med cron-formatet. For mer informasjon henvises til f.eks. Crontab Guru", @@ -189,9 +191,9 @@ "nightly_tasks_database_cleanup_setting": "Opprydningsjobber for databasen", "nightly_tasks_database_cleanup_setting_description": "Rydder opp i gamle, utgåtte data fra databasen", "nightly_tasks_generate_memories_setting": "Genererer minner", - "nightly_tasks_generate_memories_setting_description": "Generer nye minner fra objekter", + "nightly_tasks_generate_memories_setting_description": "Generer nye minner fra elementer", "nightly_tasks_missing_thumbnails_setting": "Generer manglende miniatyrbilder", - "nightly_tasks_missing_thumbnails_setting_description": "Legg til objekter i kø som mangler miniatyrbilder for generering", + "nightly_tasks_missing_thumbnails_setting_description": "Legg til elementer i kø som mangler miniatyrbilder for generering", "nightly_tasks_settings": "Innstillinger for nattjobber", "nightly_tasks_settings_description": "Endre på nattjobber", "nightly_tasks_start_time_setting": "Starttid", @@ -275,7 +277,7 @@ "storage_template_hash_verification_enabled_description": "Aktiver hasjverifisering. Ikke deaktiver dette med mindre du er sikker på konsekvensene", "storage_template_migration": "Implementer lagringsmal", "storage_template_migration_description": "Bruk gjeldende {template} på tidligere opplastede bilder", - "storage_template_migration_info": "Lagringsmalen vil endre filtypen til små bokstaver. Malendringer vil kun gjelde nye objekter. For å anvende malen på tidligere opplastede objekter, kjør {job}.", + "storage_template_migration_info": "Lagringsmalen vil endre filtypen til små bokstaver. Malendringer vil kun gjelde nye elementer. For å anvende malen på tidligere opplastede elementer, kjør {job}.", "storage_template_migration_job": "Migreringsjobb for lagringsmal", "storage_template_more_details": "For mer informasjon om denne funksjonen, se lagringsmalen og dens konsekvenser", "storage_template_onboarding_description_v2": "Når aktivert vil denne funksjonen automatisk organisere filer basert på en brukerdefinert mal. For mer informasjon, se denne linken dokumentasjon.", @@ -367,7 +369,7 @@ "trash_settings_description": "Administrer papirkurv-innstillinger", "unlink_all_oauth_accounts": "Koble fra alle OAuth-kontoer", "unlink_all_oauth_accounts_description": "Husk å koble fra alle OAuth-kontoer før du migrerer til ny leverandør.", - "unlink_all_oauth_accounts_prompt": "Er du sikker på at du vil koble fra alle OAuth-kontoer? Dette vil nullstille OAuth ID for hver bruker, og kan ikke angres.", + "unlink_all_oauth_accounts_prompt": "Vil du virkelig koble fra alle OAuth-kontoer? Dette vil nullstille OAuth ID for hver bruker, og kan ikke angres.", "user_cleanup_job": "Bruker opprydning", "user_delete_delay": "{user}s konto og elementer vil legges i kø for permanent sletting om {delay, plural, one {# dag} other {# dager}}.", "user_delete_delay_settings": "Sletteforsinkelse", @@ -390,8 +392,8 @@ "video_conversion_job": "Transkod videoer", "video_conversion_job_description": "Konverter videoer for bedre kompatibilitet med nettlesere og enheter" }, - "admin_email": "Administrator E-post", - "admin_password": "Administrator Passord", + "admin_email": "Administrator e-post", + "admin_password": "Administratorpassord", "administration": "Administrasjon", "advanced": "Avansert", "advanced_settings_enable_alternate_media_filter_subtitle": "Bruk denne innstillingen for å filtrere mediefiler under synkronisering basert på alternative kriterier. Bruk kun denne innstillingen dersom man opplever problemer med at applikasjonen ikke oppdager alle album.", @@ -413,33 +415,33 @@ "age_months": "Alder {months, plural, one {# måned} other {# måneder}}", "age_year_months": "Alder 1 år, {months, plural, one {# måned} other {# måneder}}", "age_years": "{years, plural, other {Alder #}}", - "album_added": "Album lagt til", + "album_added": "Album opprettet", "album_added_notification_setting_description": "Motta en e-postvarsling når du legges til i et delt album", "album_cover_updated": "Albumomslag oppdatert", - "album_delete_confirmation": "Er du sikker på at du vil slette albumet {album}?", + "album_delete_confirmation": "Vil du virkelig slette albumet {album}?", "album_delete_confirmation_description": "Hvis dette albumet deles, vil andre brukere miste tilgangen til dette.", "album_deleted": "Album slettet", "album_info_card_backup_album_excluded": "EKSKLUDERT", "album_info_card_backup_album_included": "INKLUDERT", "album_info_updated": "Albuminformasjon oppdatert", "album_leave": "Forlate album?", - "album_leave_confirmation": "Er du sikker på at du vil forlate {album}?", - "album_name": "Album Navn", + "album_leave_confirmation": "Vil du virkelig forlate {album}?", + "album_name": "Albumnavn", "album_options": "Albumalternativer", "album_remove_user": "Fjerne bruker?", - "album_remove_user_confirmation": "Er du sikker på at du vil fjerne {user}?", + "album_remove_user_confirmation": "Vil du virkelig fjerne {user}?", "album_search_not_found": "Ingen album ble funnet som traff ditt søk", - "album_share_no_users": "Ser ut til at du har delt dette albumet med alle brukere, eller du ikke har noen brukere å dele det med.", + "album_share_no_users": "Dette albumet er allerede delt med du har delt dette albumet med alle brukere, eller du ikke har noen brukere å dele det med.", "album_summary": "Oppsummering av album", "album_updated": "Album oppdatert", "album_updated_setting_description": "Motta e-postvarsling når et delt album får nye filer", "album_user_left": "Forlot {album}", "album_user_removed": "Fjernet {user}", - "album_viewer_appbar_delete_confirm": "Er du sikker på at du vil slette dette albumet fra kontoen din?", + "album_viewer_appbar_delete_confirm": "Vil du virkelig slette dette albumet fra kontoen din?", "album_viewer_appbar_share_err_delete": "Kunne ikke slette albumet", "album_viewer_appbar_share_err_leave": "Kunne ikke forlate albumet", - "album_viewer_appbar_share_err_remove": "Det oppstod et problem ved fjerning av objekter fra albumet", - "album_viewer_appbar_share_err_title": "Feilet ved endring av albumtittel", + "album_viewer_appbar_share_err_remove": "Det oppstod et problem ved fjerning av elementer fra albumet", + "album_viewer_appbar_share_err_title": "Mislyktes ved endring av albumtittel", "album_viewer_appbar_share_leave": "Forlat album", "album_viewer_appbar_share_to": "Del til", "album_viewer_page_share_add_users": "Legg til brukere", @@ -460,11 +462,11 @@ "allow_public_user_to_upload": "Tillat uautentiserte brukere å laste opp", "alt_text_qr_code": "QR-kodebilde", "anti_clockwise": "Mot klokken", - "api_key": "API Nøkkel", + "api_key": "API-nøkkel", "api_key_description": "Denne verdien vil vises kun én gang. Pass på å kopiere den før du lukker vinduet.", "api_key_empty": "API-nøkkelnavnet bør ikke være tomt", "api_keys": "API-nøkler", - "app_bar_signout_dialog_content": "Er du sikker på at du vil logge ut?", + "app_bar_signout_dialog_content": "Vil du virkelig logge ut?", "app_bar_signout_dialog_ok": "Ja", "app_bar_signout_dialog_title": "Logg ut", "app_settings": "Appinstillinger", @@ -473,16 +475,16 @@ "archive": "Arkiv", "archive_action_prompt": "{count} lagt til i arkivet", "archive_or_unarchive_photo": "Arkiver eller ta ut av arkivet", - "archive_page_no_archived_assets": "Ingen arkiverte objekter funnet", + "archive_page_no_archived_assets": "Ingen arkiverte elementer funnet", "archive_page_title": "Arkiv ({count})", "archive_size": "Arkivstørrelse", "archive_size_description": "Konfigurer arkivstørrelsen for nedlastinger (i GiB)", "archived": "Arkivert", "archived_count": "{count, plural, other {Arkivert #}}", "are_these_the_same_person": "Er disse samme person?", - "are_you_sure_to_do_this": "Er du sikker på at du vil gjøre dette?", - "asset_action_delete_err_read_only": "Kan ikke slette objekt(er) med kun lese-rettighet, hopper over", - "asset_action_share_err_offline": "Kan ikke hente offline objekt(er), hopper over", + "are_you_sure_to_do_this": "Vil du virkelig gjøre dette?", + "asset_action_delete_err_read_only": "Kunne ikke slette element(er) med kun lese-rettighet, hopper over", + "asset_action_share_err_offline": "Kunne ikke hente offline element(er), hopper over", "asset_added_to_album": "Lagt til i album", "asset_adding_to_album": "Legger til i album…", "asset_description_updated": "Elementbeskrivelse har blitt oppdatert", @@ -498,37 +500,37 @@ "asset_list_settings_subtitle": "Innstillinger for layout av fotorutenett", "asset_list_settings_title": "Fotorutenett", "asset_offline": "Fil utilgjengelig", - "asset_offline_description": "Dette elementet er offline. Immich kan ikke aksessere dets lokasjon. Vennlist påse at elementet er tilgijengelig og skann så biblioteket på nytt.", + "asset_offline_description": "Dette elementet er offline. Immich kan ikke aksessere dets lokasjon. Vennligst påse at elementet er tilgjengelig og skann så biblioteket på nytt.", "asset_restored_successfully": "Objekt(er) gjenopprettet", "asset_skipped": "Hoppet over", - "asset_skipped_in_trash": "I søppelbøtten", + "asset_skipped_in_trash": "I papirkurven", "asset_trashed": "Objekt slettet", - "asset_troubleshoot": "Feilsøk objekt", + "asset_troubleshoot": "Feilsøk element", "asset_uploaded": "Lastet opp", "asset_uploading": "Laster opp…", "asset_viewer_settings_subtitle": "Endre dine visningsinnstillinger for galleriet", "asset_viewer_settings_title": "Objektviser", "assets": "Filer", - "assets_added_count": "Lagt til {count, plural, one {# objekt} other {# objekter}}", - "assets_added_to_album_count": "Lagt til {count, plural, one {# objekter} other {# objekt}} i album", + "assets_added_count": "Lagt til {count, plural, one {# element} other {# elementer}}", + "assets_added_to_album_count": "Lagt til {count, plural, one {# elementer} other {# element}} i album", "assets_added_to_albums_count": "Lagt til {assetTotal, plural, one {# asset} other {# assets}} til {albumTotal, plural, one {# album} other {# albums}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Objektet} other {Objektene}} kan ikke legges til i albumet", "assets_cannot_be_added_to_albums": "{count, plural, one {Asset} other {Assets}} kan ikke legges til i noen av albumene", "assets_count": "{count, plural, one {# fil} other {# filer}}", - "assets_deleted_permanently": "{count} objekt(er) slettet permanent", - "assets_deleted_permanently_from_server": "{count} objekt(er) slettet permanent fra Immich-serveren", + "assets_deleted_permanently": "{count} element(er) slettet permanent", + "assets_deleted_permanently_from_server": "{count} element(er) slettet permanent fra Immich-serveren", "assets_downloaded_failed": "{count, plural, one {Nedlasting av # fil - {error} fil feilet} other {Nedlastede # filer - {error} filer feilet}}", "assets_downloaded_successfully": "{count, plural, one {Nedlastet # fil vellykket} other {Nedlastede # filer vellykket}}", - "assets_moved_to_trash_count": "Flyttet {count, plural, one {# objekt} other {# objekter}} til søppel", - "assets_permanently_deleted_count": "Slettet {count, plural, one {# objekt} other {# objekter}} permanent", - "assets_removed_count": "Slettet {count, plural, one {# objekt} other {# objekter}}", - "assets_removed_permanently_from_device": "{count} objekt(er) slettet permanent fra enheten din", - "assets_restore_confirmation": "Er du sikker på at du vil gjenopprette alle slettede eiendeler? Denne handlingen kan ikke angres! Vær oppmerksom på at frakoblede ressurser ikke kan gjenopprettes på denne måten.", - "assets_restored_count": "Gjenopprettet {count, plural, one {# objekt} other {# objekter}}", - "assets_restored_successfully": "{count} objekt(er) gjenopprettet", - "assets_trashed": "{count} objekt(er) slettet", - "assets_trashed_count": "Kastet {count, plural, one {# objekt} other {# objekter}}", - "assets_trashed_from_server": "{count} objekt(er) slettet fra Immich serveren", + "assets_moved_to_trash_count": "Flyttet {count, plural, one {# element} other {# elementer}} til søppel", + "assets_permanently_deleted_count": "Slettet {count, plural, one {# element} other {# elementer}} permanent", + "assets_removed_count": "Slettet {count, plural, one {# element} other {# elementer}}", + "assets_removed_permanently_from_device": "{count} element(er) slettet permanent fra enheten din", + "assets_restore_confirmation": "Vil du virkelig gjenopprette alle slettede eiendeler? Denne handlingen kan ikke angres! Vær oppmerksom på at frakoblede ressurser ikke kan gjenopprettes på denne måten.", + "assets_restored_count": "Gjenopprettet {count, plural, one {# element} other {# elementer}}", + "assets_restored_successfully": "{count} element(er) gjenopprettet", + "assets_trashed": "{count} element(er) slettet", + "assets_trashed_count": "Kastet {count, plural, one {# element} other {# elementer}}", + "assets_trashed_from_server": "{count} element(er) slettet fra Immich serveren", "assets_were_part_of_album_count": "{count, plural, one {Objektet} other {Objektene}} er allerede lagt til i albumet", "assets_were_part_of_albums_count": "{count, plural, one {Asset was} other {Assets were}} allerede inkludert i albumet", "authorized_devices": "Autoriserte enheter", @@ -547,15 +549,15 @@ "backup_album_selection_page_assets_scatter": "Objekter kan bli spredd over flere album. Album kan derfor bli inkludert eller ekskludert under sikkerhetskopieringen.", "backup_album_selection_page_select_albums": "Velg album", "backup_album_selection_page_selection_info": "Valginformasjon", - "backup_album_selection_page_total_assets": "Totalt antall unike objekter", + "backup_album_selection_page_total_assets": "Totalt antall unike elementer", "backup_albums_sync": "Synkronisering av sikkerhetskopialbum", "backup_all": "Alle", - "backup_background_service_backup_failed_message": "Sikkerhetskopiering av objekter feilet. Prøver på nytt…", + "backup_background_service_backup_failed_message": "Sikkerhetskopiering av elementer feilet. Prøver på nytt…", "backup_background_service_connection_failed_message": "Tilkobling til server feilet. Prøver på nytt…", "backup_background_service_current_upload_notification": "Laster opp {filename}", - "backup_background_service_default_notification": "Ser etter nye objekter…", - "backup_background_service_error_title": "Sikkerhetskopieringsfeil", - "backup_background_service_in_progress_notification": "Sikkerhetskopierer objekter…", + "backup_background_service_default_notification": "Ser etter nye elementer…", + "backup_background_service_error_title": "Feil under sikkerhetskopiering", + "backup_background_service_in_progress_notification": "Sikkerhetskopierer elementer…", "backup_background_service_upload_failure_notification": "Opplasting feilet {filename}", "backup_controller_page_albums": "Sikkerhetskopier albumer", "backup_controller_page_background_app_refresh_disabled_content": "Aktiver bakgrunnsoppdatering i Innstillinger > Generelt > Bakgrunnsoppdatering for å bruke sikkerhetskopiering i bakgrunnen.", @@ -567,8 +569,8 @@ "backup_controller_page_background_battery_info_title": "Batterioptimalisering", "backup_controller_page_background_charging": "Kun ved lading", "backup_controller_page_background_configure_error": "Konfigurering av bakgrunnstjenesten feilet", - "backup_controller_page_background_delay": "Forsink sikkerhetskopiering av nye objekter: {duration}", - "backup_controller_page_background_description": "Skru på bakgrunnstjenesten for å automatisk sikkerhetskopiere alle nye objekter uten å måtte åpne appen", + "backup_controller_page_background_delay": "Forsink sikkerhetskopiering av nye elementer: {duration}", + "backup_controller_page_background_description": "Skru på bakgrunnstjenesten for å automatisk sikkerhetskopiere alle nye elementer uten å måtte åpne appen", "backup_controller_page_background_is_off": "Automatisk sikkerhetskopiering i bakgrunnen er deaktivert", "backup_controller_page_background_is_on": "Automatisk sikkerhetskopiering i bakgrunnen er aktivert", "backup_controller_page_background_turn_off": "Skru av bakgrunnstjenesten", @@ -578,9 +580,9 @@ "backup_controller_page_backup_selected": "Valgte: ", "backup_controller_page_backup_sub": "Opplastede bilder og videoer", "backup_controller_page_created": "Opprettet: {date}", - "backup_controller_page_desc_backup": "Slå på sikkerhetskopiering i forgrunnen for automatisk å laste opp nye objekter til serveren når du åpner appen.", + "backup_controller_page_desc_backup": "Slå på sikkerhetskopiering i forgrunnen for automatisk å laste opp nye elementer til serveren når du åpner appen.", "backup_controller_page_excluded": "Ekskludert: ", - "backup_controller_page_failed": "Feilet ({count})", + "backup_controller_page_failed": "Mislyktes ({count})", "backup_controller_page_filename": "Filnavn: {filename} [{size}]", "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Informasjon om sikkerhetskopi", @@ -597,8 +599,9 @@ "backup_controller_page_turn_off": "Slå av sikkerhetskopiering i forgrunnen", "backup_controller_page_turn_on": "Slå på sikkerhetskopiering i forgrunnen", "backup_controller_page_uploading_file_info": "Laster opp filinformasjon", - "backup_err_only_album": "Kan ikke fjerne det eneste albumet", - "backup_info_card_assets": "objekter", + "backup_err_only_album": "Kunne ikke fjerne det eneste albumet", + "backup_error_sync_failed": "Synkronisering feilet. Kunne ikke fortsette sikkerhetskopiering.", + "backup_info_card_assets": "elementer", "backup_manual_cancelled": "Avbrutt", "backup_manual_in_progress": "Opplasting er allerede i gang. Prøv igjen om litt", "backup_manual_success": "Vellykket", @@ -618,15 +621,15 @@ "bugs_and_feature_requests": "Feil og funksjonsforespørsler", "build": "Bygg", "build_image": "Lag Bilde", - "bulk_delete_duplicates_confirmation": "Er du sikker på at du vil slette {count, plural, one {# duplisert fil} other {# dupliserte filer}}? Dette vil beholde største filen fra hver gruppe og vil permanent slette alle andre duplikater. Du kan ikke angre denne handlingen!", - "bulk_keep_duplicates_confirmation": "Er du sikker på at du vil beholde {count, plural, one {# duplikat} other {# duplikater}}? Dette vil løse alle duplikatgrupper uten å slette noe.", - "bulk_trash_duplicates_confirmation": "Er du sikker på ønsker å slette {count, plural, one {# duplisert objekt} other {# dupliserte objekter}}? Dette vil beholde største filen fra hver gruppe, samt slette alle andre duplikater.", + "bulk_delete_duplicates_confirmation": "Vil du virkelig slette {count, plural, one {# duplisert fil} other {# dupliserte filer}}? Dette vil beholde største filen fra hver gruppe og vil permanent slette alle andre duplikater. Du kan ikke angre denne handlingen!", + "bulk_keep_duplicates_confirmation": "Vil du virkelig beholde {count, plural, one {# duplikat} other {# duplikater}}? Dette vil løse alle duplikatgrupper uten å slette noe.", + "bulk_trash_duplicates_confirmation": "Vil du virkelig å slette {count, plural, one {# duplisert element} other {# dupliserte elementer}}? Dette vil beholde største filen fra hver gruppe, samt slette alle andre duplikater.", "buy": "Kjøp Immich", "cache_settings_clear_cache_button": "Tøm buffer", "cache_settings_clear_cache_button_title": "Tømmer app-ens buffer. Dette vil ha betydelig innvirkning på appens ytelse inntil bufferen er gjenoppbygd.", "cache_settings_duplicated_assets_clear_button": "TØM", "cache_settings_duplicated_assets_subtitle": "Bilder og videoer som er ignorert av app'en", - "cache_settings_duplicated_assets_title": "Dupliserte objekter ({count})", + "cache_settings_duplicated_assets_title": "Dupliserte elementer ({count})", "cache_settings_statistics_album": "Bibliotekminiatyrbilder", "cache_settings_statistics_full": "Originalbilder", "cache_settings_statistics_shared": "Delte albumminiatyrbilder", @@ -643,9 +646,9 @@ "cancel_search": "Avbryt søk", "canceled": "Avbrutt", "canceling": "Avbryter", - "cannot_merge_people": "Kan ikke slå sammen personer", + "cannot_merge_people": "Kunne ikke slå sammen personer", "cannot_undo_this_action": "Du kan ikke gjøre om denne handlingen!", - "cannot_update_the_description": "Kan ikke oppdatere beskrivelsen", + "cannot_update_the_description": "Kunne ikke oppdatere beskrivelsen", "cast": "Strøm", "cast_description": "Konfigurer tilgjengelige cast-destinasjoner", "change_date": "Endre dato", @@ -655,21 +658,21 @@ "change_location": "Endre sted", "change_name": "Endre navn", "change_name_successfully": "Navneendring vellykket", - "change_password": "Endre Passord", + "change_password": "Endre passord", "change_password_description": "Dette er enten første gang du logger inn i systemet, eller det har blitt gjort en forespørsel om å endre passordet ditt. Vennligst skriv inn det nye passordet nedenfor.", "change_password_form_confirm_password": "Bekreft passord", "change_password_form_description": "Hei {name}!\n\nDette er enten første gang du logger på systemet, eller det er sendt en forespørsel om å endre passordet ditt. Vennligst skriv inn det nye passordet nedenfor.", "change_password_form_new_password": "Nytt passord", "change_password_form_password_mismatch": "Passordene stemmer ikke", "change_password_form_reenter_new_password": "Skriv nytt passord igjen", - "change_pin_code": "Endre PIN kode", + "change_pin_code": "Endre PIN-kode", "change_your_password": "Endre passordet ditt", "changed_visibility_successfully": "Endret synlighet vellykket", "charging": "Lading", "charging_requirement_mobile_backup": "Bakgrunnsbackup krever at enheten lader", - "check_corrupt_asset_backup": "Sjekk etter korrupte backupobjekter", + "check_corrupt_asset_backup": "Sjekk etter korrupte backupelementer", "check_corrupt_asset_backup_button": "Utfør sjekk", - "check_corrupt_asset_backup_description": "Kjør denne sjekken kun over Wi-Fi og når alle objekter har blitt lastet opp. Denne sjekken kan ta noen minutter.", + "check_corrupt_asset_backup_description": "Kjør denne sjekken kun over Wi-Fi og når alle elementer har blitt lastet opp. Denne sjekken kan ta noen minutter.", "check_logs": "Sjekk Logger", "choose_matching_people_to_merge": "Velg personer som skal slås sammen", "city": "By", @@ -702,8 +705,8 @@ "completed": "Fullført", "confirm": "Bekreft", "confirm_admin_password": "Bekreft administratorpassord", - "confirm_delete_face": "Er du sikker på at du vil slette {name} sitt ansikt fra ativia?", - "confirm_delete_shared_link": "Er du sikker på at du vil slette denne delte lenken?", + "confirm_delete_face": "Vil du virkelig slette {name} sitt ansikt fra ativia?", + "confirm_delete_shared_link": "Vil du virkelig slette denne delte lenken?", "confirm_keep_this_delete_others": "Alle andre ressurser i denne stabelen vil bli slettet bortsett fra denne ressursen. Er du sikker på at du vil fortsette?", "confirm_new_pin_code": "Bekreft ny PIN kode", "confirm_password": "Bekreft passord", @@ -721,7 +724,7 @@ "control_bottom_app_bar_edit_time": "Endre Dato og tid", "control_bottom_app_bar_share_link": "Del Lenke", "control_bottom_app_bar_share_to": "Del til", - "control_bottom_app_bar_trash_from_immich": "Flytt til søppelkasse", + "control_bottom_app_bar_trash_from_immich": "Flytt til papirkurv", "copied_image_to_clipboard": "Bildet er kopiert til utklippstavlen.", "copied_to_clipboard": "Kopiert til utklippstavlen!", "copy_error": "Kopi feil", @@ -736,9 +739,9 @@ "covers": "Omslag", "create": "Opprett", "create_album": "Opprett album", - "create_album_page_untitled": "Uten navn", + "create_album_page_untitled": "Navnløst", "create_library": "Opprett Bibliotek", - "create_link": "Opprett link", + "create_link": "Opprett lenke", "create_link_to_share": "Opprett delelink", "create_link_to_share_description": "La alle med lenken se de(t) valgte bildet/bildene", "create_new": "LAG NY", @@ -747,8 +750,8 @@ "create_new_user": "Opprett ny bruker", "create_shared_album_page_share_add_assets": "LEGG TIL OBJEKTER", "create_shared_album_page_share_select_photos": "Velg bilder", - "create_shared_link": "Opprett delt link", - "create_tag": "Lag tag", + "create_shared_link": "Opprett delt lenke", + "create_tag": "Lag merkelapp", "create_tag_description": "Lag en ny tag. For undertag, vennligst fullfør hele stien til taggen, inkludert forovervendt skråstrek.", "create_user": "Opprett Bruker", "created": "Opprettet", @@ -782,23 +785,23 @@ "default_locale": "Standard språkinnstilling", "default_locale_description": "Formater datoer og tall basert på nettleserens språkinnstilling", "delete": "Slett", - "delete_action_confirmation_message": "Er du sikker på at du vil slette dette objektet? Dette vil flytte objektet til søppelkassen og vil gi deg beskjed om du vil slette det lokalt", + "delete_action_confirmation_message": "Vil du virkelig slette dette elementet? Dette vil flytte elementet til papirkurvn og vil gi deg beskjed om du vil slette det lokalt", "delete_action_prompt": "{count} slettet", "delete_album": "Slett album", - "delete_api_key_prompt": "Er du sikker på at du vil slette denne API-nøkkelen?", - "delete_dialog_alert": "Disse objektene vil bli slettet permanent fra Immich og fra enheten din", - "delete_dialog_alert_local": "Disse objektene vil bli permanent slettet fra enheten din, men vil fortsatt være tilgjengelige fra Immich serveren", - "delete_dialog_alert_local_non_backed_up": "Noen av objektene er ikke sikkerhetskopiert til Immich og vil bli permanent fjernet fra enheten din", - "delete_dialog_alert_remote": "Disse objektene vil bli permanent slettet fra Immich serveren", + "delete_api_key_prompt": "Vil du virkelig slette denne API-nøkkelen?", + "delete_dialog_alert": "Disse elementene vil bli slettet permanent fra Immich og fra enheten din", + "delete_dialog_alert_local": "Disse elementene vil bli permanent slettet fra enheten din, men vil fortsatt være tilgjengelige fra Immich serveren", + "delete_dialog_alert_local_non_backed_up": "Noen av elementene er ikke sikkerhetskopiert til Immich og vil bli permanent fjernet fra enheten din", + "delete_dialog_alert_remote": "Disse elementene vil bli permanent slettet fra Immich serveren", "delete_dialog_ok_force": "Slett uansett", "delete_dialog_title": "Slett permanent", - "delete_duplicates_confirmation": "Er du sikker på at du vil slette disse duplikatene permanent?", + "delete_duplicates_confirmation": "Vil du virkelig slette disse duplikatene permanent?", "delete_face": "Slett ansik", "delete_key": "Slett nøkkel", "delete_library": "Slett bibliotek", "delete_link": "Slett lenke", "delete_local_action_prompt": "{count} slettet lokalt", - "delete_local_dialog_ok_backed_up_only": "Slett kun sikkerhetskopierte objekter", + "delete_local_dialog_ok_backed_up_only": "Slett kun sikkerhetskopierte elementer", "delete_local_dialog_ok_force": "Slett uansett", "delete_others": "Slett andre", "delete_permanently": "Slett permanent", @@ -806,7 +809,7 @@ "delete_shared_link": "Slett delt lenke", "delete_shared_link_dialog_title": "Slett delt link", "delete_tag": "Slett tag", - "delete_tag_confirmation_prompt": "Er du sikker på at du vil slette {tagName} tag?", + "delete_tag_confirmation_prompt": "Vil du virkelig slette {tagName} tag?", "delete_user": "Slett bruker", "deleted_shared_link": "Slettet delt lenke", "deletes_missing_assets": "Slett eiendeler som mangler fra disk", @@ -831,7 +834,7 @@ "documentation": "Dokumentasjon", "done": "Ferdig", "download": "Last ned", - "download_action_prompt": "Laster ned {count} objekter", + "download_action_prompt": "Laster ned {count} elementer", "download_canceled": "Nedlasting avbrutt", "download_complete": "Nedlasting fullført", "download_enqueue": "Nedlasting satt i kø", @@ -890,7 +893,7 @@ "email_notifications": "Epostvarsler", "empty_folder": "Denne mappen er tom", "empty_trash": "Tøm papirkurv", - "empty_trash_confirmation": "Er du sikker på at du vil tømme søppelbøtta? Dette vil slette alle filene i søppelbøtta permanent fra Immich.\nDu kan ikke angre denne handlingen!", + "empty_trash_confirmation": "Vil du virkelig Tømme søppelbøtta? Dette vil slette alle filene i søppelbøtta permanent fra Immich.\nDu kan ikke angre denne handlingen!", "enable": "Aktivere", "enable_backup": "Aktiver backup", "enable_biometric_auth_description": "Skriv inn PINkoden for å aktivere biometrisk autentisering", @@ -901,7 +904,7 @@ "enter_your_pin_code": "Skriv inn din PIN kode", "enter_your_pin_code_subtitle": "Skriv inn din PIN kode for å få tilgang til låst mappe", "error": "Feil", - "error_change_sort_album": "Feilet ved endring av sorteringsrekkefølge på albumer", + "error_change_sort_album": "Mislyktes ved endring av sorteringsrekkefølge på albumer", "error_delete_face": "Feil ved sletting av ansikt fra aktivia", "error_getting_places": "Feil ved henting av steder", "error_loading_image": "Feil ved lasting av bilde", @@ -910,16 +913,16 @@ "error_tag_face_bounding_box": "Feil ved merking av ansikt - klarte ikke å få koordinatene på omrisset", "error_title": "Feil - Noe gikk galt", "errors": { - "cannot_navigate_next_asset": "Kan ikke navigere til neste fil", - "cannot_navigate_previous_asset": "Kan ikke navigere til forrige fil", - "cant_apply_changes": "Kan ikke legge til endringene", - "cant_change_activity": "Kan ikke {enabled, select, true {disable} other {enable}} aktivitet", - "cant_change_asset_favorite": "Kan ikke endre favoritt til filen", - "cant_change_metadata_assets_count": "Kan ikke endre metadata for {count, plural, one {# objekt} other {# objekter}}", - "cant_get_faces": "Kan ikke finne ansikter", - "cant_get_number_of_comments": "Kan ikke hente antall kommentarer", - "cant_search_people": "Kan ikke søke etter mennesker", - "cant_search_places": "Kan ikke søke etter plasser", + "cannot_navigate_next_asset": "Kunne ikke navigere til neste fil", + "cannot_navigate_previous_asset": "Kunne ikke navigere til forrige fil", + "cant_apply_changes": "Kunne ikke legge til endringene", + "cant_change_activity": "Kunne ikke {enabled, select, true {disable} other {enable}} aktivitet", + "cant_change_asset_favorite": "Kunne ikke endre favoritt til filen", + "cant_change_metadata_assets_count": "Kunne ikke endre metadata for {count, plural, one {# element} other {# elementer}}", + "cant_get_faces": "Kunne ikke finne ansikter", + "cant_get_number_of_comments": "Kunne ikke hente antall kommentarer", + "cant_search_people": "Kunne ikke søke etter mennesker", + "cant_search_places": "Kunne ikke søke etter plasser", "error_adding_assets_to_album": "Feil med å legge til bilder til album", "error_adding_users_to_album": "Feil, kan ikke legge til brukere til album", "error_deleting_shared_user": "Feil med å slette delt bruker", @@ -930,17 +933,17 @@ "exclusion_pattern_already_exists": "Dette eksklusjonsmønsteret eksisterer allerede.", "failed_to_create_album": "Feil med å lage album", "failed_to_create_shared_link": "Feil med å lage delt lenke", - "failed_to_edit_shared_link": "Feilet med å redigere delt lenke", - "failed_to_get_people": "Feilet med å finne mennesker", - "failed_to_keep_this_delete_others": "Feilet med å beholde dette bilde og slette de andre", - "failed_to_load_asset": "Feilet med å laste bilder", - "failed_to_load_assets": "Feilet med å laste bilde", + "failed_to_edit_shared_link": "Mislyktes med å redigere delt lenke", + "failed_to_get_people": "Mislyktes med å finne mennesker", + "failed_to_keep_this_delete_others": "Mislyktes med å beholde dette bilde og slette de andre", + "failed_to_load_asset": "Mislyktes med å laste bilder", + "failed_to_load_assets": "Mislyktes med å laste bilde", "failed_to_load_notifications": "Kunne ikke laste inn varsler", "failed_to_load_people": "Feilen med å laste mennesker", - "failed_to_remove_product_key": "Feilet med å ta bort produkt nøkkel", + "failed_to_remove_product_key": "Mislyktes med å ta bort produkt nøkkel", "failed_to_reset_pin_code": "Kunne ikke tilbakestille PIN-koden", - "failed_to_stack_assets": "Feilet med å stable bilder", - "failed_to_unstack_assets": "Feilet med å avstable bilder", + "failed_to_stack_assets": "Mislyktes med å stable bilder", + "failed_to_unstack_assets": "Mislyktes med å avstable bilder", "failed_to_update_notification_status": "Kunne ikke oppdatere varslingsstatusen", "import_path_already_exists": "Denne importstien eksisterer allerede.", "incorrect_email_or_password": "Feil epost eller passord", @@ -948,87 +951,87 @@ "profile_picture_transparent_pixels": "Profil bilde kan ikke ha gjennomsiktige piksler. Vennligst zoom inn og/eller flytt bilde.", "quota_higher_than_disk_size": "Du har satt kvoten større enn diskstørrelsen", "something_went_wrong": "Noe gikk galt", - "unable_to_add_album_users": "Kan ikke legge til brukere i albumet", - "unable_to_add_assets_to_shared_link": "Kan ikke legge til bilder til delt lenke", - "unable_to_add_comment": "Kan ikke legge til kommentar", - "unable_to_add_exclusion_pattern": "Kan ikke legge til eksklusjonsmønster", - "unable_to_add_import_path": "Kan ikke legge til importsti", - "unable_to_add_partners": "Kan ikke legge til partnere", - "unable_to_add_remove_archive": "Kan ikke {archived, select, true {fjerne objekt fra} other {flytte objekt til}} arkivet", - "unable_to_add_remove_favorites": "Kan ikke {favorite, select, true {legge til objekt til} other {fjerne objekt fra}} favoritter", - "unable_to_archive_unarchive": "Kan ikke {archived, select, true {archive} other {unarchive}}", - "unable_to_change_album_user_role": "Kan ikke endre brukerens rolle i albumet", - "unable_to_change_date": "Kan ikke endre dato", + "unable_to_add_album_users": "Kunne ikke legge til brukere i albumet", + "unable_to_add_assets_to_shared_link": "Kunne ikke legge til bilder til delt lenke", + "unable_to_add_comment": "Kunne ikke legge til kommentar", + "unable_to_add_exclusion_pattern": "Kunne ikke legge til eksklusjonsmønster", + "unable_to_add_import_path": "Kunne ikke legge til importsti", + "unable_to_add_partners": "Kunne ikke legge til partnere", + "unable_to_add_remove_archive": "Kunne ikke {archived, select, true {fjerne element fra} other {flytte element til}} arkivet", + "unable_to_add_remove_favorites": "Kunne ikke {favorite, select, true {legge til element til} other {fjerne element fra}} favoritter", + "unable_to_archive_unarchive": "Kunne ikke {archived, select, true {archive} other {unarchive}}", + "unable_to_change_album_user_role": "Kunne ikke endre brukerens rolle i albumet", + "unable_to_change_date": "Kunne ikke endre dato", "unable_to_change_description": "Klarte ikke å oppdatere beskrivelse", - "unable_to_change_favorite": "Kan ikke endre favoritt for bildet", - "unable_to_change_location": "Kan ikke endre plassering", - "unable_to_change_password": "Kan ikke endre passord", - "unable_to_change_visibility": "Kan ikke endre synlighet for {count, plural, one {# person} other {# people}}", + "unable_to_change_favorite": "Kunne ikke endre favoritt for bildet", + "unable_to_change_location": "Kunne ikke endre plassering", + "unable_to_change_password": "Kunne ikke endre passord", + "unable_to_change_visibility": "Kunne ikke endre synlighet for {count, plural, one {# person} other {# people}}", "unable_to_complete_oauth_login": "Kunne ikke fullføre OAuth innlogging", - "unable_to_connect": "Kan ikke koble til", - "unable_to_copy_to_clipboard": "Kan ikke kopiere til utklippstavlen, sørg for at du får tilgang til siden via HTTPS", - "unable_to_create_admin_account": "Kan ikke opprette administrator bruker", - "unable_to_create_api_key": "Kan ikke opprette en ny API-nøkkel", - "unable_to_create_library": "Kan ikke opprette bibliotek", - "unable_to_create_user": "Kan ikke opprette bruker", - "unable_to_delete_album": "Kan ikke slette album", - "unable_to_delete_asset": "Kan ikke slette filen", + "unable_to_connect": "Kunne ikke koble til", + "unable_to_copy_to_clipboard": "Kunne ikke kopiere til utklippstavlen, sørg for at du får tilgang til siden via HTTPS", + "unable_to_create_admin_account": "Kunne ikke opprette administrator bruker", + "unable_to_create_api_key": "Kunne ikke opprette en ny API-nøkkel", + "unable_to_create_library": "Kunne ikke opprette bibliotek", + "unable_to_create_user": "Kunne ikke opprette bruker", + "unable_to_delete_album": "Kunne ikke slette album", + "unable_to_delete_asset": "Kunne ikke slette filen", "unable_to_delete_assets": "Feil med å slette bilde", - "unable_to_delete_exclusion_pattern": "Kan ikke slette eksklusjonsmønster", - "unable_to_delete_import_path": "Kan ikke slette importsti", - "unable_to_delete_shared_link": "Kan ikke slette delt lenke", - "unable_to_delete_user": "Kan ikke slette bruker", - "unable_to_download_files": "Kan ikke laste ned filer", - "unable_to_edit_exclusion_pattern": "Kan ikke redigere eksklusjonsmønster", - "unable_to_edit_import_path": "Kan ikke redigere importsti", - "unable_to_empty_trash": "Kan ikke tømme papirkurven", - "unable_to_enter_fullscreen": "Kan ikke gå inn i fullskjerm", - "unable_to_exit_fullscreen": "Kan ikke gå ut fra fullskjerm", - "unable_to_get_comments_number": "Kan ikke hente antall kommentarer", - "unable_to_get_shared_link": "Kan ikke hente delt lenke", - "unable_to_hide_person": "Kan ikke skjule person", - "unable_to_link_motion_video": "Kan ikke lenke bevegelig video", - "unable_to_link_oauth_account": "Kan ikke lenke til OAuth-konto", - "unable_to_log_out_all_devices": "Kan ikke logge ut fra alle enheter", - "unable_to_log_out_device": "Kan ikke logge ut av enhet", - "unable_to_login_with_oauth": "Kan ikke logge inn med OAuth", - "unable_to_play_video": "Kan ikke spille av video", + "unable_to_delete_exclusion_pattern": "Kunne ikke slette eksklusjonsmønster", + "unable_to_delete_import_path": "Kunne ikke slette importsti", + "unable_to_delete_shared_link": "Kunne ikke slette delt lenke", + "unable_to_delete_user": "Kunne ikke slette bruker", + "unable_to_download_files": "Kunne ikke laste ned filer", + "unable_to_edit_exclusion_pattern": "Kunne ikke redigere eksklusjonsmønster", + "unable_to_edit_import_path": "Kunne ikke redigere importsti", + "unable_to_empty_trash": "Kunne ikke Tømme papirkurven", + "unable_to_enter_fullscreen": "Kunne ikke gå inn i fullskjerm", + "unable_to_exit_fullscreen": "Kunne ikke gå ut fra fullskjerm", + "unable_to_get_comments_number": "Kunne ikke hente antall kommentarer", + "unable_to_get_shared_link": "Kunne ikke hente delt lenke", + "unable_to_hide_person": "Kunne ikke skjule person", + "unable_to_link_motion_video": "Kunne ikke lenke bevegelig video", + "unable_to_link_oauth_account": "Kunne ikke lenke til OAuth-konto", + "unable_to_log_out_all_devices": "Kunne ikke logge ut fra alle enheter", + "unable_to_log_out_device": "Kunne ikke logge ut av enhet", + "unable_to_login_with_oauth": "Kunne ikke logge inn med OAuth", + "unable_to_play_video": "Kunne ikke spille av video", "unable_to_reassign_assets_existing_person": "Kunne ikke endre bruker på bildene til {name, select, null {an existing person} other {{name}}}", "unable_to_reassign_assets_new_person": "Kunne ikke tildele bildene til en ny person", - "unable_to_refresh_user": "Kan ikke oppdatere bruker", - "unable_to_remove_album_users": "Kan ikke fjerne brukere fra album", - "unable_to_remove_api_key": "Kan ikke fjerne API-nøkkel", + "unable_to_refresh_user": "Kunne ikke oppdatere bruker", + "unable_to_remove_album_users": "Kunne ikke fjerne brukere fra album", + "unable_to_remove_api_key": "Kunne ikke fjerne API-nøkkel", "unable_to_remove_assets_from_shared_link": "Kunne ikke fjerne bilder fra delt lenke", - "unable_to_remove_library": "Kan ikke fjerne bibliotek", - "unable_to_remove_partner": "Kan ikke fjerne partner", - "unable_to_remove_reaction": "Kan ikke fjerne reaksjon", - "unable_to_reset_password": "Kan ikke tilbakestille passord", + "unable_to_remove_library": "Kunne ikke fjerne bibliotek", + "unable_to_remove_partner": "Kunne ikke fjerne partner", + "unable_to_remove_reaction": "Kunne ikke fjerne reaksjon", + "unable_to_reset_password": "Kunne ikke tilbakestille passord", "unable_to_reset_pin_code": "Klarte ikke å resette PIN kode", - "unable_to_resolve_duplicate": "Kan ikke løse duplikat", - "unable_to_restore_assets": "Kan ikke gjenopprette filer", - "unable_to_restore_trash": "Kan ikke gjenopprette papirkurven", - "unable_to_restore_user": "Kan ikke gjenopprette bruker", - "unable_to_save_album": "Kan ikke lagre album", - "unable_to_save_api_key": "Kan ikke lagre API-nøkkel", + "unable_to_resolve_duplicate": "Kunne ikke løse duplikat", + "unable_to_restore_assets": "Kunne ikke gjenopprette filer", + "unable_to_restore_trash": "Kunne ikke gjenopprette papirkurven", + "unable_to_restore_user": "Kunne ikke gjenopprette bruker", + "unable_to_save_album": "Kunne ikke lagre album", + "unable_to_save_api_key": "Kunne ikke lagre API-nøkkel", "unable_to_save_date_of_birth": "Kunne ikke lagre bursdag", - "unable_to_save_name": "Kan ikke lagre navn", - "unable_to_save_profile": "Kan ikke lagre profil", - "unable_to_save_settings": "Kan ikke lagre instillinger", - "unable_to_scan_libraries": "Kan ikke skanne biblioteker", - "unable_to_scan_library": "Kan ikke skanne bibliotek", + "unable_to_save_name": "Kunne ikke lagre navn", + "unable_to_save_profile": "Kunne ikke lagre profil", + "unable_to_save_settings": "Kunne ikke lagre instillinger", + "unable_to_scan_libraries": "Kunne ikke skanne biblioteker", + "unable_to_scan_library": "Kunne ikke skanne bibliotek", "unable_to_set_feature_photo": "Kunne ikke sette funksjonsbilde", - "unable_to_set_profile_picture": "Kan ikke sette profilbilde", - "unable_to_submit_job": "Kan ikke sende inn jobb", - "unable_to_trash_asset": "Kan ikke flytte filen til papirkurven", - "unable_to_unlink_account": "Kan ikke fjerne kobling til konto", + "unable_to_set_profile_picture": "Kunne ikke sette profilbilde", + "unable_to_submit_job": "Kunne ikke sende inn jobb", + "unable_to_trash_asset": "Kunne ikke flytte filen til papirkurven", + "unable_to_unlink_account": "Kunne ikke fjerne kobling til konto", "unable_to_unlink_motion_video": "Kunne ikke ta på kobling på bevegelig video", "unable_to_update_album_cover": "Kunne ikke oppdatere album bilde", "unable_to_update_album_info": "Kunne ikke oppdatere informasjon i album", - "unable_to_update_library": "Kan ikke oppdatere bibliotek", - "unable_to_update_location": "Kan ikke oppdatere plassering", - "unable_to_update_settings": "Kan ikke oppdatere innstillinger", - "unable_to_update_timeline_display_status": "Kan ikke oppdatere visningsstatus for tidslinje", - "unable_to_update_user": "Kan ikke oppdatere bruker", + "unable_to_update_library": "Kunne ikke oppdatere bibliotek", + "unable_to_update_location": "Kunne ikke oppdatere plassering", + "unable_to_update_settings": "Kunne ikke oppdatere innstillinger", + "unable_to_update_timeline_display_status": "Kunne ikke oppdatere visningsstatus for tidslinje", + "unable_to_update_user": "Kunne ikke oppdatere bruker", "unable_to_upload_file": "Kunne ikke laste opp fil" }, "exif": "EXIF", @@ -1059,15 +1062,15 @@ "external_network": "Eksternt nettverk", "external_network_sheet_info": "Når du ikke er på det foretrukne Wi-Fi-nettverket, vil appen koble seg til serveren via den første av URL-ene nedenfor den kan nå, fra topp til bunn", "face_unassigned": "Ikke tilordnet", - "failed": "Feilet", + "failed": "Mislyktes", "failed_to_authenticate": "Kunne ikke autentisere", - "failed_to_load_assets": "Feilet med å laste fil", + "failed_to_load_assets": "Mislyktes med å laste fil", "failed_to_load_folder": "Kunne ikke laste inn mappe", "favorite": "Favoritt", "favorite_action_prompt": "{count} lagt til i favoritter", "favorite_or_unfavorite_photo": "Merk som favoritt eller fjern som favoritt", "favorites": "Favoritter", - "favorites_page_no_favorites": "Ingen favorittobjekter funnet", + "favorites_page_no_favorites": "Ingen favorittelementer funnet", "feature_photo_updated": "Fremhevet bilde oppdatert", "features": "Funksjoner", "features_in_development": "Funksjoner under utvikling", @@ -1091,7 +1094,7 @@ "gcast_enabled": "Google Cast", "gcast_enabled_description": "Denne funksjonen laster eksterne ressurser fra Google for å fungere.", "general": "Generelt", - "geolocation_instruction_location": "Klikk på et objekt med GPS-koordinater for å bruke posisjonen, eller velg en posisjon direkte fra kartet", + "geolocation_instruction_location": "Klikk på et element med GPS-koordinater for å bruke posisjonen, eller velg en posisjon direkte fra kartet", "get_help": "Få Hjelp", "get_wifiname_error": "Kunne ikke hente Wi-Fi-navnet. Sørg for at du har gitt de nødvendige tillatelsene og er koblet til et Wi-Fi-nettverk", "getting_started": "Kom i gang", @@ -1110,8 +1113,8 @@ "haptic_feedback_switch": "Aktivert haptisk tilbakemelding", "haptic_feedback_title": "Haptisk tilbakemelding", "has_quota": "Kvote", - "hash_asset": "Hash objekter", - "hashed_assets": "Hashede objekter", + "hash_asset": "Hash elementer", + "hashed_assets": "Hashede elementer", "hashing": "Hasher", "header_settings_add_header_tip": "Legg til header", "header_settings_field_validator_msg": "Verdi kan ikke være null", @@ -1126,22 +1129,22 @@ "hide_password": "Skjul passord", "hide_person": "Skjul person", "hide_unnamed_people": "Skjul mennesker uten navn", - "home_page_add_to_album_conflicts": "Lagt til {added} objekter til album {album}. {failed} objekter er allerede i albumet.", - "home_page_add_to_album_err_local": "Kan ikke legge til lokale objekter til album enda, hopper over", - "home_page_add_to_album_success": "Lagt til {added} objekter til album {album}.", - "home_page_album_err_partner": "Kan ikke legge til partnerobjekter i album enda, hopper over", - "home_page_archive_err_local": "Kan ikke arkivere lokale objekter enda, hopper over", - "home_page_archive_err_partner": "Kan ikke arkivere partnerobjekter, hopper over", + "home_page_add_to_album_conflicts": "Lagt til {added} elementer til album {album}. {failed} elementer er allerede i albumet.", + "home_page_add_to_album_err_local": "Kunne ikke legge til lokale elementer til album enda, hopper over", + "home_page_add_to_album_success": "Lagt til {added} elementer til album {album}.", + "home_page_album_err_partner": "Kunne ikke legge til partnerelementer i album enda, hopper over", + "home_page_archive_err_local": "Kunne ikke arkivere lokale elementer enda, hopper over", + "home_page_archive_err_partner": "Kunne ikke arkivere partnerelementer, hopper over", "home_page_building_timeline": "Genererer tidslinjen", - "home_page_delete_err_partner": "Kan ikke slette partnerobjekter, hopper over", - "home_page_delete_remote_err_local": "Lokale objekter i fjernslettingsvalgene, hopper over", - "home_page_favorite_err_local": "Kan ikke sette favoritt på lokale objekter enda, hopper over", - "home_page_favorite_err_partner": "Kan ikke merke partnerobjekter som favoritt enda, hopper over", + "home_page_delete_err_partner": "Kunne ikke slette partnerelementer, hopper over", + "home_page_delete_remote_err_local": "Lokale elementer i fjernslettingsvalgene, hopper over", + "home_page_favorite_err_local": "Kunne ikke sette favoritt på lokale elementer enda, hopper over", + "home_page_favorite_err_partner": "Kunne ikke merke partnerelementer som favoritt enda, hopper over", "home_page_first_time_notice": "Hvis dette er første gangen du benytter appen, velg et album (eller flere) for sikkerhetskopiering, slik at tidslinjen kan fylles med dine bilder og videoer", - "home_page_locked_error_local": "Kunne ikke flytte lokale objekter til låst mappe, hopper over", - "home_page_locked_error_partner": "Kunne ikke flytte partner objekter til låst mappe, hopper over", - "home_page_share_err_local": "Kan ikke dele lokale objekter via link, hopper over", - "home_page_upload_err_limit": "Maksimalt 30 objekter kan lastes opp om gangen, hopper over", + "home_page_locked_error_local": "Kunne ikke flytte lokale elementer til låst mappe, hopper over", + "home_page_locked_error_partner": "Kunne ikke flytte partner elementer til låst mappe, hopper over", + "home_page_share_err_local": "Kunne ikke dele lokale elementer via link, hopper over", + "home_page_upload_err_limit": "Maksimalt 30 elementer kan lastes opp om gangen, hopper over", "host": "Vert", "hour": "Time", "hours": "Timer", @@ -1197,7 +1200,7 @@ "keep": "Behold", "keep_all": "Behold alle", "keep_this_delete_others": "Behold denne, slett de andre", - "kept_this_deleted_others": "Behold denne filen og slett {count, plural, one {# objekt} other {# objekter}}", + "kept_this_deleted_others": "Behold denne filen og slett {count, plural, one {# element} other {# elementer}}", "keyboard_shortcuts": "Tastatursnarveier", "language": "Språk", "language_no_results_subtitle": "Prøv å endre søkeord", @@ -1218,7 +1221,7 @@ "library_options": "Bibliotekalternativer", "library_page_device_albums": "Albumer på enheten", "library_page_new_album": "Nytt album", - "library_page_sort_asset_count": "Antall objekter", + "library_page_sort_asset_count": "Antall elementer", "library_page_sort_created": "Nylig opplastet", "library_page_sort_last_modified": "Sist endret", "library_page_sort_title": "Albumtittel", @@ -1233,8 +1236,8 @@ "loading": "Laster", "loading_search_results_failed": "Klarte ikke å laste inn søkeresultater", "local": "Lokal", - "local_asset_cast_failed": "Kan ikke caste et bilde som ikke er lastet opp til serveren", - "local_assets": "Lokale objekter", + "local_asset_cast_failed": "Kunne ikke caste et bilde som ikke er lastet opp til serveren", + "local_assets": "Lokale elementer", "local_media_summary": "Oppsummering av lokale media", "local_network": "Lokalt nettverk", "local_network_sheet_info": "Appen vil koble til serveren via denne URL-en når du bruker det angitte Wi-Fi-nettverket", @@ -1272,12 +1275,12 @@ "login_form_password_hint": "passord", "login_form_save_login": "Forbli innlogget", "login_form_server_empty": "Skriv inn en server-URL.", - "login_form_server_error": "Kan ikke koble til server.", + "login_form_server_error": "Kunne ikke koble til server.", "login_has_been_disabled": "Innlogging har blitt deaktivert.", "login_password_changed_error": "Det skjedde en feil ved oppdatering av passordet", "login_password_changed_success": "Passord oppdatert", - "logout_all_device_confirmation": "Er du sikker på at du vil logge ut av alle enheter?", - "logout_this_device_confirmation": "Er du sikker på at du vil logge ut av denne enheten?", + "logout_all_device_confirmation": "Vil du virkelig logge ut av alle enheter?", + "logout_this_device_confirmation": "Vil du virkelig logge ut av denne enheten?", "logs": "Logger", "longitude": "Lengdegrad", "look": "Se", @@ -1296,14 +1299,14 @@ "manage_your_oauth_connection": "Administrer tilkoblingen din med OAuth", "map": "Kart", "map_assets_in_bounds": "{count, plural, =0 {Ingen bilder i dette området} one {# photo} other {# photos}}", - "map_cannot_get_user_location": "Kan ikke hente brukerlokasjon", + "map_cannot_get_user_location": "Kunne ikke hente brukerlokasjon", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Bruk denne lokasjonen", - "map_location_service_disabled_content": "Lokasjonstjeneste må være aktivert for å vise objekter fra din nåværende lokasjon. Vil du aktivere det nå?", + "map_location_service_disabled_content": "Lokasjonstjeneste må være aktivert for å vise elementer fra din nåværende lokasjon. Vil du aktivere det nå?", "map_location_service_disabled_title": "Lokasjonstjeneste deaktivert", "map_marker_for_images": "Kart makeringer for bilder tatt i {city}, {country}", "map_marker_with_image": "Kartmarkør med bilde", - "map_no_location_permission_content": "Lokasjonstilgang er påkrevet for å vise objekter fra din nåværende lokasjon. Vil du tillate det nå?", + "map_no_location_permission_content": "Lokasjonstilgang er påkrevet for å vise elementer fra din nåværende lokasjon. Vil du tillate det nå?", "map_no_location_permission_title": "Lokasjonstilgang avvist", "map_settings": "Kartinnstillinger", "map_settings_dark_mode": "Mørk modus", @@ -1321,7 +1324,7 @@ "mark_as_read": "Merk som lest", "marked_all_as_read": "Merket alle som lest", "matches": "Samsvarende", - "matching_assets": "Matchende objekter", + "matching_assets": "Matchende elementer", "media_type": "Mediatype", "memories": "Minner", "memories_all_caught_up": "Alt utført", @@ -1351,11 +1354,11 @@ "move_to_lock_folder_action_prompt": "{count} lagt til i låst mappe", "move_to_locked_folder": "Flytt til låst mappe", "move_to_locked_folder_confirmation": "Disse bildene og videoene vil bli fjernet fra alle albumer, og kun tilgjengelige via den låste mappen", - "moved_to_archive": "Flyttet {count, plural, one {# objekt} other {# objekter}} til arkivet", - "moved_to_library": "Flyttet {count, plural, one {# objekt} other {# objekter}} til biblioteket", + "moved_to_archive": "Flyttet {count, plural, one {# element} other {# elementer}} til arkivet", + "moved_to_library": "Flyttet {count, plural, one {# element} other {# elementer}} til biblioteket", "moved_to_trash": "Flyttet til papirkurven", - "multiselect_grid_edit_date_time_err_read_only": "Kan ikke endre dato på objekt(er) med kun lese-rettigheter, hopper over", - "multiselect_grid_edit_gps_err_read_only": "Kan ikke endre lokasjon på objekt(er) med kun lese-rettigheter, hopper over", + "multiselect_grid_edit_date_time_err_read_only": "Kunne ikke endre dato på element(er) med kun lese-rettigheter, hopper over", + "multiselect_grid_edit_gps_err_read_only": "Kunne ikke endre lokasjon på element(er) med kun lese-rettigheter, hopper over", "mute_memories": "Demp minner", "my_albums": "Mine album", "name": "Navn", @@ -1385,22 +1388,22 @@ "no_albums_yet": "Det ser ut som om du ikke har noen album enda.", "no_archived_assets_message": "Arkiver bilder og videoer for å skjule dem fra visningen av bildene dine", "no_assets_message": "KLIKK FOR Å LASTE OPP DITT FØRSTE BILDE", - "no_assets_to_show": "Ingen objekter å vise", + "no_assets_to_show": "Ingen elementer å vise", "no_cast_devices_found": "Ingen caste-enheter oppdaget", - "no_checksum_local": "Ingen sjekksum tilgjengelig - Kan ikke hente lokale objekter", - "no_checksum_remote": "Ingen sjekksum tilgjengelig - Kan ikke hente eksterne objekter", + "no_checksum_local": "Ingen sjekksum tilgjengelig - Kunne ikke hente lokale elementer", + "no_checksum_remote": "Ingen sjekksum tilgjengelig - Kunne ikke hente eksterne elementer", "no_duplicates_found": "Ingen duplikater ble funnet.", "no_exif_info_available": "Ingen EXIF-informasjon tilgjengelig", "no_explore_results_message": "Last opp flere bilder for å utforske samlingen din.", "no_favorites_message": "Legg til favoritter for å finne dine beste bilder og videoer raskt", "no_libraries_message": "Opprett et eksternt bibliotek for å se bildene og videoene dine", - "no_local_assets_found": "Ingen lokale objekter funnet med denne sjekksummen", + "no_local_assets_found": "Ingen lokale elementer funnet med denne sjekksummen", "no_locked_photos_message": "Bilder og videoer i den låste mappen er skjult og vil ikke vises når du blar i biblioteket.", "no_name": "Ingen navn", "no_notifications": "Ingen varsler", "no_people_found": "Ingen samsvarende personer funnet", "no_places": "Ingen steder", - "no_remote_assets_found": "Ingen eksterne objekter funnet med denne sjekksummen", + "no_remote_assets_found": "Ingen eksterne elementer funnet med denne sjekksummen", "no_results": "Ingen resultater", "no_results_description": "Prøv et synonym eller mer generelt søkeord", "no_shared_albums_message": "Opprett et album for å dele bilder og videoer med personer i nettverket ditt", @@ -1446,7 +1449,7 @@ "original": "original", "other": "Annet", "other_devices": "Andre enheter", - "other_entities": "Andre objekter", + "other_entities": "Andre elementer", "other_variables": "Andre variabler", "owned": "Dine", "owner": "Eier", @@ -1486,10 +1489,10 @@ "permanent_deletion_warning": "Advarsel om permanent sletting", "permanent_deletion_warning_setting_description": "Vis en advarsel ved permanent sletting av filer", "permanently_delete": "Slett permanent", - "permanently_delete_assets_count": "Slett {count, plural, one {objekt} other {objekter}} permanent", - "permanently_delete_assets_prompt": "Er du sikker på at du vil permanent slette {count, plural, one {dette objektet?} other {disse # objektene?}} Dette vil også slette {count, plural, one {det fra dets} other {de fra deres}} album(er).", + "permanently_delete_assets_count": "Slett {count, plural, one {element} other {elementer}} permanent", + "permanently_delete_assets_prompt": "Vil du virkelig permanent slette {count, plural, one {dette elementet?} other {disse # elementene?}} Dette vil også slette {count, plural, one {det fra dets} other {de fra deres}} album(er).", "permanently_deleted_asset": "Filen har blitt permanent slettet", - "permanently_deleted_assets_count": "Permanent slett {count, plural, one {# objekt} other {# objekter}}", + "permanently_deleted_assets_count": "Permanent slett {count, plural, one {# element} other {# elementer}}", "permission": "Tillatelse", "permission_empty": "Dine tillatelser burde ikke være tomme", "permission_onboarding_back": "Tilbake", @@ -1562,7 +1565,7 @@ "purchase_button_reminder": "Påminn meg om 30 dager", "purchase_button_remove_key": "Ta bort produktnøkkel", "purchase_button_select": "Velg", - "purchase_failed_activation": "Feilet med å aktivere! Vennligst sjekk eposten for riktig produktnøkkel!", + "purchase_failed_activation": "Mislyktes med å aktivere! Vennligst sjekk eposten for riktig produktnøkkel!", "purchase_individual_description_1": "For en person", "purchase_individual_description_2": "Støttespiller status", "purchase_individual_title": "Individuell", @@ -1576,14 +1579,14 @@ "purchase_per_server": "For hver server", "purchase_per_user": "For hver bruker", "purchase_remove_product_key": "Ta bor Produktnøkkel", - "purchase_remove_product_key_prompt": "Er du sikker på at du vil ta bort produktnøkkelen?", + "purchase_remove_product_key_prompt": "Vil du virkelig ta bort produktnøkkelen?", "purchase_remove_server_product_key": "Ta bort Server Produktnøkkel", - "purchase_remove_server_product_key_prompt": "Er du sikker på at du vil ta bort Server Produktnøkkelen?", + "purchase_remove_server_product_key_prompt": "Vil du virkelig ta bort Server Produktnøkkelen?", "purchase_server_description_1": "For hele serveren", "purchase_server_description_2": "Støttespiller status", "purchase_server_title": "Server", "purchase_settings_server_activated": "Produktnøkkel for server er administrert av administratoren", - "query_asset_id": "Forespør objektID", + "query_asset_id": "Forespør elementID", "queue_status": "Kø {count}/{total}", "rating": "Stjernevurdering", "rating_clear": "Slett vurdering", @@ -1595,8 +1598,8 @@ "readonly_mode_enabled": "Skrivebeskyttet modus aktivert", "ready_for_upload": "Klar for opplasting", "reassign": "Tilordne på nytt", - "reassigned_assets_to_existing_person": "Flyttet {count, plural, one {# objekt} other {# objekter}} to {name, select, null {en eksisterende person} other {{name}}}", - "reassigned_assets_to_new_person": "Flyttet {count, plural, one {# objekt} other {# objekter}} til en ny person", + "reassigned_assets_to_existing_person": "Flyttet {count, plural, one {# element} other {# elementer}} to {name, select, null {en eksisterende person} other {{name}}}", + "reassigned_assets_to_new_person": "Flyttet {count, plural, one {# element} other {# elementer}} til en ny person", "reassing_hint": "Tilordne valgte eiendeler til en eksisterende person", "recent": "Nylig", "recent-albums": "Nylige album", @@ -1617,11 +1620,11 @@ "refreshing_metadata": "Oppdaterer matadata", "regenerating_thumbnails": "Regenererer miniatyrbilder", "remote": "Eksternt", - "remote_assets": "Eksterne objekter", + "remote_assets": "Eksterne elementer", "remote_media_summary": "Oppsummering av eksterne media", "remove": "Fjern", - "remove_assets_album_confirmation": "Er du sikker på at du fil slette {count, plural, one {# objekt} other {# objekter}} fra albumet?", - "remove_assets_shared_link_confirmation": "Er du sikker på at du vil slette {count, plural, one {# objekt} other {# objekter}} fra den delte lenken?", + "remove_assets_album_confirmation": "Er du sikker på at du fil slette {count, plural, one {# element} other {# elementer}} fra albumet?", + "remove_assets_shared_link_confirmation": "Vil du virkelig slette {count, plural, one {# element} other {# elementer}} fra den delte lenken?", "remove_assets_title": "Vil du fjerne eiendeler?", "remove_custom_date_range": "Fjern egendefinert datoperiode", "remove_deleted_assets": "Fjern fra frakoblede filer", @@ -1630,7 +1633,7 @@ "remove_from_favorites": "Fjern fra favoritter", "remove_from_lock_folder_action_prompt": "{count} fjernet fra låst mappe", "remove_from_locked_folder": "Fjern fra låst mappe", - "remove_from_locked_folder_confirmation": "Er du sikker på at du vil flytte disse bildene og videoene ut av den låste mappen? De vil bli synlige i biblioteket.", + "remove_from_locked_folder_confirmation": "Vil du virkelig flytte disse bildene og videoene ut av den låste mappen? De vil bli synlige i biblioteket.", "remove_from_shared_link": "Fjern fra delt lenke", "remove_memory": "Slett minne", "remove_photo_from_memory": "Slett bilde fra dette minne", @@ -1643,7 +1646,7 @@ "removed_from_favorites_count": "{count, plural, other {Removed #}} fra favoritter", "removed_memory": "Slettet minne", "removed_photo_from_memory": "Slettet bilde fra minne", - "removed_tagged_assets": "Fjern tag fra {count, plural, one {# objekt} other {# objekter}}", + "removed_tagged_assets": "Fjern tag fra {count, plural, one {# element} other {# elementer}}", "rename": "Gi nytt navn", "repair": "Reparer", "repair_no_results_message": "Usporrede og savnede filer vil vises her", @@ -1660,14 +1663,14 @@ "reset_pin_code_success": "PIN-koden er tilbakestilt", "reset_pin_code_with_password": "Du kan alltid tilbakestiller PIN-koden med passordet ditt", "reset_sqlite": "Reset SQLite Databasen", - "reset_sqlite_confirmation": "Er du sikker på at du vil resette SQLite databasen? Du blir nødt til å logge ut og inn igjen for å resynkronisere data", + "reset_sqlite_confirmation": "Vil du virkelig resette SQLite databasen? Du blir nødt til å logge ut og inn igjen for å resynkronisere data", "reset_sqlite_success": "Vellykket resetting av SQLite databasen", "reset_to_default": "Tilbakestill til standard", "resolve_duplicates": "Løs duplikater", "resolved_all_duplicates": "Løste alle duplikater", "restore": "Gjenopprett", "restore_all": "Gjenopprett alle", - "restore_trash_action_prompt": "{count} gjenopprettet fra søppelbøtten", + "restore_trash_action_prompt": "{count} gjenopprettet fra papirkurven", "restore_user": "Gjenopprett bruker", "restored_asset": "Gjenopprettet ressurs", "resume": "Fortsett", @@ -1723,7 +1726,7 @@ "search_options": "Søke alternativer", "search_page_categories": "Kategorier", "search_page_motion_photos": "Bevegelige bilder", - "search_page_no_objects": "Ingen objektinfo tilgjengelig", + "search_page_no_objects": "Ingen elementinfo tilgjengelig", "search_page_no_places": "Ingen stedsinformasjon er tilgjengelig", "search_page_screenshots": "Skjermbilder", "search_page_search_photos_videos": "Søk etter dine bilder og videoer", @@ -1762,7 +1765,7 @@ "select_person_to_tag": "Velg en person å tagge", "select_photos": "Velg bilder", "select_trash_all": "Velg å flytte alt til papirkurven", - "select_user_for_sharing_page_err_album": "Feilet ved oppretting av album", + "select_user_for_sharing_page_err_album": "Mislyktes ved oppretting av album", "selected": "Valgt", "selected_count": "{count, plural, other {# valgt}}", "selected_gps_coordinates": "Valgte GPS-koordinater", @@ -1798,10 +1801,10 @@ "setting_notifications_notify_minutes": "{count} minutter", "setting_notifications_notify_never": "aldri", "setting_notifications_notify_seconds": "{count} sekunder", - "setting_notifications_single_progress_subtitle": "Detaljert opplastingsinformasjon per objekt", + "setting_notifications_single_progress_subtitle": "Detaljert opplastingsinformasjon per element", "setting_notifications_single_progress_title": "Vis detaljert status på sikkerhetskopiering i bakgrunnen", "setting_notifications_subtitle": "Juster notifikasjonsinnstillinger", - "setting_notifications_total_progress_subtitle": "Total opplastingsstatus (fullført/totalt objekter)", + "setting_notifications_total_progress_subtitle": "Total opplastingsstatus (fullført/totalt elementer)", "setting_notifications_total_progress_title": "Vis status på sikkerhetskopiering i bakgrunnen", "setting_video_viewer_looping_title": "Looping", "setting_video_viewer_original_video_subtitle": "Når det streames en video fra serveren, spill originalkvaliteten selv om en omkodet versjon finnes. Dette kan medføre buffring. Videoer som er lagret lokalt på enheten spilles i originalkvalitet uavhengig av denne innstillingen.", @@ -1811,7 +1814,7 @@ "settings_saved": "Innstillinger lagret", "setup_pin_code": "Sett opp en PINkode", "share": "Del", - "share_action_prompt": "Delte {count} objekter", + "share_action_prompt": "Delte {count} elementer", "share_add_photos": "Legg til bilder", "share_assets_selected": "{count} valgt", "share_dialog_preparing": "Forbereder ...", @@ -1845,7 +1848,7 @@ "shared_link_edit_expire_after_option_year": "{count} år", "shared_link_edit_password_hint": "Skriv inn dele-passord", "shared_link_edit_submit_button": "Oppdater link", - "shared_link_error_server_url_fetch": "Kan ikke hente server-url", + "shared_link_error_server_url_fetch": "Kunne ikke hente server-url", "shared_link_expires_day": "Utgår om {count} dag", "shared_link_expires_days": "Utgår om {count} dager", "shared_link_expires_hour": "Utgår om {count} time", @@ -1921,7 +1924,7 @@ "stack_duplicates": "Stable duplikater", "stack_select_one_photo": "Velg hovedbilde for bildestabbel", "stack_selected_photos": "Stable valgte bilder", - "stacked_assets_count": "Stable {count, plural, one {# objekt} other {# objekter}}", + "stacked_assets_count": "Stable {count, plural, one {# element} other {# elementer}}", "stacktrace": "Stakkspor", "start": "Start", "start_date": "Startdato", @@ -1960,15 +1963,15 @@ "tag_not_found_question": "Finner du ikke en merke? Opprett en nytt merke.", "tag_people": "Tag Folk", "tag_updated": "Oppdater merke: {tag}", - "tagged_assets": "Merket {count, plural, one {# objekt} other {# objekter}}", + "tagged_assets": "Merket {count, plural, one {# element} other {# elementer}}", "tags": "Merker", "tap_to_run_job": "Trykk for å kjøre jobben", "template": "Mal", "theme": "Tema", "theme_selection": "Temavalg", "theme_selection_description": "Automatisk sett tema til lys eller mørk basert på nettleserens systeminnstilling", - "theme_setting_asset_list_storage_indicator_title": "Vis lagringsindiaktor på objekter i fotorutenettet", - "theme_setting_asset_list_tiles_per_row_title": "Antall objekter per rad ({count})", + "theme_setting_asset_list_storage_indicator_title": "Vis lagringsindiaktor på elementer i fotorutenettet", + "theme_setting_asset_list_tiles_per_row_title": "Antall elementer per rad ({count})", "theme_setting_colorful_interface_subtitle": "Angi primærfarge til bakgrunner.", "theme_setting_colorful_interface_title": "Fargefullt grensesnitt", "theme_setting_image_viewer_quality_subtitle": "Juster kvaliteten på bilder i detaljvisning", @@ -2000,15 +2003,15 @@ "trash_action_prompt": "{count} flyttet til søppel", "trash_all": "Slett alt", "trash_count": "Slett {count, number}", - "trash_delete_asset": "Slett objekt", - "trash_emptied": "Søppelbøtte tømt", + "trash_delete_asset": "Slett element", + "trash_emptied": "Søppelbøtte Tømt", "trash_no_results_message": "Her vises bilder og videoer som er flyttet til papirkurven.", "trash_page_delete_all": "Slett alt", - "trash_page_empty_trash_dialog_content": "Vil du tømme søppelbøtten? Objektene vil bli permanent fjernet fra Immich", - "trash_page_info": "Objekter i søppelbøtten blir permanent fjernet etter {days} dager", - "trash_page_no_assets": "Ingen forkastede objekter", + "trash_page_empty_trash_dialog_content": "Vil du Tømme papirkurven? Objektene vil bli permanent fjernet fra Immich", + "trash_page_info": "Objekter i papirkurven blir permanent fjernet etter {days} dager", + "trash_page_no_assets": "Ingen forkastede elementer", "trash_page_restore_all": "Gjenopprett alt", - "trash_page_select_assets_btn": "Velg objekter", + "trash_page_select_assets_btn": "Velg elementer", "trash_page_title": "Søppelbøtte ({count})", "trashed_items_will_be_permanently_deleted_after": "Elementer i papirkurven vil bli permanent slettet etter {days, plural, one {# dag} other {# dager}}.", "troubleshoot": "Feilsøk", @@ -2031,7 +2034,7 @@ "unlinked_oauth_account": "Koblet fra OAuth-konto", "unmute_memories": "Opphev demping av minner", "unnamed_album": "Navnløst album", - "unnamed_album_delete_confirmation": "Er du sikker på at du vil slette dette albumet?", + "unnamed_album_delete_confirmation": "Vil du virkelig slette dette albumet?", "unnamed_share": "Deling uten navn", "unsaved_change": "Ulagrede endringer", "unselect_all": "Fjern alle valg", @@ -2039,22 +2042,22 @@ "unselect_all_in": "Fjern velging av alle i {group}", "unstack": "avstable", "unstack_action_prompt": "{count} ustakket", - "unstacked_assets_count": "Ikke stablet {count, plural, one {# objekt} other {# objekter}}", + "unstacked_assets_count": "Ikke stablet {count, plural, one {# element} other {# elementer}}", "untagged": "Umerket", "up_next": "Neste", - "update_location_action_prompt": "Oppdater plasseringen til {count} valgte objekter med:", + "update_location_action_prompt": "Oppdater plasseringen til {count} valgte elementer med:", "updated_at": "Oppdatert", "updated_password": "Passord oppdatert", "upload": "Last opp", "upload_action_prompt": "{count} i kø for opplasting", "upload_concurrency": "Samtidig opplastning", "upload_details": "Opplastingsdetaljer", - "upload_dialog_info": "Vil du utføre backup av valgte objekt(er) til serveren?", - "upload_dialog_title": "Last opp objekt", + "upload_dialog_info": "Vil du utføre backup av valgte element(er) til serveren?", + "upload_dialog_title": "Last opp element", "upload_errors": "Opplasting fullført med {count, plural, one {# error} other {# errors}}, oppdater siden for å se nye opplastingsressurser.", "upload_finished": "Opplasting fullført", "upload_progress": "Gjenstående {remaining, number} – behandlet {processed, number}/{total, number}", - "upload_skipped_duplicates": "Hoppet over {count, plural, one {# duplisert objekt} other {# dupliserte objekter}}", + "upload_skipped_duplicates": "Hoppet over {count, plural, one {# duplisert element} other {# dupliserte elementer}}", "upload_status_duplicates": "Duplikater", "upload_status_errors": "Feil", "upload_status_uploaded": "Opplastet", @@ -2070,7 +2073,7 @@ "user": "Bruker", "user_has_been_deleted": "Denne brukeren har blitt slettet.", "user_id": "Bruker ID", - "user_liked": "{user} likte {type, select, photo {dette bildet} video {denne videoen} asset {dette objektet} other {dette}}", + "user_liked": "{user} likte {type, select, photo {dette bildet} video {denne videoen} asset {dette elementet} other {dette}}", "user_pin_code_settings": "PINkode", "user_pin_code_settings_description": "Håndter din PINkode", "user_privacy": "Personverninnstillinger", @@ -2113,7 +2116,7 @@ "view_stack": "Vis stabel", "view_user": "Vis bruker", "viewer_remove_from_stack": "Fjern fra stabling", - "viewer_stack_use_as_main_asset": "Bruk som hovedobjekt", + "viewer_stack_use_as_main_asset": "Bruk som hovedelement", "viewer_unstack": "avstable", "visibility_changed": "Synlighet endret for {count, plural, one {# person} other {# people}}", "waiting": "Venter", diff --git a/i18n/nl.json b/i18n/nl.json index 0b5ae82326..28be27f00f 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -28,10 +28,12 @@ "add_to_album": "Aan album toevoegen", "add_to_album_bottom_sheet_added": "Toegevoegd aan {album}", "add_to_album_bottom_sheet_already_exists": "Staat al in {album}", + "add_to_album_bottom_sheet_some_local_assets": "Sommige lokale items konden niet aan album toegevoegd worden", "add_to_album_toggle": "Selectie inschakelen voor {album}", "add_to_albums": "Toevoegen aan albums", "add_to_albums_count": "Toevoegen aan albums ({count})", "add_to_shared_album": "Aan gedeeld album toevoegen", + "add_upload_to_stack": "Voeg upload toe aan stack", "add_url": "URL toevoegen", "added_to_archive": "Toegevoegd aan archief", "added_to_favorites": "Toegevoegd aan favorieten", @@ -41,7 +43,7 @@ "admin_user": "Beheerder gebruiker", "asset_offline_description": "Dit item uit een externe bibliotheek is niet meer beschikbaar op de schijf en is naar de prullenbak verplaatst. Als het bestand binnen de bibliotheek is verplaatst, controleer dan je tijdlijn voor het nieuwe bijbehorende item. Om dit bestand te herstellen, zorg ervoor dat het onderstaande bestandspad toegankelijk is voor Immich en scan de bibliotheek opnieuw.", "authentication_settings": "Authenticatie-instellingen", - "authentication_settings_description": "Wachtwoord, OAuth, en andere authenticatie-instellingen beheren", + "authentication_settings_description": "Wachtwoord-, OAuth-, en andere authenticatie-instellingen beheren", "authentication_settings_disable_all": "Weet je zeker dat je alle inlogmethoden wilt uitschakelen? Inloggen zal volledig worden uitgeschakeld.", "authentication_settings_reenable": "Gebruik een servercommando om opnieuw in te schakelen.", "background_task_job": "Achtergrondtaken", @@ -64,7 +66,7 @@ "confirm_email_below": "Typ hieronder \"{email}\" ter bevestiging", "confirm_reprocess_all_faces": "Weet je zeker dat je alle gezichten opnieuw wilt verwerken? Hiermee worden ook alle mensen gewist.", "confirm_user_password_reset": "Weet je zeker dat je het wachtwoord van {user} wilt resetten?", - "confirm_user_pin_code_reset": "Weet je zeker dat je de PIN code van {user} wilt resetten?", + "confirm_user_pin_code_reset": "Weet je zeker dat je de pincode van {user} wilt resetten?", "create_job": "Taak maken", "cron_expression": "Cron expressie", "cron_expression_description": "Stel het scaninterval in met het cron-formaat. Voor meer informatie kun je bijvoorbeeld kijken naar Crontab Guru", @@ -85,7 +87,7 @@ "image_fullsize_enabled_description": "Genereer afbeelding op volledig formaat voor niet-webvriendelijke formaten. Als “Verkies ingesloten voorbeeldafbeelding” is ingeschakeld, worden ingesloten voorvertoningen direct gebruikt zonder conversie. Heeft geen invloed op webvriendelijke formaten zoals JPEG.", "image_fullsize_quality_description": "Beeldkwaliteit op ware grootte van 1-100. Hoger is beter, maar genereert grotere bestanden.", "image_fullsize_title": "Instellingen afbeelding op ware grootte", - "image_prefer_embedded_preview": "Verkies ingesloten voorbeeldafbeelding", + "image_prefer_embedded_preview": "Voorkeur geven aan ingesloten voorbeeldafbeelding", "image_prefer_embedded_preview_setting_description": "Gebruik ingesloten voorbeeldafbeelding van RAW-bestanden als invoer voor beeldverwerking wanneer beschikbaar. Dit kan preciezere kleuren produceren voor sommige afbeeldingen, maar de kwaliteit van het voorbeeld is afhankelijk van de camera en de afbeelding kan mogelijk meer compressie-artefacten bevatten.", "image_prefer_wide_gamut": "Voorkeur geven aan wide gamut", "image_prefer_wide_gamut_setting_description": "Display P3 gebruiken voor voorbeeldafbeeldingen. Dit behoudt de levendigheid van afbeeldingen met brede kleurruimtes beter, maar afbeeldingen kunnen er anders uitzien op oude apparaten met een oude browserversie. sRGB-afbeeldingen blijven sRGB gebruiken om kleurverschuivingen te vermijden.", @@ -102,7 +104,7 @@ "image_thumbnail_title": "Thumbnailinstellingen", "job_concurrency": "{job} gelijktijdigheid", "job_created": "Taak aangemaakt", - "job_not_concurrency_safe": "Deze taak kan niet gelijktijdig worden uitgevoerd.", + "job_not_concurrency_safe": "Deze taak kan niet parallel worden uitgevoerd.", "job_settings": "Achtergrondtaak-instellingen", "job_settings_description": "Beheer aantal gelijktijdige taken", "job_status": "Taakstatus", @@ -122,20 +124,20 @@ "library_watching_settings_description": "Automatisch gewijzigde bestanden bijhouden", "logging_enable_description": "Logboek inschakelen", "logging_level_description": "Indien ingeschakeld, welk logniveau er wordt gebruikt.", - "logging_settings": "Logging", - "machine_learning_availability_checks": "Beschikbaarheid", + "logging_settings": "Logboek", + "machine_learning_availability_checks": "Beschikbaarheidscontroles", "machine_learning_availability_checks_description": "Automatisch detecteren en selecteren van beschikbare machine learning servers", - "machine_learning_availability_checks_enabled": "Activeer beschikbaarheid controles", + "machine_learning_availability_checks_enabled": "Activeer beschikbaarheidscontroles", "machine_learning_availability_checks_interval": "Controleinterval", - "machine_learning_availability_checks_interval_description": "Interval in milliseconden tussen beschikbaarheid checks", + "machine_learning_availability_checks_interval_description": "Interval in milliseconden tussen beschikbaarheidscontroles", "machine_learning_availability_checks_timeout": "Verzoek time-out", - "machine_learning_availability_checks_timeout_description": "Time-out in milliseconden voor beschikbaarheidschecks", - "machine_learning_clip_model": "CLIP model", - "machine_learning_clip_model_description": "De naam van een CLIP-model dat hier is vermeld. Let op: je moet de 'Slim Zoeken -taak opnieuw uitvoeren voor alle afbeeldingen wanneer je een model wijzigt.", - "machine_learning_duplicate_detection": "Duplicaat detectie", + "machine_learning_availability_checks_timeout_description": "Time-out in milliseconden voor beschikbaarheidscontroles", + "machine_learning_clip_model": "CLIP-model", + "machine_learning_clip_model_description": "De naam van een CLIP-model dat hier is vermeld. Let op: je moet de 'Slim Zoeken'-taak voor alle afbeeldingen opnieuw uitvoeren wanneer je een model wijzigt.", + "machine_learning_duplicate_detection": "Duplicaatdetectie", "machine_learning_duplicate_detection_enabled": "Duplicaatdetectie inschakelen", "machine_learning_duplicate_detection_enabled_description": "Indien uitgeschakeld, worden identieke items nog steeds gededupliceerd.", - "machine_learning_duplicate_detection_setting_description": "Gebruik CLIP om exactie kopieën te vinden", + "machine_learning_duplicate_detection_setting_description": "Gebruik CLIP-embeddings om mogelijke kopieën te vinden", "machine_learning_enabled": "Machine learning inschakelen", "machine_learning_enabled_description": "Wanneer uitgeschakeld zullen alle ML instellingen uitgezet worden, ongeacht onderstaande instellingen.", "machine_learning_facial_recognition": "Gezichtsherkenning", @@ -163,8 +165,8 @@ "manage_log_settings": "Beheer logboekinstellingen", "map_dark_style": "Donkere stijl", "map_enable_description": "Kaartfuncties inschakelen", - "map_gps_settings": "Kaart & GPS Instellingen", - "map_gps_settings_description": "Beheer kaart & GPS (omgekeerde geocodering) instellingen", + "map_gps_settings": "Kaart- & gps-instellingen", + "map_gps_settings_description": "Beheer kaart- & gps-instellingen (omgekeerde geocodering)", "map_implications": "De kaartfunctie is afhankelijk van een externe service (tiles.immich.cloud)", "map_light_style": "Lichte stijl", "map_manage_reverse_geocoding_settings": "Beheer omgekeerde geocodering instellingen", @@ -177,27 +179,27 @@ "memory_cleanup_job": "Herinneringen opschonen", "memory_generate_job": "Herinneringen genereren", "metadata_extraction_job": "Metadata ophalen", - "metadata_extraction_job_description": "Metadata ophalen van ieder item, zoals GPS, gezichten en resolutie", + "metadata_extraction_job_description": "Metadata ophalen van ieder item, zoals gps, gezichten en resolutie", "metadata_faces_import_setting": "Gezichten importeren inschakelen", "metadata_faces_import_setting_description": "Gezichten importeren uit EXIF-gegevens van afbeeldingen en sidecar bestanden", - "metadata_settings": "Metadata instellingen", - "metadata_settings_description": "Beheer metadata instellingen", + "metadata_settings": "Metadata-instellingen", + "metadata_settings_description": "Beheer metadata-instellingen", "migration_job": "Migratie", "migration_job_description": "Migreer thumbnails voor items en gezichten naar de nieuwste mapstructuur", "nightly_tasks_cluster_faces_setting_description": "Gezichtsherkenning uitvoeren op nieuw gedetecteerde gezichten", "nightly_tasks_cluster_new_faces_setting": "Cluster nieuwe gezichten", - "nightly_tasks_database_cleanup_setting": "Database opschoon taken", - "nightly_tasks_database_cleanup_setting_description": "Ruim oude data op van de database", + "nightly_tasks_database_cleanup_setting": "Database-opruimtaken", + "nightly_tasks_database_cleanup_setting_description": "Ruim oude, niet meer geldige data op uit de database", "nightly_tasks_generate_memories_setting": "Genereer herinneringen", "nightly_tasks_generate_memories_setting_description": "Maak nieuwe herinneringen van items", "nightly_tasks_missing_thumbnails_setting": "Genereer ontbrekende thumbnails", "nightly_tasks_missing_thumbnails_setting_description": "Items zonder thumbnail in een wachtrij plaatsen voor het genereren van thumbnails", - "nightly_tasks_settings": "Instellingen voor nacht taken", - "nightly_tasks_settings_description": "Beheer nacht taken", - "nightly_tasks_start_time_setting": "Start tijd", - "nightly_tasks_start_time_setting_description": "De tijd waarop de server begint met het uitvoeren van de nacht taken", - "nightly_tasks_sync_quota_usage_setting": "Synchroniseer quota gebruik", - "nightly_tasks_sync_quota_usage_setting_description": "update gebruiker opslag quota, gebaseerd op huidig gebruik", + "nightly_tasks_settings": "Instellingen voor nachtelijke taken", + "nightly_tasks_settings_description": "Beheer nachtelijke taken", + "nightly_tasks_start_time_setting": "Starttijd", + "nightly_tasks_start_time_setting_description": "De tijd waarop de server begint met het uitvoeren van de nachtelijke taken", + "nightly_tasks_sync_quota_usage_setting": "Synchroniseer opslaglimieten", + "nightly_tasks_sync_quota_usage_setting_description": "Update opslaglimieten van gebruikers, gebaseerd op huidig gebruik", "no_paths_added": "Geen paden toegevoegd", "no_pattern_added": "Geen patroon toegevoegd", "note_apply_storage_label_previous_assets": "Opmerking: om het opslaglabel toe te passen op eerder geüploade items, voer de volgende taak uit", @@ -272,12 +274,12 @@ "storage_template_date_time_sample": "Voorbeeldtijd {date}", "storage_template_enable_description": "Engine voor opslagtemplate inschakelen", "storage_template_hash_verification_enabled": "Hashverificatie ingeschakeld", - "storage_template_hash_verification_enabled_description": "Zet hashverificatie aan, schakel dit niet uit tenzij je zeker bent van de implicaties", + "storage_template_hash_verification_enabled_description": "Zet hashverificatie aan. Schakel dit niet uit tenzij je zeker bent van de gevolgen", "storage_template_migration": "Opslagtemplate migratie", "storage_template_migration_description": "Pas de huidige {template} toe op eerder geüploade items", "storage_template_migration_info": "Wijzigingen in de opslagtemplate worden alleen toegepast op nieuwe items. Om de template met terugwerkende kracht toe te passen op eerder geüploade items, voer je de {job} uit.", "storage_template_migration_job": "Opslagtemplate migratietaak", - "storage_template_more_details": "Voor meer details over deze functie, bekijk de Opslagstemplate en de implicaties daarvan", + "storage_template_more_details": "Meer details over deze functie vind je onder Opslagtemplate, net als de gevolgen daarvan", "storage_template_onboarding_description_v2": "Wanneer ingeschakeld, zal deze functie bestanden automatisch organiseren gebaseerd op een template gedefinieerd door de gebruiker. Voor meer informatie, bekijk de documentatie.", "storage_template_path_length": "Geschatte padlengte: {length, number}/{limit, number}", "storage_template_settings": "Opslagtemplate", @@ -352,7 +354,7 @@ "transcoding_temporal_aq_description": "Alleen van toepassing op NVENC. Verhoogt de kwaliteit van scènes met veel details en weinig beweging. Is mogelijk niet compatibel met oudere apparaten.", "transcoding_threads": "Threads", "transcoding_threads_description": "Hogere waarden leiden tot snellere codering, maar laten minder ruimte over voor de server om andere taken te verwerken terwijl deze actief is. Deze waarde mag niet groter zijn dan het aantal CPU cores. Maximaliseert het gebruik als deze is ingesteld op 0.", - "transcoding_tone_mapping": "Tone-mapping", + "transcoding_tone_mapping": "Tone mapping", "transcoding_tone_mapping_description": "Probeert het uiterlijk van HDR-video's te behouden wanneer ze worden geconverteerd naar SDR. Elk algoritme maakt verschillende afwegingen voor kleur, detail en helderheid. Hable behoudt detail, Mobius behoudt kleur en Reinhard behoudt helderheid.", "transcoding_transcode_policy": "Transcodeerbeleid", "transcoding_transcode_policy_description": "Beleid voor wanneer een video getranscodeerd moet worden. HDR-video's worden altijd getranscodeerd (behalve als transcodering is uitgeschakeld).", @@ -371,7 +373,7 @@ "user_cleanup_job": "Gebruiker opschoning", "user_delete_delay": "Het account en de items van {user} worden over {delay, plural, one {# dag} other {# dagen}} permanent verwijderd.", "user_delete_delay_settings": "Verwijder vertraging", - "user_delete_delay_settings_description": "Aantal dagen na verwijdering om het account en de items van een gebruiker permanent te verwijderen. De taak voor het verwijderen van gebruikers wordt om middernacht uitgevoerd om te controleren of gebruikers verwijderd kunnen worden. Wijzigingen in deze instelling worden bij de volgende uitvoering meegenomen.", + "user_delete_delay_settings_description": "Aantal dagen na verwijdering om het account en de items van een gebruiker permanent te verwijderen. De taak voor het verwijderen van gebruikers wordt om middernacht uitgevoerd om te controleren of gebruiker te verwijderen zijn. Wijzigingen in deze instelling worden bij de volgende uitvoering meegenomen.", "user_delete_immediately": "Het account en de items van {user} worden onmiddellijk in de wachtrij geplaatst voor permanente verwijdering.", "user_delete_immediately_checkbox": "Gebruikers en items in de wachtrij plaatsen voor onmiddellijke verwijdering", "user_details": "Gebruiker details", @@ -415,9 +417,9 @@ "age_years": "{years, plural, other {Leeftijd #}}", "album_added": "Album toegevoegd", "album_added_notification_setting_description": "Ontvang een e-mailmelding wanneer je aan een gedeeld album wordt toegevoegd", - "album_cover_updated": "Album cover is bijgewerkt", + "album_cover_updated": "Albumomslag is bijgewerkt", "album_delete_confirmation": "Weet je zeker dat je het album {album} wilt verwijderen?", - "album_delete_confirmation_description": "Als dit album gedeeld is, hebben andere gebruikers er geen toegang meer toe.", + "album_delete_confirmation_description": "Als dit album gedeeld is, zullen andere gebruikers geen toegang meer hebben.", "album_deleted": "Album verwijderd", "album_info_card_backup_album_excluded": "UITGESLOTEN", "album_info_card_backup_album_included": "INBEGREPEN", @@ -598,6 +600,7 @@ "backup_controller_page_turn_on": "Back-up op de voorgrond aanzetten", "backup_controller_page_uploading_file_info": "Bestandsgegevens uploaden", "backup_err_only_album": "Kan het enige album niet verwijderen", + "backup_error_sync_failed": "Synchronisatie mislukt. Kan back-up niet verwerken.", "backup_info_card_assets": "bestanden", "backup_manual_cancelled": "Geannuleerd", "backup_manual_in_progress": "Het uploaden is al bezig. Probeer het na een tijdje", @@ -662,7 +665,7 @@ "change_password_form_new_password": "Nieuw wachtwoord", "change_password_form_password_mismatch": "Wachtwoorden komen niet overeen", "change_password_form_reenter_new_password": "Vul het wachtwoord opnieuw in", - "change_pin_code": "Wijzig PIN code", + "change_pin_code": "Wijzig pincode", "change_your_password": "Wijzig je wachtwoord", "changed_visibility_successfully": "Zichtbaarheid succesvol gewijzigd", "charging": "Opladen", @@ -705,7 +708,7 @@ "confirm_delete_face": "Weet je zeker dat je het gezicht van {name} wilt verwijderen uit het item?", "confirm_delete_shared_link": "Weet je zeker dat je deze gedeelde link wilt verwijderen?", "confirm_keep_this_delete_others": "Alle andere items in de stack worden verwijderd, behalve deze. Weet je zeker dat je wilt doorgaan?", - "confirm_new_pin_code": "Bevestig nieuwe PIN code", + "confirm_new_pin_code": "Bevestig nieuwe pincode", "confirm_password": "Bevestig wachtwoord", "confirm_tag_face": "Wil je dit gezicht taggen als {name}?", "confirm_tag_face_unnamed": "Wil je dit gezicht taggen?", @@ -733,7 +736,7 @@ "copy_to_clipboard": "Kopiëren naar klembord", "country": "Land", "cover": "Bedekken", - "covers": "Covers", + "covers": "Omslagen", "create": "Aanmaken", "create_album": "Album aanmaken", "create_album_page_untitled": "Naamloos", @@ -757,7 +760,7 @@ "crop": "Bijsnijden", "curated_object_page_title": "Dingen", "current_device": "Huidig apparaat", - "current_pin_code": "Huidige PIN code", + "current_pin_code": "Huidige pincode", "current_server_address": "Huidig serveradres", "custom_locale": "Aangepaste landinstelling", "custom_locale_description": "Formatteer datums en getallen op basis van de taal en de regio", @@ -819,7 +822,7 @@ "disabled": "Uitgeschakeld", "disallow_edits": "Geen bewerkingen toestaan", "discord": "Discord", - "discover": "Zoeken", + "discover": "Zoek", "discovered_devices": "Gevonden apparaten", "dismiss_all_errors": "Negeer alle fouten", "dismiss_error": "Negeer fout", @@ -839,7 +842,7 @@ "download_failed": "Download mislukt", "download_finished": "Download voltooid", "download_include_embedded_motion_videos": "Ingesloten video's", - "download_include_embedded_motion_videos_description": "Voeg video's toe die ingesloten zijn in bewegende foto's als een apart bestand", + "download_include_embedded_motion_videos_description": "Voeg video's die in bewegingsfoto's zijn ingebed toe als een apart bestand", "download_notfound": "Download niet gevonden", "download_paused": "Download gepauseerd", "download_settings": "Downloaden", @@ -938,7 +941,7 @@ "failed_to_load_notifications": "Kon meldingen niet laden", "failed_to_load_people": "Kan mensen niet laden", "failed_to_remove_product_key": "Fout bij het verwijderen van de licentiesleutel", - "failed_to_reset_pin_code": "Resetten van PIN code mislukt", + "failed_to_reset_pin_code": "Resetten van pincode mislukt", "failed_to_stack_assets": "Fout bij stapelen van items", "failed_to_unstack_assets": "Fout bij ontstapelen van items", "failed_to_update_notification_status": "Kon notificatiestatus niet updaten", @@ -1003,7 +1006,7 @@ "unable_to_remove_partner": "Kan partner niet verwijderen", "unable_to_remove_reaction": "Kan reactie niet verwijderen", "unable_to_reset_password": "Kan wachtwoord niet resetten", - "unable_to_reset_pin_code": "Kan PIN code niet resetten", + "unable_to_reset_pin_code": "Kan pincode niet resetten", "unable_to_resolve_duplicate": "Kan duplicaat niet oplossen", "unable_to_restore_assets": "Kan items niet herstellen", "unable_to_restore_trash": "Kan niet herstellen uit prullenbak", @@ -1022,7 +1025,7 @@ "unable_to_trash_asset": "Kan item niet naar prullenbak verplaatsen", "unable_to_unlink_account": "Kan account niet ontkoppelen", "unable_to_unlink_motion_video": "Kan bewegende video niet ontkoppelen", - "unable_to_update_album_cover": "Kan album cover niet bijwerken", + "unable_to_update_album_cover": "Kan albumomslag niet bijwerken", "unable_to_update_album_info": "Kan albumgegevens niet bijwerken", "unable_to_update_library": "Kan bibliotheek niet bijwerken", "unable_to_update_location": "Kan locatie niet bijwerken", @@ -1036,6 +1039,7 @@ "exif_bottom_sheet_description_error": "Fout bij het bijwerken van de beschrijving", "exif_bottom_sheet_details": "DETAILS", "exif_bottom_sheet_location": "LOCATIE", + "exif_bottom_sheet_no_description": "Geen beschrijving", "exif_bottom_sheet_people": "MENSEN", "exif_bottom_sheet_person_add_person": "Naam toevoegen", "exit_slideshow": "Diavoorstelling sluiten", @@ -1086,12 +1090,12 @@ "folder_not_found": "Map niet gevonden", "folders": "Mappen", "folders_feature_description": "Bladeren door de mapweergave van de foto's en video's op het bestandssysteem", - "forgot_pin_code_question": "PIN vergeten?", + "forgot_pin_code_question": "Pincode vergeten?", "forward": "Vooruit", "gcast_enabled": "Google Cast", "gcast_enabled_description": "Deze functie gebruikt externe bronnen van Google om te kunnen werken.", "general": "Algemeen", - "geolocation_instruction_location": "Klik op een item met GPS coördinaten om de locatie te gebruiken, of selecteer een locatie direct vanaf de kaart", + "geolocation_instruction_location": "Klik op een item met gps-coördinaten om de locatie te gebruiken, of kies een locatie direct op de kaart", "get_help": "Krijg hulp", "get_wifiname_error": "Kon de WiFi-naam niet ophalen. Zorg ervoor dat je de benodigde machtigingen hebt verleend en verbonden bent met een WiFi-netwerk", "getting_started": "Aan de slag", @@ -1136,7 +1140,7 @@ "home_page_delete_err_partner": "Partner items kunnen niet verwijderd worden, overslaan", "home_page_delete_remote_err_local": "Lokale items staan in verwijder selectie externe items, overslaan", "home_page_favorite_err_local": "Lokale items kunnen nog niet als favoriet worden aangemerkt, overslaan", - "home_page_favorite_err_partner": "Partner items kunnen nog niet ge-favoriet worden, overslaan", + "home_page_favorite_err_partner": "Partner items kunnen nog niet als favoriet gemarkeerd worden, overslaan", "home_page_first_time_notice": "Als dit de eerste keer is dat je de app gebruikt, zorg er dan voor dat je een back-up album kiest, zodat de tijdlijn gevuld kan worden met foto's en video's uit het album", "home_page_locked_error_local": "Kan lokale bestanden niet naar de vergrendelde map verplaatsen, sla over", "home_page_locked_error_partner": "Kan partnerbestanden niet naar de vergrendelde map verplaatsen, sla over", @@ -1180,7 +1184,7 @@ "day_at_onepm": "Iedere dag om 13 uur", "hours": "{hours, plural, one {Ieder uur} other {Iedere {hours, number} uren}}", "night_at_midnight": "Iedere avond om middernacht", - "night_at_twoam": "Iedere nacht om 2 uur" + "night_at_twoam": "Elke nacht om 2 uur" }, "invalid_date": "Ongeldige datum", "invalid_date_format": "Ongeldig datumformaat", @@ -1371,7 +1375,7 @@ "new_api_key": "Nieuwe API-sleutel", "new_password": "Nieuw wachtwoord", "new_person": "Nieuw persoon", - "new_pin_code": "Nieuwe PIN code", + "new_pin_code": "Nieuwe pincode", "new_pin_code_subtitle": "Dit is de eerste keer dat u de vergrendelde map opent. Stel een pincode in om deze pagina veilig te openen", "new_timeline": "Nieuwe tijdlijn", "new_user_created": "Nieuwe gebruiker aangemaakt", @@ -1405,7 +1409,7 @@ "no_results_description": "Probeer een synoniem of een algemener zoekwoord", "no_shared_albums_message": "Maak een album om foto's en video's te delen met mensen in je netwerk", "no_uploads_in_progress": "Geen uploads bezig", - "not_available": "N.B.", + "not_available": "n.v.t.", "not_in_any_album": "Niet in een album", "not_selected": "Niet geselecteerd", "note_apply_storage_label_to_previously_uploaded assets": "Opmerking: om het opslaglabel toe te passen op eerder geüploade items, voer de volgende taak uit", @@ -1425,7 +1429,7 @@ "ok": "Ok", "oldest_first": "Oudste eerst", "on_this_device": "Op dit apparaat", - "onboarding": "Onboarding", + "onboarding": "Introductie", "onboarding_locale_description": "Selecteer je voorkeurstaal. Je dan dit later wijzigen in je instellingen.", "onboarding_privacy_description": "De volgende (optionele) functies zijn afhankelijk van externe services en kunnen op elk moment worden uitgeschakeld in de instellingen.", "onboarding_server_welcome_description": "Laten we je instantie instellen met een aantal veelgebruikte instellingen.", @@ -1512,9 +1516,9 @@ "photos_count": "{count, plural, one {{count, number} foto} other {{count, number} foto's}}", "photos_from_previous_years": "Foto's van voorgaande jaren", "pick_a_location": "Kies een locatie", - "pin_code_changed_successfully": "PIN code succesvol gewijzigd", - "pin_code_reset_successfully": "PIN code succesvol gereset", - "pin_code_setup_successfully": "PIN code succesvol ingesteld", + "pin_code_changed_successfully": "Pincode succesvol gewijzigd", + "pin_code_reset_successfully": "Pincode succesvol gereset", + "pin_code_setup_successfully": "Pincode succesvol ingesteld", "pin_verification": "Pincodeverificatie", "place": "Plaats", "places": "Plaatsen", @@ -1569,7 +1573,7 @@ "purchase_input_suggestion": "Heb je een licentiesleutel? Voer deze hieronder in", "purchase_license_subtitle": "Koop Immich om de verdere ontwikkeling van de service te ondersteunen", "purchase_lifetime_description": "Levenslange aankoop", - "purchase_option_title": "AANKOOP MOGELIJKHEDEN", + "purchase_option_title": "AANKOOPMOGELIJKHEDEN", "purchase_panel_info_1": "Het bouwen van Immich kost veel tijd en moeite, en we hebben fulltime engineers die eraan werken om het zo goed mogelijk te maken. Onze missie is om open-source software en ethische bedrijfspraktijken een duurzame inkomstenbron te laten worden voor ontwikkelaars en een ecosysteem te creëren dat de privacy respecteert met echte alternatieven voor uitbuitende cloudservices.", "purchase_panel_info_2": "Omdat we ons inzetten om geen paywalls toe te voegen, krijg je met deze aankoop geen extra functies in Immich. We vertrouwen op gebruikers zoals jij om de verdere ontwikkeling van Immich te ondersteunen.", "purchase_panel_title": "Steun het project", @@ -1583,7 +1587,7 @@ "purchase_server_description_2": "Supporterstatus", "purchase_server_title": "Server", "purchase_settings_server_activated": "De licentiesleutel van de server wordt beheerd door de beheerder", - "query_asset_id": "Query Asset ID", + "query_asset_id": "Item-ID opvragen", "queue_status": "Wachtrij {count}/{total}", "rating": "Sterwaardering", "rating_clear": "Waardering verwijderen", @@ -1655,10 +1659,10 @@ "reset": "Resetten", "reset_password": "Wachtwoord resetten", "reset_people_visibility": "Zichtbaarheid mensen resetten", - "reset_pin_code": "Reset PIN code", - "reset_pin_code_description": "Als je jouw PIN code bent vergeten, neem dan contact op met de administrator van de server om deze te resetten", - "reset_pin_code_success": "Resetten van PIN code gelukt", - "reset_pin_code_with_password": "Je kan altijd je PIN code resetten met je wachtwoord", + "reset_pin_code": "Reset pincode", + "reset_pin_code_description": "Als je jouw pincode bent vergeten, neem dan contact op met de administrator van de server om deze te resetten", + "reset_pin_code_success": "Resetten van pincode gelukt", + "reset_pin_code_with_password": "Je kan je pincode altijd resetten met je wachtwoord", "reset_sqlite": "SQLite database resetten", "reset_sqlite_confirmation": "Ben je zeker dat je de SQLite database wilt resetten? Je zal moeten uitloggen om de data opnieuw te synchroniseren", "reset_sqlite_success": "De SQLite database is succesvol gereset", @@ -1748,7 +1752,7 @@ "second": "Seconde", "see_all_people": "Bekijk alle mensen", "select": "Selecteer", - "select_album_cover": "Selecteer album cover", + "select_album_cover": "Selecteer albumomslag", "select_all": "Alles selecteren", "select_all_duplicates": "Selecteer alle duplicaten", "select_all_in": "Selecteer alles in {group}", @@ -1765,7 +1769,7 @@ "select_user_for_sharing_page_err_album": "Album aanmaken mislukt", "selected": "Geselecteerd", "selected_count": "{count, plural, other {# geselecteerd}}", - "selected_gps_coordinates": "Geselecteerde GPS Coördinaten", + "selected_gps_coordinates": "Geselecteerde gps-coördinaten", "send_message": "Bericht versturen", "send_welcome_email": "Stuur welkomstmail", "server_endpoint": "Server-URL", @@ -1777,7 +1781,7 @@ "server_stats": "Serverstatistieken", "server_version": "Serverversie", "set": "Instellen", - "set_as_album_cover": "Stel in als album cover", + "set_as_album_cover": "Stel in als albumomslag", "set_as_featured_photo": "Instellen als uitgelichte foto", "set_as_profile_picture": "Instellen als profielfoto", "set_date_of_birth": "Geboortedatum instellen", @@ -1809,7 +1813,7 @@ "settings": "Instellingen", "settings_require_restart": "Start Immich opnieuw op om deze instelling toe te passen", "settings_saved": "Instellingen opgeslagen", - "setup_pin_code": "Stel een PIN code in", + "setup_pin_code": "Stel een pincode in", "share": "Delen", "share_action_prompt": "{count} item(s) gedeeld", "share_add_photos": "Foto's toevoegen", @@ -1945,7 +1949,7 @@ "support_and_feedback": "Ondersteuning & feedback", "support_third_party_description": "Je Immich installatie is door een derde partij samengesteld. Problemen die je ervaart, kunnen door dat pakket veroorzaakt zijn. Meld problemen in eerste instantie bij hen via de onderstaande links.", "swap_merge_direction": "Wissel richting voor samenvoegen om", - "sync": "Sync", + "sync": "Synchroniseren", "sync_albums": "Albums synchroniseren", "sync_albums_manual_subtitle": "Synchroniseer alle geüploade video’s en foto’s naar de geselecteerde back-up albums", "sync_local": "Lokaal synchroniseren", @@ -2013,8 +2017,8 @@ "trashed_items_will_be_permanently_deleted_after": "Items in de prullenbak worden na {days, plural, one {# dag} other {# dagen}} permanent verwijderd.", "troubleshoot": "Problemen oplossen", "type": "Type", - "unable_to_change_pin_code": "PIN code kan niet gewijzigd worden", - "unable_to_setup_pin_code": "PIN code kan niet ingesteld worden", + "unable_to_change_pin_code": "Pincode kan niet gewijzigd worden", + "unable_to_setup_pin_code": "Pincode kan niet ingesteld worden", "unarchive": "Herstellen uit archief", "unarchive_action_prompt": "{count} verwijderd uit het archief", "unarchived_count": "{count, plural, other {# verwijderd uit archief}}", @@ -2071,8 +2075,8 @@ "user_has_been_deleted": "Deze gebruiker is verwijderd.", "user_id": "Gebruikers ID", "user_liked": "{user} heeft {type, select, photo {deze foto} video {deze video} asset {} other {dit item}} geliket", - "user_pin_code_settings": "PIN Code", - "user_pin_code_settings_description": "Beheer je PIN code", + "user_pin_code_settings": "Pincode", + "user_pin_code_settings_description": "Beheer je pincode", "user_privacy": "Gebruikersprivacy", "user_purchase_settings": "Kopen", "user_purchase_settings_description": "Beheer je aankoop", @@ -2093,8 +2097,8 @@ "version_history": "Versiegeschiedenis", "version_history_item": "{version} geïnstalleerd op {date}", "video": "Video", - "video_hover_setting": "Speel videothumbnail af bij hoveren", - "video_hover_setting_description": "Speel videothumbnail af wanneer de muis over het item beweegt. Zelfs wanneer uitgeschakeld, kan het afspelen worden gestart door de muis over het afspeelpictogram te bewegen.", + "video_hover_setting": "Speel videominiatuur af bij hoveren", + "video_hover_setting_description": "Speel videominiatuur af wanneer de muis over het item beweegt. Zelfs wanneer uitgeschakeld, kan het afspelen worden gestart door de muis over het afspeelpictogram te bewegen.", "videos": "Video's", "videos_count": "{count, plural, one {# video} other {# video's}}", "view": "Bekijken", @@ -2106,14 +2110,14 @@ "view_link": "Bekijk link", "view_links": "Links bekijken", "view_name": "Bekijken", - "view_next_asset": "Bekijk volgende item", - "view_previous_asset": "Bekijk vorige item", + "view_next_asset": "Bekijk volgend item", + "view_previous_asset": "Bekijk vorig item", "view_qr_code": "QR-code bekijken", "view_similar_photos": "Bekijk vergelijkbare foto's", "view_stack": "Bekijk stapel", "view_user": "Bekijk gebruiker", - "viewer_remove_from_stack": "Verwijder van Stapel", - "viewer_stack_use_as_main_asset": "Gebruik als Hoofd Item", + "viewer_remove_from_stack": "Verwijder van stapel", + "viewer_stack_use_as_main_asset": "Zet bovenaan de stapel", "viewer_unstack": "Ontstapel", "visibility_changed": "Zichtbaarheid gewijzigd voor {count, plural, one {# persoon} other {# mensen}}", "waiting": "Wachtend", diff --git a/i18n/pa.json b/i18n/pa.json index 0967ef424b..0801a54351 100644 --- a/i18n/pa.json +++ b/i18n/pa.json @@ -1 +1,21 @@ -{} +{ + "about": "ਐਪ ਬਾਰੇ", + "account": "ਖ਼ਾਤਾ", + "account_settings": "ਖ਼ਾਤਾ ਸੈਟਿੰਗਾਂ", + "action": "ਕਾਰਵਾਈ", + "action_common_update": "ਅੱਪਡੇਟ", + "actions": "ਕਾਰਵਾਈਆਂ", + "active": "ਕਿਰਿਆਸ਼ੀਲ", + "activity": "ਗਤੀਵਿਧੀ", + "add": "ਸ਼ਾਮਲ ਕਰੋ", + "add_a_description": "ਵੇਰਵਾ ਸ਼ਾਮਲ ਕਰੋ", + "add_a_location": "ਇੱਕ ਸਥਾਨ ਸ਼ਾਮਲ ਕਰੋ", + "add_a_name": "ਨਾਮ ਸ਼ਾਮਲ ਕਰੋ", + "add_a_title": "ਸਿਰਲੇਖ ਸ਼ਾਮਲ ਕਰੋ", + "add_birthday": "ਜਨਮਦਿਨ ਸ਼ਾਮਲ ਕਰੋ", + "add_endpoint": "ਐਂਡਪੁਆਇੰਟ ਸ਼ਾਮਲ ਕਰੋ", + "add_exclusion_pattern": "ਅਲਹਿਦਗੀ ਪੈਟਰਨ ਸ਼ਾਮਲ ਕਰੋ", + "add_import_path": "ਆਯਾਤ ਮਾਰਗ ਸ਼ਾਮਲ ਕਰੋ", + "add_location": "ਸਥਾਨ ਸ਼ਾਮਲ ਕਰੋ", + "add_more_users": "ਹੋਰ ਉਪਭੋਗਤਾ ਸ਼ਾਮਲ ਕਰੋ" +} diff --git a/i18n/pl.json b/i18n/pl.json index 04337fcebf..2531f8210d 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -28,10 +28,12 @@ "add_to_album": "Dodaj do albumu", "add_to_album_bottom_sheet_added": "Dodano do {album}", "add_to_album_bottom_sheet_already_exists": "Już jest w {album}", + "add_to_album_bottom_sheet_some_local_assets": "Niektóre lokalne zasoby nie mogły zostać dodane do albumu", "add_to_album_toggle": "Przełącz wybieranie dla {album}", "add_to_albums": "Dodaj do albumów", "add_to_albums_count": "Dodaj do albumów ({count})", "add_to_shared_album": "Dodaj do udostępnionego albumu", + "add_upload_to_stack": "Dodaj przesłane do stosu", "add_url": "Dodaj URL", "added_to_archive": "Dodano do archiwum", "added_to_favorites": "Dodano do ulubionych", @@ -598,6 +600,7 @@ "backup_controller_page_turn_on": "Włącz kopię zapasową na pierwszym planie", "backup_controller_page_uploading_file_info": "Informacje o przesyłanym pliku", "backup_err_only_album": "Nie można usunąć jedynego albumu", + "backup_error_sync_failed": "Synchronizacja nie powiodła się. Nie można wykonać kopii zapasowej.", "backup_info_card_assets": "zasoby", "backup_manual_cancelled": "Anulowano", "backup_manual_in_progress": "Przesyłanie już trwa. Spróbuj po pewnym czasie", @@ -674,10 +677,10 @@ "choose_matching_people_to_merge": "Wybierz osoby, aby złączyć je w jedną", "city": "Miasto", "clear": "Wyczyść", - "clear_all": "Wyczyść", + "clear_all": "Wyczyść wszystko", "clear_all_recent_searches": "Usuń ostatnio wyszukiwane", "clear_file_cache": "Wyczyść pamięć podręczną plików", - "clear_message": "Zamknij wiadomość", + "clear_message": "Wyczyść wiadomość", "clear_value": "Wyczyść wartość", "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "Wprowadź hasło", @@ -1036,6 +1039,7 @@ "exif_bottom_sheet_description_error": "Wystąpił błąd podczas aktualizacji opisu", "exif_bottom_sheet_details": "SZCZEGÓŁY", "exif_bottom_sheet_location": "LOKALIZACJA", + "exif_bottom_sheet_no_description": "Brak opisu", "exif_bottom_sheet_people": "LUDZIE", "exif_bottom_sheet_person_add_person": "Dodaj nazwę", "exit_slideshow": "Zamknij Pokaz Slajdów", @@ -1694,7 +1698,7 @@ "search_albums": "Przeszukaj albumy", "search_by_context": "Wyszukaj według treści", "search_by_description": "Wyszukaj według opisu", - "search_by_description_example": "Jednodniowa wycieczka górska w Bieszczady", + "search_by_description_example": "Całodniowa wycieczka w Bieszczady", "search_by_filename": "Szukaj według nazwy pliku lub rozszerzenia", "search_by_filename_example": "np. IMG_1234.JPG lub PNG", "search_camera_make": "Wyszukaj markę aparatu...", diff --git a/i18n/pt.json b/i18n/pt.json index 4859e7e9a6..fdb3561ed2 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -28,10 +28,12 @@ "add_to_album": "Adicionar ao álbum", "add_to_album_bottom_sheet_added": "Adicionado a {album}", "add_to_album_bottom_sheet_already_exists": "Já existe em {album}", + "add_to_album_bottom_sheet_some_local_assets": "Alguns conteúdos locais não puderam ser adicionados no álbum", "add_to_album_toggle": "Alternar seleção para {album}", "add_to_albums": "Adicionar aos álbuns", "add_to_albums_count": "Adicionar aos álbuns ({count})", "add_to_shared_album": "Adicionar ao álbum partilhado", + "add_upload_to_stack": "Adicionar carregamento à fila", "add_url": "Adicionar URL", "added_to_archive": "Adicionado ao arquivo", "added_to_favorites": "Adicionado aos favoritos", @@ -125,10 +127,11 @@ "logging_settings": "Registo", "machine_learning_availability_checks": "Verificação de disponibilidade", "machine_learning_availability_checks_description": "Detectar automaticamente e dar preferência aos servidores de aprendizagem automática disponíveis", - "machine_learning_availability_checks_enabled": "Activar confirmações de disponibilidade", + "machine_learning_availability_checks_enabled": "Ativar confirmações de disponibilidade", "machine_learning_availability_checks_interval": "Confirmação de intervalo", "machine_learning_availability_checks_interval_description": "Intervalo, em milisegundos, entre confirmações de disponibilidade", "machine_learning_availability_checks_timeout": "Tempo limite para requisição", + "machine_learning_availability_checks_timeout_description": "Tempo limite em milissegundos para verificações de disponibilidade", "machine_learning_clip_model": "Modelo CLIP", "machine_learning_clip_model_description": "O nome do modelo CLIP definido aqui. Tome nota de que é necessário voltar a executar a tarefa de \"Pesquisa Inteligente\" para todas as imagens depois de alterar o modelo.", "machine_learning_duplicate_detection": "Deteção de Itens Duplicados", @@ -366,7 +369,7 @@ "trash_settings_description": "Gerir definições da reciclagem", "unlink_all_oauth_accounts": "Desvincular todas as contas OAuth", "unlink_all_oauth_accounts_description": "Lembre-se de desvincular todas as contas OAuth antes de migrar para um novo provedor.", - "unlink_all_oauth_accounts_prompt": "Tem a certeza de que deseja desvincular todas as contas OAuth? Isso redefinirá o ID OAuth de cada utilizador e não poderá ser desfeito.", + "unlink_all_oauth_accounts_prompt": "Tem a certeza de que deseja desvincular todas as contas OAuth? Isto irá redefinir o ID OAuth de cada utilizador e não poderá ser anulado.", "user_cleanup_job": "Limpeza de utilizadores", "user_delete_delay": "A conta e os ficheiros de {user} serão agendados para eliminação permanente dentro de {delay, plural, one {# dia} other {# dias}}.", "user_delete_delay_settings": "Atraso de eliminação", @@ -400,8 +403,8 @@ "advanced_settings_prefer_remote_title": "Preferir imagens do servidor", "advanced_settings_proxy_headers_subtitle": "Defina os cabeçalhos do proxy que o Immich deve enviar em todas comunicações com a rede", "advanced_settings_proxy_headers_title": "Cabeçalhos do Proxy", - "advanced_settings_readonly_mode_subtitle": "Activa o modo somente leitura, onde as fotos podem ser visualizadas. Recursos como selecionar várias imagens, partilhar, transmitir e excluir ficam deactivados. Activar/Desactivar o modo somente leitura via avatar do utilizador na janela principal", - "advanced_settings_readonly_mode_title": "Modo somente leitura", + "advanced_settings_readonly_mode_subtitle": "Ativa o modo só de leitura, onde as fotos apenas podem ser visualizadas. Funções como selecionar várias imagens, partilhar, transmitir e eliminar ficam deactivadas. Pode ativar ou desativar o modo só de leitura através da imagem de perfil do utilizador na janela principal", + "advanced_settings_readonly_mode_title": "Modo só de leitura", "advanced_settings_self_signed_ssl_subtitle": "Não validar o certificado SSL com o endereço do servidor. Isto é necessário para certificados auto-assinados.", "advanced_settings_self_signed_ssl_title": "Permitir certificados SSL auto-assinados", "advanced_settings_sync_remote_deletions_subtitle": "Automaticamente eliminar ou restaurar um ficheiro neste dispositivo quando essa mesma ação for efetuada na web", @@ -501,6 +504,8 @@ "asset_restored_successfully": "Arquivo restaurado com sucesso", "asset_skipped": "Ignorado", "asset_skipped_in_trash": "Na reciclagem", + "asset_trashed": "Ficheiro apagado", + "asset_troubleshoot": "Resolução de problemas com conteúdos", "asset_uploaded": "Enviado", "asset_uploading": "A enviar…", "asset_viewer_settings_subtitle": "Gerenciar as configurações do visualizador da galeria", @@ -508,9 +513,9 @@ "assets": "Ficheiros", "assets_added_count": "{count, plural, one {# ficheiro adicionado} other {# ficheiros adicionados}}", "assets_added_to_album_count": "{count, plural, one {# ficheiro adicionado} other {# ficheiros adicionados}} ao álbum", - "assets_added_to_albums_count": "Adicionado {assetTotal, plural, one {# asset} other {# assets}} a {albumTotal, plural, one {# album} other {# albums}}", + "assets_added_to_albums_count": "{assetTotal, plural, one {Foi adicionado # ficheiro} other {Foram adiciondos # ficheiros}} a {albumTotal, plural, one {# álbum} other {# albuns}}", "assets_cannot_be_added_to_album_count": "Não foi possível adicionar {count, plural, one {ficheiro} other {ficheiros}} ao álbum", - "assets_cannot_be_added_to_albums": "{count, plural, one {Asset} other {Assets}} não pode ser adicionado a nenhum dos álbuns", + "assets_cannot_be_added_to_albums": "{count, plural, one {Ficheiro não pode ser adicionado} other {Ficheiros não podem ser adiciondos}} a nenhum dos álbuns", "assets_count": "{count, plural, one {# ficheiro} other {# ficheiros}}", "assets_deleted_permanently": "{count} ficheiro(s) eliminado(s) permanentemente", "assets_deleted_permanently_from_server": "{count} ficheiro(s) eliminado(s) permanentemente do servidor Immich", @@ -527,15 +532,17 @@ "assets_trashed_count": "{count, plural, one {# ficheiro enviado} other {# ficheiros enviados}} para a reciclagem", "assets_trashed_from_server": "{count} ficheiro(s) do servidor Immich foi/foram enviados para a reciclagem", "assets_were_part_of_album_count": "{count, plural, one {O ficheiro já fazia} other {Os ficheiros já faziam}} parte do álbum", - "assets_were_part_of_albums_count": "{count, plural, one {Asset was} other {Assets were}} já faz parte dos álbuns", + "assets_were_part_of_albums_count": "{count, plural, one {Ficheiro já fazia} other {Ficheiros já faziam}} parte dos álbuns", "authorized_devices": "Dispositivos Autorizados", "automatic_endpoint_switching_subtitle": "Conecte-se localmente quando estiver em uma rede uma Wi-Fi específica e use conexões alternativas em outras redes", "automatic_endpoint_switching_title": "Troca automática de URL", "autoplay_slideshow": "Apresentação automática de diapositivos", "back": "Voltar", "back_close_deselect": "Voltar, fechar ou desmarcar", + "background_backup_running_error": "Com a cópia de segurança de fundo em execução, não é possível inicar uma manual", "background_location_permission": "Permissão de localização em segundo plano", "background_location_permission_content": "Para que seja possível trocar a URL quando estiver executando em segundo plano, o Immich deve *sempre* ter a permissão de localização precisa para que o aplicativo consiga ler o nome da rede Wi-Fi", + "background_options": "Opções de fundo", "backup": "Cópia de segurança", "backup_album_selection_page_albums_device": "Álbuns no dispositivo ({count})", "backup_album_selection_page_albums_tap": "Toque para incluir, duplo toque para excluir", @@ -543,6 +550,7 @@ "backup_album_selection_page_select_albums": "Selecione Álbuns", "backup_album_selection_page_selection_info": "Informações da Seleção", "backup_album_selection_page_total_assets": "Total de arquivos únicos", + "backup_albums_sync": "Cópia de segurança de sincronização de álbuns", "backup_all": "Tudo", "backup_background_service_backup_failed_message": "Ocorreu um erro ao efetuar cópia de segurança dos ficheiros. A tentar de novo…", "backup_background_service_connection_failed_message": "Ocorreu um erro na ligação ao servidor. A tentar de novo…", @@ -582,7 +590,7 @@ "backup_controller_page_remainder": "Restante", "backup_controller_page_remainder_sub": "Fotos e vídeos selecionados restantes para fazer backup", "backup_controller_page_server_storage": "Armazenamento no servidor", - "backup_controller_page_start_backup": "Iniciar Backup", + "backup_controller_page_start_backup": "Iniciar Cópia de Segurança", "backup_controller_page_status_off": "Backup automático desativado", "backup_controller_page_status_on": "Backup automático ativado", "backup_controller_page_storage_format": "{used} de {total} utilizado", @@ -592,13 +600,14 @@ "backup_controller_page_turn_on": "Ativar backup", "backup_controller_page_uploading_file_info": "Enviando arquivo", "backup_err_only_album": "Não é possível remover apenas o álbum", + "backup_error_sync_failed": "A sincronização falhou. Não é possível fazer a cópia de segurança.", "backup_info_card_assets": "arquivos", "backup_manual_cancelled": "Cancelado", "backup_manual_in_progress": "Envio já está em progresso. Tente novamente mais tarde", "backup_manual_success": "Sucesso", "backup_manual_title": "Estado do envio", "backup_options": "Definições de cópia de segurança", - "backup_options_page_title": "Opções de backup", + "backup_options_page_title": "Opções de cópia de segurança", "backup_setting_subtitle": "Gerenciar as configurações de envio em primeiro e segundo plano", "backup_settings_subtitle": "Gerir definições de carregamento", "backward": "Para trás", @@ -659,6 +668,8 @@ "change_pin_code": "Alterar código PIN", "change_your_password": "Alterar a sua palavra-passe", "changed_visibility_successfully": "Visibilidade alterada com sucesso", + "charging": "A carregar", + "charging_requirement_mobile_backup": "Cópia de segurança de fundo necessita que o dispositivo esteja a carregar", "check_corrupt_asset_backup": "Verificar por backups corrompidos", "check_corrupt_asset_backup_button": "Verificar", "check_corrupt_asset_backup_description": "Execute esta verificação somente em uma rede Wi-Fi e quando o backup de todos os arquivos já estiver concluído. O processo demora alguns minutos.", @@ -745,6 +756,7 @@ "create_user": "Criar utilizador", "created": "Criado", "created_at": "Criado a", + "creating_linked_albums": "A criar albuns ligados...", "crop": "Cortar", "curated_object_page_title": "Objetos", "current_device": "Dispositivo atual", @@ -894,7 +906,9 @@ "error": "Erro", "error_change_sort_album": "Ocorreu um erro ao mudar a ordem de exibição", "error_delete_face": "Falha ao remover rosto do ficheiro", + "error_getting_places": "Erro ao obter locais", "error_loading_image": "Erro ao carregar a imagem", + "error_loading_partners": "Erro ao carregar parceiros: {error}", "error_saving_image": "Erro: {error}", "error_tag_face_bounding_box": "Erro ao marcar o rosto - não foi possível localizar o rosto", "error_title": "Erro - Algo correu mal", @@ -927,7 +941,7 @@ "failed_to_load_notifications": "Ocorreu um erro ao carregar notificações", "failed_to_load_people": "Ocorreu um erro ao carregar pessoas", "failed_to_remove_product_key": "Ocorreu um erro ao remover chave de produto", - "failed_to_reset_pin_code": "Falha ao repor o código PIN", + "failed_to_reset_pin_code": "Ocorreu um erro ao repor o código PIN", "failed_to_stack_assets": "Ocorreu um erro ao empilhar os ficheiros", "failed_to_unstack_assets": "Ocorreu um erro ao desempilhar ficheiros", "failed_to_update_notification_status": "Ocorreu um erro ao atualizar o estado das notificações", @@ -936,7 +950,7 @@ "paths_validation_failed": "Ocorreu um erro na validação de {paths, plural, one {# caminho} other {# caminhos}}", "profile_picture_transparent_pixels": "Imagem de perfil não pode ter pixeis transparentes. Por favor amplie e/ou mova a imagem.", "quota_higher_than_disk_size": "Definiu uma quota maior do que o tamanho do disco", - "something_went_wrong": "Algo deu errado", + "something_went_wrong": "Algo correu mal", "unable_to_add_album_users": "Não foi possível adicionar utilizadores ao álbum", "unable_to_add_assets_to_shared_link": "Não foi possível adicionar os ficheiros ao link partilhado", "unable_to_add_comment": "Não foi possível adicionar o comentário", @@ -1059,6 +1073,7 @@ "favorites_page_no_favorites": "Nenhum favorito encontrado", "feature_photo_updated": "Foto principal atualizada", "features": "Funcionalidades", + "features_in_development": "Funcionalidades em Desenvolvimento", "features_setting_description": "Configurar as funcionalidades da aplicação", "file_name": "Nome do ficheiro", "file_name_or_extension": "Nome do ficheiro ou extensão", @@ -1074,12 +1089,12 @@ "folder_not_found": "Pasta não encontrada", "folders": "Pastas", "folders_feature_description": "Navegar na vista de pastas por fotos e vídeos no sistema de ficheiros", - "forgot_pin_code_question": "Esqueceu o seu PIN?", + "forgot_pin_code_question": "Esqueceu-se do seu PIN?", "forward": "Para a frente", "gcast_enabled": "Google Cast", "gcast_enabled_description": "Esta funcionalidade requer o carregamento de recursos externos da Google para poder funcionar.", "general": "Geral", - "geolocation_instruction_location": "Clique num ativo com coordenadas GPS para usar a sua localização ou seleccione um local diretamente do mapa", + "geolocation_instruction_location": "Clique num ficheiro com coordenadas GPS para usar a sua localização ou selecione um local diretamente do mapa", "get_help": "Obter Ajuda", "get_wifiname_error": "Não foi possível obter o nome do Wi-Fi. Verifique se concedeu as permissões necessárias e se está conectado a uma rede Wi-Fi", "getting_started": "Primeiros Passos", @@ -1212,7 +1227,7 @@ "library_page_sort_title": "Título do álbum", "licenses": "Licenças", "light": "Claro", - "like": "Gostar", + "like": "Gosto", "like_deleted": "Gosto removido", "link_motion_video": "Relacionar video animado", "link_to_oauth": "Link do OAuth", @@ -1223,6 +1238,7 @@ "local": "Local", "local_asset_cast_failed": "Não é possível transmitir um ficheiro que não tenha sido enviado antes para o servidor", "local_assets": "Ficheiros Locais", + "local_media_summary": "Sumário de conteúdo local", "local_network": "Rede local", "local_network_sheet_info": "O aplicativo irá se conectar ao servidor através desta URL quando estiver na rede Wi-Fi especificada", "location_permission": "Permissão de localização", @@ -1234,12 +1250,13 @@ "location_picker_longitude_hint": "Digite a longitude", "lock": "Trancar", "locked_folder": "Pasta Trancada", + "log_detail_title": "Detalhes de registo", "log_out": "Sair", "log_out_all_devices": "Terminar a sessão de todos os dispositivos", "logged_in_as": "Utilizador atual: {user}", "logged_out_all_devices": "Sessão terminada em todos os dispositivos", "logged_out_device": "Sessão terminada no dispositivo", - "login": "Iniciar sessão", + "login": "Iniciar Sessão", "login_disabled": "Login desativado", "login_form_api_exception": "Erro de API. Verifique a URL do servidor e tente novamente.", "login_form_back_button_text": "Voltar", @@ -1248,7 +1265,7 @@ "login_form_endpoint_url": "URL do servidor", "login_form_err_http": "Por favor especifique http:// ou https://", "login_form_err_invalid_email": "Email Inválido", - "login_form_err_invalid_url": "URL inválida", + "login_form_err_invalid_url": "URL inválido", "login_form_err_leading_whitespace": "Espaço em branco no início", "login_form_err_trailing_whitespace": "Espaço em branco no fim", "login_form_failed_get_oauth_server_config": "Ocorreu um erro ao iniciar sessão com o OAuth, verifique o URL do servidor", @@ -1264,11 +1281,12 @@ "login_password_changed_success": "Palavra-passe atualizada com sucesso", "logout_all_device_confirmation": "Tem a certeza de que deseja terminar a sessão em todos os dispositivos?", "logout_this_device_confirmation": "Tem a certeza de que deseja terminar a sessão deste dispositivo?", + "logs": "Logs", "longitude": "Longitude", "look": "Estilo", "loop_videos": "Repetir vídeos", "loop_videos_description": "Ativar para repetir os vídeos automaticamente durante a exibição.", - "main_branch_warning": "Está a utilizar uma versão de desenvolvimento, recomendamos vivamente que utilize uma versão estável!", + "main_branch_warning": "Está a usar uma versão de desenvolvimento; recomendamos vivamente que use uma versão de lançamento!", "main_menu": "Menu Principal", "make": "Marca", "manage_geolocation": "Gerir localização", @@ -1280,7 +1298,7 @@ "manage_your_devices": "Gerir os seus dispositivos com sessão iniciada", "manage_your_oauth_connection": "Gerir a sua ligação ao OAuth", "map": "Mapa", - "map_assets_in_bounds": "{count, plural, =0 {No photos in this area} one {# photo} other {# photos}}", + "map_assets_in_bounds": "{count, plural, =0 {Sem fotos nesta área} one {# foto} other {# fotos}}", "map_cannot_get_user_location": "Impossível obter a sua localização", "map_location_dialog_yes": "Sim", "map_location_picker_page_use_location": "Utilizar esta localização", @@ -1306,6 +1324,7 @@ "mark_as_read": "Marcar como lido", "marked_all_as_read": "Tudo marcado como lido", "matches": "Correspondências", + "matching_assets": "Conteúdos coincidentes", "media_type": "Tipo de média", "memories": "Memórias", "memories_all_caught_up": "Finalizamos por hoje", @@ -1344,11 +1363,12 @@ "my_albums": "Os meus álbuns", "name": "Nome", "name_or_nickname": "Nome ou alcunha", - "network_requirement_photos_upload": "Usar dados móveis para fazer backup de fotos", - "network_requirement_videos_upload": "Usar dados móveis para fazer backup de vídeos", - "network_requirements_updated": "Requisitos de rede alterados, redefinindo fila de backup", - "networking_settings": "Conexões", - "networking_subtitle": "Gerencie a conexão do servidor", + "network_requirement_photos_upload": "Usar dados móveis para fazer cópia de segurança de fotos", + "network_requirement_videos_upload": "Usar dados móveis para fazer cópia de segurança de vídeos", + "network_requirements": "Requisitos de rede", + "network_requirements_updated": "Requisitos de rede alterados, a redefinir fila de cópia de segurança", + "networking_settings": "Ligações", + "networking_subtitle": "Gerir as ligações de rede do servidor", "never": "Nunca", "new_album": "Novo Álbum", "new_api_key": "Nova Chave de API", @@ -1356,6 +1376,7 @@ "new_person": "Nova Pessoa", "new_pin_code": "Novo código PIN", "new_pin_code_subtitle": "Esta é a primeira vez que acede à pasta trancada. Crie um código PIN para aceder a esta página de forma segura", + "new_timeline": "Nova Linha do Tempo", "new_user_created": "Novo utilizador criado", "new_version_available": "NOVA VERSÃO DISPONÍVEL", "newest_first": "Mais recente primeiro", @@ -1369,20 +1390,25 @@ "no_assets_message": "FAÇA CLIQUE PARA CARREGAR A SUA PRIMEIRA FOTO", "no_assets_to_show": "Não há arquivos para exibir", "no_cast_devices_found": "Nenhum dispositivo de transmissão encontrado", + "no_checksum_local": "Sem cálculo de verificação disponível - não pode capturar conteúdos locais", + "no_checksum_remote": "Soma de verificação (checksum) não disponível - não é possível obter o recurso remoto", "no_duplicates_found": "Nenhum item duplicado foi encontrado.", "no_exif_info_available": "Sem informações exif disponíveis", "no_explore_results_message": "Carregue mais fotos para explorar a sua coleção.", "no_favorites_message": "Adicione aos favoritos para encontrar as suas melhores fotos e vídeos rapidamente", "no_libraries_message": "Crie uma biblioteca externa para ver as suas fotos e vídeos", + "no_local_assets_found": "Sem cálculo de verificação disponível", "no_locked_photos_message": "Fotos e vídeos na pasta trancada estão ocultos e não serão exibidos enquanto explora ou pesquisa na biblioteca.", "no_name": "Sem nome", "no_notifications": "Sem notificações", "no_people_found": "Nenhuma pessoa encontrada", "no_places": "Sem lugares", + "no_remote_assets_found": "Soma de verificação (checksum) não disponível - não é possível obter o recurso remoto", "no_results": "Sem resultados", "no_results_description": "Tente um sinónimo ou uma palavra-chave mais comum", "no_shared_albums_message": "Crie um álbum para partilhar fotos e vídeos com pessoas na sua rede", "no_uploads_in_progress": "Nenhum carregamento em curso", + "not_available": "N/A", "not_in_any_album": "Não está em nenhum álbum", "not_selected": "Não selecionado", "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar o Rótulo de Armazenamento a ficheiros carregados anteriormente, execute o", @@ -1478,9 +1504,9 @@ "permission_onboarding_permission_limited": "Permissão limitada. Para permitir que o Immich faça backups e gerencie sua galeria, conceda permissões para fotos e vídeos nas configurações.", "permission_onboarding_request": "O Immich requer autorização para ver as suas fotos e vídeos.", "person": "Pessoa", - "person_age_months": "{months, plural, one {# month} other {# months}} idade", - "person_age_year_months": "1 ano, {months, plural, one {# month} other {# months}} idade", - "person_age_years": "{years, plural, other {# years}} idade", + "person_age_months": "{months, plural, one {# month} other {# months}} de idade", + "person_age_year_months": "1 ano, {months, plural, one {# month} other {# months}} de idade", + "person_age_years": "{years, plural, other {# anos}} de idade", "person_birthdate": "Nasceu a {date}", "person_hidden": "{name}{hidden, select, true { (oculto)} other {}}", "photo_shared_all_users": "Parece que partilhou as suas fotos com todos os utilizadores ou não tem nenhum utilizador para partilhar.", @@ -1504,6 +1530,7 @@ "port": "Porta", "preferences_settings_subtitle": "Gerenciar preferências do aplicativo", "preferences_settings_title": "Preferências", + "preparing": "A Preparar", "preset": "Predefinição", "preview": "Pré-visualizar", "previous": "Anterior", @@ -1520,7 +1547,7 @@ "profile_drawer_client_out_of_date_minor": "O aplicativo está desatualizado. Por favor, atualize para a versão mais recente.", "profile_drawer_client_server_up_to_date": "Cliente e Servidor atualizados", "profile_drawer_github": "GitHub", - "profile_drawer_readonly_mode": "Modo somente leitura ativado. Toque duas vezes no ícone do avatar do utilizador para sair.", + "profile_drawer_readonly_mode": "Modo só de leitura ativado. Faça um toque longo no ícone do perfil do utilizador para sair.", "profile_drawer_server_out_of_date_major": "O servidor está desatualizado. Atualize para a versão principal mais recente.", "profile_drawer_server_out_of_date_minor": "O servidor está desatualizado. Atualize para a versão mais recente.", "profile_image_of_user": "Imagem de perfil de {user}", @@ -1547,7 +1574,7 @@ "purchase_lifetime_description": "Compra vitalícia", "purchase_option_title": "OPÇÕES DE COMPRA", "purchase_panel_info_1": "O desenvolvimento do Immich requer muito tempo e esforço, e temos engenheiros a tempo inteiro a trabalhar nele para melhorá-lo quanto possível. A nossa missão é para que o software de código aberto e práticas de negócio éticas se tornem numa fonte de rendimento sustentável para os desenvolvedores e criar um ecossistema que respeite a privacidade dos utilizadores e que ofereça alternativas reais a serviços cloud explorativos.", - "purchase_panel_info_2": "Como estamos comprometidos a não adicionar acesso pago, esta compra não lhe dará acesso a nenhuma funcionalidade adicional do Immich. Contamos com utilizadores como você para dar suporte ao desenvolvimento contínuo do Immich.", + "purchase_panel_info_2": "Uma vez que estamos empenhados em não adicionar barreiras de pagamento, esta compra não lhe dará quaisquer funcionalidades adicionais no Immich. Contamos com utilizadores como você para apoiar o desenvolvimento contínuo do Immich.", "purchase_panel_title": "Apoie o projeto", "purchase_per_server": "Por servidor", "purchase_per_user": "Por utilizador", @@ -1559,7 +1586,7 @@ "purchase_server_description_2": "Status de apoiante", "purchase_server_title": "Servidor", "purchase_settings_server_activated": "A chave de produto do servidor é gerida pelo administrador", - "query_asset_id": "Consultar ID do recurso", + "query_asset_id": "Consultar ID do ficheiro", "queue_status": "Em fila {count}/{total}", "rating": "Classificação por estrelas", "rating_clear": "Limpar classificação", @@ -1567,8 +1594,9 @@ "rating_description": "Mostrar a classificação EXIF no painel de informações", "reaction_options": "Opções de reação", "read_changelog": "Ler Novidades", - "readonly_mode_disabled": "Modo somente leitura desactivado", - "readonly_mode_enabled": "Modo somente leitura activado", + "readonly_mode_disabled": "Modo só de leitura desativado", + "readonly_mode_enabled": "Modo só de leitura ativado", + "ready_for_upload": "Pronto para upload", "reassign": "Reatribuir", "reassigned_assets_to_existing_person": "Reatribuir {count, plural, one {# ficheiro} other {# ficheiros}} para {name, select, null {uma pessoa existente} other {{name}}}", "reassigned_assets_to_new_person": "Reatribuído {count, plural, one {# ficheiro} other {# ficheiros}} a uma nova pessoa", @@ -1593,6 +1621,7 @@ "regenerating_thumbnails": "A atualizar miniaturas", "remote": "Remoto", "remote_assets": "Ficheiros Remotos", + "remote_media_summary": "Sumário de Ficheiros Remotos", "remove": "Remover", "remove_assets_album_confirmation": "Tem a certeza de que deseja remover {count, plural, one {# ficheiro} other {# ficheiros}} do álbum?", "remove_assets_shared_link_confirmation": "Tem certeza de que deseja remover {count, plural, one {# ficheiro} other {# ficheiros}} deste link partilhado?", @@ -1630,9 +1659,9 @@ "reset_password": "Redefinir palavra-passe", "reset_people_visibility": "Redefinir pessoas ocultas", "reset_pin_code": "Repor código PIN", - "reset_pin_code_description": "Se esqueceu o seu código PIN, pode entrar em contato com o administrador do servidor para o repor", + "reset_pin_code_description": "Se esqueceu o seu código PIN, pode entrar em contacto com o administrador do servidor para o repor", "reset_pin_code_success": "Código PIN redefinido com sucesso", - "reset_pin_code_with_password": "Pode sempre repor o seu código PIN com a sua senha", + "reset_pin_code_with_password": "Pode sempre repor o seu código PIN com a sua palavra-passe", "reset_sqlite": "Reiniciar Base de Dados SQLite", "reset_sqlite_confirmation": "Tem a certeza de que quer reiniciar a base de dados SQLite? Vai ter de terminar a sessão e entrar outra vez para sincronizar os dados de novo", "reset_sqlite_success": "Base de dados SQLite reiniciada com sucesso", @@ -1645,10 +1674,10 @@ "restore_user": "Restaurar utilizador", "restored_asset": "Ficheiro restaurado", "resume": "Continuar", - "resume_paused_jobs": "Continuar {count, plural, one {# trabalho em pausa} other {# trabalhos pausados}}", + "resume_paused_jobs": "Continuar {count, plural, one {# trabalho em pausa} other {# trabalhos em pausa}}", "retry_upload": "Tentar carregar novamente", "review_duplicates": "Rever itens duplicados", - "review_large_files": "Rever arquivos grandes", + "review_large_files": "Rever ficheiros grandes", "role": "Função", "role_editor": "Editor", "role_viewer": "Visualizador", @@ -1833,7 +1862,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gerenciar links compartilhados", "shared_link_options": "Opções de link partilhado", - "shared_link_password_description": "Exigir uma senha para aceder a este link partilhado", + "shared_link_password_description": "Exigir uma palavra-passe para aceder a este link partilhado", "shared_links": "Links partilhados", "shared_links_description": "Partilhar fotos e videos com um link", "shared_photos_and_videos_count": "{assetCount, plural, other {# Fotos & videos partilhados.}}", @@ -1868,6 +1897,7 @@ "show_slideshow_transition": "Mostrar transições no Modo de Apresentação", "show_supporter_badge": "Emblema de apoiante", "show_supporter_badge_description": "Mostrar um emblema de apoiante", + "show_text_search_menu": "Mostrar menu de pesquisa de texto", "shuffle": "Aleatório", "sidebar": "Barra lateral", "sidebar_display_description": "Mostrar um link para a vista na barra lateral", @@ -1898,6 +1928,7 @@ "stacktrace": "Stacktrace", "start": "Iniciar", "start_date": "Data de início", + "start_date_before_end_date": "A data de início deve ser anterior à data de fim", "state": "Estado/Distrito", "status": "Estado", "stop_casting": "Parar transmissão", @@ -1922,7 +1953,7 @@ "sync_albums_manual_subtitle": "Sincronizar todas as fotos e vídeos enviados para o álbum de backup selecionado", "sync_local": "Sincronização Local", "sync_remote": "Sincronização Remota", - "sync_status": "Status da sincronização", + "sync_status": "Estado da sincronização", "sync_status_subtitle": "Ver e gerir o sistema de sincronização", "sync_upload_album_setting_subtitle": "Crie e envie suas fotos e vídeos para o álbum selecionado no Immich", "tag": "Etiqueta", @@ -2014,7 +2045,7 @@ "unstacked_assets_count": "Desempilhados {count, plural, one {# ficheiro} other {# ficheiros}}", "untagged": "Marcador removido", "up_next": "A seguir", - "update_location_action_prompt": "Actualize a localização de {count} activos seleccionados com:", + "update_location_action_prompt": "Atualize a localização de {count} ficheiros selecionados com:", "updated_at": "Atualizado a", "updated_password": "Palavra-passe atualizada", "upload": "Carregar", @@ -2100,5 +2131,6 @@ "yes": "Sim", "you_dont_have_any_shared_links": "Não tem links partilhados", "your_wifi_name": "Nome da sua rede Wi-Fi", - "zoom_image": "Ampliar/Reduzir imagem" + "zoom_image": "Ampliar/Reduzir imagem", + "zoom_to_bounds": "Aproximar aos limites" } diff --git a/i18n/pt_BR.json b/i18n/pt_BR.json index d6ead94cd6..5103df2ebc 100644 --- a/i18n/pt_BR.json +++ b/i18n/pt_BR.json @@ -28,10 +28,12 @@ "add_to_album": "Adicionar ao álbum", "add_to_album_bottom_sheet_added": "Adicionado ao {album}", "add_to_album_bottom_sheet_already_exists": "Já existe em {album}", + "add_to_album_bottom_sheet_some_local_assets": "Alguns arquivos / mídias não puderam ser adicionados ao álbum", "add_to_album_toggle": "Alternar a seleção de {album}", "add_to_albums": "Adicionar aos álbuns", "add_to_albums_count": "Adicionar aos álbuns ({count})", "add_to_shared_album": "Adicionar ao álbum compartilhado", + "add_upload_to_stack": "Adicionar upload ao grupo", "add_url": "Adicionar URL", "added_to_archive": "Adicionado ao arquivo", "added_to_favorites": "Adicionado aos favoritos", @@ -598,6 +600,7 @@ "backup_controller_page_turn_on": "Ativar backup automático", "backup_controller_page_uploading_file_info": "Informações do arquivo", "backup_err_only_album": "Não é possível remover o único álbum", + "backup_error_sync_failed": "A sincronização falhou. Não foi possível processar o backup.", "backup_info_card_assets": "arquivos", "backup_manual_cancelled": "Cancelado", "backup_manual_in_progress": "Envio já está em progresso. Tente novamente mais tarde", @@ -782,7 +785,7 @@ "default_locale": "Localização Padrão", "default_locale_description": "Formatar datas e números baseados na linguagem do seu navegador", "delete": "Excluir", - "delete_action_confirmation_message": "Confirma deletar este arquivo? O arquivo será enviado para a lixeira do servidor e depois perguntará se deseja deletar do seu dispositivo local", + "delete_action_confirmation_message": "Tem certeza? O arquivo será enviado para a lixeira do servidor, depois você poderá confirmar se deseja também deletar do seu dispositivo local", "delete_action_prompt": "{count} deletados", "delete_album": "Excluir álbum", "delete_api_key_prompt": "Tem certeza de que deseja excluir esta chave de API?", @@ -2037,7 +2040,7 @@ "unselect_all": "Desselecionar todos", "unselect_all_duplicates": "Desselecionar todas as duplicatas", "unselect_all_in": "Remover seleção de {group}", - "unstack": "Retirar do grupo", + "unstack": "Desagrupar", "unstack_action_prompt": "{count} desagrupados", "unstacked_assets_count": "{count, plural, one {# arquivo retirado} other {# arquivos retirados}} do grupo", "untagged": "Marcador removido", diff --git a/i18n/ro.json b/i18n/ro.json index 637460ae34..ffa7d6f6f0 100644 --- a/i18n/ro.json +++ b/i18n/ro.json @@ -1,12 +1,12 @@ { "about": "Despre", "account": "Cont", - "account_settings": "Setări Cont", + "account_settings": "Setări cont", "acknowledge": "Văzut", "action": "Acţiune", "action_common_update": "Actualizează", "actions": "Acţiuni", - "active": "Activ", + "active": "Active", "activity": "Activitate", "activity_changed": "Activitatea este {enabled, select, true {activată} other {dezactivată}}", "add": "Adaugă", @@ -28,6 +28,7 @@ "add_to_album": "Adaugă în album", "add_to_album_bottom_sheet_added": "Adăugat în {album}", "add_to_album_bottom_sheet_already_exists": "Deja în {album}", + "add_to_album_bottom_sheet_some_local_assets": "Unele resurse locale nu au putut fi adăugate la album", "add_to_album_toggle": "Selectează/deselectează {album}", "add_to_albums": "Adaugă la albume", "add_to_albums_count": "Adaugă la albume ({count})", @@ -40,12 +41,12 @@ "add_exclusion_pattern_description": "Adăugați modele de excludere. Globing folosind *, ** și ? este suportat. Pentru a ignora toate fișierele din orice director numit „Raw”, utilizați „**/Raw/**”. Pentru a ignora toate fișierele care se termină în „.tif”, utilizați „**/*.tif”. Pentru a ignora o cale absolută, utilizați „/path/to/ignore/**”.", "admin_user": "Utilizator admin", "asset_offline_description": "Acest material din biblioteca externă nu se mai găsește pe disc și a fost mutat în coșul de gunoi. Dacă fișierul a fost mutat în bibliotecă, verificați cronologia pentru noul material corespunzător. Pentru a restabili acest material, asigurați-vă că calea fișierului de mai jos poate fi accesată de Immich și scanați biblioteca.", - "authentication_settings": "Setări de Autentificare", + "authentication_settings": "Setări de autentificare", "authentication_settings_description": "Gestionează parola, OAuth și alte setări de autentificare", "authentication_settings_disable_all": "Ești sigur că vrei sa dezactivezi toate metodele de autentificare? Autentificarea va fi complet dezactivată.", "authentication_settings_reenable": "Pentru a reactiva, folosește Comandă Server.", "background_task_job": "Activități de Fundal", - "backup_database": "Salvare Bază de Date", + "backup_database": "Salvare bază de date", "backup_database_enable_description": "Activare salvarea bazei de date", "backup_keep_last_amount": "Număr de copii de rezervă anterioare de păstrat", "backup_onboarding_1_description": "copie externă în cloud sau într-o altă locație fizică.", @@ -72,9 +73,9 @@ "disable_login": "Dezactivați autentificarea", "duplicate_detection_job_description": "Rulați învățarea automată pe materiale pentru a detecta imagini similare. Se bazează pe Căutare Inteligentă", "exclusion_pattern_description": "Modelele de excludere vă permit să ignorați fișierele și folderele atunci când vă scanați biblioteca. Acest lucru este util dacă aveți foldere care conțin fișiere pe care nu doriți să le importați, cum ar fi fișierele RAW.", - "external_library_management": "Managementul Bibliotecii Externe", + "external_library_management": "Gestionarea bibliotecilor externe", "face_detection": "Detecție facială", - "face_detection_description": "Detectează fețele din fișiere folosind învățare automată. Pentru videoclipuri, este luată în considerare doar miniatura. „Reînprospătează” (re)procesează toate fișierele. „Resetează” adaugă în coadă fișierele care nu au fost încă procesate. Fețele detectate vor fi puse în coadă pentru recunoașterea facială după finalizarea detectării feței, grupându-le în persoane existente sau noi.", + "face_detection_description": "Detectează fețele din fișiere folosind învățare automată. Pentru videoclipuri, este luată în considerare doar miniatura. „Reîmprospătează” (re)procesează toate fișierele. „Resetează” adaugă în coadă fișierele care nu au fost încă procesate. Fețele detectate vor fi puse în coadă pentru recunoașterea facială după finalizarea detectării feței, grupându-le în persoane existente sau noi.", "facial_recognition_job_description": "Grupați fețele detectate în persoane. Acest pas rulează după ce Detectarea Feței este finalizată. „Resetează” (re)grupează toate fețele. „Lipsă” adaugă în coadă fețe care nu au o persoană desemnată.", "failed_job_command": "Comanda {command} a eșuat pentru jobul: {job}", "force_delete_user_warning": "AVERTISMENT: Acest lucru va elimina imediat utilizatorul și toate activele sale. Acest lucru nu poate fi anulat și fișierele nu pot fi recuperate.", @@ -91,30 +92,30 @@ "image_prefer_wide_gamut_setting_description": "Utilizați Display P3 pentru miniaturi. Acest lucru păstrează mai bine vibrația imaginilor cu spații de culoare largi, dar imaginile pot apărea diferit pe dispozitivele cu o versiune mai veche de browser. Imaginile sRGB sunt păstrate ca sRGB pentru a evita schimbările de culoare.", "image_preview_description": "Imagine de dimensiune medie cu metadate eliminate, utilizată la vizualizarea unui singur element și pentru învățarea automată", "image_preview_quality_description": "Calitatea previzualizării de la 1 la 100. O valoare mai mare oferă o calitate mai bună, dar produce fișiere mai mari și poate reduce receptivitatea aplicației. Setarea unei valori scăzute poate afecta calitatea învățării automate.", - "image_preview_title": "Previzualizați Setările", + "image_preview_title": "Previzualizați setările", "image_quality": "Calitate", "image_resolution": "Rezolutie", "image_resolution_description": "Rezoluțiile mai mari pot păstra mai multe detalii, dar necesită mai mult timp pentru a fi codificate, au dimensiuni mai mari ale fișierelor și pot reduce răspunsul aplicației.", - "image_settings": "Setări Imagine", + "image_settings": "Setări imagine", "image_settings_description": "Gestionează calitatea și rezoluția imaginilor generate", "image_thumbnail_description": "Miniatură mică cu metadate eliminate, utilizată la vizualizarea grupurilor de fotografii, cum ar fi în cronologia principală", "image_thumbnail_quality_description": "Calitatea miniaturii de la 1 la 100. O valoare mai mare oferă o calitate mai bună, dar produce fișiere mai mari și poate reduce receptivitatea aplicației.", - "image_thumbnail_title": "Setari Miniaturi", + "image_thumbnail_title": "Setari miniaturi", "job_concurrency": "Concurență {job}", "job_created": "Sarcină creată", "job_not_concurrency_safe": "Această sarcină nu este sigură pentru a rula în concurență.", - "job_settings": "Setări Sarcină", + "job_settings": "Setări sarcină", "job_settings_description": "Administrează concurența sarcinilor", - "job_status": "Starea Sarcinii", + "job_status": "Starea sarcinii", "jobs_delayed": "{jobCount, plural, other {# întârziat}}", "jobs_failed": "{jobCount, plural, other {# eșuat}}", - "library_created": "Librărie creată:{library}", + "library_created": "Librărie creată: {library}", "library_deleted": "Bibliotecă ștearsă", "library_import_path_description": "Specificați un folder pentru a îl importa. Acest folder, inclusiv sub-folderele, vor fi scanate pentru imagini și videoclipuri.", - "library_scanning": "Scanare Periodică", + "library_scanning": "Scanare periodică", "library_scanning_description": "Configurează scanarea periodică pentru bibliotecă", "library_scanning_enable_description": "Activează scanarea periodică pentru bibliotecă", - "library_settings": "Bibliotecă Externă", + "library_settings": "Bibliotecă externă", "library_settings_description": "Administrează setările pentru biblioteci externe", "library_tasks_description": "Scanează bibliotecile externe de active noi sau modificate", "library_watching_enable_description": "Urmărește bibliotecile externe pentru schimbări ale fișierelor", @@ -128,9 +129,11 @@ "machine_learning_availability_checks_enabled": "Activează verificare disponibilitate", "machine_learning_availability_checks_interval": "Interval verificare", "machine_learning_availability_checks_interval_description": "Interval in milisecunde între verificările de disponibilitate", + "machine_learning_availability_checks_timeout": "Timp de expirare cerere", + "machine_learning_availability_checks_timeout_description": "Timp de așteptare în milisecunde pentru verificările de disponibilitate", "machine_learning_clip_model": "Model CLIP", "machine_learning_clip_model_description": "Numele unui model CLIP listat aici. Rețineți că trebuie să rulați din nou funcția „Smart Search” pentru toate imaginile la schimbarea unui model.", - "machine_learning_duplicate_detection": "Detectare Duplicate", + "machine_learning_duplicate_detection": "Detectare duplicate", "machine_learning_duplicate_detection_enabled": "Activează detectarea duplicatelor", "machine_learning_duplicate_detection_enabled_description": "Dacă este dezactivată, elementele identice vor fi în continuare de-duplicate.", "machine_learning_duplicate_detection_setting_description": "Utilizați încorporările CLIP pentru a găsi dubluri probabile", @@ -157,11 +160,11 @@ "machine_learning_smart_search_enabled": "Activați căutarea inteligentă", "machine_learning_smart_search_enabled_description": "Dacă este dezactivată, imaginile nu vor fi codificate pentru căutarea inteligentă.", "machine_learning_url_description": "URL-ul serverului de învățare automată. Dacă sunt furnizate mai multe URL-uri, fiecare server va fi încercat pe rând, până când unul răspunde cu succes, în ordine de la primul până la ultimul. Serverele care nu răspund vor fi ignorate temporar până revin online.", - "manage_concurrency": "Gestionarea Simultaneității", + "manage_concurrency": "Gestionarea simultaneității", "manage_log_settings": "Administrați setările jurnalului", "map_dark_style": "Mod întunecat", "map_enable_description": "Activați funcțiile hărții", - "map_gps_settings": "Setări Hartă & GPS", + "map_gps_settings": "Setări hartă & GPS", "map_gps_settings_description": "Gestionare setări Hartă & GPS (localizare inversă)", "map_implications": "Caracteristica hărții se bazează pe un serviciu extern de planșe (tiles.immich.cloud)", "map_light_style": "Mod deschis", @@ -178,7 +181,7 @@ "metadata_extraction_job_description": "Extragere informații metadate din fiecare fișier cum ar fi localizare GPS, fețe și rezoluție,", "metadata_faces_import_setting": "Activare import fețe", "metadata_faces_import_setting_description": "Importă fețe din datele EXIF ale imaginii și din fișiere tip \"sidecar\"", - "metadata_settings": "Setări Metadate", + "metadata_settings": "Setări metadate", "metadata_settings_description": "Gestionează setările pentru metadate", "migration_job": "Migrare", "migration_job_description": "Migrați miniaturile pentru elemente și fețe la cea mai recentă structură de foldere", @@ -256,7 +259,7 @@ "send_welcome_email": "Trimite email de bun-venit", "server_external_domain_settings": "Domeniu extern", "server_external_domain_settings_description": "Domeniu pentru distribuire publicǎ a scurtǎturilor, incluzând http(s)://", - "server_public_users": "Utilizatori Publici", + "server_public_users": "Utilizatori publici", "server_public_users_description": "Toți utilizatorii (nume și e-mail) sunt listați atunci când adăugați un utilizator la albumele partajate. Când este dezactivată, lista de utilizatori va fi disponibilă numai pentru utilizatorii admin.", "server_settings": "Setǎri Server", "server_settings_description": "Gestioneazǎ setǎrile serverului", @@ -278,7 +281,7 @@ "storage_template_more_details": "Pentru mai multe detalii despre aceasta caracteristică, accesați Șablon stocare si implicațiile", "storage_template_onboarding_description_v2": "Când este activată, această funcție va organiza automat fișierele pe baza șablonului definit de către utilizator. Pentru mai multe informații, accesează documentația.", "storage_template_path_length": "Limita de lungime pentru calea aproximativă: {length, number}/{limit, number}", - "storage_template_settings": "Șablon Stocare", + "storage_template_settings": "Șablon stocare", "storage_template_settings_description": "Gestionează structura folderelor și numele fișierelor pentru elementele încărcate", "storage_template_user_label": "{label} este eticheta de stocare a utilizatorului", "system_settings": "Setǎri de Sistem", @@ -294,9 +297,9 @@ "template_settings_description": "Gestionați șabloanele personalizate pentru notificări", "theme_custom_css_settings": "CSS personalizat", "theme_custom_css_settings_description": "Foile de stil în cascadă (CSS) permit personalizarea designului Immich.", - "theme_settings": "Setări Temă", + "theme_settings": "Setări temă", "theme_settings_description": "Gestionează personalizarea interfeței web Immich", - "thumbnail_generation_job": "Generare Miniaturi", + "thumbnail_generation_job": "Generare miniaturi", "thumbnail_generation_job_description": "Generează miniaturi mari, mici și estompate pentru fiecare resursă, precum și miniaturi pentru fiecare persoană", "transcoding_acceleration_api": "API de accelerare", "transcoding_acceleration_api_description": "API-ul care va interacționa cu dispozitivul tău pentru a accelera transcodarea. Această setare este 'cel mai bun efort': va reveni la transcodarea software în caz de eșec. VP9 poate funcționa sau nu, în funcție de hardware-ul tău.", @@ -322,7 +325,7 @@ "transcoding_disabled_description": "Nu transcodifică niciun videoclip; acest lucru poate afecta redarea pe anumite dispozitive", "transcoding_encoding_options": "Opțiuni codificare", "transcoding_encoding_options_description": "Setează codecuri , calitatea, rezoluția și alte opțiuni pentru videoclipuri codificare", - "transcoding_hardware_acceleration": "Accelerare Hardware", + "transcoding_hardware_acceleration": "Accelerare hardware", "transcoding_hardware_acceleration_description": "Experimental: transcodare mai rapidă, dar poate reduce calitatea la aceeași rată de biți", "transcoding_hardware_decoding": "Decodare hardware", "transcoding_hardware_decoding_setting_description": "Se aplică doar pentru NVENC, QSV și RKMPP. Activează accelerarea completă în loc de doar accelerarea codificării. S-ar putea să nu funcționeze pentru toate videoclipurile.", @@ -342,7 +345,7 @@ "transcoding_reference_frames": "Cadre de referință", "transcoding_reference_frames_description": "Numărul de cadre de referință atunci când se comprimă un cadru dat. Valorile mai mari îmbunătățesc eficiența compresiei, dar încetinesc codarea. 0 setează această valoare automat.", "transcoding_required_description": "Numai videoclipuri care nu sunt într-un format acceptat", - "transcoding_settings": "Setări de Transcodare Video", + "transcoding_settings": "Setări de transcodare video", "transcoding_settings_description": "Gestionează care videoclipuri să transcodam și cum să le procesam", "transcoding_target_resolution": "Rezoluția țintă", "transcoding_target_resolution_description": "Rezoluțiile mai mari pot păstra mai multe detalii, dar necesită mai mult timp pentru codare, au dimensiuni mai mari ale fișierelor și pot reduce răspunsul aplicației.", @@ -373,23 +376,23 @@ "user_delete_immediately": "Contul și resursele utilizatorului {user} vor fi puse în coadă pentru ștergere permanentă imediat.", "user_delete_immediately_checkbox": "Pune utilizatorul și resursele în coadă pentru ștergere imediată", "user_details": "Detalii utilizator", - "user_management": "Gestionarea Utilizatorilor", + "user_management": "Gestionarea utilizatorilor", "user_password_has_been_reset": "Parola utilizatorului a fost resetată:", "user_password_reset_description": "Vă rugăm să furnizați utilizatorului parola temporară și să îi informați că va trebui să o schimbe la următoarea autentificare.", "user_restore_description": "Contul utilizatorului {user} va fi restaurat.", "user_restore_scheduled_removal": "Restaurare utilizator - ștergere programată pe {date, date, long}", - "user_settings": "Setǎri Utilizator", + "user_settings": "Setǎri utilizator", "user_settings_description": "Gestioneazǎ setǎrile utilizatorului", "user_successfully_removed": "Utilizatorul {email} a fost eliminat cu succes.", "version_check_enabled_description": "Activează verificarea versiunii", "version_check_implications": "Funcția de verificare a versiunii se bazează pe comunicarea periodică cu github.com", - "version_check_settings": "Verificare Versiune", + "version_check_settings": "Verificare versiune", "version_check_settings_description": "Activeazǎ/dezactiveazǎ notificarea unei noi versiuni", "video_conversion_job": "Transcodați videoclipuri", "video_conversion_job_description": "Transcodați videoclipurile pentru o compatibilitate mai mare cu browserele și dispozitivele" }, - "admin_email": "E-mail Administrator", - "admin_password": "Parolă Administrator", + "admin_email": "E-mail administrator", + "admin_password": "Parolă administrator", "administration": "Administrare", "advanced": "Avansat", "advanced_settings_enable_alternate_media_filter_subtitle": "Utilizați această opțiune pentru a filtra conținutul media în timpul sincronizării pe baza unor criterii alternative. Încercați numai dacă întâmpinați probleme cu aplicația la detectarea tuturor albumelor.", @@ -399,7 +402,7 @@ "advanced_settings_prefer_remote_title": "Preferă fotografii la distanță", "advanced_settings_proxy_headers_subtitle": "Definește antetele proxy pe care Immich ar trebui să le trimită cu fiecare solicitare de rețea", "advanced_settings_proxy_headers_title": "Antete Proxy", - "advanced_settings_readonly_mode_subtitle": "Activează modul doar-citire, în care fotografiile pot fi doar vizualizate, iar acțiuni precum selectarea mai multor imagini, partajarea, redarea pe alt dispozitiv sau ștergerea sunt dezactivate. Activează/Dezactivează modul doar-citire din avatarul utilizatorului de pe ecranul principal.", + "advanced_settings_readonly_mode_subtitle": "Activează modul doar-citire, în care fotografiile pot fi doar vizualizate, iar acțiuni precum selectarea mai multor imagini, partajarea, redarea pe alt dispozitiv sau ștergerea sunt dezactivate. Activează/Dezactivează modul doar-citire din avatarul utilizatorului de pe ecranul principal", "advanced_settings_readonly_mode_title": "Mod doar-citire", "advanced_settings_self_signed_ssl_subtitle": "Omite verificare certificate SSL pentru distinația server-ului, necesar pentru certificate auto-semnate.", "advanced_settings_self_signed_ssl_title": "Permite certificate SSL auto-semnate", @@ -465,7 +468,7 @@ "app_bar_signout_dialog_content": "Ești sigur că vrei să te deconectezi?", "app_bar_signout_dialog_ok": "Da", "app_bar_signout_dialog_title": "Deconectare", - "app_settings": "Setări Aplicație", + "app_settings": "Setări aplicație", "appears_in": "Apare în", "apply_count": "Aplică ({count, number})", "archive": "Arhivă", @@ -476,7 +479,7 @@ "archive_size": "Mărime arhivă", "archive_size_description": "Configurează dimensiunea arhivei pentru descărcări (în GiB)", "archived": "Arhivat", - "archived_count": "{count, plural, other {Arhivat/e#}}", + "archived_count": "{count, plural, one {Arhivat} few {# arhivate} other {# arhivate}}", "are_these_the_same_person": "Sunt aceștia aceeași persoană?", "are_you_sure_to_do_this": "Sunteți sigur că doriți să faceți acest lucru?", "asset_action_delete_err_read_only": "Fișierele cu permisiuni doar de citire nu au putut fi șterse, omitere", @@ -528,6 +531,7 @@ "assets_trashed_count": "Mutat în coșul de gunoi {count, plural, one {# resursă} other {# resurse}}", "assets_trashed_from_server": "{count} resursă(e) eliminate de pe serverul Immich", "assets_were_part_of_album_count": "{count, plural, one {Resursa era} other {Resursele erau}} deja parte din album", + "assets_were_part_of_albums_count": "{count, plural, one {Asset was} other {Assets were}} deja parte din albume", "authorized_devices": "Dispozitive Autorizate", "automatic_endpoint_switching_subtitle": "Conectează-te local prin rețeaua Wi‐Fi configurată când este valabilă și prin rețele alternative în caz contrar", "automatic_endpoint_switching_title": "Alternare URL automată", @@ -537,6 +541,7 @@ "background_backup_running_error": "Procesul de backup în fundal este activ, nu se poate porni backup manual", "background_location_permission": "Permisiune locație în fundal", "background_location_permission_content": "Pentru a putea schimba rețeaua activă în fundal, Immich are nevoie de acces *permanent* la locația precisă pentru a citi numele rețelei Wi-Fi", + "background_options": "Opțiuni de fundal", "backup": "Backup", "backup_album_selection_page_albums_device": "Albume în dispozitiv ({count})", "backup_album_selection_page_albums_tap": "Apasă odata pentru a include, de două ori pentru a exclude", @@ -544,6 +549,7 @@ "backup_album_selection_page_select_albums": "Selectează albume", "backup_album_selection_page_selection_info": "Informații selecție", "backup_album_selection_page_total_assets": "Total resurse unice", + "backup_albums_sync": "Sincronizarea albumelor de backup", "backup_all": "Toate", "backup_background_service_backup_failed_message": "Eșuare backup resurse. Reîncercare…", "backup_background_service_connection_failed_message": "Conectare la server eșuată. Reîncercare…", @@ -583,7 +589,7 @@ "backup_controller_page_remainder": "Rămas(e)", "backup_controller_page_remainder_sub": "Fotografii și videoclipuri din selecție rămase pentru backup", "backup_controller_page_server_storage": "Stocare server", - "backup_controller_page_start_backup": "Începe backup", + "backup_controller_page_start_backup": "Începe copia de rezervă", "backup_controller_page_status_off": "Backup-ul automat în prim-plan este oprit", "backup_controller_page_status_on": "Backup-ul automat în prim-plan este pornit", "backup_controller_page_storage_format": "{used} din {total} folosit", @@ -593,12 +599,14 @@ "backup_controller_page_turn_on": "Activează backup-ul în prim-plan", "backup_controller_page_uploading_file_info": "Informații încărcare fișier", "backup_err_only_album": "Nu poți șterge singurul album", + "backup_error_sync_failed": "Sincronizarea a eșuat. Nu se poate procesa copia de rezervă.", "backup_info_card_assets": "resurse", "backup_manual_cancelled": "Anulat", "backup_manual_in_progress": "Încărcarea este deja în curs. Încearcă din nou mai târziu", "backup_manual_success": "Succes", "backup_manual_title": "Status încărcare", - "backup_options_page_title": "Opțiuni Backup", + "backup_options": "Opțiuni copie de rezervă", + "backup_options_page_title": "Opțiuni copie de rezervă", "backup_setting_subtitle": "Schimbă opțiuni pentru backup în prim-plan și în fundal", "backup_settings_subtitle": "Gestionați setările de încărcare", "backward": "În sens invers", @@ -609,7 +617,7 @@ "birthdate_saved": "Data nașterii salvată cu succes", "birthdate_set_description": "Data nașterii este utilizată pentru a calcula vârsta acestei persoane la momentul realizării fotografiei.", "blurred_background": "Fundal neclar", - "bugs_and_feature_requests": "Erori și Solicitări de Caracteristici", + "bugs_and_feature_requests": "Erori și solicitări de caracteristici", "build": "Versiunea", "build_image": "Versiune Imagine", "bulk_delete_duplicates_confirmation": "Ești sigur că vrei să ștergi în masă {count, plural, one {# resursă duplicată} other {# resurse duplicate}}? Aceasta va păstra cea mai mare resursă din fiecare grup și va șterge permanent toate celelalte duplicate. Nu poți anula această acțiune!", @@ -659,6 +667,8 @@ "change_pin_code": "Schimbă codul PIN", "change_your_password": "Schimbă-ți parola", "changed_visibility_successfully": "Schimbare vizibilitate cu succes", + "charging": "Încărcare", + "charging_requirement_mobile_backup": "Pentru copia de rezervă în fundal, dispozitivul trebuie să fie în curs de încărcare", "check_corrupt_asset_backup": "Verifică copii de rezervă a resurselor corupte", "check_corrupt_asset_backup_button": "Efectuează verificarea", "check_corrupt_asset_backup_description": "Rulează această verificare doar prin Wi-Fi și doar după ce toate resursele au fost salvate în copia de rezerva. Procedura poate dura câteva minute.", @@ -710,7 +720,7 @@ "control_bottom_app_bar_delete_from_immich": "Șterge din Immich", "control_bottom_app_bar_delete_from_local": "Șterge din dispozitiv", "control_bottom_app_bar_edit_location": "Editează locație", - "control_bottom_app_bar_edit_time": "Editează Data și Ora", + "control_bottom_app_bar_edit_time": "Editează data și ora", "control_bottom_app_bar_share_link": "Partajează linkul", "control_bottom_app_bar_share_to": "Distribuire către", "control_bottom_app_bar_trash_from_immich": "Mută în coș", @@ -745,6 +755,7 @@ "create_user": "Creează utilizator", "created": "Creat", "created_at": "Creat", + "creating_linked_albums": "Crearea albumelor cu link...", "crop": "Decupează", "curated_object_page_title": "Obiecte", "current_device": "Dispozitiv curent", @@ -764,6 +775,7 @@ "date_of_birth_saved": "Data nașterii salvată cu succes", "date_range": "Interval de date", "day": "Zi", + "days": "Zile", "deduplicate_all": "Deduplicați Toate", "deduplication_criteria_1": "Marimea imagini în octeți", "deduplication_criteria_2": "Numărul de date EXIF", @@ -848,10 +860,12 @@ "edit": "Editare", "edit_album": "Editare album", "edit_avatar": "Editare avatar", - "edit_birthday": "Editează ziua de naștere", + "edit_birthday": "Modifică ziua de naștere", "edit_date": "Editare dată", "edit_date_and_time": "Editare dată și oră", "edit_date_and_time_action_prompt": "{count} data și ora modificării", + "edit_date_and_time_by_offset": "Schimbă data prin decalaj", + "edit_date_and_time_by_offset_interval": "Noul interval de date: {from} - {to}", "edit_description": "Editează descrierea", "edit_description_prompt": "Vă rugăm să selectați o descriere nouă:", "edit_exclusion_pattern": "Editarea modelului de excludere", @@ -861,7 +875,7 @@ "edit_key": "Tastă de editare", "edit_link": "Editare link", "edit_location": "Editare locație", - "edit_location_action_prompt": "{count} locație(i) editată(e)", + "edit_location_action_prompt": "{count} locație(i) modificată(e)", "edit_location_dialog_title": "Locație", "edit_name": "Editare nume", "edit_people": "Editare persoane", @@ -891,7 +905,9 @@ "error": "Eroare", "error_change_sort_album": "Nu s-a putut modifica ordinea de sortare a albumului", "error_delete_face": "Eroare la ștergerea feței din activ", + "error_getting_places": "Eroare la obținerea locațiilor", "error_loading_image": "Eroare la încărcarea imaginii", + "error_loading_partners": "Eroare la încărcarea partenerilor: {error}", "error_saving_image": "Eroare: {error}", "error_tag_face_bounding_box": "Eroare la etichetarea feței - nu se pot obține coordonatele casetei de delimitare", "error_title": "Eroare - ceva nu a mers", @@ -924,6 +940,7 @@ "failed_to_load_notifications": "Nu s-au putut încărca notificările", "failed_to_load_people": "Eșec la încărcarea persoanelor", "failed_to_remove_product_key": "Eșec la eliminarea cheii de produs", + "failed_to_reset_pin_code": "Nu s-a reușit resetarea codului PIN", "failed_to_stack_assets": "Eșec la combinarea resurselor", "failed_to_unstack_assets": "Eșec la desfășurarea resurselor", "failed_to_update_notification_status": "Nu s-a putut actualiza starea notificării", @@ -932,6 +949,7 @@ "paths_validation_failed": "{paths, plural, one {# cale} other {# căi}} nu a trecut validarea", "profile_picture_transparent_pixels": "Pozele de profil nu pot avea pixeli transparenți. Te rugăm să mărești imaginea și/sau să o muți.", "quota_higher_than_disk_size": "Ați stabilit o valoare a spațiului de stocare mai mare decât dimensiunea discului", + "something_went_wrong": "Ceva nu a mers bine", "unable_to_add_album_users": "Imposibil de adăugat utilizatori în album", "unable_to_add_assets_to_shared_link": "Imposibil de adăugat resurse la link-ul partajat", "unable_to_add_comment": "Imposibil de adăugat comentariu", @@ -1039,7 +1057,7 @@ "export_database_description": "Exportați baza de date SQLite", "extension": "Extensie", "external": "Extern", - "external_libraries": "Biblioteci Externe", + "external_libraries": "Biblioteci externe", "external_network": "Rețea externă", "external_network_sheet_info": "Când nu se află în rețeaua Wi-Fi preferată, aplicația se va conecta la server prin prima dintre adresele URL de mai jos pe care o poate accesa, începând de sus în jos", "face_unassigned": "Nealocat", @@ -1054,6 +1072,7 @@ "favorites_page_no_favorites": "Nu au fost găsite resurse favorite", "feature_photo_updated": "Fotografie caracteristică actualizată", "features": "Caracteristici", + "features_in_development": "Funcții în dezvoltare", "features_setting_description": "Gestionați funcțiile aplicației", "file_name": "Nume de fișier", "file_name_or_extension": "Numele sau extensia fișierului", @@ -1063,21 +1082,26 @@ "filter_people": "Filtrați persoanele", "filter_places": "Filtrează locurile", "find_them_fast": "Găsiți-le rapid prin căutare după nume", + "first": "Primul", "fix_incorrect_match": "Remediați potrivirea incorectă", "folder": "Dosar", "folder_not_found": "Dosar negăsit", - "folders": "Foldere", + "folders": "Fișiere", "folders_feature_description": "Răsfoire în conținutul folderului pentru fotografiile și videoclipurile din sistemul de fișiere", + "forgot_pin_code_question": "Ai uitat codul PIN?", "forward": "Redirecționare", "gcast_enabled": "Google Cast", "gcast_enabled_description": "Această funcție încarcă resurse externe de la Google pentru a funcționa.", "general": "General", + "geolocation_instruction_location": "Apasă pe o resursă cu coordonate GPS pentru a folosi locația sa, sau selectează direct o locație de pe hartă", "get_help": "Obțineți Ajutor", "get_wifiname_error": "Nu s-a putut obține numele rețelei Wi-Fi. Asigurați-vă că ați acordat permisiunile necesare și că sunteți conectat la o rețea Wi-Fi", "getting_started": "Noțiuni de Bază", "go_back": "Întoarcere", "go_to_folder": "Accesați folderul", "go_to_search": "Spre căutare", + "gps": "GPS", + "gps_missing": "Fără GPS", "grant_permission": "Acordați permisiunea", "group_albums_by": "Grupați albume de...", "group_country": "Grupare după țară", @@ -1088,6 +1112,9 @@ "haptic_feedback_switch": "Activează feedback-ul haptic", "haptic_feedback_title": "Feedback haptic", "has_quota": "Are spațiu de stocare", + "hash_asset": "Hash-ul resursei", + "hashed_assets": "Resurse hashed", + "hashing": "Generare hash", "header_settings_add_header_tip": "Adăugați antet", "header_settings_field_validator_msg": "Valoarea nu poate fi goală", "header_settings_header_name_input": "Numele antetului", @@ -1114,11 +1141,12 @@ "home_page_favorite_err_partner": "Momentan nu se pot adăuga fișierele partenerului la favorite, omitere", "home_page_first_time_notice": "Dacă este prima dată când utilizezi aplicația, te rugăm să te asiguri că alegi unul sau mai multe albume de backup, astfel încât cronologia să poată fi populată cu fotografiile și videoclipurile din aceste albume", "home_page_locked_error_local": "Nu se pot muta resursele locale în folderul blocat, se omit", - "home_page_locked_error_partner": "Nu se pot muta materialele partenerului în folderul blocat, se omit.", + "home_page_locked_error_partner": "Nu se pot muta resursele partenerului în folderul blocat, se omit.", "home_page_share_err_local": "Nu se pot distribui fișiere locale prin link, omitere", "home_page_upload_err_limit": "Se pot încărca maxim 30 de resurse odată, omitere", "host": "Gazdă", "hour": "Oră", + "hours": "Ore", "id": "ID", "idle": "Inactiv", "ignore_icloud_photos": "Ignoră fotografiile din iCloud", @@ -1178,10 +1206,13 @@ "language_no_results_title": "Nu au fost găsite limbi", "language_search_hint": "Căutați limbi...", "language_setting_description": "Selectați limba preferată", + "large_files": "Fișiere mari", + "last": "Ultimul", "last_seen": "Văzut ultima dată", "latest_version": "Ultima Versiune", "latitude": "Latitudine", "leave": "Părăsiți", + "leave_album": "Părăsește albumul", "lens_model": "Model obiectiv", "let_others_respond": "Permite altora să răspundă", "level": "Nivel", @@ -1195,6 +1226,7 @@ "library_page_sort_title": "Titlu album", "licenses": "Licențe", "light": "Lumină", + "like": "Îmi place", "like_deleted": "Preferat șters", "link_motion_video": "Link video în mișcare", "link_to_oauth": "Link către OAuth", @@ -1205,6 +1237,7 @@ "local": "Local", "local_asset_cast_failed": "Nu se poate converti un element care nu este încărcat pe server", "local_assets": "Asset-uri locale", + "local_media_summary": "Rezumatul fișierelor media locale", "local_network": "Rețea locală", "local_network_sheet_info": "Aplicația se va conecta la server prin intermediul acestei adrese URL atunci când utilizează rețeaua Wi-Fi specificată", "location_permission": "Permisiunea de locație", @@ -1216,6 +1249,7 @@ "location_picker_longitude_hint": "Introdu longitudinea aici", "lock": "Blocare", "locked_folder": "Dosar blocat", + "log_detail_title": "Detalii jurnal", "log_out": "Deconectare", "log_out_all_devices": "Deconectați-vă de la toate dispozitivele", "logged_in_as": "Conectat ca {user}", @@ -1246,13 +1280,15 @@ "login_password_changed_success": "Parola a fost actualizată cu succes", "logout_all_device_confirmation": "Sigur doriți să deconectați toate dispozitivele?", "logout_this_device_confirmation": "Sigur doriți să deconectați acest dispozitiv?", + "logs": "Jurnale", "longitude": "Longitudine", "look": "Examinare", "loop_videos": "Buclă videoclipuri", "loop_videos_description": "Activați pentru a rula in buclă automat un videoclip în vizualizatorul de detalii.", "main_branch_warning": "Utilizați o versiune de dezvoltare; vă recomandăm insistent să utilizați o versiune de lansare!", "main_menu": "Meniu principal", - "make": "Face", + "make": "Marcă", + "manage_geolocation": "Gestionați locația", "manage_shared_links": "Administrați link-urile distribuite", "manage_sharing_with_partners": "Gestionați partajarea cu partenerii", "manage_the_app_settings": "Gestionați setările aplicației", @@ -1261,7 +1297,7 @@ "manage_your_devices": "Gestionați-vă dispozitivele conectate", "manage_your_oauth_connection": "Gestionați-vă conexiunea OAuth", "map": "Hartă", - "map_assets_in_bounds": "{count, plural, one {# poză} other {# poze}}", + "map_assets_in_bounds": "{count, plural, =0 {Nu există fotografii în această zonă} one {# fotografie} other {# fotografii}}", "map_cannot_get_user_location": "Nu se poate obține locația utilizatorului", "map_location_dialog_yes": "Da", "map_location_picker_page_use_location": "Folosește această locație", @@ -1287,6 +1323,7 @@ "mark_as_read": "Marchează ca citit", "marked_all_as_read": "Marcate toate ca citite", "matches": "Corespunde", + "matching_assets": "Resurse similare", "media_type": "Tip media", "memories": "Amintiri", "memories_all_caught_up": "Sunteți la zi", @@ -1304,7 +1341,8 @@ "merge_people_successfully": "Persoane îmbinate cu succes", "merged_people_count": "Imbinate {count, plural, one {# persoană} other {# persoane}}", "minimize": "Minimizare", - "minute": "Minute", + "minute": "Minut", + "minutes": "Minute", "missing": "Lipsă", "model": "Model", "month": "Lună", @@ -1324,6 +1362,10 @@ "my_albums": "Albumele mele", "name": "Nume", "name_or_nickname": "Nume sau poreclǎ", + "network_requirement_photos_upload": "Utilizați datele mobile pentru a face copii de rezervă ale fotografiilor", + "network_requirement_videos_upload": "Utilizați datele mobile pentru a face copii de rezervă ale videoclipurilor", + "network_requirements": "Cerințe privind rețeaua", + "network_requirements_updated": "Cerințele rețelei s-au modificat, resetarea cozii copiei de rezervă", "networking_settings": "Rețele", "networking_subtitle": "Gestionați setările endpoint-ului serverului", "never": "Niciodată", @@ -1333,6 +1375,7 @@ "new_person": "Persoanǎ nouǎ", "new_pin_code": "Cod PIN nou", "new_pin_code_subtitle": "Aceasta este prima dată când accesați folderul blocat. Creați un cod PIN pentru a accesa în siguranță această pagină", + "new_timeline": "Noua cronologie", "new_user_created": "Utilizator nou creat", "new_version_available": "VERSIUNE NOUĂ DISPONIBILĂ", "newest_first": "Cel mai nou primul", @@ -1346,20 +1389,25 @@ "no_assets_message": "CLICK PENTRU A ÎNCĂRCA PRIMA TA FOTOGRAFIE", "no_assets_to_show": "Nicio resursă de afișat", "no_cast_devices_found": "Nu s-au găsit dispozitive de difuzare", + "no_checksum_local": "Nu există checksum – nu se pot prelua resursele locale", + "no_checksum_remote": "Nu există checksum – nu se pot prelua resursele la distanță", "no_duplicates_found": "Nu au fost găsite duplicate.", "no_exif_info_available": "Nu există informații exif disponibile", "no_explore_results_message": "Încarcați mai multe fotografii pentru a vă explora colecția.", "no_favorites_message": "Adăugați favorite pentru a găsi rapid cele mai bune fotografii și videoclipuri", "no_libraries_message": "Creați o bibliotecă externă pentru a vă vizualiza fotografiile și videoclipurile", + "no_local_assets_found": "Nicio resursă locală găsită cu acest checksum", "no_locked_photos_message": "Fotografiile și videoclipurile din folderul blocat sunt ascunse și nu vor apărea atunci când răsfoiți sau căutați în bibliotecă.", "no_name": "Fără Nume", "no_notifications": "Nicio notificare", "no_people_found": "Nu au fost găsite persoane potrivite căutării", "no_places": "Nu există locuri", + "no_remote_assets_found": "Nicio resursă de la distanță găsită cu acest checksum", "no_results": "Fără rezultate", "no_results_description": "Încercați un sinonim sau un cuvânt cheie mai general", "no_shared_albums_message": "Creați un album pentru a partaja fotografii și videoclipuri cu persoanele din rețeaua dvs", "no_uploads_in_progress": "Nicio încărcare în curs", + "not_available": "N/A", "not_in_any_album": "Nu există în niciun album", "not_selected": "Neselectat", "note_apply_storage_label_to_previously_uploaded assets": "Notă: Pentru a aplica eticheta de stocare la resursele încărcate anterior, rulați", @@ -1375,6 +1423,7 @@ "oauth": "OAuth", "official_immich_resources": "Resurse Oficiale Immich", "offline": "Offline", + "offset": "Decalaj", "ok": "Bine", "oldest_first": "Cel mai vechi mai întâi", "on_this_device": "Pe acest dispozitiv", @@ -1393,6 +1442,8 @@ "open_the_search_filters": "Deschideți filtrele de căutare", "options": "Opțiuni", "or": "sau", + "organize_into_albums": "Organizați în albume", + "organize_into_albums_description": "Pune fotografiile existente în albume folosind setările curente de sincronizare", "organize_your_library": "Organizează-ți biblioteca", "original": "original", "other": "Alte", @@ -1452,6 +1503,9 @@ "permission_onboarding_permission_limited": "Permisiune limitată. Pentru a permite Immich să facă copii de siguranță și să gestioneze întreaga colecție de galerii, acordă permisiuni pentru fotografii și videoclipuri în Setări.", "permission_onboarding_request": "Immich necesită permisiunea de a vizualiza fotografiile și videoclipurile tale.", "person": "Persoanǎ", + "person_age_months": "{months, plural, one {# lună} other {# luni}}", + "person_age_year_months": "1 an, {months, plural, one {# lună} other {# luni}}", + "person_age_years": "{years, plural, other {# years}} vechime", "person_birthdate": "Născut pe {date}", "person_hidden": "{name}{hidden, select, true { (ascuns)} other {}}", "photo_shared_all_users": "Se pare că ți-ai partajat fotografiile tuturor utilizatorilor sau că nu ai niciun utilizator căruia să le distribui.", @@ -1475,6 +1529,7 @@ "port": "Port", "preferences_settings_subtitle": "Gestionați preferințele aplicației", "preferences_settings_title": "Preferințe", + "preparing": "Se prepară", "preset": "Presetat", "preview": "Previzualizare", "previous": "Anterior", @@ -1491,6 +1546,7 @@ "profile_drawer_client_out_of_date_minor": "Aplicația nu folosește ultima versiune. Te rugăm să actualizezi la ultima versiune minoră.", "profile_drawer_client_server_up_to_date": "Aplicația client și server-ul sunt actualizate", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Mod doar citire activat. Ține apăsat pe pictograma avatarului utilizatorului pentru a ieși.", "profile_drawer_server_out_of_date_major": "Server-ul nu folosește ultima versiune. Te rugăm să actualizezi la ultima versiune majoră.", "profile_drawer_server_out_of_date_minor": "Server-ul nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune minoră.", "profile_image_of_user": "Imagine de profil a lui {user}", @@ -1529,6 +1585,7 @@ "purchase_server_description_2": "Statutul de suporter", "purchase_server_title": "Server", "purchase_settings_server_activated": "Cheia de produs a serverului este gestionată de administrator", + "query_asset_id": "Interoghează ID-ul resursei", "queue_status": "Se pun în coadă {count}/{total}", "rating": "Evaluare cu stele", "rating_clear": "Anulați evaluarea", @@ -1536,6 +1593,9 @@ "rating_description": "Afișați evaluarea EXIF în panoul de informații", "reaction_options": "Opțiuni de reacție", "read_changelog": "Citiți Jurnalul de Modificări", + "readonly_mode_disabled": "Modul doar citire dezactivat", + "readonly_mode_enabled": "Modul doar citire activat", + "ready_for_upload": "Pregătit pentru încărcare", "reassign": "Reatribuiți", "reassigned_assets_to_existing_person": "Re-alocat {count, plural, one {# resursă} other {# resurse}} to {name, select, null {unei persoane existente} other {{name}}}", "reassigned_assets_to_new_person": "Re-alocat {count, plural, one {# resursă} other {# resurse}} unei noi persoane", @@ -1560,6 +1620,7 @@ "regenerating_thumbnails": "Se regenerează miniaturile", "remote": "De la distanță", "remote_assets": "Elemente la distanță", + "remote_media_summary": "Rezumat media de la distanță", "remove": "Eliminați", "remove_assets_album_confirmation": "Sigur doriți să eliminați {count, plural, one {# resursă} other {# resurse}} din album?", "remove_assets_shared_link_confirmation": "Sigur doriți să eliminați {count, plural, one {# resursă} other {# resurse}} din acest link comun?", @@ -1597,6 +1658,9 @@ "reset_password": "Resetare parolă", "reset_people_visibility": "Resetați vizibilitatea persoanelor", "reset_pin_code": "Resetare cod PIN", + "reset_pin_code_description": "Dacă ți-ai uitat codul PIN, poți contacta administratorul serverului pentru a-l reseta", + "reset_pin_code_success": "Codul PIN a fost resetat cu succes", + "reset_pin_code_with_password": "Puteți reseta oricând codul PIN cu ajutorul parolei", "reset_sqlite": "Resetare bază de date SQLite", "reset_sqlite_confirmation": "Sigur doriți să resetați baza de date SQLite? Va trebui să vă deconectați și să vă conectați din nou pentru a resincroniza datele", "reset_sqlite_success": "Resetarea cu succes a bazei de date SQLite", @@ -1609,8 +1673,10 @@ "restore_user": "Restabiliți utilizatorul", "restored_asset": "Resursă restaurată", "resume": "Reluare", + "resume_paused_jobs": "Reluați {count, plural, one {# paused job} other {# paused jobs}}", "retry_upload": "Reîncercați încărcarea", "review_duplicates": "Examinați duplicatele", + "review_large_files": "Revizuirea fișierelor mari", "role": "Rol", "role_editor": "Editor", "role_viewer": "Vizualizator", @@ -1622,7 +1688,7 @@ "saved_settings": "Setări salvate", "say_something": "Spuneți ceva", "scaffold_body_error_occurred": "A apărut o eroare", - "scan_all_libraries": "Scanați Toate Bibliotecile", + "scan_all_libraries": "Scanați toate bibliotecile", "scan_library": "Scanare", "scan_settings": "Setări Scanare", "scanning_for_album": "Se scanează după album...", @@ -1635,8 +1701,8 @@ "search_by_filename_example": "i.e. IMG_1234.JPG sau PNG", "search_camera_make": "Se caută marca camerei...", "search_camera_model": "Se caută modelul camerei...", - "search_city": "Se caută orașul...", - "search_country": "Se caută țara...", + "search_city": "Caută în orașul...", + "search_country": "Caută în țara...", "search_filter_apply": "Aplicați filtrul", "search_filter_camera_title": "Selectați tipul de cameră", "search_filter_date": "Dată", @@ -1651,7 +1717,7 @@ "search_filter_media_type_title": "Selectați tipul media", "search_filter_people_title": "Selectați persoane", "search_for": "Căutare după", - "search_for_existing_person": "Se caută o persoană existentă", + "search_for_existing_person": "Caută o persoană existentă", "search_no_more_result": "Nu mai există rezultate", "search_no_people": "Fără persoane", "search_no_people_named": "Nicio persoană numită \"{name}\"", @@ -1673,7 +1739,7 @@ "search_rating": "Caută după notă...", "search_result_page_new_search_hint": "Căutare nouă", "search_settings": "Setări de căutare", - "search_state": "Starea căutării...", + "search_state": "Caută în Stat/Județ...", "search_suggestion_list_smart_search_hint_1": "Căutarea inteligentă este activată în mod implicit, pentru a căuta metadata, utilizează sintaxa ", "search_suggestion_list_smart_search_hint_2": "m:termen-de-căutare", "search_tags": "Căutați etichete...", @@ -1701,6 +1767,7 @@ "select_user_for_sharing_page_err_album": "Creare album eșuată", "selected": "Selectat", "selected_count": "{count, plural, other {# selectat}}", + "selected_gps_coordinates": "Coordonate GPS selectate", "send_message": "Trimiteți mesaj", "send_welcome_email": "Trimiteți email de bun venit", "server_endpoint": "Endpoint server", @@ -1709,7 +1776,7 @@ "server_offline": "Serverul este offline", "server_online": "Server online", "server_privacy": "Confidențialitatea serverului", - "server_stats": "Statistici Server", + "server_stats": "Statistici server", "server_version": "Versiune Server", "set": "Setați", "set_as_album_cover": "Setați ca și copertă a albumului", @@ -1768,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "Copiat în clipboard", "shared_link_clipboard_text": "Link: {link}\nParolă: {password}", "shared_link_create_error": "Eroare în timpul creării linkului de distribuire", + "shared_link_custom_url_description": "Accesează acest link partajat cu un URL personalizat", "shared_link_edit_description_hint": "Introdu descrierea distribuirii", "shared_link_edit_expire_after_option_day": "1 zi", "shared_link_edit_expire_after_option_days": "{count} zile", @@ -1793,6 +1861,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Administrează link-urile distribuite", "shared_link_options": "Opțiuni de link partajat", + "shared_link_password_description": "Solicită o parolă pentru a accesa acest link partajat", "shared_links": "Link-uri distribuite", "shared_links_description": "Partajare imagini și clipuri printr-un link", "shared_photos_and_videos_count": "{assetCount, plural, other {# fotografii și videoclipuri partajate.}}", @@ -1827,6 +1896,7 @@ "show_slideshow_transition": "Afișați tranziția de prezentare", "show_supporter_badge": "Insigna suporterului", "show_supporter_badge_description": "Arată o insignă de suporter", + "show_text_search_menu": "Afișează meniul de căutare text", "shuffle": "Amestecați", "sidebar": "Bara laterală", "sidebar_display_description": "Afișați un link către vizualizare în bara laterală", @@ -1842,6 +1912,7 @@ "sort_created": "Data creării", "sort_items": "Numărul de articole", "sort_modified": "Data modificării", + "sort_newest": "Cea mai nouă fotografie", "sort_oldest": "Cea mai veche fotografie", "sort_people_by_similarity": "Sortează oameni după asemanare", "sort_recent": "Cea mai recentă fotografie", @@ -1856,7 +1927,8 @@ "stacktrace": "Urmă stivă", "start": "Început", "start_date": "Data de începere", - "state": "Situaţie", + "start_date_before_end_date": "Data de început trebuie să fie înainte de data de sfârșit", + "state": "Stat/Județ", "status": "Stare", "stop_casting": "Opriți difuzarea", "stop_motion_photo": "Opriți Fotografia in Mișcare", @@ -1880,6 +1952,8 @@ "sync_albums_manual_subtitle": "Sincronizează toate videoclipurile și fotografiile încărcate cu albumele de rezervă selectate", "sync_local": "Sincronizare locală", "sync_remote": "Sincronizare la distanță", + "sync_status": "Status-ul sincronizării", + "sync_status_subtitle": "Vizualizează și gestionează sistemul de sincronizare", "sync_upload_album_setting_subtitle": "Creează și încarcă fotografiile și videoclipurile tale în albumele selectate de pe Immich", "tag": "Etichetă", "tag_assets": "Eticheta resurselor", @@ -1917,7 +1991,9 @@ "to_change_password": "Schimbaţi parola", "to_favorite": "Favorit", "to_login": "Conectare", + "to_multi_select": "pentru selecție multiplă", "to_parent": "Du-te la părinte", + "to_select": "a selecta", "to_trash": "Coș de gunoi", "toggle_settings": "Activați setările", "total": "Total", @@ -1937,6 +2013,7 @@ "trash_page_select_assets_btn": "Selectează resurse", "trash_page_title": "Coș ({count})", "trashed_items_will_be_permanently_deleted_after": "Elementele din coșul de gunoi vor fi șterse definitiv după {days, plural, one {# zi} other {# zile}}.", + "troubleshoot": "Depanați", "type": "Tip", "unable_to_change_pin_code": "Nu se poate schimba codul PIN", "unable_to_setup_pin_code": "Nu se poate configura codul PIN", @@ -1963,10 +2040,11 @@ "unselect_all_duplicates": "Deselectați toate duplicatele", "unselect_all_in": "Deselectați toate din {group}", "unstack": "Dezasamblați", - "unstack_action_prompt": "{count} unstacked", + "unstack_action_prompt": "{count} neîmpachetate", "unstacked_assets_count": "Nestivuit {count, plural, one {# resursă} other {# resurse}}", "untagged": "Neetichetat", "up_next": "Mai departe", + "update_location_action_prompt": "Actualizează locația pentru {count} resurse selectate cu:", "updated_at": "Actualizat", "updated_password": "Parolă actualizată", "upload": "Încărcați", @@ -2033,13 +2111,14 @@ "view_next_asset": "Vizualizați următoarea resursă", "view_previous_asset": "Vizualizați resursa anterioară", "view_qr_code": "Vezi cod QR", + "view_similar_photos": "Vizualizați poze similare", "view_stack": "Vizualizați Stiva", "view_user": "Vizualizare utilizator", "viewer_remove_from_stack": "Șterge din grup", "viewer_stack_use_as_main_asset": "Folosește ca resursă principală", "viewer_unstack": "Anulează grup", "visibility_changed": "Vizibilitatea schimbată pentru {count, plural, one {# persoană} other {# persoane}}", - "waiting": "Așteptați", + "waiting": "În așteptare", "warning": "Avertisment", "week": "Sǎptǎmânǎ", "welcome": "Bun venit", @@ -2051,5 +2130,6 @@ "yes": "Da", "you_dont_have_any_shared_links": "Nu aveți linkuri partajate", "your_wifi_name": "Numele rețelei tale WiFi", - "zoom_image": "Măriți Imaginea" + "zoom_image": "Măriți Imaginea", + "zoom_to_bounds": "Mărește la margini" } diff --git a/i18n/ru.json b/i18n/ru.json index 60c271af16..17d6c804a2 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -28,10 +28,12 @@ "add_to_album": "Добавить в альбом", "add_to_album_bottom_sheet_added": "Добавлено в альбом {album}", "add_to_album_bottom_sheet_already_exists": "Уже в альбоме {album}", + "add_to_album_bottom_sheet_some_local_assets": "Некоторые объекты не добавлены в альбом, поскольку еще не загружены на сервер", "add_to_album_toggle": "Переключить выделение для альбома {album}", "add_to_albums": "Добавить в альбомы", "add_to_albums_count": "Добавить в альбомы ({count})", "add_to_shared_album": "Добавить в общий альбом", + "add_upload_to_stack": "Загрузить и добавить в группу", "add_url": "Добавить URL", "added_to_archive": "Добавлено в архив", "added_to_favorites": "Добавлено в избранное", @@ -39,7 +41,7 @@ "admin": { "add_exclusion_pattern_description": "Добавьте шаблоны исключений. Поддерживаются символы подстановки *, ** и ?. Чтобы игнорировать все файлы в любом каталоге с именем \"Raw\", укажите \"**/Raw/**\". Чтобы игнорировать все файлы, заканчивающиеся на \".tif\", используйте \"**/*.tif\". Чтобы игнорировать путь целиком, укажите \"/path/to/ignore/**\".", "admin_user": "Администратор", - "asset_offline_description": "Этот файл внешней библиотеки не был найден на диске и был перемещён в корзину. Если файл был перемещён внутри библиотеки, проверьте временную шкалу, чтобы найти новый соответствующий ресурс. Чтобы восстановить файл, убедитесь, что путь ниже доступен для Immich и выполните сканирование библиотеки.", + "asset_offline_description": "Этот объект из внешней библиотеки не был обнаружен на диске и поэтому перемещён в корзину. Если файл объекта был перемещён внутри библиотеки, проверьте временную шкалу, чтобы найти новый соответствующий объект. Чтобы восстановить файл, убедитесь, что следующий путь доступен для Immich, и выполните сканирование библиотеки.", "authentication_settings": "Настройки аутентификации", "authentication_settings_description": "Управление паролями, OAuth и другими настройками аутентификации", "authentication_settings_disable_all": "Вы уверены, что хотите отключить все методы входа? Вход будет полностью отключен.", @@ -77,7 +79,7 @@ "face_detection_description": "Обнаруживает лица на объектах с использованием машинного обучения. Для видео анализируется только миниатюра. Кнопка \"Обновить\" запускает повторную обработку всех объектов. \"Сброс\" — дополнительно удаляет все имеющиеся данные о лицах. \"Отсутствующие\" — ставит в очередь объекты, которые ещё не были обработаны. Обнаруженные лица помещаются в очередь для задачи Распознавание лиц и последующей их привязки к существующим или новым людям.", "facial_recognition_job_description": "Группирует и назначает обнаруженные лица людям. Выполняется после завершения задачи Обнаружение лиц. Кнопка \"Сброс\" (пере)назначает все лица. \"Отсутствующие\" — добавляет в очередь обработки лица, не привязанные к человеку.", "failed_job_command": "Команда {command} не выполнена для задачи: {job}", - "force_delete_user_warning": "ПРЕДУПРЕЖДЕНИЕ: Это приведет к немедленному удалению пользователя и его ресурсов. Это действие невозможно отменить, и файлы не могут быть восстановлены.", + "force_delete_user_warning": "ПРЕДУПРЕЖДЕНИЕ: Это приведет к немедленному удалению пользователя и всех его объектов. Это действие невозможно отменить, файлы не смогут быть восстановлены.", "image_format": "Формат", "image_format_description": "WebP создает файлы меньшего размера, чем JPEG, но кодирует медленнее.", "image_fullsize_description": "Полноразмерное изображение без метаданных, используется при увеличении", @@ -275,7 +277,7 @@ "storage_template_hash_verification_enabled_description": "Включает проверку хеша, не отключайте её, если не уверены в последствиях", "storage_template_migration": "Применение шаблона хранилища", "storage_template_migration_description": "Применяет текущий {template} к ранее загруженным объектам", - "storage_template_migration_info": "Расширения файлов всегда будут сохраняться в нижнем регистре. Изменения в шаблоне будут применяться только к новым ресурсам. Чтобы применить шаблон к ранее загруженным ресурсам, запустите {job}.", + "storage_template_migration_info": "Расширения файлов всегда будут сохраняться в нижнем регистре. Изменения в шаблоне будут применяться только к новым объектам. Чтобы применить шаблон к ранее загруженным объектам, запустите {job}.", "storage_template_migration_job": "Задача по применению шаблона хранилища", "storage_template_more_details": "Для получения дополнительной информации об этой функции обратитесь к разделам документации Шаблон хранилища и Структура хранения файлов", "storage_template_onboarding_description_v2": "Если эта функция включена, она автоматически организует файлы на основе заданного пользователем шаблона. Для получения дополнительной информации обратитесь к документации.", @@ -425,7 +427,7 @@ "album_leave": "Покинуть альбом?", "album_leave_confirmation": "Вы уверены, что хотите покинуть {album}?", "album_name": "Название альбома", - "album_options": "Действия с альбомом", + "album_options": "Параметры альбома", "album_remove_user": "Удалить пользователя?", "album_remove_user_confirmation": "Вы уверены, что хотите удалить пользователя {user}?", "album_search_not_found": "Не найдено альбомов по вашему запросу", @@ -506,8 +508,8 @@ "asset_troubleshoot": "Данные для диагностики", "asset_uploaded": "Загружено", "asset_uploading": "Загрузка…", - "asset_viewer_settings_subtitle": "Настройка параметров отображения", - "asset_viewer_settings_title": "Просмотр изображений", + "asset_viewer_settings_subtitle": "Параметры отображения", + "asset_viewer_settings_title": "Просмотр объектов", "assets": "Объекты", "assets_added_count": "{count, plural, one {Добавлен # объект} many {Добавлено # объектов} other {Добавлено # объекта}}", "assets_added_to_album_count": "В альбом {count, plural, one {добавлен # объект} many {добавлено # объектов} other {добавлено # объекта}}", @@ -598,6 +600,7 @@ "backup_controller_page_turn_on": "Включить", "backup_controller_page_uploading_file_info": "Информация о загружаемом файле", "backup_err_only_album": "Невозможно удалить единственный альбом", + "backup_error_sync_failed": "Сбой синхронизации. Невозможно выполнить резервное копирование.", "backup_info_card_assets": "объектов", "backup_manual_cancelled": "Отменено", "backup_manual_in_progress": "Загрузка в процессе. Попробуйте позже", @@ -703,7 +706,7 @@ "confirm": "Подтвердить", "confirm_admin_password": "Подтвердите пароль администратора", "confirm_delete_face": "Удалить лицо человека {name} из этого объекта?", - "confirm_delete_shared_link": "Вы уверены, что хотите удалить эту публичную ссылку?", + "confirm_delete_shared_link": "Вы действительно хотите удалить эту публичную ссылку?", "confirm_keep_this_delete_others": "Все остальные объекты в группе будут удалены, кроме этого объекта. Вы уверены, что хотите продолжить?", "confirm_new_pin_code": "Подтвердите новый PIN-код", "confirm_password": "Подтвердите пароль", @@ -718,19 +721,19 @@ "control_bottom_app_bar_delete_from_immich": "Удалить из Immich", "control_bottom_app_bar_delete_from_local": "Удалить с устройства", "control_bottom_app_bar_edit_location": "Изменить место", - "control_bottom_app_bar_edit_time": "Изменить дату и время", - "control_bottom_app_bar_share_link": "Поделиться ссылкой", + "control_bottom_app_bar_edit_time": "Изменить дату", + "control_bottom_app_bar_share_link": "Создать ссылку", "control_bottom_app_bar_share_to": "Поделиться с", "control_bottom_app_bar_trash_from_immich": "В корзину", "copied_image_to_clipboard": "Изображение скопировано в буфер обмена.", "copied_to_clipboard": "Скопировано в буфер обмена!", - "copy_error": "Ошибка копирования", + "copy_error": "Скопировать ошибку", "copy_file_path": "Копировать путь к файлу", "copy_image": "Копировать", "copy_link": "Копировать ссылку", "copy_link_to_clipboard": "Скопировать ссылку в буфер обмена", "copy_password": "Скопировать пароль", - "copy_to_clipboard": "Скопировать настройки в буфер обмена", + "copy_to_clipboard": "Скопировать в буфер обмена", "country": "Страна", "cover": "Обложка", "covers": "Обложки", @@ -740,7 +743,7 @@ "create_library": "Создать библиотеку", "create_link": "Создать ссылку", "create_link_to_share": "Создать ссылку общего доступа", - "create_link_to_share_description": "Разрешить всем, у кого есть ссылка, просмотреть выбранные фотографии", + "create_link_to_share_description": "Разрешить всем, у кого есть ссылка, просматривать выбранные фотографии", "create_new": "СОЗДАТЬ НОВЫЙ", "create_new_person": "Добавить нового человека", "create_new_person_hint": "Назначить выбранные объекты на нового человека", @@ -765,7 +768,7 @@ "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Тёмная", - "dark_theme": "Тёмная тема", + "dark_theme": "Включить/выключить тёмную тему", "date_after": "Дата после", "date_and_time": "Дата и Время", "date_before": "Дата до", @@ -911,22 +914,22 @@ "error_title": "Ошибка - Что-то пошло не так", "errors": { "cannot_navigate_next_asset": "Не удалось перейти к следующему объекту", - "cannot_navigate_previous_asset": "Не удалось перейти к предыдущему ресурсу", + "cannot_navigate_previous_asset": "Не удалось перейти к предыдущему объекту", "cant_apply_changes": "Не удается применить изменения", "cant_change_activity": "Не удается {enabled, select, true {отключить} other {включить}} активность", - "cant_change_asset_favorite": "Не удалось изменить статус \"избранное\" для ресурса", + "cant_change_asset_favorite": "Не удалось изменить статус \"Избранное\" для объекта", "cant_change_metadata_assets_count": "Не удалось изменить метаданные у {count, plural, one {# объекта} other {# объектов}}", "cant_get_faces": "Не удается получить лица", "cant_get_number_of_comments": "Не удается получить количество комментариев", "cant_search_people": "Не удается выполнить поиск людей", "cant_search_places": "Не удается выполнить поиск мест", - "error_adding_assets_to_album": "Ошибка при добавлении ресурсов в альбом", + "error_adding_assets_to_album": "Ошибка при добавлении объектов в альбом", "error_adding_users_to_album": "Ошибка при добавлении пользователей в альбом", "error_deleting_shared_user": "Ошибка при удалении пользователя с общим доступом", "error_downloading": "Ошибка при загрузке {filename}", "error_hiding_buy_button": "Ошибка скрытия кнопки", - "error_removing_assets_from_album": "Ошибка при удалении ресурсов из альбома, проверьте консоль для получения дополнительной информации", - "error_selecting_all_assets": "Ошибка при выборе всех ресурсов", + "error_removing_assets_from_album": "Ошибка при удалении объектов из альбома, проверьте консоль для получения дополнительной информации", + "error_selecting_all_assets": "Ошибка при выборе всех объектов", "exclusion_pattern_already_exists": "Такая модель исключения уже существует.", "failed_to_create_album": "Не удалось создать альбом", "failed_to_create_shared_link": "Не удалось создать публичную ссылку", @@ -1036,6 +1039,7 @@ "exif_bottom_sheet_description_error": "Не удалось обновить описание", "exif_bottom_sheet_details": "ПОДРОБНОСТИ", "exif_bottom_sheet_location": "МЕСТО", + "exif_bottom_sheet_no_description": "Нет описания", "exif_bottom_sheet_people": "ЛЮДИ", "exif_bottom_sheet_person_add_person": "Добавить имя", "exit_slideshow": "Выйти из слайд-шоу", @@ -1347,9 +1351,9 @@ "monthly_title_text_date_format": "MMMM y", "more": "Дополнительные действия", "move": "Переместить", - "move_off_locked_folder": "Переместить из личной папки", + "move_off_locked_folder": "Убрать из личной папки", "move_to_lock_folder_action_prompt": "Объекты добавлены в личную папку ({count} шт.)", - "move_to_locked_folder": "Переместить в личную папку", + "move_to_locked_folder": "В личную папку", "move_to_locked_folder_confirmation": "Эти фото и видео будут удалены из всех альбомов и будут доступны только в личной папке", "moved_to_archive": "{count, plural, one {# объект перемещён} many {# объектов перемещены} other {# объекта перемещены}} в архив", "moved_to_library": "{count, plural, one {# объект перемещён} many {# объектов перемещены} other {# объекта перемещены}} в библиотеку", @@ -1408,7 +1412,7 @@ "not_available": "Нет данных", "not_in_any_album": "Ни в одном альбоме", "not_selected": "Не выбрано", - "note_apply_storage_label_to_previously_uploaded assets": "Примечание: Чтобы применить метку хранилища к ранее загруженным ресурсам, запустите", + "note_apply_storage_label_to_previously_uploaded assets": "Примечание: Чтобы применить метку хранилища к ранее загруженным объектам, запустите", "notes": "Примечание", "nothing_here_yet": "Здесь пока ничего нет", "notification_permission_dialog_content": "Чтобы включить уведомления, перейдите в «Настройки» и выберите «Разрешить».", @@ -1539,7 +1543,7 @@ "primary": "Главное", "privacy": "Конфиденциальность", "profile": "Профиль", - "profile_drawer_app_logs": "Журнал", + "profile_drawer_app_logs": "Журнал событий", "profile_drawer_client_out_of_date_major": "Версия мобильного приложения устарела. Пожалуйста, обновите его.", "profile_drawer_client_out_of_date_minor": "Версия мобильного приложения устарела. Пожалуйста, обновите его.", "profile_drawer_client_server_up_to_date": "Клиент и сервер обновлены", @@ -1590,14 +1594,14 @@ "rating_count": "{count, plural, one {# звезда} many {# звезд} other {# звезды}}", "rating_description": "Система оценки объектов в панели информации", "reaction_options": "Действия с отметкой", - "read_changelog": "Прочитать список изменений", + "read_changelog": "История релизов", "readonly_mode_disabled": "Режим «только просмотр» отключён", "readonly_mode_enabled": "Режим «только просмотр» включён", "ready_for_upload": "Готово к загрузке", "reassign": "Переназначить", "reassigned_assets_to_existing_person": "Лица на {count, plural, one {# объекте} other {# объектах}} переназначены на {name, select, null {другого человека} other {человека с именем {name}}}", "reassigned_assets_to_new_person": "Лица на {count, plural, one {# объекте} other {# объектах}} переназначены на нового человека", - "reassing_hint": "Назначить выбранные ресурсы указанному человеку", + "reassing_hint": "Назначить выбранные объекты указанному человеку", "recent": "Недавние", "recent-albums": "Недавние альбомы", "recent_searches": "Недавние поисковые запросы", @@ -1630,14 +1634,14 @@ "remove_from_favorites": "Удалить из избранного", "remove_from_lock_folder_action_prompt": "Объекты удалены из личной папки ({count} шт.)", "remove_from_locked_folder": "Удалить из личной папки", - "remove_from_locked_folder_confirmation": "Вы действительно хотите переместить эти фото и видео из личной папки? Они станут доступны в вашей библиотеке.", + "remove_from_locked_folder_confirmation": "Вы хотите убрать выделенные объекты из личной папки? Они снова станут доступны в вашей библиотеке.", "remove_from_shared_link": "Удалить из публичной ссылки", "remove_memory": "Удалить воспоминание", "remove_photo_from_memory": "Удалить фото из воспоминания", "remove_tag": "Удалить тег", "remove_url": "Удалить URL", "remove_user": "Удалить пользователя", - "removed_api_key": "Удалён API ключ {name}", + "removed_api_key": "API ключ \"{name}\" удалён", "removed_from_archive": "Удален из архива", "removed_from_favorites": "Удалено из избранного", "removed_from_favorites_count": "{count, plural, one {# объект удалён} many {# объектов удалено} other {# объекта удалено}} из избранного", @@ -1777,7 +1781,7 @@ "server_stats": "Статистика сервера", "server_version": "Версия сервера", "set": "Установить", - "set_as_album_cover": "Установить в качестве обложки альбома", + "set_as_album_cover": "Установить обложкой альбома", "set_as_featured_photo": "Установить как основное фото", "set_as_profile_picture": "Установить как фото профиля", "set_date_of_birth": "Установить дату рождения", @@ -1791,7 +1795,7 @@ "setting_image_viewer_preview_title": "Загружать уменьшенное изображение", "setting_image_viewer_title": "Изображения", "setting_languages_apply": "Применить", - "setting_languages_subtitle": "Изменить язык приложения", + "setting_languages_subtitle": "Изменение языка приложения", "setting_notifications_notify_failures_grace_period": "Уведомлять об ошибках фонового резервного копирования: {duration}", "setting_notifications_notify_hours": "{count} ч.", "setting_notifications_notify_immediately": "немедленно", @@ -1800,7 +1804,7 @@ "setting_notifications_notify_seconds": "{count} сек.", "setting_notifications_single_progress_subtitle": "Подробная информация о ходе загрузки для каждого объекта", "setting_notifications_single_progress_title": "Показать ход выполнения фонового резервного копирования", - "setting_notifications_subtitle": "Настройка параметров уведомлений", + "setting_notifications_subtitle": "Параметры уведомлений", "setting_notifications_total_progress_subtitle": "Общий прогресс загрузки (выполнено/всего объектов)", "setting_notifications_total_progress_title": "Показать общий прогресс фонового резервного копирования", "setting_video_viewer_looping_title": "Циклическое воспроизведение", @@ -1833,17 +1837,17 @@ "shared_link_clipboard_copied_massage": "Скопировано в буфер обмена", "shared_link_clipboard_text": "Ссылка: {link}\nПароль: {password}", "shared_link_create_error": "Ошибка при создании публичной ссылки", - "shared_link_custom_url_description": "Доступ к этой общей ссылке с помощью заданного пользователем URL-адреса", - "shared_link_edit_description_hint": "Введите описание публичного доступа", + "shared_link_custom_url_description": "Пользовательский URL-адрес общего доступа", + "shared_link_edit_description_hint": "Введите описание", "shared_link_edit_expire_after_option_day": "1 день", "shared_link_edit_expire_after_option_days": "{count} дней", "shared_link_edit_expire_after_option_hour": "1 час", "shared_link_edit_expire_after_option_hours": "{count} часов", "shared_link_edit_expire_after_option_minute": "1 минуту", "shared_link_edit_expire_after_option_minutes": "{count} минут", - "shared_link_edit_expire_after_option_months": "{count} месяцев", + "shared_link_edit_expire_after_option_months": "{count} месяца", "shared_link_edit_expire_after_option_year": "{count} лет", - "shared_link_edit_password_hint": "Введите пароль для публичного доступа", + "shared_link_edit_password_hint": "Защитите доступ паролем", "shared_link_edit_submit_button": "Обновить ссылку", "shared_link_error_server_url_fetch": "Невозможно запросить URL с сервера", "shared_link_expires_day": "Истечёт через {count} день", @@ -1859,7 +1863,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Управление публичными ссылками", "shared_link_options": "Действия со ссылкой", - "shared_link_password_description": "Требовать пароль для доступа к этой общей ссылке", + "shared_link_password_description": "Требовать пароль для доступа к объектам", "shared_links": "Публичные ссылки", "shared_links_description": "Делитесь фотографиями и видео по ссылке", "shared_photos_and_videos_count": "{assetCount, plural, other {# фото и видео.}}", @@ -1874,7 +1878,7 @@ "sharing_silver_appbar_create_shared_album": "Создать общий альбом", "sharing_silver_appbar_share_partner": "Поделиться с партнёром", "shift_to_permanent_delete": "нажмите ⇧ чтобы удалить объект навсегда", - "show_album_options": "Показать параметры альбома", + "show_album_options": "Действия с альбомом", "show_albums": "Показать альбомы", "show_all_people": "Показать всех людей", "show_and_hide_people": "Показать и скрыть людей", @@ -1967,7 +1971,7 @@ "theme": "Тема", "theme_selection": "Выбор темы", "theme_selection_description": "Автоматически устанавливать светлую или тёмную тему в зависимости от настроек вашего браузера", - "theme_setting_asset_list_storage_indicator_title": "Показать индикатор хранилища на плитках объектов", + "theme_setting_asset_list_storage_indicator_title": "Отображать индикатор хранилища на плитках объектов", "theme_setting_asset_list_tiles_per_row_title": "Количество объектов в строке ({count})", "theme_setting_colorful_interface_subtitle": "Добавить оттенок к фону.", "theme_setting_colorful_interface_title": "Цвет фона", diff --git a/i18n/sk.json b/i18n/sk.json index 6c009deea1..97f4ca07f4 100644 --- a/i18n/sk.json +++ b/i18n/sk.json @@ -28,10 +28,12 @@ "add_to_album": "Pridať do albumu", "add_to_album_bottom_sheet_added": "Pridané do {album}", "add_to_album_bottom_sheet_already_exists": "Už je v {album}", + "add_to_album_bottom_sheet_some_local_assets": "Niektoré lokálne súbory nebolo možné pridať do albumu", "add_to_album_toggle": "Prepnúť výber pre {album}", "add_to_albums": "Pridať do albumov", "add_to_albums_count": "Pridať do albumov ({count})", "add_to_shared_album": "Pridať do zdieľaného albumu", + "add_upload_to_stack": "Pridať nahraté do zoskupených", "add_url": "Pridať URL", "added_to_archive": "Pridané do archívu", "added_to_favorites": "Pridané do obľúbených", @@ -363,7 +365,7 @@ "trash_enabled_description": "Povoliť funkcie koša", "trash_number_of_days": "Počet dní", "trash_number_of_days_description": "Počet dní, počas ktorých sa majú médiá ponechať v koši pred ich trvalým odstránením", - "trash_settings": "Kôš", + "trash_settings": "Nastavenia koša", "trash_settings_description": "Spravovať nastavenia koša", "unlink_all_oauth_accounts": "Odpojiť všetky účty OAuth", "unlink_all_oauth_accounts_description": "Nezabudnite odpojiť všetky účty OAuth pred prechodom na nového poskytovateľa.", @@ -496,9 +498,9 @@ "asset_list_layout_settings_group_by_month_day": "Mesiac + deň", "asset_list_layout_sub_title": "Rozvrhnutie", "asset_list_settings_subtitle": "Nastavenia rozloženia mriežky fotografií", - "asset_list_settings_title": "Fotografická mriežka", + "asset_list_settings_title": "Mriežka fotografií", "asset_offline": "Médium je offline", - "asset_offline_description": "Toto externý obsah sa už nenachádza na disku. Požiadajte o pomoc svojho správcu Immich.", + "asset_offline_description": "Tento externá položka sa už nenachádza na disku. Pre pomoc sa prosím obráťte na správcu systému Immich.", "asset_restored_successfully": "Položky boli úspešne obnovené", "asset_skipped": "Preskočené", "asset_skipped_in_trash": "V koši", @@ -598,6 +600,7 @@ "backup_controller_page_turn_on": "Povoliť zálohovanie na popredí", "backup_controller_page_uploading_file_info": "Nahrávaný súbor", "backup_err_only_album": "Nie je možné odstrániť jediný vybraný album", + "backup_error_sync_failed": "Synchronizácia sa nepodarila. Nie je možné spracovať zálohu.", "backup_info_card_assets": "položiek", "backup_manual_cancelled": "Zrušené", "backup_manual_in_progress": "Nahrávanie už prebieha. Vyskúšajte neskôr", @@ -701,10 +704,10 @@ "common_server_error": "Skontrolujte svoje sieťové pripojenie, uistite sa, že server je dostupný a verzie aplikácie/server sú kompatibilné.", "completed": "Dokončené", "confirm": "Potvrdiť", - "confirm_admin_password": "Potvrdiť Administrátorské Heslo", + "confirm_admin_password": "Potvrdiť heslo správcu", "confirm_delete_face": "Naozaj chcete z položky odstrániť tvár osoby {name}?", "confirm_delete_shared_link": "Ste si istý, že chcete odstrániť tento zdieľaný odkaz?", - "confirm_keep_this_delete_others": "Všetky ostatné položky v zásobníku budú odstránené okrem tejto položky. Naozaj chcete pokračovať?", + "confirm_keep_this_delete_others": "Všetky ostatné položky v zoskupení budú odstránené okrem tejto položky. Naozaj chcete pokračovať?", "confirm_new_pin_code": "Potvrdiť nový PIN kód", "confirm_password": "Potvrdiť heslo", "confirm_tag_face": "Chcete označiť túto tvár ako {name}?", @@ -940,7 +943,7 @@ "failed_to_remove_product_key": "Nepodarilo sa odstrániť produktový kľúč", "failed_to_reset_pin_code": "PIN kód sa nepodarilo obnoviť", "failed_to_stack_assets": "Nepodarilo sa zoskupiť položky", - "failed_to_unstack_assets": "Nepodarilo sa rozdeliť položky", + "failed_to_unstack_assets": "Nepodarilo sa zrušiť zoskupenie položiek", "failed_to_update_notification_status": "Nepodarilo sa aktualizovať stav oznámenia", "import_path_already_exists": "Táto cesta importu už existuje.", "incorrect_email_or_password": "Nesprávny e-mail alebo heslo", @@ -1054,7 +1057,7 @@ "export_database": "Exportovať databázu", "export_database_description": "Exportovať databázu SQLite", "extension": "Prípona", - "external": "Externý", + "external": "Externá", "external_libraries": "Externé knižnice", "external_network": "Externá sieť", "external_network_sheet_info": "Ak nie ste v preferovanej sieti Wi-Fi, aplikácia sa pripojí k serveru prostredníctvom prvej z nižšie uvedených adries URL, na ktorú sa dostane, počnúc zhora nadol", @@ -1196,8 +1199,8 @@ "jobs": "Úlohy", "keep": "Ponechať", "keep_all": "Ponechať všetko", - "keep_this_delete_others": "Ponechať toto, odstrániť ostatné", - "kept_this_deleted_others": "Ponechal túto položku a odstránil {count, plural, one {# položku} few {# položky} other {# položiek}}", + "keep_this_delete_others": "Ponechať túto, odstrániť ostatné", + "kept_this_deleted_others": "Táto položka bola ponechaná a {count, plural, one {odstránila sa # položka} few {odstránili sa # položky} other {odstránilo sa # položiek}}", "keyboard_shortcuts": "Klávesové skratky", "language": "Jazyk", "language_no_results_subtitle": "Skúste upraviť hľadaný výraz", @@ -1380,7 +1383,7 @@ "next": "Ďalej", "next_memory": "Ďalšia spomienka", "no": "Nie", - "no_albums_message": "Vytvorí album na organizovanie fotiek a videí", + "no_albums_message": "Vytvorte album na usporiadanie svojich fotiek a videí", "no_albums_with_name_yet": "Vyzerá, že zatiaľ nemáte album s týmto názvom.", "no_albums_yet": "Vyzerá, že zatiaľ nemáte žiadne albumy.", "no_archived_assets_message": "Archivujte fotografie a videá a skryte ich z vášho zobrazenia fotografií", @@ -1393,7 +1396,7 @@ "no_exif_info_available": "Nie sú dostupné exif údaje", "no_explore_results_message": "Nahrajte viac fotiek na objavovanie vašej zbierky.", "no_favorites_message": "Pridajte si obľúbené, aby ste rýchlo našli svoje najlepšie obrázky a videá", - "no_libraries_message": "Vytvorí externú knižnicu na prezeranie fotiek a videí", + "no_libraries_message": "Vytvorte externú knižnicu na prezeranie fotiek a videí", "no_local_assets_found": "Neboli nájdené žiadne lokálne položky s touto kontrolnou sumou", "no_locked_photos_message": "Fotografie a videá v zamknutom priečinku sú skryté a nezobrazujú sa pri prehľadávaní alebo vyhľadávaní v knižnici.", "no_name": "Bez mena", @@ -1403,7 +1406,7 @@ "no_remote_assets_found": "Neboli nájdené žiadne vzdialené položky s touto kontrolnou sumou", "no_results": "Žiadne výsledky", "no_results_description": "Skúste synonymum alebo všeobecnejší výraz", - "no_shared_albums_message": "Vytvorí album na zdieľanie fotiek a videí s ľuďmi vo vašej sieti", + "no_shared_albums_message": "Vytvorte album na zdieľanie fotiek a videí s ľuďmi vo vašej sieti", "no_uploads_in_progress": "Žiadne prebiehajúce nahrávanie", "not_available": "Nedostupné", "not_in_any_album": "Nie je v žiadnom albume", @@ -1448,7 +1451,7 @@ "other_devices": "Ďalšie zariadenia", "other_entities": "Ostatné subjekty", "other_variables": "Ostatné premenné", - "owned": "Vlastnené", + "owned": "Vlastné", "owner": "Vlastník", "partner": "Partner", "partner_can_access": "{partner} môže pristupovať", @@ -1508,7 +1511,7 @@ "person_hidden": "{name}{hidden, select, true { (skryté)} other {}}", "photo_shared_all_users": "Vyzerá, že zdieľate svoje fotky so všetkými používateľmi alebo nemáte žiadnych používateľov.", "photos": "Fotografie", - "photos_and_videos": "Fotografie & Videa", + "photos_and_videos": "Fotografie a videá", "photos_count": "{count, plural, one {{count, number} fotka} few {{count, number} fotky} other {{count, number} fotiek}}", "photos_from_previous_years": "Fotky z minulých rokov", "pick_a_location": "Vyberte polohu", @@ -1861,7 +1864,7 @@ "shared_link_options": "Možnosti zdieľaných odkazov", "shared_link_password_description": "Vyžadovať heslo pre prístup k tomuto zdieľanému odkazu", "shared_links": "Zdieľané odkazy", - "shared_links_description": "Zdieľanie fotografií a videí pomocou odkazu", + "shared_links_description": "Zdieľajte fotografie a videá pomocou odkazu", "shared_photos_and_videos_count": "{assetCount, plural, few {# zdieľané fotky a videá.} other {# zdieľaných fotiek a videí.}}", "shared_with_me": "Zdieľané so mnou", "shared_with_partner": "Zdieľané s {partner}", @@ -1916,9 +1919,9 @@ "sort_recent": "Najnovšia fotografia", "sort_title": "Názov", "source": "Zdroj", - "stack": "Zoskupenie", + "stack": "Zoskupiť", "stack_action_prompt": "{count} zoskupených", - "stack_duplicates": "Zoskupiť duplicity", + "stack_duplicates": "Zoskupiť duplikáty", "stack_select_one_photo": "Vyberte jednu hlavnú fotku pre zoskupenie", "stack_selected_photos": "Zoskupiť vybraté fotky", "stacked_assets_count": "{count, plural, one {Zoskupená # položka} few {Zoskupené # položky} other {Zoskupených # položiek}}", @@ -1930,7 +1933,7 @@ "status": "Stav", "stop_casting": "Zastaviť prenos", "stop_motion_photo": "Stopmotion fotka", - "stop_photo_sharing": "Zastaviť zdieľanie vašich fotiek?", + "stop_photo_sharing": "Zastaviť zdieľanie vašich fotografií?", "stop_photo_sharing_description": "{partner} už nebude mať prístup k vašim fotkám.", "stop_sharing_photos_with_user": "Zastaviť zdieľanie vašich fotiek s týmto používateľom", "storage": "Ukladací priestor", @@ -1992,7 +1995,7 @@ "to_multi_select": "na viacnásobný výber", "to_parent": "Prejsť k nadradenému", "to_select": "na výber", - "to_trash": "Kôš", + "to_trash": "Do koša", "toggle_settings": "Prepnúť nastavenie", "total": "Celkom", "total_usage": "Celkové využitie", @@ -2004,7 +2007,7 @@ "trash_emptied": "Kôš vyprázdnený", "trash_no_results_message": "Vymazané fotografie a videá sa zobrazia tu.", "trash_page_delete_all": "Vymazať všetky", - "trash_page_empty_trash_dialog_content": "Skutočne chcete vyprázdniť kôš? Tieto položky budú permanentne odstránené z aplikácie Immich", + "trash_page_empty_trash_dialog_content": "Skutočne chcete vyprázdniť kôš? Tieto položky budú natrvalo odstránené z aplikácie Immich", "trash_page_info": "Médiá v koši sa permanentne odstránia po {days} dňoch", "trash_page_no_assets": "Žiadne médiá v koši", "trash_page_restore_all": "Obnoviť všetky", @@ -2037,9 +2040,9 @@ "unselect_all": "Zrušiť výber všetkých", "unselect_all_duplicates": "Zrušiť výber všetkých duplicít", "unselect_all_in": "Zrušiť výber všetkých v {group}", - "unstack": "Odskupiť", + "unstack": "Zrušiť zoskupenie", "unstack_action_prompt": "{count} nezoskupených", - "unstacked_assets_count": "Zrušenie zoskupenia pre {count, plural, one {# položku} few {# položky} other {# položiek}}", + "unstacked_assets_count": "Zrušené zoskupenia pre {count, plural, one {# položku} few {# položky} other {# položiek}}", "untagged": "Bez štítku", "up_next": "To je všetko", "update_location_action_prompt": "Aktualizovať polohu {count} vybraných položiek pomocou:", @@ -2114,7 +2117,7 @@ "view_user": "Zobraziť používateľa", "viewer_remove_from_stack": "Odstrániť zo zoskupenia", "viewer_stack_use_as_main_asset": "Použiť ako hlavnú fotku", - "viewer_unstack": "Odskupiť", + "viewer_unstack": "Zrušiť zoskupenie", "visibility_changed": "Viditeľnosť zmenená pre {count, plural, one {# osobu} few {# osoby} other {# osôb}}", "waiting": "Čakajúce", "warning": "Varovanie", diff --git a/i18n/sl.json b/i18n/sl.json index 23701836f2..3939cdfa52 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -28,10 +28,12 @@ "add_to_album": "Dodaj v album", "add_to_album_bottom_sheet_added": "Dodano v {album}", "add_to_album_bottom_sheet_already_exists": "Že v {album}", + "add_to_album_bottom_sheet_some_local_assets": "Nekaterih lokalnih sredstev ni bilo mogoče dodati v album", "add_to_album_toggle": "Preklopi izbiro za {album}", "add_to_albums": "Dodaj v albume", "add_to_albums_count": "Dodaj v albume ({count})", "add_to_shared_album": "Dodaj k deljenemu albumu", + "add_upload_to_stack": "Dodaj nalaganje v sklad", "add_url": "Dodaj URL", "added_to_archive": "Dodano v arhiv", "added_to_favorites": "Dodano med priljubljene", @@ -598,6 +600,7 @@ "backup_controller_page_turn_on": "Vklopite varnostno kopiranje v ospredju", "backup_controller_page_uploading_file_info": "Nalaganje podatkov o datoteki", "backup_err_only_album": "Edinega albuma ni mogoče odstraniti", + "backup_error_sync_failed": "Sinhronizacija ni uspela. Varnostne kopije ni mogoče obdelati.", "backup_info_card_assets": "sredstva", "backup_manual_cancelled": "Preklicano", "backup_manual_in_progress": "Nalaganje že poteka. Poskusite čez nekaj časa", @@ -1036,6 +1039,7 @@ "exif_bottom_sheet_description_error": "Napaka pri posodabljanju opisa", "exif_bottom_sheet_details": "PODROBNOSTI", "exif_bottom_sheet_location": "LOKACIJA", + "exif_bottom_sheet_no_description": "Ni opisa", "exif_bottom_sheet_people": "OSEBE", "exif_bottom_sheet_person_add_person": "Dodaj ime", "exit_slideshow": "Zapustite diaprojekcijo", diff --git a/i18n/sr_Latn.json b/i18n/sr_Latn.json index 06a76f8f0a..4e4afe02f8 100644 --- a/i18n/sr_Latn.json +++ b/i18n/sr_Latn.json @@ -123,6 +123,10 @@ "logging_enable_description": "Omogući evidentiranje", "logging_level_description": "Kada je omogućeno, koji nivo evidencije koristiti.", "logging_settings": "Evidentiranje", + "machine_learning_availability_checks": "Provere dostupnosti", + "machine_learning_availability_checks_enabled": "Omogući provere dostupnosti", + "machine_learning_availability_checks_interval": "Interval provere", + "machine_learning_availability_checks_interval_description": "Interval u milisekundama između provera dostupnosti", "machine_learning_clip_model": "Model CLIP", "machine_learning_clip_model_description": "Naziv CLIP modela je naveden ovde. Imajte na umu da morate ponovo da pokrenete posao „Pametno pretraživanje“ za sve slike nakon promene modela.", "machine_learning_duplicate_detection": "Detekcija duplikata", @@ -182,11 +186,13 @@ "nightly_tasks_database_cleanup_setting": "Zadaci čiščenja baze podataka", "nightly_tasks_database_cleanup_setting_description": "Očisti stare, istekle podatke iz baze podataka", "nightly_tasks_generate_memories_setting": "Generiši sjećanja", - "nightly_tasks_generate_memories_setting_description": "Kreiraj nova sjećanja", + "nightly_tasks_generate_memories_setting_description": "Stvorite nova sećanja iz imovine", "nightly_tasks_missing_thumbnails_setting": "Generiši nedostajuće sličice", "nightly_tasks_missing_thumbnails_setting_description": "Dodajte elemente bez sličica u red za generisanje sličica", "nightly_tasks_settings": "Podešavanja noćnih zadataka", "nightly_tasks_settings_description": "Upravljaj noćnim zadacima", + "nightly_tasks_start_time_setting": "Vreme početka", + "nightly_tasks_start_time_setting_description": "Vreme kada server započinje noćne zadatke", "nightly_tasks_sync_quota_usage_setting_description": "Ažurirajte kvotu memorijskog prostora korisnika na osnovu trenutne upotrebe", "no_paths_added": "Nema dodatih putanja", "no_pattern_added": "Nije dodat obrazac", diff --git a/i18n/sv.json b/i18n/sv.json index 6acee96f66..4057a6aa56 100644 --- a/i18n/sv.json +++ b/i18n/sv.json @@ -28,10 +28,12 @@ "add_to_album": "Lägg till i album", "add_to_album_bottom_sheet_added": "Tillagd till {album}", "add_to_album_bottom_sheet_already_exists": "Redan i {album}", + "add_to_album_bottom_sheet_some_local_assets": "Vissa lokala tillgångar kunde inte läggas till i albumet", "add_to_album_toggle": "Växla val för {album}", "add_to_albums": "Lägg till i album", "add_to_albums_count": "Lägg till i album ({count})", "add_to_shared_album": "Lägg till i delat album", + "add_upload_to_stack": "Lägg till uppladdning till stack", "add_url": "Lägg till URL", "added_to_archive": "Tillagd i arkiv", "added_to_favorites": "Tillagd till favoriter", @@ -598,6 +600,7 @@ "backup_controller_page_turn_on": "Aktivera automatisk säkerhetskopiering", "backup_controller_page_uploading_file_info": "Laddar upp filinformation", "backup_err_only_album": "Kan inte ta bort det enda albumet", + "backup_error_sync_failed": "Synkroniseringen misslyckades. Det går inte att bearbeta säkerhetskopian.", "backup_info_card_assets": "objekt", "backup_manual_cancelled": "Avbrutet", "backup_manual_in_progress": "Uppladdning pågår redan. Försök igen om en liten stund", diff --git a/i18n/ta.json b/i18n/ta.json index f8996ad44b..978a36ab64 100644 --- a/i18n/ta.json +++ b/i18n/ta.json @@ -28,10 +28,12 @@ "add_to_album": "ஆல்பமில் சேர்க்க", "add_to_album_bottom_sheet_added": "{album}-இல் சேர்க்கப்பட்டது", "add_to_album_bottom_sheet_already_exists": "ஏற்கனவே {album}-இல் உள்ளது", + "add_to_album_bottom_sheet_some_local_assets": "சில உள்ளக சொத்துக்களை ஆல்பத்தில் சேர்க்க முடியவில்லை", "add_to_album_toggle": "{album} க்கான தேர்வை மாற்று", "add_to_albums": "ஆல்பத்தில் சேர்", "add_to_albums_count": "ஆல்பங்களில் சேர்({count})", "add_to_shared_album": "பகிரப்பட்ட ஆல்பமில் சேர்க்க", + "add_upload_to_stack": "அடுக்கில் பதிவேற்றத்தைச் சேர்", "add_url": "URL ஐச் சேர்க்கவும்", "added_to_archive": "காப்பகத்தில் சேர்க்கப்பட்டது", "added_to_favorites": "விருப்பங்களில் (பேவரிட்ஸ்) சேர்க்கப்பட்டது", @@ -39,16 +41,16 @@ "admin": { "add_exclusion_pattern_description": "விலக்கு வடிவங்களைச் சேர்க்கவும். *, **, மற்றும் ? ஆதரிக்கப்படுகிறது. \"Raw\" என்ற பெயரிடப்பட்ட எந்த கோப்பகத்திலும் உள்ள எல்லா கோப்புகளையும் புறக்கணிக்க, \"**/Raw/**\" ஐப் பயன்படுத்தவும். \".tif\" இல் முடியும் எல்லா கோப்புகளையும் புறக்கணிக்க, \"**/*.tif\" ஐப் பயன்படுத்தவும். ஒரு முழுமையான பாதையை புறக்கணிக்க, \"/path/to/ignore/**\" ஐப் பயன்படுத்தவும்.", "admin_user": "நிர்வாக பயனர்", - "asset_offline_description": "இந்த வெளிப்புற நூலக சொத்து இனி வட்டில் காணப்படவில்லை மற்றும் குப்பைக்கு நகர்த்தப்பட்டுள்ளது. கோப்பு நூலகத்திற்குள் நகர்த்தப்பட்டிருந்தால், புதிய தொடர்புடைய சொத்துக்கான உங்கள் காலவரிசையை சரிபார்க்கவும். இந்த சொத்தை மீட்டெடுக்க, கீழேயுள்ள கோப்பு பாதையை இம்மிச் மூலம் அணுகலாம் மற்றும் நூலகத்தை ச்கேன் செய்ய முடியும் என்பதை உறுதிப்படுத்தவும்.", - "authentication_settings": "அடையாள உறுதிப்படுத்தல் அமைப்புகள் (செட்டிங்ஸ்)", - "authentication_settings_description": "கடவுச்சொல், OAuth, மற்றும் பிற அடையாள அமைப்புகள்", - "authentication_settings_disable_all": "எல்லா உள்நுழைவு முறைகளையும் நிச்சயமாக முடக்க விரும்புகிறீர்களா? உள்நுழைவு முற்றிலும் முடக்கப்படும்.", - "authentication_settings_reenable": "மீண்டும் இயக்க, சர்வர் கட்டளை பயன்படுத்தவும்.", + "asset_offline_description": "இந்த வெளிப்புற நூலகச் சொத்து (external library asset) இனி கோப்புப் பதிப்பில் காணப்படவில்லை மற்றும் குப்பைத்தொட்டியில் (trash) நகர்த்தப்பட்டுள்ளது.கோப்பு நூலகத்தில் உள்ளே நகர்த்தப்பட்டிருந்தால், புதிய இணை சொத்தை (corresponding asset) கண்டுபிடிக்க உங்கள் காலவரிசையை (timeline) சரிபார்க்கவும். இந்த சொத்தை மீட்டமைக்க, கீழே உள்ள கோப்புப் பாதை Immich மூலம் அணுகக்கூடியதா என்பதை உறுதி செய்து, நூலகத்தை (library) மீண்டும் ஸ்கேன் செய்யவும்.", + "authentication_settings": "அங்கீகார அமைப்புகள்", + "authentication_settings_description": "அடையாளச் சொல்லுகள் (Password), OAuth மற்றும் பிற அங்கீகார அமைப்புகளை நிர்வகிக்கவும்", + "authentication_settings_disable_all": "நீங்கள் உண்மையில் அனைத்து உள்நுழைவு முறைகளையும் முடக்க விரும்புகிறீர்களா? உள்நுழைவு முழுமையாக முடக்கப்படும்.", + "authentication_settings_reenable": "மீண்டும் செயல்படுத்த, சேவையகம் கட்டளை பயன்படுத்தவும்.", "background_task_job": "பின்னணி பணிகள்", - "backup_database": "தரவுத்தள காப்புப்பிரதியை உருவாக்கு", - "backup_database_enable_description": "தரவுத்தள திணிப்புகள் இயக்கவும்", - "backup_keep_last_amount": "வைத்திருக்க முந்தைய திணிப்புகள் அளவு", - "backup_onboarding_1_description": "மேகக்கட்டத்தில் அல்லது மற்றொரு உடல் இடத்தில் ஆஃப்சைட் நகல்.", + "backup_database": "தரவுத்தள நகல் உருவாக்கவும்", + "backup_database_enable_description": "தரவுத்தள நகல்களை இயக்கவும்", + "backup_keep_last_amount": "வைத்திருக்க முந்தைய நகல்களின் அளவு", + "backup_onboarding_1_description": "மேகம் அல்லது வேறு இடத்தில் நகல்.", "backup_onboarding_2_description": "வெவ்வேறு சாதனங்களில் உள்ள நகல் பிரதிகள். இதில் முக்கிய கோப்புகள் மற்றும் அந்தக் கோப்புகளின் நகல் காப்புப்பிரதி ஆகியவை அடங்கும்.", "backup_onboarding_3_description": "உங்கள் தரவின் மொத்த கோப்புகள் அசல் மற்றும் நகல்கள் உட்பட. இதில் 1 வெளிப்புற நகல் மற்றும் 2 சாதனப் பிரதிகள் அடங்கும்.", "backup_onboarding_description": "உங்கள் தரவை பாதுகாப்பதற்காக ஒரு 3-2-1 காப்புப் பிரதி பரிந்துரைக்கப்படுகிறது. முழுமையான காப்பு பாதுகாப்பு தீர்விற்காக, நீங்கள் பதிவேற்றிய புகைப்படங்கள்/வீடியோக்கள் மற்றும் Immich தரவுத்தளத்தின் நகல்களையும் வைத்திருக்க வேண்டும்.", @@ -56,7 +58,7 @@ "backup_onboarding_parts_title": "3-2-1 காப்புப்பிரதியில் பின்வருவன அடங்கும்:", "backup_onboarding_title": "காப்புப்பிரதிகள்", "backup_settings": "தரவுத்தள திணிப்பு அமைப்புகள்", - "backup_settings_description": "தரவுத்தள திணிப்பு அமைப்புகளை நிர்வகிக்கவும்", + "backup_settings_description": "தரவுத்தள நகல் அமைப்புகளை நிர்வகி.", "cleared_jobs": "முடித்த வேலைகள்: {job}", "config_set_by_file": "கட்டமைப்பு, தற்போது ஒரு கட்டமைப்பு கோப்பு மூலம் அமைக்கப்பட்டுள்ளது", "confirm_delete_library": "{library} படங்கள் நூலகத்தை நிச்சயமாக நீக்க விரும்புகிறீர்களா?", @@ -67,7 +69,7 @@ "confirm_user_pin_code_reset": "{user} இன் பின் குறியீட்டை மீட்டமைக்க விரும்புகிறீர்களா?", "create_job": "வேலையை உருவாக்கு", "cron_expression": "க்ரோன் வெளிப்பாடு", - "cron_expression_description": "CRON வடிவமைப்பைப் பயன்படுத்தி ச்கேனிங் இடைவெளியை அமைக்கவும். மேலும் தகவலுக்கு எ.கா. <இணைப்பு> க்ரோன்டாப் குரு ", + "cron_expression_description": "CRON வடிவமைப்பைப் பயன்படுத்தி ச்கேனிங் இடைவெளியை அமைக்கவும். மேலும் தகவலுக்கு எ.கா. க்ரோன்டாப் குரு ", "cron_expression_presets": "க்ரோன் வெளிப்பாடு முன்னமைவுகள்", "disable_login": "உள்நுழைவை முடக்கு", "duplicate_detection_job_description": "ஒத்த படங்களைக் கண்டறிய, சொத்துக்களில் இயந்திரக் கற்றலை இயக்கவும். ஸ்மார்ட் தேடலை நம்பியுள்ளது", @@ -116,7 +118,7 @@ "library_scanning_enable_description": "நியமிக்கப்பட்ட புகைப்பட நூலக ஸ்கேனிங்கை இயக்கு", "library_settings": "வெளிப்புற புகைப்பட நூலகம்", "library_settings_description": "வெளிப்புற புகைப்பட நூலக அமைப்புகளை மேலாண்மை செய்யவும்", - "library_tasks_description": "புதிய மற்றும்/அல்லது மாற்றப்பட்ட சொத்துக்களுக்கு வெளிப்புற நூலகங்களை ஸ்கேன் செய்யவும்.", + "library_tasks_description": "புதிய மற்றும்/அல்லது மாற்றப்பட்ட சொத்துக்களுக்கு வெளிப்புற நூலகங்களை வருடவும்", "library_watching_enable_description": "கோப்பு மாற்றங்களுக்கு வெளிப்புற நூலகங்களைப் பாருங்கள்", "library_watching_settings": "நூலகப் பார்ப்பது (சோதனை)", "library_watching_settings_description": "மாற்றப்பட்ட புகைப்படங்களைத் தானாகவே பார்க்கவும்", @@ -316,7 +318,7 @@ "transcoding_audio_codec": "ஆடியோ கோடெக்", "transcoding_audio_codec_description": "ஓபச் மிக உயர்ந்த தரமான விருப்பமாகும், ஆனால் பழைய சாதனங்கள் அல்லது மென்பொருளுடன் குறைந்த பொருந்தக்கூடிய தன்மையைக் கொண்டுள்ளது.", "transcoding_bitrate_description": "மேக்ச் பிட்ரேட்டை விட அதிகமான வீடியோக்கள் அல்லது ஏற்றுக்கொள்ளப்பட்ட வடிவத்தில் இல்லை", - "transcoding_codecs_learn_more": "இங்கே பயன்படுத்தப்படும் சொற்களைப் பற்றி மேலும் அறிய, H.264 கோடெக் , HEVC கோடெக் மற்றும் VP9 க்கான FFMPEG ஆவணங்களைப் பார்க்கவும் கோடெக் .", + "transcoding_codecs_learn_more": "இங்கே பயன்படுத்தப்படும் சொற்களைப் பற்றி மேலும் அறிய, H.264 கோடெக், HEVC கோடெக் மற்றும் VP9 க்கான கோடெக் FFMPEG ஆவணங்களைப் பார்க்கவும்.", "transcoding_constant_quality_mode": "நிலையான தர முறை", "transcoding_constant_quality_mode_description": "CQP ஐ விட ICQ சிறந்தது, ஆனால் சில வன்பொருள் முடுக்கம் சாதனங்கள் இந்த பயன்முறையை ஆதரிக்கவில்லை. இந்த விருப்பத்தை அமைப்பது தர அடிப்படையிலான குறியாக்கத்தைப் பயன்படுத்தும் போது குறிப்பிட்ட பயன்முறையை விரும்புகிறது. NVENC ஆல் புறக்கணிக்கப்பட்டது, ஏனெனில் இது ICQ ஐ ஆதரிக்காது.", "transcoding_constant_rate_factor": "நிலையான வீத காரணி (-crf)", @@ -325,7 +327,7 @@ "transcoding_encoding_options": "குறியீட்டு விருப்பங்கள்", "transcoding_encoding_options_description": "குறியிடப்பட்ட வீடியோக்களுக்கான கோடெக்குகள், தெளிவுத்திறன், தரம் மற்றும் பிற விருப்பங்களை அமைக்கவும்", "transcoding_hardware_acceleration": "வன்பொருள் முடுக்கம்", - "transcoding_hardware_acceleration_description": "பரிசோதனை: வேகமான டிரான்ஸ்கோடிங் ஆனால் அதே பிட்ரேட்டில் தரத்தைக் குறைக்கலாம்.", + "transcoding_hardware_acceleration_description": "பரிசோதனை: வேகமான டிரான்ஸ்கோடிங் ஆனால் அதே பிட்ரேட்டில் தரத்தைக் குறைக்கலாம்", "transcoding_hardware_decoding": "வன்பொருள் டிகோடிங்", "transcoding_hardware_decoding_setting_description": "குறியாக்கத்தை விரைவுபடுத்துவதற்கு பதிலாக இறுதி முதல் இறுதி முடுக்கம் ஆகியவற்றை செயல்படுத்துகிறது. எல்லா வீடியோக்களிலும் வேலை செய்யக்கூடாது.", "transcoding_max_b_frames": "அதிகபட்ச பி-பிரேம்கள்", @@ -598,6 +600,7 @@ "backup_controller_page_turn_on": "முன்புற காப்புப்பிரதியை இயக்கவும்", "backup_controller_page_uploading_file_info": "கோப்பு தகவலைப் பதிவேற்றுகிறது", "backup_err_only_album": "ஒரே ஆல்பத்தை அகற்ற முடியாது", + "backup_error_sync_failed": "ஒத்திசைவு தோல்வியுற்றது. காப்புப்பிரதியை செயலாக்க முடியாது.", "backup_info_card_assets": "சொத்துக்கள்", "backup_manual_cancelled": "ரத்து செய்யப்பட்டது", "backup_manual_in_progress": "ஏற்கனவே முன்னேற்றத்தில் பதிவேற்றவும். சிறிது நேரம் கழித்து முயற்சிக்கவும்", @@ -618,9 +621,9 @@ "bugs_and_feature_requests": "பிழைகள் மற்றும் அம்ச கோரிக்கைகள்", "build": "உருவாக்கு", "build_image": "படத்தை உருவாக்குங்கள்", - "bulk_delete_duplicates_confirmation": "{எண்ணிக்கை, பன்மை, ஒன்று {# நகல் சொத்து} பிற {# நகல் சொத்துக்கள்}}}}}}}}} {{# நகல் சொத்து ஆகியவற்றை மொத்தமாக நீக்க விரும்புகிறீர்களா? இது ஒவ்வொரு குழுவின் மிகப்பெரிய சொத்தை வைத்திருக்கும் மற்றும் மற்ற அனைத்து நகல்களையும் நிரந்தரமாக நீக்குகிறது. இந்த செயலை நீங்கள் செயல்தவிர்க்க முடியாது!", - "bulk_keep_duplicates_confirmation": "நீங்கள் {எண்ணிக்கை, பன்மை, ஒன்று {# நகல் சொத்து} பிற {# நகல் சொத்துக்கள்} be வைக்க விரும்புகிறீர்களா? இது எதையும் நீக்காமல் அனைத்து நகல் குழுக்களையும் தீர்க்கும்.", - "bulk_trash_duplicates_confirmation": "நீங்கள் மொத்தமாக குப்பை {எண்ணிக்கை, பன்மை, ஒன்று {# நகல் சொத்து} பிற {# நகல் சொத்துக்கள்}}}} செய்ய விரும்புகிறீர்களா? இது ஒவ்வொரு குழுவின் மிகப்பெரிய சொத்தை வைத்திருக்கும் மற்றும் மற்ற அனைத்து நகல்களையும் குப்பைத் தொட்டியாக இருக்கும்.", + "bulk_delete_duplicates_confirmation": "நீங்கள் நிச்சயமாக {count, plural, one {# நகல் சொத்து} other {# நகல் சொத்துகள்}} மொத்தமாக நீக்க விரும்புகிறீர்களா? இது ஒவ்வொரு குழுவின் மிகப்பெரிய சொத்தை வைத்திருக்கும் மற்றும் மற்ற அனைத்து நகல்களையும் நிரந்தரமாக நீக்கும். இந்தச் செயலை நீங்கள் செயல்தவிர்க்க முடியாது!", + "bulk_keep_duplicates_confirmation": "நீங்கள் நிச்சயமாக {count, plural, one {# நகல் சொத்து} other {# நகல் சொத்துக்கள்}} வைத்திருக்க விரும்புகிறீர்களா? இது எதையும் நீக்காமல் அனைத்து நகல் குழுக்களையும் தீர்க்கும்.", + "bulk_trash_duplicates_confirmation": "நீங்கள் நிச்சயமாக மொத்தமாகக் குப்பையில் போட விரும்புகிறீர்களா {count, plural, one {# நகல் சொத்து} other {# நகல் சொத்துக்கள்}}? இது ஒவ்வொரு குழுவின் மிகப்பெரிய சொத்தையும் வைத்திருக்கும், மற்ற அனைத்து நகல்களையும் குப்பையில் போடும்.", "buy": "இம்மியை வாங்கவும்", "cache_settings_clear_cache_button": "தெளிவான தற்காலிக சேமிப்பு", "cache_settings_clear_cache_button_title": "பயன்பாட்டின் தற்காலிக சேமிப்பை அழிக்கிறது. கேச் மீண்டும் கட்டப்படும் வரை இது பயன்பாட்டின் செயல்திறனை கணிசமாக பாதிக்கும்.", @@ -707,7 +710,7 @@ "confirm_keep_this_delete_others": "இந்த சொத்தைத் தவிர அடுக்கில் உள்ள மற்ற அனைத்து சொத்துகளும் நீக்கப்படும். நீங்கள் தொடர விரும்புகிறீர்களா?", "confirm_new_pin_code": "புதிய முள் குறியீட்டை உறுதிப்படுத்தவும்", "confirm_password": "கடவுச்சொல்லை உறுதிப்படுத்தவும்", - "confirm_tag_face": "இந்த முகத்தை {பெயர் அச் எனக் குறிக்க விரும்புகிறீர்களா?", + "confirm_tag_face": "இந்த முகத்தை {name} எனக் குறிக்க விரும்புகிறீர்களா?", "confirm_tag_face_unnamed": "இந்த முகத்தை குறிக்க விரும்புகிறீர்களா?", "connected_device": "இணைக்கப்பட்ட சாதனம்", "connected_to": "இணைக்கப்பட்டுள்ளது", @@ -913,9 +916,9 @@ "cannot_navigate_next_asset": "அடுத்த சொத்துக்கு செல்ல முடியாது", "cannot_navigate_previous_asset": "முந்தைய சொத்துக்கு செல்ல முடியாது", "cant_apply_changes": "மாற்றங்களைப் பயன்படுத்த முடியாது", - "cant_change_activity": "{செயல்படுத்த முடியாது, தேர்ந்தெடுக்கவும், உண்மை {disable} பிற {enable}} செயல்பாடு", + "cant_change_activity": "செயல்பாட்டை {enabled, select, true {முடக்க} other {இயக்க}} முடியாது", "cant_change_asset_favorite": "சொத்துக்கு பிடித்ததை மாற்ற முடியாது", - "cant_change_metadata_assets_count": "{எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} பிற {# சொத்துக்கள்}} இன் மெட்டாடேட்டாவை மாற்ற முடியாது", + "cant_change_metadata_assets_count": "{count, plural, one {# சொத்து} other {# சொத்துக்கள்}}இன் மீள்தரவை மாற்ற முடியாது", "cant_get_faces": "முகங்களைப் பெற முடியாது", "cant_get_number_of_comments": "கருத்துகளின் எண்ணிக்கையைப் பெற முடியாது", "cant_search_people": "மக்களைத் தேட முடியாது", @@ -944,7 +947,7 @@ "failed_to_update_notification_status": "அறிவிப்பு நிலையைப் புதுப்பிக்கத் தவறிவிட்டது", "import_path_already_exists": "இந்த இறக்குமதி பாதை ஏற்கனவே உள்ளது.", "incorrect_email_or_password": "தவறான மின்னஞ்சல் அல்லது கடவுச்சொல்", - "paths_validation_failed": "{பாதைகள், பன்மை, ஒன்று {# பாதை} மற்ற {# பாதைகள்}} தோல்வியுற்ற சரிபார்ப்பு", + "paths_validation_failed": "தோல்வியுற்ற சரிபார்ப்பு {paths, plural, one {# பாதை} other {# பாதைகள்}}", "profile_picture_transparent_pixels": "சுயவிவரப் படங்களுக்கு வெளிப்படையான படப்புள்ளிகள் இருக்க முடியாது. தயவுசெய்து பெரிதாக்கவும்/அல்லது படத்தை நகர்த்தவும்.", "quota_higher_than_disk_size": "வட்டு அளவை விட அதிகமாக ஒதுக்கீட்டை அமைத்துள்ளீர்கள்", "something_went_wrong": "ஏதோ தவறு நடந்தது", @@ -954,9 +957,9 @@ "unable_to_add_exclusion_pattern": "விலக்கு முறையைச் சேர்க்க முடியவில்லை", "unable_to_add_import_path": "இறக்குமதி பாதையைச் சேர்க்க முடியவில்லை", "unable_to_add_partners": "கூட்டாளர்களைச் சேர்க்க முடியவில்லை", - "unable_to_add_remove_archive": "{காப்பகப்படுத்த முடியவில்லை, தேர்ந்தெடுக்கவும், உண்மையாகவும்} பிற {remove asset from}}} காப்பகத்திற்குச் சேர்க்கவும்", - "unable_to_add_remove_favorites": "{பிடித்த, தேர்ந்தெடுக்கவும், உண்மையாகவும்}}} பிடித்தவைகளிலிருந்து சொத்தை அகற்று", - "unable_to_archive_unarchive": "{காப்பகப்படுத்த முடியவில்லை, தேர்ந்தெடுக்க முடியவில்லை, உண்மை {archive} பிற {unarchive}}", + "unable_to_add_remove_archive": "காப்பகத்தில் {archived, select, true {இருந்து சொத்தை அகற்ற} other {சொத்தைச் சேர்க்க}} முடியவில்லை", + "unable_to_add_remove_favorites": "பிடித்தவையில் {பிடித்தது, தேர்ந்தெடு, உண்மை {சொத்தைச் சேர்க்க} மற்ற {சொத்தை அகற்ற}} முடியவில்லை", + "unable_to_archive_unarchive": "{காப்பகப்படுத்த முடியவில்லை, தேர்ந்தெடு, உண்மை {காப்பகம்} மற்றவை {காப்பகத்தை அகற்று}}", "unable_to_change_album_user_role": "ஆல்பத்தின் பயனரின் பாத்திரத்தை மாற்ற முடியவில்லை", "unable_to_change_date": "தேதியை மாற்ற முடியவில்லை", "unable_to_change_description": "விளக்கத்தை மாற்ற முடியவில்லை", @@ -1064,7 +1067,7 @@ "failed_to_load_assets": "சொத்துக்களை ஏற்றுவதில் தோல்வி", "failed_to_load_folder": "கோப்புறையை ஏற்றுவதில் தோல்வி", "favorite": "பிடித்த", - "favorite_action_prompt": "{எண்ணிக்கை the பிடித்தவைகளில் சேர்க்கப்பட்டது", + "favorite_action_prompt": "{count} பிடித்தவைகளில் சேர்க்கப்பட்டது", "favorite_or_unfavorite_photo": "பிடித்த அல்லது சாதகமற்ற புகைப்படம்", "favorites": "பிடித்தவை", "favorites_page_no_favorites": "பிடித்த சொத்துக்கள் எதுவும் கிடைக்கவில்லை", @@ -1126,9 +1129,9 @@ "hide_password": "கடவுச்சொல்லை மறைக்கவும்", "hide_person": "நபரை மறைக்க", "hide_unnamed_people": "பெயரிடப்படாதவர்களை மறைக்கவும்", - "home_page_add_to_album_conflicts": "சேர்க்கப்பட்டது {ஆல்பம் {added} சொத்துக்கள் {ஆல்பம். {album} சொத்துக்கள் ஏற்கனவே ஆல்பத்தில் உள்ளன.", + "home_page_add_to_album_conflicts": "{album} ஆல்பத்தில் {added} சொத்துக்கள் சேர்க்கப்பட்டன. {failed} சொத்துக்கள் ஏற்கனவே ஆல்பத்தில் உள்ளன.", "home_page_add_to_album_err_local": "ஆல்பங்களில் உள்ளக சொத்துக்களை இன்னும் சேர்க்க முடியாது, தவிர்க்கவும்", - "home_page_add_to_album_success": "சேர்க்கப்பட்டது {ஆல்பம் {added} சொத்துக்கள் {ஆல்பம்.", + "home_page_add_to_album_success": "{album} ஆல்பத்தில் {added} சொத்துக்கள் சேர்க்கப்பட்டன.", "home_page_album_err_partner": "ஒரு ஆல்பத்திற்கு இன்னும் கூட்டாளர் சொத்துக்களைச் சேர்க்க முடியவில்லை, தவிர்க்கவும்", "home_page_archive_err_local": "உள்ளக சொத்துக்களை இன்னும் காப்பகப்படுத்த முடியாது, தவிர்க்கவும்", "home_page_archive_err_partner": "கூட்டாளர் சொத்துக்களை காப்பகப்படுத்த முடியாது, தவிர்க்கவும்", @@ -1150,8 +1153,8 @@ "ignore_icloud_photos": "ICloud புகைப்படங்களை புறக்கணிக்கவும்", "ignore_icloud_photos_description": "ICloud இல் சேமிக்கப்படும் புகைப்படங்கள் இம்மிச் சேவையகத்தில் பதிவேற்றப்படாது", "image": "படம்", - "image_alt_text_date": "{isvideo, தேர்ந்தெடு, உண்மை {Video} பிற {Image}} {date} இல் எடுக்கப்பட்டது", - "image_alt_text_date_1_person": "{isvideo, தேர்ந்தெடு, உண்மை {Video} பிற {Image}} {{person1} இல் {date}", + "image_alt_text_date": "{isVideo, select, true {Video} மற்ற {Image}} {date} அன்று எடுக்கப்பட்டது", + "image_alt_text_date_1_person": "{isVideo, தேர்ந்தெடு, உண்மை {Video} பிற {Image}} {person1} இல் {date}", "image_alt_text_date_2_people": "{isvideo, தேர்ந்தெடுக்கவும், உண்மை {Video} பிற {Image}} {{person1} மற்றும் {person2} {date} இல் எடுக்கப்பட்டது", "image_alt_text_date_3_people": "{isvideo, தேர்ந்தெடு, உண்மை {Video} பிற {Image} the {person1}, {person2}, மற்றும் {person3} இல் எடுக்கப்பட்டது {date}", "image_alt_text_date_4_or_more_people": "{isvideo, தேர்ந்தெடு, உண்மை {Video} பிற {Image} the {person1}, {person2}, மற்றும் {கூடுதல் COUNT, எண்} மற்றவர்கள் {date}", @@ -1487,7 +1490,7 @@ "permanent_deletion_warning_setting_description": "சொத்துக்களை நிரந்தரமாக நீக்கும்போது ஒரு எச்சரிக்கையைக் காட்டுங்கள்", "permanently_delete": "நிரந்தரமாக நீக்கு", "permanently_delete_assets_count": "நிரந்தரமாக நீக்கு {எண்ணிக்கை, பன்மை, ஒன்று {asset} மற்ற {assets}}", - "permanently_delete_assets_prompt": "நீங்கள் நிச்சயமாக {எண்ணிக்கை, பன்மை, ஒன்று {இந்த சொத்து?} மற்ற {இந்த # சொத்துக்கள்?", + "permanently_delete_assets_prompt": "{count, plural, one {this asset?} other {these # assets?}} என்பதை நிரந்தரமாக நீக்க விரும்புகிறீர்களா? இது {count, plural, one {it from its} other {them from their}} ஆல்பத்தையும் நீக்கும்.", "permanently_deleted_asset": "நிரந்தரமாக நீக்கப்பட்ட சொத்து", "permanently_deleted_assets_count": "நிரந்தரமாக நீக்கப்பட்டது {எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} பிற {# சொத்துக்கள்}}", "permission": "இசைவு", @@ -1957,7 +1960,7 @@ "tag_assets": "குறிச்சொல் சொத்துக்கள்", "tag_created": "உருவாக்கப்பட்ட குறிச்சொல்: {tag}", "tag_feature_description": "தர்க்கரீதியான குறிச்சொல் தலைப்புகளால் தொகுக்கப்பட்ட புகைப்படங்கள் மற்றும் வீடியோக்களை உலாவுதல்", - "tag_not_found_question": "குறிச்சொல்லைக் கண்டுபிடிக்க முடியவில்லையா? <இணைப்பு> புதிய குறிச்சொல்லை உருவாக்கவும். ", + "tag_not_found_question": "குறிச்சொல்லைக் கண்டுபிடிக்க முடியவில்லையா?புதிய குறிச்சொல்லை உருவாக்கவும்.", "tag_people": "மக்களை குறிக்கவும்", "tag_updated": "புதுப்பிக்கப்பட்ட குறிச்சொல்: {tag}", "tagged_assets": "குறித்துள்ளார் {எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} மற்ற {# சொத்துக்கள்}}", @@ -1978,7 +1981,7 @@ "theme_setting_system_primary_color_title": "கணினி நிறத்தைப் பயன்படுத்துங்கள்", "theme_setting_system_theme_switch": "தானியங்கி (கணினி அமைப்பைப் பின்பற்றவும்)", "theme_setting_theme_subtitle": "பயன்பாட்டின் கருப்பொருள் அமைப்பைத் தேர்வுசெய்க", - "theme_setting_three_stage_loading_subtitle": "மூன்று-நிலை ஏற்றுதல் ஏற்றுதல் செயல்திறனை அதிகரிக்கக்கூடும், ஆனால் கணிசமாக அதிக பிணைய சுமையை ஏற்படுத்துகிறது", + "theme_setting_three_stage_loading_subtitle": "மூன்று-நிலை ஏற்றுதல் இயக்கினால் ஏற்றுதல் செயல்திறனை அதிகரிக்கக்கூடும், ஆனால் கணிசமாக மிகை பிணையச் சுமையை ஏற்படுத்துகிறது", "theme_setting_three_stage_loading_title": "மூன்று-நிலை ஏற்றுதலை இயக்கவும்", "they_will_be_merged_together": "அவர்கள் ஒன்றாக இணைக்கப்படுவார்கள்", "third_party_resources": "மூன்றாம் தரப்பு வளங்கள்", @@ -2115,7 +2118,7 @@ "viewer_remove_from_stack": "அடுக்கிலிருந்து அகற்று", "viewer_stack_use_as_main_asset": "பிரதான சொத்தாகப் பயன்படுத்தவும்", "viewer_unstack": "அடுக்கை நீக்கு", - "visibility_changed": "{எண்ணிக்கை, பன்மை, ஒன்று {# நபர்} மற்ற {# நபர்கள்} க்கு க்கு தெரிவுநிலை மாற்றப்பட்டது", + "visibility_changed": "{count, plural, one {# நபர்} other {# நபர்கள்}} க்கான தெரிவுநிலை மாற்றப்பட்டது", "waiting": "காத்திருக்கிறது", "warning": "எச்சரிக்கை", "week": "வாரம்", diff --git a/i18n/tr.json b/i18n/tr.json index 710d20a580..0f80e60e85 100644 --- a/i18n/tr.json +++ b/i18n/tr.json @@ -20,7 +20,7 @@ "add_import_path": "İçe aktarma yolu ekle", "add_location": "Konum ekle", "add_more_users": "Daha fazla kullanıcı ekle", - "add_partner": "Partner ekle", + "add_partner": "Ortak ekle", "add_path": "Yol ekle", "add_photos": "Fotoğraf ekle", "add_tag": "Etiket ekle", @@ -28,10 +28,12 @@ "add_to_album": "Albüme ekle", "add_to_album_bottom_sheet_added": "{album} albümüne eklendi", "add_to_album_bottom_sheet_already_exists": "Zaten {album} albümüne ekli", + "add_to_album_bottom_sheet_some_local_assets": "Bazı yerel öğeler albüme eklenemedi", "add_to_album_toggle": "{album} için seçimi değiştir", "add_to_albums": "Albümlere ekle", "add_to_albums_count": "{count} albümlerine ekle", "add_to_shared_album": "Paylaşılan albüme ekle", + "add_upload_to_stack": "Yüklemeyi yığına ekle", "add_url": "URL ekle", "added_to_archive": "Arşive eklendi", "added_to_favorites": "Favorilere eklendi", @@ -61,7 +63,7 @@ "config_set_by_file": "Ayarlar şuanda config dosyası tarafından ayarlanmıştır", "confirm_delete_library": "{library} kütüphanesini silmek istediğinize emin misiniz?", "confirm_delete_library_assets": "Bu kütüphaneyi silmek istediğinize emin misiniz? Bu işlem {count, plural, one {# tane öğeyi} other {all # tane öğeyi}} Immich'den silecek ve bu işlem geri alınamaz. Dosyalar diskte kalacaktır.", - "confirm_email_below": "Onaylamak için aşağıya {email} yazın", + "confirm_email_below": "Onaylamak için aşağıya \"{email}\" yazın", "confirm_reprocess_all_faces": "Tüm yüzleri tekrardan işlemek istediğinize emin misiniz? Bu işlem isimlendirilmiş insanları da silecek.", "confirm_user_password_reset": "{user} adlı kullanıcının şifresini sıfırlamak istediğinize emin misiniz?", "confirm_user_pin_code_reset": "{user} adlı kullanıcının PIN kodunu sıfırlamak istediğinize emin misiniz?", @@ -400,7 +402,7 @@ "advanced_settings_prefer_remote_subtitle": "Bazı cihazlar yerel öğelerden küçük resimleri yüklerken çok yavaş çalışır. Bunun yerine uzak görüntüleri yüklemek için bu ayarı etkinleştirin.", "advanced_settings_prefer_remote_title": "Uzak görüntüleri tercih et", "advanced_settings_proxy_headers_subtitle": "Immich'in her ağ isteğiyle birlikte göndermesi gereken proxy header'ları tanımlayın", - "advanced_settings_proxy_headers_title": "Proxy Header'lar", + "advanced_settings_proxy_headers_title": "Proxy Başlıkları", "advanced_settings_readonly_mode_subtitle": "Fotoğrafların yalnızca görüntülenebildiği salt okunur modu etkinleştirir; birden fazla görüntü seçme, paylaşma, aktarma, silme gibi işlemler devre dışı bırakılır. Ana ekrandan kullanıcı avatarı aracılığıyla salt okunur modu Etkinleştirin/Devre dışı bırakın", "advanced_settings_readonly_mode_title": "Salt okunur Mod", "advanced_settings_self_signed_ssl_subtitle": "Sunucu uç noktası için SSL sertifika doğrulamasını atlar. Kendinden imzalı sertifikalar için gereklidir.", @@ -414,15 +416,15 @@ "age_year_months": "1 yaş, {months, plural, one {# ay} other {# ay}}", "age_years": "{years, plural, other {Yaş #}}", "album_added": "Albüm eklendi", - "album_added_notification_setting_description": "Paylaşılan bir albüme eklendiğinizde email bildirimi alın", - "album_cover_updated": "Albüm Kapağı güncellendi", + "album_added_notification_setting_description": "Paylaşılan bir albüme eklendiğinizde e-posta bildirimi alın", + "album_cover_updated": "Albüm kapağı güncellendi", "album_delete_confirmation": "{album} albümünü silmek istediğinize emin misiniz?", "album_delete_confirmation_description": "Albüm paylaşılıyorsa, diğer kullanıcılar artık bu albüme erişemeyecektir.", "album_deleted": "Albüm silindi", "album_info_card_backup_album_excluded": "HARİÇ", "album_info_card_backup_album_included": "DAHİL", "album_info_updated": "Albüm bilgisi güncellendi", - "album_leave": "Albümden Ayrıl?", + "album_leave": "Albümden ayrıl?", "album_leave_confirmation": "{album} albümünden ayrılmak istediğinize emin misiniz?", "album_name": "Albüm Adı", "album_options": "Albüm seçenekleri", @@ -574,16 +576,16 @@ "backup_controller_page_background_turn_off": "Arka plan hizmetini kapat", "backup_controller_page_background_turn_on": "Arka plan hizmetini aç", "backup_controller_page_background_wifi": "Sadece Wi-Fi", - "backup_controller_page_backup": "Yedekle", + "backup_controller_page_backup": "Yedek", "backup_controller_page_backup_selected": "Seçilen: ", - "backup_controller_page_backup_sub": "Yedeklenen öğeler", + "backup_controller_page_backup_sub": "Yedeklenen fotoğraflar ve videolar", "backup_controller_page_created": "Oluşturma tarihi: {date}", "backup_controller_page_desc_backup": "Uygulamayı açtığınızda yeni öğelerin sunucuya otomatik olarak yüklenmesi için ön planda yedeklemeyi açın.", "backup_controller_page_excluded": "Hariç tutuldu: ", "backup_controller_page_failed": "Başarısız ({count})", "backup_controller_page_filename": "Dosya adı: {filename} [{size}]", "backup_controller_page_id": "KNu: {id}", - "backup_controller_page_info": "Yedekleme bilgileri", + "backup_controller_page_info": "Yedekleme Bilgileri", "backup_controller_page_none_selected": "Hiçbiri seçilmedi", "backup_controller_page_remainder": "Kalan", "backup_controller_page_remainder_sub": "Seçili albümlerden yedeklenecek kalan öğeler", @@ -598,6 +600,7 @@ "backup_controller_page_turn_on": "Ön planda yedeklemeyi aç", "backup_controller_page_uploading_file_info": "Dosya bilgisi yükleniyor", "backup_err_only_album": "Tek albüm kaldırılamaz", + "backup_error_sync_failed": "Senkronizasyon başarısız. Yedekleme işlemi gerçekleştirilemiyor.", "backup_info_card_assets": "öğeler", "backup_manual_cancelled": "İptal Edildi", "backup_manual_in_progress": "Yükleme halihazırda devam ediyor. Bir süre sonra deneyin", @@ -614,7 +617,7 @@ "biometric_not_available": "Bu cihazda biyometrik kimlik doğrulama mevcut değil", "birthdate_saved": "Doğum günü başarılı bir şekilde kaydedildi", "birthdate_set_description": "Doğum günü, fotoğraftaki insanın fotoğraf çekildiği zamandaki yaşının hesaplanması için kullanılır.", - "blurred_background": "Bulanık arkaplan", + "blurred_background": "Bulanık arka plan", "bugs_and_feature_requests": "Hatalar ve Özellik Talepleri", "build": "Yapı", "build_image": "Görüntü Oluştur", @@ -676,7 +679,7 @@ "clear": "Temizle", "clear_all": "Hepsini temizle", "clear_all_recent_searches": "Son aramaların hepsini temizle", - "clear_file_cache": "Dosya Önbelleği Temizle", + "clear_file_cache": "Dosya Önbelleğini Temizle", "clear_message": "Mesajı temizle", "clear_value": "Değeri temizle", "client_cert_dialog_msg_confirm": "Tamam", @@ -711,7 +714,7 @@ "confirm_tag_face_unnamed": "Bu yüzü etiketlemek ister misin?", "connected_device": "Cihaz bağlandı", "connected_to": "Bağlı", - "contain": "İçermek", + "contain": "Sığdır", "context": "Bağlam", "continue": "Devam et", "control_bottom_app_bar_create_new_album": "Yeni albüm", @@ -733,7 +736,7 @@ "copy_to_clipboard": "Panoya Kopyala", "country": "Ülke", "cover": "Kapla", - "covers": "Kaplar", + "covers": "Kapak", "create": "Oluştur", "create_album": "Albüm oluştur", "create_album_page_untitled": "Başlıksız", @@ -1117,7 +1120,7 @@ "header_settings_field_validator_msg": "Değer boş olamaz", "header_settings_header_name_input": "Header adı", "header_settings_header_value_input": "Header değeri", - "headers_settings_tile_subtitle": "Uygulamanın her ağ isteğiyle birlikte göndermesi gereken proxy header'ları tanımlayın", + "headers_settings_tile_subtitle": "Uygulamanın her ağ isteğinde göndermesi gereken proxy başlıklarını tanımlayın", "headers_settings_tile_title": "Özel proxy headers", "hi_user": "Merhaba {name} {email}", "hide_all_people": "Tüm kişileri gizle", @@ -1129,11 +1132,11 @@ "home_page_add_to_album_conflicts": "{album} albümüne {added} öğe eklendi. {failed} öğe zaten albümdeydi.", "home_page_add_to_album_err_local": "Yerel öğeler henüz albümlere eklenemiyor, atlanıyor", "home_page_add_to_album_success": "{album} albümüne {added} öğe eklendi.", - "home_page_album_err_partner": "Partner öğeler henüz bir albüme eklenemiyor, atlanıyor", + "home_page_album_err_partner": "Ortak öğeler henüz bir albüme eklenemiyor, atlanıyor", "home_page_archive_err_local": "Yerel öğeler henüz arşivlenemiyor, atlanıyor", - "home_page_archive_err_partner": "Partner öğeler henüz arşivlenemiyor, atlanıyor", + "home_page_archive_err_partner": "Ortak öğeler henüz arşivlenemiyor, atlanıyor", "home_page_building_timeline": "Zaman çizelgesi oluşturuluyor", - "home_page_delete_err_partner": "Partner öğeler silinemez, atlanıyor", + "home_page_delete_err_partner": "Ortak öğeler silinemez, atlanıyor", "home_page_delete_remote_err_local": "Uzaktan silme seçimindeki yerel öğeler atlanıyor", "home_page_favorite_err_local": "Yerel öğeler henüz favorilere eklenemiyor, atlanıyor", "home_page_favorite_err_partner": "Ortak öğeler henüz favorilere eklenemiyor, atlanıyor", @@ -1236,7 +1239,7 @@ "local_asset_cast_failed": "Sunucuya yüklenmemiş bir öğe yansıtılamaz", "local_assets": "Yerel Öğeler", "local_media_summary": "Yerel Medya Özeti", - "local_network": "Yerel Wi-Fi", + "local_network": "Yerel ağ", "local_network_sheet_info": "Uygulama belirlenmiş Wi-Fi ağını kullanırken bu URL üzerinden sunucuya bağlanacaktır", "location_permission": "Konum izni", "location_permission_content": "Otomatik geçiş özelliğinin çalışabilmesi için Immich'in mevcut Wi-Fi ağının adını bilmesi, bunu sağlamak için de tam konum iznine ihtiyacı vardır", @@ -1313,7 +1316,7 @@ "map_settings_date_range_option_years": "Son {years} yıl", "map_settings_dialog_title": "Harita Ayarları", "map_settings_include_show_archived": "Arşivdekileri dahil et", - "map_settings_include_show_partners": "Partnerleri Dahil Et", + "map_settings_include_show_partners": "Ortakları Dahil Et", "map_settings_only_show_favorites": "Sadece Favorileri Göster", "map_settings_theme_settings": "Harita Teması", "map_zoom_to_see_photos": "Fotoğrafları görmek için uzaklaştırın", @@ -1340,7 +1343,7 @@ "merged_people_count": "{count, plural, one {# kişi} other {# kişi}} birleştirildi", "minimize": "Küçült", "minute": "Dakika", - "minutes": "Dakikalar", + "minutes": "Dakika", "missing": "Eksik", "model": "Model", "month": "Ay", @@ -1389,14 +1392,14 @@ "no_cast_devices_found": "Yansıtılacak cihaz bulunamadı", "no_checksum_local": "Sağlama toplamı mevcut değil - yerel varlıkları alamıyor", "no_checksum_remote": "Sağlama toplamı mevcut değil - uzak varlık alınamıyor", - "no_duplicates_found": "Çift bulunamadı.", + "no_duplicates_found": "Hiçbir kopya bulunamadı.", "no_exif_info_available": "EXIF bilgisi mevcut değil", "no_explore_results_message": "Koleksiyonunuzu keşfetmek için daha fazla fotoğraf yükleyin.", "no_favorites_message": "En sevdiğiniz fotoğraf ve videoları hızlıca bulmak için favorilere ekleyin", "no_libraries_message": "Fotoğraf ve videolarınızı görmek için bir harici kütüphane oluşturun", "no_local_assets_found": "Bu sağlama toplamı ile yerel varlık bulunamadı", "no_locked_photos_message": "Kilitli klasördeki fotoğraf ve videolar gizlidir; kitaplığınızda gezinirken veya arama yaparken görünmezler.", - "no_name": "İsim yok", + "no_name": "İsim Yok", "no_notifications": "Bildirim yok", "no_people_found": "Eşleşen kişi bulunamadı", "no_places": "Yer yok", @@ -1456,13 +1459,13 @@ "partner_can_access_location": "Fotoğraf ve videolarınızın çekildiği konum", "partner_list_user_photos": "{user} fotoğrafları", "partner_list_view_all": "Tümünü gör", - "partner_page_empty_message": "Fotoğraflarınız henüz hiçbir partnerle paylaşılmadı.", + "partner_page_empty_message": "Fotoğraflarınız henüz hiçbir ortakla paylaşılmadı.", "partner_page_no_more_users": "Eklenecek başka kullanıcı yok", - "partner_page_partner_add_failed": "Partner eklenemedi", - "partner_page_select_partner": "Partner seç", + "partner_page_partner_add_failed": "Ortak eklenemedi", + "partner_page_select_partner": "Ortak seç", "partner_page_shared_to_title": "Paylaşıldı", "partner_page_stop_sharing_content": "{partner} artık fotoğraflarınıza erişemeyecek.", - "partner_sharing": "Ortak paylaşımı", + "partner_sharing": "Ortak Paylaşımı", "partners": "Ortaklar", "password": "Şifre", "password_does_not_match": "Şifre eşleşmiyor", @@ -1486,10 +1489,10 @@ "permanent_deletion_warning": "Kalıcı silme uyarısı", "permanent_deletion_warning_setting_description": "Nesneleri kalıcı olarak silerken uyarı göster", "permanently_delete": "Kalıcı olarak sil", - "permanently_delete_assets_count": "{count, plural, one {öğe} other {öğeler}} kalıcı olarak silindi", + "permanently_delete_assets_count": "{count, plural, one {öğe} other {öğe}} kalıcı olarak silindi", "permanently_delete_assets_prompt": "Bu {count, plural, one {öğeyi} other {# öğeleri}} kalıcı olarak silmek istediğinizden emin misiniz? Bu işlem {count, plural, one {bu öğeyi} other {bu öğeleri}} albümlerinizden de kaldırır.", "permanently_deleted_asset": "Kalıcı olarak silinmiş öğeler", - "permanently_deleted_assets_count": "{count, plural, one {# öğe} other {# öğeler}} kalıcı olarak silindi", + "permanently_deleted_assets_count": "{count, plural, one {# öğe} other {# öğe}} kalıcı olarak silindi", "permission": "İzin", "permission_empty": "İzniniz boş olmamalı", "permission_onboarding_back": "Geri", @@ -1501,9 +1504,9 @@ "permission_onboarding_permission_limited": "Sınırlı izin. Immich'in tüm fotoğrav ve videolarınızı yedeklemesine ve yönetmesine izin vermek için Ayarlar'da fotoğraf ve video izinlerini verin.", "permission_onboarding_request": "Immich'in fotoğraflarınızı ve videolarınızı görüntüleyebilmesi için izne ihtiyacı var.", "person": "Kişi", - "person_age_months": "{months, plural, one {# month} other {# months}} eski", - "person_age_year_months": "1 yıl, {months, plural, one {# month} other {# months}} eski", - "person_age_years": "{years, plural, other {# sene}} önce", + "person_age_months": "{months, plural, one {# aylık} other {# aylık}}", + "person_age_year_months": "1 yıl, {months, plural, one {# aylık} other {# aylık}}", + "person_age_years": "{years, plural, other {# yaşında}}", "person_birthdate": "{date} tarihinde doğdu", "person_hidden": "{name}{hidden, select, true { (gizli)} other {}}", "photo_shared_all_users": "Fotoğraflarınızı tüm kullanıcılarla paylaştınız gibi görünüyor veya paylaşacak kullanıcı bulunmuyor.", @@ -1673,8 +1676,8 @@ "resume": "Devam et", "resume_paused_jobs": "Sürdür {count, plural, one {# duraklatılmış iş} other {# duraklatılmış işler}}", "retry_upload": "Yeniden yüklemeyi dene", - "review_duplicates": "Çiftleri gözden geçir", - "review_large_files": "Büyük dosyaları inceleyin", + "review_duplicates": "Kopyaları gözden geçir", + "review_large_files": "Büyük dosyaları incele", "role": "Rol", "role_editor": "Düzenleyici", "role_viewer": "Görüntüleyici", @@ -1785,7 +1788,7 @@ "set_slideshow_to_fullscreen": "Slayt gösterisini tam ekran yap", "set_stack_primary_asset": "Birincil öğe olarak ayarla", "setting_image_viewer_help": "Görüntüleyici önce küçük resmi gösterir, ardından orta boy önizlemeyi (etkinleştirilmişse) ve son olarak orijinali (etkinleştirilmişse) gösterir.", - "setting_image_viewer_original_subtitle": "Orijinal tam çözünürlüklü görüntüyü göstermek için etkinleştirin. Veri kullanımını azaltmak için devre dışı bırakın (hem ağ hem de cihaz önbelleği).", + "setting_image_viewer_original_subtitle": "Orijinal tam çözünürlüklü görüntüyü (büyük!) yüklemek için etkinleştirin. Veri kullanımını azaltmak için devre dışı bırakın (hem ağ hem de cihaz önbelleği).", "setting_image_viewer_original_title": "Orijinal görüntüyü göster", "setting_image_viewer_preview_subtitle": "Orta çözünürlüklü bir görüntü göstermek için etkinleştirin. Orijinali doğrudan göstermek veya yalnızca küçük resmi kullanmak için devre dışı bırakın.", "setting_image_viewer_preview_title": "Önizleme görüntüsü göster", @@ -1865,14 +1868,14 @@ "shared_photos_and_videos_count": "{assetCount, plural, other {# paylaşılan fotoğraflar & videolar.}}", "shared_with_me": "Benimle paylaşılanlar", "shared_with_partner": "{partner} ile paylaşıldı", - "sharing": "Paylaşılıyor", + "sharing": "Paylaşım", "sharing_enter_password": "Bu sayfayı görebilmek için lütfen şifreyi giriniz.", "sharing_page_album": "Paylaşılan albümler", "sharing_page_description": "Ağınızdaki kişilerle fotoğraf ve video paylaşmak için paylaşımlı albümler oluşturun.", "sharing_page_empty_list": "LİSTEYİ BOŞALT", "sharing_sidebar_description": "Yan panelde paylaşılanlara kısa yol göster", "sharing_silver_appbar_create_shared_album": "Yeni paylaşılan albüm", - "sharing_silver_appbar_share_partner": "Partnerle paylaş", + "sharing_silver_appbar_share_partner": "Ortakla paylaş", "shift_to_permanent_delete": "Öğeyi kalıcı olarak silmek için ⇧ tuşuna basın", "show_album_options": "Albüm ayarlarını göster", "show_albums": "Albümleri göster", @@ -1978,7 +1981,7 @@ "theme_setting_system_primary_color_title": "Sistem rengini kullan", "theme_setting_system_theme_switch": "Otomatik (sistem ayarına göre)", "theme_setting_theme_subtitle": "Uygulama teması seç", - "theme_setting_three_stage_loading_subtitle": "Üç aşamalı yükleme, yükleme performansını artırabilir ancak önemli ölçüde daha yüksek ağ yüküne sebep olur", + "theme_setting_three_stage_loading_subtitle": "Üç aşamalı yükleme, yükleme performansını artırabilir ancak ağ yükünü önemli ölçüde artırır", "theme_setting_three_stage_loading_title": "Üç aşamalı yüklemeyi etkinleştir", "they_will_be_merged_together": "Birlikte birleştirilecekler", "third_party_resources": "Üçüncü taraf kaynaklar", @@ -2083,7 +2086,7 @@ "username": "Kullanıcı adı", "users": "Kullanıcılar", "users_added_to_album_count": "Albüme {count, plural, one {# user} other {# users}} eklendi", - "utilities": "Yardımcı Uygulamalar", + "utilities": "Yardımcı Programlar", "validate": "Doğrula", "validate_endpoint_error": "Lütfen geçerli bir URL girin", "variables": "Değişkenler", diff --git a/i18n/uk.json b/i18n/uk.json index f34f581315..0640982fe6 100644 --- a/i18n/uk.json +++ b/i18n/uk.json @@ -28,10 +28,12 @@ "add_to_album": "Додати у альбом", "add_to_album_bottom_sheet_added": "Додано до {album}", "add_to_album_bottom_sheet_already_exists": "Вже є в {album}", + "add_to_album_bottom_sheet_some_local_assets": "Деякі локальні ресурси не вдалося додати до альбому", "add_to_album_toggle": "Перемикання вибору для {album}", "add_to_albums": "Додати до альбомів", "add_to_albums_count": "Додати до альбомів ({count})", "add_to_shared_album": "Додати у спільний альбом", + "add_upload_to_stack": "Додати завантаження до стеку", "add_url": "Додати URL", "added_to_archive": "Додано до архіву", "added_to_favorites": "Додано до обраного", @@ -598,6 +600,7 @@ "backup_controller_page_turn_on": "Увімкнути резервне копіювання в активному режимі", "backup_controller_page_uploading_file_info": "Завантажую інформацію про файл", "backup_err_only_album": "Не можу видалити єдиний альбом", + "backup_error_sync_failed": "Помилка синхронізації. Не вдається обробити резервну копію.", "backup_info_card_assets": "елементи", "backup_manual_cancelled": "Скасовано", "backup_manual_in_progress": "Завантаження вже відбувається. Спробуйте згодом", @@ -1036,6 +1039,7 @@ "exif_bottom_sheet_description_error": "Помилка під час оновлення опису", "exif_bottom_sheet_details": "ПОДРОБИЦІ", "exif_bottom_sheet_location": "МІСЦЕ", + "exif_bottom_sheet_no_description": "Без опису", "exif_bottom_sheet_people": "ЛЮДИ", "exif_bottom_sheet_person_add_person": "Додати ім'я", "exit_slideshow": "Вийти зі слайд-шоу", @@ -1111,7 +1115,7 @@ "haptic_feedback_title": "Тактильна віддача", "has_quota": "Квота", "hash_asset": "Гешувати файл", - "hashed_assets": "Гешовані фото та відео", + "hashed_assets": "Хеши", "hashing": "Хешування", "header_settings_add_header_tip": "Додати заголовок", "header_settings_field_validator_msg": "Значення не може бути порожнім", diff --git a/i18n/zh_Hant.json b/i18n/zh_Hant.json index b972639153..267eb767c3 100644 --- a/i18n/zh_Hant.json +++ b/i18n/zh_Hant.json @@ -28,18 +28,19 @@ "add_to_album": "加入到相簿", "add_to_album_bottom_sheet_added": "新增到 {album}", "add_to_album_bottom_sheet_already_exists": "已在 {album} 中", + "add_to_album_bottom_sheet_some_local_assets": "無法將某些本機資產新增到相簿", "add_to_album_toggle": "選擇相簿{album}", "add_to_albums": "加入相簿", - "add_to_albums_count": "將({count})個項目加入相簿", + "add_to_albums_count": "將 ({count}) 個項目加入相簿", "add_to_shared_album": "加到共享相簿", - "add_url": "建立連結", + "add_url": "新增 URL", "added_to_archive": "移至封存", "added_to_favorites": "加入收藏", "added_to_favorites_count": "將 {count, number} 個項目加入收藏", "admin": { "add_exclusion_pattern_description": "新增排除條件。支援使用「*」、「 **」、「?」來找尋符合規則的字串。如果要在任何名為「Raw」的目錄內排除所有符合條件的檔案,請使用「**/Raw/**」。如果要排除所有「.tif」結尾的檔案,請使用「**/*.tif」。如果要排除某個絕對路徑,請使用「/path/to/ignore/**」。", "admin_user": "管理員", - "asset_offline_description": "此外部媒體庫項目已無法在磁碟上找到,並已移至垃圾桶。若該檔案是在媒體庫內移動,請在時間軸中查看新的對應項目。若要還原此項目,請確保下方的檔案路徑可供 Immich 存取,並重新掃描媒體庫。", + "asset_offline_description": "此外部媒體庫項目已無法在磁碟上找到,並已移至垃圾桶。若該檔案是在媒體庫內移動,請在時間軸中檢視新的對應項目。若要還原此項目,請確保下方的檔案路徑可供 Immich 存取,並重新掃描媒體庫。", "authentication_settings": "驗證設定", "authentication_settings_description": "管理密碼、OAuth 與其他驗證設定", "authentication_settings_disable_all": "確定要停用所有登入方式嗎?這樣會完全無法登入。", @@ -49,8 +50,8 @@ "backup_database_enable_description": "啟用資料庫備份", "backup_keep_last_amount": "保留先前備份的數量", "backup_onboarding_1_description": "在雲端或其他實體位置的異地備份副本。", - "backup_onboarding_2_description": "儲存在不同裝置上的本地副本。這包括主要檔案及其本地備份。", - "backup_onboarding_3_description": "您資料的總備份份數,包括原始檔案在內。這包括 1 份異地備份與 2 份本地副本。", + "backup_onboarding_2_description": "儲存在不同裝置上的本機副本。這包括主要檔案及其本機備份。", + "backup_onboarding_3_description": "您資料的總備份份數,包括原始檔案在內。這包括 1 份異地備份與 2 份本機副本。", "backup_onboarding_description": "建議採用 3-2-1 備份策略 來保護您的資料。您應保留已上傳的照片/影片副本,以及 Immich 資料庫,以建立完整的備份方案。", "backup_onboarding_footer": "更多備份 Immich 資訊,請參考說明文件。", "backup_onboarding_parts_title": "遵從備份原則 3-2-1:", @@ -81,7 +82,7 @@ "image_format": "格式", "image_format_description": "WebP 能產生相對於 JPEG 更小的檔案,但編碼速度較慢。", "image_fullsize_description": "移除中繼資料的大尺寸影像,在放大圖片時使用", - "image_fullsize_enabled": "啟用大尺寸影像生成", + "image_fullsize_enabled": "啟用大尺寸影像產生", "image_fullsize_enabled_description": "產生非網頁友善格式的大尺寸影像。啟用「偏好嵌入的預覽」時,會直接使用內嵌預覽而不進行轉換。不會影響 JPEG 等網頁友善格式。", "image_fullsize_quality_description": "大尺寸影像品質,範圍為 1 到 100。數值越高品質越好,但檔案也會越大。", "image_fullsize_title": "大尺寸影像設定", @@ -123,6 +124,13 @@ "logging_enable_description": "啟用日誌記錄", "logging_level_description": "啟用時的日誌層級。", "logging_settings": "日誌", + "machine_learning_availability_checks": "可用性檢查", + "machine_learning_availability_checks_description": "自動偵測並優先選擇可用的機器學習伺服器", + "machine_learning_availability_checks_enabled": "啟用可用性檢查", + "machine_learning_availability_checks_interval": "檢查間隔", + "machine_learning_availability_checks_interval_description": "可用性檢查之間的間隔(毫秒)", + "machine_learning_availability_checks_timeout": "請求超時", + "machine_learning_availability_checks_timeout_description": "可用性檢查超時(毫秒)", "machine_learning_clip_model": "CLIP 模型", "machine_learning_clip_model_description": "這裡有份 CLIP 模型名單。注意:更換模型後須對所有圖片重新執行「智慧搜尋」任務。", "machine_learning_duplicate_detection": "重複項目偵測", @@ -151,14 +159,14 @@ "machine_learning_smart_search_description": "使用 CLIP 嵌入向量以語意方式搜尋影像", "machine_learning_smart_search_enabled": "啟用智慧搜尋", "machine_learning_smart_search_enabled_description": "如果停用,影像將不會被編碼以進行智慧搜尋。", - "machine_learning_url_description": "機器學習伺服器的 URL。若提供多個 URL,系統會依序逐一嘗試,直到其中一台成功回應為止(由前到後)。未回應的伺服器將被暫時忽略,直到其重新上線。", + "machine_learning_url_description": "機器學習伺服器的 URL。若提供多個 URL,系統會依序逐一嘗試,直到其中一臺成功回應為止(由前到後)。未回應的伺服器將被暫時忽略,直到其重新上線。", "manage_concurrency": "管理併發", "manage_log_settings": "管理日誌設定", "map_dark_style": "深色樣式", "map_enable_description": "啟用地圖功能", "map_gps_settings": "地圖與 GPS 設定", "map_gps_settings_description": "管理地圖與 GPS(反向地理編碼)設定", - "map_implications": "地圖功能依賴外部圖磚服務(tiles.immich.cloud)", + "map_implications": "地圖功能仰賴外部圖磚服務(tiles.immich.cloud)", "map_light_style": "淺色樣式", "map_manage_reverse_geocoding_settings": "管理逆向地理編碼設定", "map_reverse_geocoding": "反向地理編碼", @@ -191,7 +199,7 @@ "nightly_tasks_start_time_setting_description": "伺服器開始執行夜間任務的時間", "nightly_tasks_sync_quota_usage_setting": "同步配額使用情況", "nightly_tasks_sync_quota_usage_setting_description": "根據目前的使用量更新使用者的儲存配額", - "no_paths_added": "沒有已添加的路徑", + "no_paths_added": "沒有已新增的路徑", "no_pattern_added": "尚未新增排除規則", "note_apply_storage_label_previous_assets": "提示:若要將儲存標籤套用到先前上傳的媒體檔案,請執行", "note_cannot_be_changed_later": "注意:此設定日後無法變更!", @@ -205,12 +213,12 @@ "notification_email_sent_test_email_button": "傳送測試電子郵件並儲存", "notification_email_setting_description": "寄送電子郵件通知的設定", "notification_email_test_email": "傳送測試電子郵件", - "notification_email_test_email_failed": "無法發送測試電子郵件,請檢查您的設定資訊", - "notification_email_test_email_sent": "測試電子郵件已發送至 {email}。請檢查您的收件匣。", + "notification_email_test_email_failed": "無法傳送測試電子郵件,請檢查您的設定資訊", + "notification_email_test_email_sent": "測試電子郵件已傳送至 {email}。請檢查您的收件匣。", "notification_email_username_description": "用於與電子郵件伺服器驗證的使用者名稱", "notification_enable_email_notifications": "啟用電子郵件通知", "notification_settings": "通知設定", - "notification_settings_description": "管理通知設置,包括電子郵件", + "notification_settings_description": "管理通知設定,包括電子郵件", "oauth_auto_launch": "自動啟動", "oauth_auto_launch_description": "進入登入頁面時,自動啟動 OAuth 登入流程", "oauth_auto_register": "自動註冊", @@ -252,7 +260,7 @@ "server_external_domain_settings": "外部網域", "server_external_domain_settings_description": "公開分享連結的網域,包含 http(s)://", "server_public_users": "公開使用者", - "server_public_users_description": "在將使用者新增到共享相簿時,會列出所有使用者的姓名與電子郵件。停用此功能後,使用者清單將僅供系統管理員查看。", + "server_public_users_description": "在將使用者新增到共享相簿時,會列出所有使用者的姓名與電子郵件。停用此功能後,使用者清單將僅供系統管理員檢視。", "server_settings": "伺服器設定", "server_settings_description": "管理伺服器設定", "server_welcome_message": "歡迎訊息", @@ -262,10 +270,10 @@ "slideshow_duration_description": "每張圖片放映的秒數", "smart_search_job_description": "執行機器學習有助於智慧搜尋", "storage_template_date_time_description": "檔案的建立時間戳會用於日期與時間資訊", - "storage_template_date_time_sample": "採樣時間 {date}", + "storage_template_date_time_sample": "取樣時間 {date}", "storage_template_enable_description": "啟用儲存範本引擎", - "storage_template_hash_verification_enabled": "雜湊函数驗證已啟用", - "storage_template_hash_verification_enabled_description": "啟用雜湊函数驗證,除非您很清楚地知道這個選項的作用,否則請勿停用此功能", + "storage_template_hash_verification_enabled": "雜湊函式驗證已啟用", + "storage_template_hash_verification_enabled_description": "啟用雜湊函式驗證,除非您很清楚地知道這個選項的作用,否則請勿停用此功能", "storage_template_migration": "儲存範本遷移", "storage_template_migration_description": "將目前的 {template} 套用到先前上傳的項目", "storage_template_migration_info": "儲存範本會將所有副檔名轉換為小寫。範本變更只會套用到新的項目。若要將範本追溯套用到先前上傳的項目,請執行 {job}。", @@ -286,15 +294,15 @@ "template_email_update_album": "相簿更新範本", "template_email_welcome": "歡迎郵件範本", "template_settings": "通知範本", - "template_settings_description": "管理通知的自定義範本", - "theme_custom_css_settings": "自定義 CSS", + "template_settings_description": "管理通知的自訂範本", + "theme_custom_css_settings": "自訂 CSS", "theme_custom_css_settings_description": "可以用層疊樣式表(CSS)來自訂 Immich 的設計。", "theme_settings": "主題設定", - "theme_settings_description": "自訂 Immich 的網頁界面", + "theme_settings_description": "自訂 Immich 的網頁介面", "thumbnail_generation_job": "產生縮圖", "thumbnail_generation_job_description": "為每個檔案產生大、小及模糊縮圖,也為每位人物產生縮圖", "transcoding_acceleration_api": "加速 API", - "transcoding_acceleration_api_description": "此 API 會使用您的硬體以加速轉碼流程。此設定採「盡力而為」模式——若轉碼失敗,將會回退至軟體轉碼。VP9 是否能運作,取決於您的硬體配置。", + "transcoding_acceleration_api_description": "此 API 會使用您的硬體以加速轉碼流程。此設定採「盡力而為」模式——若轉碼失敗,將會回退至軟體轉碼。VP9 是否能運作,取決於您的硬體設定。", "transcoding_acceleration_nvenc": "NVENC(需要 NVIDIA GPU)", "transcoding_acceleration_qsv": "Quick Sync(需要第 7 代或更新的 Intel 處理器)", "transcoding_acceleration_rkmpp": "RKMPP(僅適用於 Rockchip SOCs)", @@ -314,7 +322,7 @@ "transcoding_constant_quality_mode_description": "ICQ 的效果優於 CQP,但部分硬體加速裝置不支援此模式。設定此選項時,在使用以品質為基準的編碼時會優先採用所指定的模式。NVENC 不支援 ICQ,因此此設定在 NVENC 下會被忽略。", "transcoding_constant_rate_factor": "恆定速率因子(-crf)", "transcoding_constant_rate_factor_description": "視訊品質等級。典型值為 H.264 的 23、HEVC 的 28、VP9 的 31 和 AV1 的 35。數值越低,品質越好,但會產生較大的檔案。", - "transcoding_disabled_description": "不對任何影片進行轉碼,可能會導致部分客戶端無法正常播放", + "transcoding_disabled_description": "不對任何影片進行轉碼,可能會導致部分用戶端無法正常播放", "transcoding_encoding_options": "編碼選項", "transcoding_encoding_options_description": "設定編碼影片的編解碼器、解析度、品質和其他選項", "transcoding_hardware_acceleration": "硬體加速", @@ -326,11 +334,11 @@ "transcoding_max_bitrate": "最大位元速率", "transcoding_max_bitrate_description": "設定最大位元率可以在輕微犧牲品質的情況下,讓檔案大小更容易預測。在 720p 解析度下,VP9 或 HEVC 的典型值為 2600 kbit/s,H.264 則為 4500 kbit/s。設為 0 則停用此功能。", "transcoding_max_keyframe_interval": "最大關鍵幀間隔", - "transcoding_max_keyframe_interval_description": "設置關鍵幀之間的最大幀距。較低的值會降低壓縮效率,但可以改善搜尋時間,並有可能會改善快速變動場景的品質。0 會自動設置此值。", + "transcoding_max_keyframe_interval_description": "設定關鍵幀之間的最大幀距。較低的值會降低壓縮效率,但可以改善搜尋時間,並有可能會改善快速變動場景的品質。0 會自動設定此值。", "transcoding_optimal_description": "高於目標解析度或格式不在可接受範圍的影片", "transcoding_policy": "轉碼策略", "transcoding_policy_description": "設定影片進行轉碼的條件", - "transcoding_preferred_hardware_device": "首選硬體設備", + "transcoding_preferred_hardware_device": "首選硬體裝置", "transcoding_preferred_hardware_device_description": "僅適用於 VAAPI 和 QSV。設定用於硬體轉碼的 dri 節點。", "transcoding_preset_preset": "預設值(-preset)", "transcoding_preset_preset_description": "壓縮速度。較慢的預設值會產生較小的檔案,並在鎖定位元率時提升品質。VP9 在速度高於「faster」時將忽略設定。", @@ -342,10 +350,10 @@ "transcoding_target_resolution": "目標解析度", "transcoding_target_resolution_description": "較高的解析度可以保留更多細節,但編碼時間較長,檔案也較大,且可能降低應用程式的回應速度。", "transcoding_temporal_aq": "時間自適應量化(Temporal AQ)", - "transcoding_temporal_aq_description": "僅適用於 NVENC,可提升高細節、低動態場景的畫質。可能與較舊的設備不相容。", + "transcoding_temporal_aq_description": "僅適用於 NVENC,可提升高細節、低動態場景的畫質。可能與較舊的裝置不相容。", "transcoding_threads": "執行緒數量", - "transcoding_threads_description": "較高的值會加快編碼速度,但會減少伺服器在運行過程中處理其他任務的空間。此值不應超過 CPU 核心數。設定為 0 可以最大化利用率。", - "transcoding_tone_mapping": "色調映射", + "transcoding_threads_description": "較高的值會加快編碼速度,但會減少伺服器在執行過程中處理其他任務的空間。此值不應超過 CPU 核心數。設定為 0 可以最大化利用率。", + "transcoding_tone_mapping": "色調對映", "transcoding_tone_mapping_description": "在將 HDR 影片轉換為 SDR 時,盡量維持原始觀感。每種演算法在色彩、細節和亮度方面都有不同的權衡。Hable 保留細節,Mobius 保留色彩,Reinhard 保留亮度。", "transcoding_transcode_policy": "轉碼策略", "transcoding_transcode_policy_description": "影片何時應進行轉碼的策略。HDR 影片一定會轉碼(除非停用轉碼)。", @@ -360,7 +368,7 @@ "trash_settings_description": "管理垃圾桶設定", "unlink_all_oauth_accounts": "解除所有 OAuth 帳號的連結", "unlink_all_oauth_accounts_description": "在遷移至新的服務提供者前,請不要忘記要先解除所有與 OAuth 帳戶的連結。", - "unlink_all_oauth_accounts_prompt": "您是否確認要解除所有與 OAuth 帳戶的連結? 所有相關的使用者身份會被重設,並且不能被還原。", + "unlink_all_oauth_accounts_prompt": "您是否確認要解除所有與 OAuth 帳戶的連結?所有相關的使用者身份會被重設,並且不能被還原。", "user_cleanup_job": "清理使用者", "user_delete_delay": "{user} 的帳號和項目會在 {delay, plural, one {# 天} other {# 天}} 後永久刪除。", "user_delete_delay_settings": "延後刪除", @@ -390,9 +398,9 @@ "advanced_settings_enable_alternate_media_filter_subtitle": "使用此選項可在同步時依其他條件篩選媒體。僅在應用程式無法偵測到所有相簿時再嘗試使用。", "advanced_settings_enable_alternate_media_filter_title": "[實驗性] 使用替代的裝置相簿同步篩選器", "advanced_settings_log_level_title": "日誌等級:{level}", - "advanced_settings_prefer_remote_subtitle": "部分裝置從本機ˊ體庫載入縮圖的速度非常慢。啟用此設定可改為載入遠端圖片。", + "advanced_settings_prefer_remote_subtitle": "部分裝置從本機媒體庫載入縮圖的速度非常慢。啟用此設定可改為載入遠端圖片。", "advanced_settings_prefer_remote_title": "偏好遠端影像", - "advanced_settings_proxy_headers_subtitle": "定義 Immich 在每次網路請求時應該發送的代理標頭", + "advanced_settings_proxy_headers_subtitle": "定義 Immich 在每次網路請求時應該傳送的代理標頭", "advanced_settings_proxy_headers_title": "代理標頭", "advanced_settings_readonly_mode_subtitle": "開啟唯讀模式後,照片只能瀏覽,像是多選影像、分享、投放、刪除等功能都會關閉。可在主畫面透過使用者頭像來開啟/關閉唯讀模式", "advanced_settings_readonly_mode_title": "唯讀模式", @@ -400,7 +408,7 @@ "advanced_settings_self_signed_ssl_title": "允許自簽的 SSL 憑證", "advanced_settings_sync_remote_deletions_subtitle": "當在網頁端執行刪除或還原操作時,自動在此裝置上刪除或還原該媒體", "advanced_settings_sync_remote_deletions_title": "同步遠端刪除 [實驗性]", - "advanced_settings_tile_subtitle": "進階用戶設定", + "advanced_settings_tile_subtitle": "進階使用者設定", "advanced_settings_troubleshooting_subtitle": "啟用額外功能以進行疑難排解", "advanced_settings_troubleshooting_title": "疑難排解", "age_months": "{months, plural, one {# 個月} other {# 個月}}", @@ -423,6 +431,7 @@ "album_remove_user_confirmation": "確定要移除 {user} 嗎?", "album_search_not_found": "找不到符合搜尋條件的相簿", "album_share_no_users": "看來您與所有使用者共享了這本相簿,或沒有其他使用者可供分享。", + "album_summary": "相簿摘要", "album_updated": "更新相簿時", "album_updated_setting_description": "當共享相簿有新項目時用電子郵件通知我", "album_user_left": "離開 {album}", @@ -435,12 +444,12 @@ "album_viewer_appbar_share_leave": "離開相簿", "album_viewer_appbar_share_to": "分享給", "album_viewer_page_share_add_users": "邀請其他人", - "album_with_link_access": "任何擁有連結的人都能查看此相簿中的照片與使用者。", + "album_with_link_access": "任何擁有連結的人都能檢視此相簿中的照片與人物。", "albums": "相簿", "albums_count": "{count, plural, one {{count, number} 個相簿} other {{count, number} 個相簿}}", "albums_default_sort_order": "預設相簿排序", "albums_default_sort_order_description": "建立新相簿時要初始化項目排序方式。", - "albums_feature_description": "一系列可以分享給其他用戶的項目。", + "albums_feature_description": "一系列可以分享給其他使用者的項目。", "albums_on_device_count": "此裝置有 ({count}) 個相簿", "all": "全部", "all_albums": "所有相簿", @@ -461,13 +470,14 @@ "app_bar_signout_dialog_title": "登出", "app_settings": "應用程式設定", "appears_in": "出現於", + "apply_count": "應用 ({count, number})", "archive": "封存", "archive_action_prompt": "已將 ({count}) 個加入進封存", "archive_or_unarchive_photo": "封存或取消封存照片", "archive_page_no_archived_assets": "未找到封存媒體", "archive_page_title": "封存 ({count})", "archive_size": "封存大小", - "archive_size_description": "設定要下載的封存檔案大小 (單位: GiB)", + "archive_size_description": "設定要下載的封存檔案大小 (單位:GiB)", "archived": "已封存", "archived_count": "{count, plural, other {已封存 # 個項目}}", "are_these_the_same_person": "同一位人物?", @@ -481,18 +491,20 @@ "asset_has_unassigned_faces": "媒體有未分配的臉孔", "asset_hashing": "正在計算雜湊…", "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_by": "媒體分類方式", "asset_list_layout_settings_group_by_month_day": "月份和日期", - "asset_list_layout_sub_title": "布局", - "asset_list_settings_subtitle": "相片格狀布局設定", + "asset_list_layout_sub_title": "版面", + "asset_list_settings_subtitle": "相片格狀版面設定", "asset_list_settings_title": "相片格狀檢視", "asset_offline": "媒體離線", "asset_offline_description": "此外部媒體已無法在磁碟中找到。請聯絡您的 Immich 管理員以取得協助。", "asset_restored_successfully": "媒體復原成功", "asset_skipped": "已跳過", "asset_skipped_in_trash": "已在垃圾桶", + "asset_trashed": "資產被丟棄", + "asset_troubleshoot": "資產故障排除", "asset_uploaded": "已上傳", "asset_uploading": "上傳中…", "asset_viewer_settings_subtitle": "管理您的媒體庫檢視器設定", @@ -521,13 +533,15 @@ "assets_were_part_of_album_count": "{count, plural, one {該媒體已} other {這些媒體已}}在相簿中", "assets_were_part_of_albums_count": "{count, plural, one {個} other {個}}項目已被儲存在相簿中", "authorized_devices": "已授權裝置", - "automatic_endpoint_switching_subtitle": "當可用時,透過指定的 Wi-Fi 在本地連線,其他情況則使用替代連線", + "automatic_endpoint_switching_subtitle": "當可用時,透過指定的 Wi-Fi 在本機連線,其他情況則使用替代連線", "automatic_endpoint_switching_title": "自動 URL 切換", "autoplay_slideshow": "自動播放幻燈片", - "back": "返回", - "back_close_deselect": "返回、關閉及取消選取", + "back": "上一頁", + "back_close_deselect": "回上一頁、關閉並取消選取", + "background_backup_running_error": "後臺備份目前正在執行,無法啟動手動備份", "background_location_permission": "背景存取位置權限", "background_location_permission_content": "為了在背景執行時切換網路,Immich 必須始終具有精確位置存取權限,才能讀取 Wi-Fi 網路名稱", + "background_options": "背景選項", "backup": "備份", "backup_album_selection_page_albums_device": "裝置上的相簿({count})", "backup_album_selection_page_albums_tap": "點一下以選取,點兩下以排除", @@ -535,9 +549,10 @@ "backup_album_selection_page_select_albums": "選取相簿", "backup_album_selection_page_selection_info": "選取資訊", "backup_album_selection_page_total_assets": "總不重複媒體數", + "backup_albums_sync": "備份相簿同步", "backup_all": "全部", "backup_background_service_backup_failed_message": "備份媒體失敗。正在重試…", - "backup_background_service_connection_failed_message": "連線伺服器失敗。正在重試…", + "backup_background_service_connection_failed_message": "連線至伺服器失敗。正在重試…", "backup_background_service_current_upload_notification": "正在上傳 {filename}", "backup_background_service_default_notification": "正在檢查新媒體…", "backup_background_service_error_title": "備份錯誤", @@ -554,7 +569,7 @@ "backup_controller_page_background_charging": "僅在充電時", "backup_controller_page_background_configure_error": "背景服務設定失敗", "backup_controller_page_background_delay": "新媒體備份延遲:{duration}", - "backup_controller_page_background_description": "開啟背景服務,即可在不需打開 App 的情況下,自動備份所有新媒體", + "backup_controller_page_background_description": "開啟背景服務,即可在不需開啟 App 的情況下,自動備份所有新媒體", "backup_controller_page_background_is_off": "背景自動備份已關閉", "backup_controller_page_background_is_on": "背景自動備份已開啟", "backup_controller_page_background_turn_off": "關閉背景服務", @@ -564,7 +579,7 @@ "backup_controller_page_backup_selected": "已選中: ", "backup_controller_page_backup_sub": "已備份的照片和影片", "backup_controller_page_created": "建立時間:{date}", - "backup_controller_page_desc_backup": "開啟前台備份,在打開 App 時自動將新媒體上傳至伺服器。", + "backup_controller_page_desc_backup": "開啟前臺備份,在開啟 App 時自動將新媒體上傳至伺服器。", "backup_controller_page_excluded": "已排除: ", "backup_controller_page_failed": "失敗({count})", "backup_controller_page_filename": "檔案名稱:{filename} [{size}]", @@ -575,15 +590,16 @@ "backup_controller_page_remainder_sub": "選取項目中尚未備份的照片與影片", "backup_controller_page_server_storage": "伺服器儲存空間", "backup_controller_page_start_backup": "開始備份", - "backup_controller_page_status_off": "前台自動備份已關閉", - "backup_controller_page_status_on": "前台自動備份已開啟", + "backup_controller_page_status_off": "前臺自動備份已關閉", + "backup_controller_page_status_on": "前臺自動備份已開啟", "backup_controller_page_storage_format": "{used} / {total} 已使用", "backup_controller_page_to_backup": "要備份的相簿", "backup_controller_page_total_sub": "已選取相簿中的所有不重複的照片與影片", - "backup_controller_page_turn_off": "關閉前台備份", - "backup_controller_page_turn_on": "開啟前台備份", + "backup_controller_page_turn_off": "關閉前臺備份", + "backup_controller_page_turn_on": "開啟前臺備份", "backup_controller_page_uploading_file_info": "上傳中的檔案資訊", "backup_err_only_album": "不能移除唯一的相簿", + "backup_error_sync_failed": "同步失敗,無法處理備份。", "backup_info_card_assets": "個媒體", "backup_manual_cancelled": "已取消", "backup_manual_in_progress": "上傳正在進行中,請稍後再試", @@ -591,20 +607,20 @@ "backup_manual_title": "上傳狀態", "backup_options": "備份選項", "backup_options_page_title": "備份選項", - "backup_setting_subtitle": "管理背景與前台上傳設定", + "backup_setting_subtitle": "管理背景與前臺上傳設定", "backup_settings_subtitle": "管理上傳設定", "backward": "由舊至新", "biometric_auth_enabled": "生物辨識驗證已啟用", "biometric_locked_out": "您已被鎖定無法使用生物辨識驗證", "biometric_no_options": "沒有生物辨識選項可用", - "biometric_not_available": "此設備上無法使用生物辨識驗證", + "biometric_not_available": "此裝置上無法使用生物辨識驗證", "birthdate_saved": "出生日期儲存成功", "birthdate_set_description": "出生日期用於計算此人在照片拍攝時的年齡。", "blurred_background": "背景模糊", "bugs_and_feature_requests": "錯誤及功能請求", "build": "建置編號", "build_image": "建置映像", - "bulk_delete_duplicates_confirmation": "您確定要批量刪除 {count, plural, one {# 個重複媒體} other {# 個重複媒體}} 嗎?系統將保留每組中大小最大的媒體,並永久刪除所有其他重複項目。此操作無法復原!", + "bulk_delete_duplicates_confirmation": "您確定要批次刪除 {count, plural, one {# 個重複媒體} other {# 個重複媒體}} 嗎?系統將保留每組中大小最大的媒體,並永久刪除所有其他重複項目。此操作無法復原!", "bulk_keep_duplicates_confirmation": "您確定要保留 {count, plural, one {# 個重複媒體} other {# 個重複媒體}} 嗎?這將在不刪除任何項目的情況下解決所有重複群組。", "bulk_trash_duplicates_confirmation": "您確定要批次將 {count, plural, one {# 個重複媒體} other {# 個重複媒體}}移至垃圾桶嗎?系統將保留每組中大小最大的媒體,並將所有其他重複項目移至垃圾桶。", "buy": "購買 Immich", @@ -621,7 +637,7 @@ "cache_settings_subtitle": "控制 Immich 行動應用程式的快取行為", "cache_settings_tile_subtitle": "設定本機儲存行為", "cache_settings_tile_title": "本機儲存空間", - "cache_settings_title": "緩存設定", + "cache_settings_title": "快取設定", "camera": "相機", "camera_brand": "相機品牌", "camera_model": "相機型號", @@ -632,7 +648,7 @@ "cannot_merge_people": "無法合併人物", "cannot_undo_this_action": "此操作無法復原!", "cannot_update_the_description": "無法更新描述", - "cast": "投影", + "cast": "投放", "cast_description": "設定可用的投放裝置", "change_date": "變更日期", "change_description": "變更描述", @@ -651,26 +667,28 @@ "change_pin_code": "變更 PIN 碼", "change_your_password": "變更您的密碼", "changed_visibility_successfully": "已成功變更可見性", + "charging": "充電", + "charging_requirement_mobile_backup": "後臺備份要求裝置正在充電", "check_corrupt_asset_backup": "檢查損毀的備份項目", "check_corrupt_asset_backup_button": "執行檢查", - "check_corrupt_asset_backup_description": "僅在連接 Wi-Fi 且所有媒體已完成備份後執行此檢查。此程序可能需要數分鐘。", + "check_corrupt_asset_backup_description": "僅在已連線至 Wi-Fi 且所有媒體已完成備份後執行此檢查。此程式可能需要數分鐘。", "check_logs": "檢查日誌", "choose_matching_people_to_merge": "選擇要合併的相符人物", "city": "城市", "clear": "清空", "clear_all": "全部清除", "clear_all_recent_searches": "清除所有最近的搜尋", - "clear_file_cache": "清除文件快取", + "clear_file_cache": "清除檔案快取", "clear_message": "清除訊息", "clear_value": "清除值", "client_cert_dialog_msg_confirm": "確定", "client_cert_enter_password": "輸入密碼", "client_cert_import": "匯入", - "client_cert_import_success_msg": "已匯入客戶端證書", - "client_cert_invalid_msg": "無效的證書文件或密碼錯誤", - "client_cert_remove_msg": "客戶端證書已移除", - "client_cert_subtitle": "僅支持PKCS12 (.p12, .pfx)格式。僅可在登入前進行證書的匯入和移除", - "client_cert_title": "SSL 客戶端證書", + "client_cert_import_success_msg": "已匯入用戶端憑證", + "client_cert_invalid_msg": "無效的憑證檔案或密碼錯誤", + "client_cert_remove_msg": "用戶端憑證已移除", + "client_cert_subtitle": "僅支援 PKCS12 (.p12, .pfx) 格式。僅可在登入前進行憑證的匯入和移除", + "client_cert_title": "SSL 用戶端憑證", "clockwise": "順時針", "close": "關閉", "collapse": "折疊", @@ -693,10 +711,10 @@ "confirm_password": "確認密碼", "confirm_tag_face": "您想要將此臉孔標籤為 {name} 嗎?", "confirm_tag_face_unnamed": "您想標籤這張臉嗎?", - "connected_device": "已連結裝置", - "connected_to": "已連接到", - "contain": "等比置入", - "context": "內容上下文", + "connected_device": "已連線裝置", + "connected_to": "已連線至", + "contain": "等比內縮", + "context": "脈絡", "continue": "繼續", "control_bottom_app_bar_create_new_album": "建立新相簿", "control_bottom_app_bar_delete_from_immich": "從 Immich 伺服器中刪除", @@ -724,7 +742,7 @@ "create_library": "建立媒體庫", "create_link": "建立連結", "create_link_to_share": "建立共享連結", - "create_link_to_share_description": "任何持有連結的人都允許查看所選相片", + "create_link_to_share_description": "任何持有連結的人都允許檢視所選相片", "create_new": "新增", "create_new_person": "建立新人物", "create_new_person_hint": "將選定的媒體分配給新人物", @@ -737,6 +755,7 @@ "create_user": "建立使用者", "created": "建立於", "created_at": "建立於", + "creating_linked_albums": "建立連結相簿 ...", "crop": "裁剪", "curated_object_page_title": "事物", "current_device": "目前裝置", @@ -744,7 +763,7 @@ "current_server_address": "目前的伺服器位址", "custom_locale": "自訂地區設定", "custom_locale_description": "根據語言與地區格式化日期與數字", - "custom_url": "自定義 URL", + "custom_url": "自訂 URL", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "YYYY 年 M 月 D 日 (E)", "dark": "深色", @@ -765,7 +784,7 @@ "default_locale": "預設地區", "default_locale_description": "依照您的瀏覽器地區設定格式化日期與數字", "delete": "刪除", - "delete_action_confirmation_message": "您確定要刪除此媒體嗎?此操作會將該媒體移至伺服器的垃圾桶,並會提示您是否要在本地同時刪除", + "delete_action_confirmation_message": "您確定要刪除此媒體嗎?此操作會將該媒體移至伺服器的垃圾桶,並會提示您是否要在本機同時刪除", "delete_action_prompt": "{count} 個已刪除", "delete_album": "刪除相簿", "delete_api_key_prompt": "您確定要刪除這個 API 金鑰嗎?", @@ -778,9 +797,9 @@ "delete_duplicates_confirmation": "您確定要永久刪除這些重複項目嗎?", "delete_face": "刪除臉孔", "delete_key": "刪除金鑰", - "delete_library": "刪除圖庫", + "delete_library": "刪除相簿", "delete_link": "刪除連結", - "delete_local_action_prompt": "已在本地刪除 {count} 個項目", + "delete_local_action_prompt": "已在本機刪除 {count} 個項目", "delete_local_dialog_ok_backed_up_only": "僅刪除已備份的項目", "delete_local_dialog_ok_force": "確認刪除", "delete_others": "刪除其他", @@ -803,7 +822,7 @@ "disallow_edits": "不允許編輯", "discord": "Discord", "discover": "探索", - "discovered_devices": "已探索的設備", + "discovered_devices": "已探索的裝置", "dismiss_all_errors": "忽略所有錯誤", "dismiss_error": "忽略錯誤", "display_options": "顯示選項", @@ -834,7 +853,7 @@ "downloading": "下載中", "downloading_asset_filename": "正在下載媒體 {filename}", "downloading_media": "正在下載媒體", - "drop_files_to_upload": "將文件拖放到任何位置以上傳", + "drop_files_to_upload": "將檔案拖放到任何位置以上傳", "duplicates": "重複項目", "duplicates_description": "逐一檢查每個群組,並標示其中是否有重複媒體", "duration": "顯示時長", @@ -886,7 +905,9 @@ "error": "錯誤", "error_change_sort_album": "變更相簿排序失敗", "error_delete_face": "從媒體刪除臉孔時失敗", + "error_getting_places": "取得位置時出錯", "error_loading_image": "圖片載入錯誤", + "error_loading_partners": "載入合作夥伴時出錯:{error}", "error_saving_image": "錯誤:{error}", "error_tag_face_bounding_box": "標記臉部錯誤 - 無法取得邊界框坐標", "error_title": "錯誤 - 發生錯誤", @@ -906,7 +927,7 @@ "error_deleting_shared_user": "刪除共享使用者時發生錯誤", "error_downloading": "下載 {filename} 時發生錯誤", "error_hiding_buy_button": "隱藏購買按鈕時發生錯誤", - "error_removing_assets_from_album": "從相簿移除媒體時發生錯誤,請檢查主控台以取得更多詳細資訊", + "error_removing_assets_from_album": "從相簿移除媒體時發生錯誤,請檢查主控臺以取得更多詳細資訊", "error_selecting_all_assets": "選取所有檔案時發生錯誤", "exclusion_pattern_already_exists": "此排除模式已存在。", "failed_to_create_album": "相簿建立失敗", @@ -919,22 +940,22 @@ "failed_to_load_notifications": "載入通知失敗", "failed_to_load_people": "載入人物失敗", "failed_to_remove_product_key": "移除產品金鑰失敗", - "failed_to_reset_pin_code": "重置 PIN 碼失敗", + "failed_to_reset_pin_code": "重設 PIN 碼失敗", "failed_to_stack_assets": "無法媒體堆疊", "failed_to_unstack_assets": "解除媒體堆疊失敗", "failed_to_update_notification_status": "無法更新通知狀態", "import_path_already_exists": "此匯入路徑已存在。", "incorrect_email_or_password": "電子郵件或密碼錯誤", "paths_validation_failed": "{paths, plural, one {# 個路徑} other {# 個路徑}} 驗證失敗", - "profile_picture_transparent_pixels": "個人資料圖片不能有透明像素。請放大並/或移動影像。", + "profile_picture_transparent_pixels": "個人資料圖片不能有透明畫素。請放大並/或移動影像。", "quota_higher_than_disk_size": "您所設定的配額大於磁碟大小", "something_went_wrong": "發生錯誤", "unable_to_add_album_users": "無法將使用者加入相簿", "unable_to_add_assets_to_shared_link": "無法加入媒體到共享連結", "unable_to_add_comment": "無法新增留言", - "unable_to_add_exclusion_pattern": "無法添加篩選條件", - "unable_to_add_import_path": "無法添加匯入路徑", - "unable_to_add_partners": "無法添加親朋好友", + "unable_to_add_exclusion_pattern": "無法新增篩選條件", + "unable_to_add_import_path": "無法新增匯入路徑", + "unable_to_add_partners": "無法新增親朋好友", "unable_to_add_remove_archive": "無法{archived, select, true {從封存中移除媒體} other {將檔案加入媒體}}", "unable_to_add_remove_favorites": "無法將媒體{favorite, select, true {加入收藏} other {從收藏中移除}}", "unable_to_archive_unarchive": "無法{archived, select, true {封存} other {取消封存}}", @@ -946,7 +967,7 @@ "unable_to_change_password": "無法變更密碼", "unable_to_change_visibility": "無法變更 {count, plural, one {# 位人物} other {# 位人物}} 的可見性", "unable_to_complete_oauth_login": "無法完成 OAuth 登入", - "unable_to_connect": "無法連接", + "unable_to_connect": "無法連線", "unable_to_copy_to_clipboard": "無法複製到剪貼簿,請確保您是以 https 存取本頁面", "unable_to_create_admin_account": "無法建立管理員帳號", "unable_to_create_api_key": "無法建立新的 API 金鑰", @@ -964,12 +985,12 @@ "unable_to_edit_import_path": "無法編輯匯入路徑", "unable_to_empty_trash": "無法清空垃圾桶", "unable_to_enter_fullscreen": "無法進入全螢幕", - "unable_to_exit_fullscreen": "無法退出全螢幕", + "unable_to_exit_fullscreen": "無法結束全螢幕", "unable_to_get_comments_number": "無法取得留言數量", "unable_to_get_shared_link": "取得共享連結失敗", "unable_to_hide_person": "無法隱藏人物", "unable_to_link_motion_video": "無法連結動態影片", - "unable_to_link_oauth_account": "無法連接 OAuth 帳號", + "unable_to_link_oauth_account": "無法連結 OAuth 帳號", "unable_to_log_out_all_devices": "無法登出所有裝置", "unable_to_log_out_device": "無法登出裝置", "unable_to_login_with_oauth": "無法使用 OAuth 登入", @@ -984,7 +1005,7 @@ "unable_to_remove_partner": "無法移除親朋好友", "unable_to_remove_reaction": "無法移除反應", "unable_to_reset_password": "無法重設密碼", - "unable_to_reset_pin_code": "無法重置 PIN 碼", + "unable_to_reset_pin_code": "無法重設 PIN 碼", "unable_to_resolve_duplicate": "無法解決重複項目", "unable_to_restore_assets": "無法還原媒體", "unable_to_restore_trash": "無法還原垃圾桶", @@ -1001,8 +1022,8 @@ "unable_to_set_profile_picture": "無法設定個人資料圖片", "unable_to_submit_job": "無法提交任務", "unable_to_trash_asset": "無法將媒體丟進垃圾桶", - "unable_to_unlink_account": "無法取消帳號的連接", - "unable_to_unlink_motion_video": "無法取消連接動態影片", + "unable_to_unlink_account": "無法解除帳號連結", + "unable_to_unlink_motion_video": "無法解除連結動態影片", "unable_to_update_album_cover": "無法更新相簿封面", "unable_to_update_album_info": "無法更新相簿資訊", "unable_to_update_library": "無法更新媒體庫", @@ -1012,17 +1033,17 @@ "unable_to_update_user": "無法更新使用者", "unable_to_upload_file": "無法上傳檔案" }, - "exif": "EXIF 可交換圖像文件格式", + "exif": "EXIF 可交換影像檔格式", "exif_bottom_sheet_description": "新增描述...", "exif_bottom_sheet_description_error": "更新描述時發生錯誤", "exif_bottom_sheet_details": "詳細資料", "exif_bottom_sheet_location": "位置", "exif_bottom_sheet_people": "人物", "exif_bottom_sheet_person_add_person": "新增姓名", - "exit_slideshow": "退出幻燈片", + "exit_slideshow": "結束幻燈片", "expand_all": "展開全部", "experimental_settings_new_asset_list_subtitle": "正在處理", - "experimental_settings_new_asset_list_title": "啟用實驗性相片格狀布局", + "experimental_settings_new_asset_list_title": "啟用實驗性相片格狀版面", "experimental_settings_subtitle": "使用風險自負!", "experimental_settings_title": "實驗性功能", "expire_after": "失效時間", @@ -1038,7 +1059,7 @@ "external": "外部", "external_libraries": "外部媒體庫", "external_network": "外部網路", - "external_network_sheet_info": "若未連接偏好的 Wi-Fi,將依列表從上到下選擇可連線的伺服器網址", + "external_network_sheet_info": "若未連線至偏好的 Wi-Fi,將依列表從上到下選擇可連線的伺服器網址", "face_unassigned": "未指定", "failed": "失敗", "failed_to_authenticate": "身份驗證失敗", @@ -1051,6 +1072,7 @@ "favorites_page_no_favorites": "未找到收藏項目", "feature_photo_updated": "特色照片已更新", "features": "功能", + "features_in_development": "發展中的特點", "features_setting_description": "管理應用程式功能", "file_name": "檔案名稱", "file_name_or_extension": "檔案名稱或副檔名", @@ -1071,13 +1093,15 @@ "gcast_enabled": "Google Cast", "gcast_enabled_description": "此功能需要從 Google 載入外部資源才能正常運作。", "general": "一般", - "geolocation_instruction_location": "點擊具有 GPS 座標的項目以使用其位置,或直接從地圖中選擇地點", - "get_help": "線上求助", - "get_wifiname_error": "無法取得 Wi-Fi 名稱。請確認您已授予必要的權限,並已連接至 Wi-Fi 網路", + "geolocation_instruction_location": "點選具有 GPS 座標的項目以使用其位置,或直接從地圖中選擇地點", + "get_help": "取得協助", + "get_wifiname_error": "無法取得 Wi-Fi 名稱。請確認您已授予必要的權限,並已連線至 Wi-Fi 網路", "getting_started": "開始使用", - "go_back": "返回", + "go_back": "上一頁", "go_to_folder": "前往資料夾", "go_to_search": "前往搜尋", + "gps": "GPS", + "gps_missing": "無 GPS", "grant_permission": "授予權限", "group_albums_by": "分類群組的方式...", "group_country": "按照國家分類", @@ -1096,7 +1120,7 @@ "header_settings_header_name_input": "標頭名稱", "header_settings_header_value_input": "標頭值", "headers_settings_tile_subtitle": "定義應用程式在每次網路請求時應附帶的代理標頭", - "headers_settings_tile_title": "自定義代理標頭", + "headers_settings_tile_title": "自訂代理標頭", "hi_user": "嗨!{name}({email})", "hide_all_people": "隱藏所有人物", "hide_gallery": "隱藏媒體庫", @@ -1105,21 +1129,21 @@ "hide_person": "隱藏人物", "hide_unnamed_people": "隱藏未命名的人物", "home_page_add_to_album_conflicts": "已將 {added} 個媒體新增到相簿 {album}。{failed} 個媒體已在該相簿中。", - "home_page_add_to_album_err_local": "暫時不能將本地媒體新增到相簿,已略過", + "home_page_add_to_album_err_local": "暫時不能將本機媒體新增到相簿,已略過", "home_page_add_to_album_success": "已在 {album} 相簿中新增 {added} 個媒體。", "home_page_album_err_partner": "暫時不能無法將親朋好友的媒體新增到相簿,已略過", - "home_page_archive_err_local": "暫時不能封存本地媒體,已略過", + "home_page_archive_err_local": "暫時不能封存本機媒體,已略過", "home_page_archive_err_partner": "無法封存親朋好友的媒體,已略過", "home_page_building_timeline": "正在建立時間軸", "home_page_delete_err_partner": "無法刪除親朋好友的媒體,已略過", "home_page_delete_remote_err_local": "刪除遠端媒體的選取中包含本機媒體,已略過", - "home_page_favorite_err_local": "暫不能收藏本地項目,略過", + "home_page_favorite_err_local": "暫不能收藏本機項目,略過", "home_page_favorite_err_partner": "暫無法收藏親朋好友的項目,略過", "home_page_first_time_notice": "如果這是您第一次使用本程式,請確保選擇一個要備份的相簿,以將照片與影片加入時間軸", "home_page_locked_error_local": "無法移動本機檔案至鎖定的資料夾,已略過", "home_page_locked_error_partner": "無法移動親朋好友分享的媒體至鎖定的資料夾,已略過", - "home_page_share_err_local": "無法通過連結共享本地媒體,已略過", - "home_page_upload_err_limit": "一次最多只能上傳 30 個媒體,已略過", + "home_page_share_err_local": "無法透過連結共享本機媒體,已略過", + "home_page_upload_err_limit": "一次最多隻能上傳 30 個媒體,已略過", "host": "主機", "hour": "小時", "hours": "小時", @@ -1142,7 +1166,7 @@ "image_viewer_page_state_provider_download_started": "下載已啟動", "image_viewer_page_state_provider_download_success": "下載成功", "image_viewer_page_state_provider_share_error": "分享時發生錯誤", - "immich_logo": "Immich 標誌", + "immich_logo": "Immich Logo", "immich_web_interface": "Immich 網頁介面", "import_from_json": "從 JSON 匯入", "import_path": "匯入路徑", @@ -1166,9 +1190,9 @@ "invite_to_album": "邀請至相簿", "ios_debug_info_fetch_ran_at": "抓取已於 {dateTime} 執行", "ios_debug_info_last_sync_at": "上次同步於 {dateTime}", - "ios_debug_info_no_processes_queued": "無排程中的背景程序", + "ios_debug_info_no_processes_queued": "無排程中的背景程式", "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} 執行處理", "items_count": "{count, plural, one {# 個項目} other {# 個項目}}", "jobs": "任務", @@ -1192,7 +1216,7 @@ "lens_model": "鏡頭型號", "let_others_respond": "允許他人回覆", "level": "等級", - "library": "圖庫", + "library": "相簿", "library_options": "資料庫選項", "library_page_device_albums": "裝置上的相簿", "library_page_new_album": "新增相簿", @@ -1203,20 +1227,21 @@ "licenses": "授權", "light": "淺色", "like": "喜歡", - "like_deleted": "已刪除的收藏", - "link_motion_video": "鏈結動態影片", - "link_to_oauth": "連接 OAuth", - "linked_oauth_account": "已連接 OAuth 帳號", + "like_deleted": "已取消喜歡", + "link_motion_video": "連結動態影片", + "link_to_oauth": "連結 OAuth", + "linked_oauth_account": "已連結 OAuth 帳號", "list": "列表", "loading": "載入中", "loading_search_results_failed": "載入搜尋結果失敗", - "local": "本地", - "local_asset_cast_failed": "無法轉換未上傳至伺服器的項目", - "local_assets": "本地項目", - "local_network": "本地網路", + "local": "本機", + "local_asset_cast_failed": "無法投放未上傳至伺服器的項目", + "local_assets": "本機項目", + "local_media_summary": "本機媒體摘要", + "local_network": "本機網路", "local_network_sheet_info": "當使用指定的 Wi-Fi 網路時,應用程式將透過此網址連線至伺服器", "location_permission": "位置權限", - "location_permission_content": "使用自動切換功能,Immich 需要精確位置權限,以取得連接的 Wi-Fi 網路名稱", + "location_permission_content": "使用自動切換功能,Immich 需要精確位置權限,以取得已連線的 Wi-Fi 網路名稱", "location_picker_choose_on_map": "在地圖上選擇", "location_picker_latitude_error": "輸入有效的緯度值", "location_picker_latitude_hint": "請在此處輸入您的緯度值", @@ -1224,42 +1249,44 @@ "location_picker_longitude_hint": "請在此處輸入您的經度值", "lock": "鎖定", "locked_folder": "鎖定的資料夾", + "log_detail_title": "日誌詳細資訊", "log_out": "登出", "log_out_all_devices": "登出所有裝置", "logged_in_as": "以{user}身分登入", "logged_out_all_devices": "已登出所有裝置", "logged_out_device": "已登出裝置", "login": "登入", - "login_disabled": "已禁用登入", - "login_form_api_exception": "API 異常,請檢查伺服器地址並重試。", - "login_form_back_button_text": "後退", + "login_disabled": "已停用登入", + "login_form_api_exception": "API 發生例外,請檢查伺服器位址後再試。", + "login_form_back_button_text": "上一頁", "login_form_email_hint": "電子郵件地址", - "login_form_endpoint_hint": "http://您的伺服器地址:端口", - "login_form_endpoint_url": "伺服器鏈接地址", - "login_form_err_http": "請注明 http:// 或 https://", - "login_form_err_invalid_email": "電郵無效", - "login_form_err_invalid_url": "無效的地址", + "login_form_endpoint_hint": "http://您的伺服器位址:連接埠", + "login_form_endpoint_url": "伺服器端點 URL", + "login_form_err_http": "請註明 http:// 或 https://", + "login_form_err_invalid_email": "電子郵件地址無效", + "login_form_err_invalid_url": "無效的 URL", "login_form_err_leading_whitespace": "帶有前導空格", "login_form_err_trailing_whitespace": "帶有尾隨空格", - "login_form_failed_get_oauth_server_config": "使用 OAuth 登入時錯誤,請檢查伺服器地址", - "login_form_failed_get_oauth_server_disable": "OAuth 功能在此伺服器上不可用", - "login_form_failed_login": "登入失敗,請檢查伺服器地址、電郵和密碼", - "login_form_handshake_exception": "與伺服器通信時出現握手異常。如果您使用的是自簽名證書,請在設定中啓用自簽名證書支持。", + "login_form_failed_get_oauth_server_config": "使用 OAuth 登入時錯誤,請檢查伺服器位址", + "login_form_failed_get_oauth_server_disable": "OAuth 功能在此伺服器上無法使用", + "login_form_failed_login": "登入失敗,請檢查伺服器位址、電子郵件地址與密碼", + "login_form_handshake_exception": "與伺服器通訊時出現握手異常。若使用自簽名憑證,請在設定中啟用自簽名憑證支援。", "login_form_password_hint": "密碼", "login_form_save_login": "保持登入", - "login_form_server_empty": "輸入伺服器連結。", - "login_form_server_error": "無法連接到伺服器。", + "login_form_server_empty": "請輸入伺服器網址。", + "login_form_server_error": "無法連線至伺服器。", "login_has_been_disabled": "已停用登入功能。", "login_password_changed_error": "密碼更新失敗", "login_password_changed_success": "密碼更新成功", "logout_all_device_confirmation": "您確定要登出所有裝置嗎?", "logout_this_device_confirmation": "要登出這臺裝置嗎?", + "logs": "日誌", "longitude": "經度", "look": "樣貌", "loop_videos": "重播影片", "loop_videos_description": "啟用後,影片結束會自動重播。", "main_branch_warning": "您現在使用的是開發版本;我們強烈您建議使用正式發行版!", - "main_menu": "主頁面", + "main_menu": "主選單", "make": "製造商", "manage_geolocation": "管理位置", "manage_shared_links": "管理共享連結", @@ -1268,34 +1295,35 @@ "manage_your_account": "管理您的帳號", "manage_your_api_keys": "管理您的 API 金鑰", "manage_your_devices": "管理已登入的裝置", - "manage_your_oauth_connection": "管理您的 OAuth 連接", + "manage_your_oauth_connection": "管理您的 OAuth 連結", "map": "地圖", "map_assets_in_bounds": "{count, plural, one {# 張照片} other {# 張照片}}", "map_cannot_get_user_location": "無法取得使用者位置", "map_location_dialog_yes": "確定", "map_location_picker_page_use_location": "使用此位置", - "map_location_service_disabled_content": "需要啓用定位服務才能顯示當前位置相關的項目。要現在啓用嗎?", - "map_location_service_disabled_title": "定位服務已禁用", - "map_marker_for_images": "在 {city}、{country} 拍攝圖像的地圖標記", - "map_marker_with_image": "帶有圖像的地圖標記", - "map_no_location_permission_content": "需要位置權限才能顯示與當前位置。要現在就授予位置權限嗎?", + "map_location_service_disabled_content": "需要啟用定位服務才能顯示目前位置相關的項目。要現在啟用嗎?", + "map_location_service_disabled_title": "定位服務已停用", + "map_marker_for_images": "在 {city}、{country} 拍攝影像的地圖示記", + "map_marker_with_image": "帶有影像的地圖示記", + "map_no_location_permission_content": "需要位置權限才能顯示與目前位置。要現在就授予位置權限嗎?", "map_no_location_permission_title": "沒有位置權限", "map_settings": "地圖設定", "map_settings_dark_mode": "深色模式", - "map_settings_date_range_option_day": "過去24小時", + "map_settings_date_range_option_day": "過去 24 小時", "map_settings_date_range_option_days": "{days} 天前", - "map_settings_date_range_option_year": "1年前", + "map_settings_date_range_option_year": "1 年前", "map_settings_date_range_option_years": "{years} 年前", "map_settings_dialog_title": "地圖設定", "map_settings_include_show_archived": "包括已封存項目", "map_settings_include_show_partners": "包含親朋好友", "map_settings_only_show_favorites": "僅顯示收藏的項目", "map_settings_theme_settings": "地圖主題", - "map_zoom_to_see_photos": "縮小以查看項目", + "map_zoom_to_see_photos": "縮小以檢視項目", "mark_all_as_read": "全部標記為已讀", "mark_as_read": "標記為已讀", "marked_all_as_read": "已全部標記為已讀", "matches": "相符", + "matching_assets": "匹配資產", "media_type": "媒體類型", "memories": "回憶", "memories_all_caught_up": "已全部看完", @@ -1308,7 +1336,7 @@ "menu": "選單", "merge": "合併", "merge_people": "合併人物", - "merge_people_limit": "一次最多只能合併 5 張臉孔", + "merge_people_limit": "一次最多隻能合併 5 張臉孔", "merge_people_prompt": "您要合併這些人物嗎?此操作無法撤銷。", "merge_people_successfully": "成功合併人物", "merged_people_count": "合併了 {count, plural, one {# 位人士} other {# 位人士}}", @@ -1336,7 +1364,8 @@ "name_or_nickname": "名稱或暱稱", "network_requirement_photos_upload": "使用行動網路流量備份照片", "network_requirement_videos_upload": "使用行動網路流量備份影片", - "network_requirements_updated": "網絡需求已變更,現重置備份佇列", + "network_requirements": "網路要求", + "network_requirements_updated": "網路需求已變更,現重設備份佇列", "networking_settings": "網路", "networking_subtitle": "管理伺服器端點設定", "never": "永不失效", @@ -1346,6 +1375,7 @@ "new_person": "新的人物", "new_pin_code": "新 PIN 碼", "new_pin_code_subtitle": "這是您第一次存取鎖定的資料夾。建立 PIN 碼以安全存取此頁面", + "new_timeline": "新時間軸", "new_user_created": "已建立新使用者", "new_version_available": "新版本已發布", "newest_first": "最新優先", @@ -1358,21 +1388,26 @@ "no_archived_assets_message": "將照片和影片封存,就不會顯示在「照片」中", "no_assets_message": "按這裡上傳您的第一張照片", "no_assets_to_show": "無項目展示", - "no_cast_devices_found": "沒有找到 Google Cast 裝置", + "no_cast_devices_found": "找不到 Google Cast 裝置", + "no_checksum_local": "沒有可用的校驗和 - 無法取得本機資產", + "no_checksum_remote": "沒有可用的校驗和 - 無法取得遠端資產", "no_duplicates_found": "沒發現重複項目。", "no_exif_info_available": "沒有可用的 Exif 資訊", "no_explore_results_message": "上傳更多照片以利探索。", "no_favorites_message": "加入收藏,加速尋找影像", - "no_libraries_message": "建立外部媒體庫以查看您的照片和影片", - "no_locked_photos_message": "鎖定的資料夾中的照片和影片會被隱藏,當您瀏覽或搜尋圖庫時不會顯示。", + "no_libraries_message": "建立外部媒體庫以檢視您的照片和影片", + "no_local_assets_found": "未找到具有此校驗和的本機資產", + "no_locked_photos_message": "鎖定的資料夾中的照片和影片會被隱藏,當您瀏覽或搜尋相簿時不會顯示。", "no_name": "無名", "no_notifications": "沒有通知", "no_people_found": "找不到符合的人物", "no_places": "沒有地點", + "no_remote_assets_found": "未找到具有此校驗和的遠端資產", "no_results": "沒有結果", "no_results_description": "試試同義詞或更通用的關鍵字吧", "no_shared_albums_message": "建立相簿分享照片和影片", "no_uploads_in_progress": "沒有正在上傳的項目", + "not_available": "不適用", "not_in_any_album": "不在任何相簿中", "not_selected": "未選擇", "note_apply_storage_label_to_previously_uploaded assets": "*註:執行套用儲存標籤前先上傳項目", @@ -1394,12 +1429,12 @@ "on_this_device": "在此裝置", "onboarding": "入門指南", "onboarding_locale_description": "選擇您想要顯示的語言。設定完成之後生效。", - "onboarding_privacy_description": "以下(可選)功能依賴外部服務,可隨時在設定中停用。", + "onboarding_privacy_description": "以下(可選)功能仰賴外部服務,可隨時在設定中停用。", "onboarding_server_welcome_description": "讓我們為您的系統進行一些基本設定。", - "onboarding_theme_description": "幫實例選色彩主題。之後也可以在設定中變更。", - "onboarding_user_welcome_description": "讓我們開始吧!", + "onboarding_theme_description": "幫執行個體選色彩主題。之後也可以在設定中變更。", + "onboarding_user_welcome_description": "讓我們開始吧!", "onboarding_welcome_user": "歡迎,{user}", - "online": "在線", + "online": "線上", "only_favorites": "僅顯示己收藏", "open": "開啟", "open_in_map_view": "開啟地圖檢視", @@ -1407,7 +1442,9 @@ "open_the_search_filters": "開啟搜尋篩選器", "options": "選項", "or": "或", - "organize_your_library": "整理您的圖庫", + "organize_into_albums": "整理成相簿", + "organize_into_albums_description": "使用目前同步設定將現有照片放入相簿", + "organize_your_library": "整理您的相簿", "original": "原圖", "other": "其他", "other_devices": "其它裝置", @@ -1422,7 +1459,7 @@ "partner_list_user_photos": "{user} 的照片", "partner_list_view_all": "展示全部", "partner_page_empty_message": "您的照片尚未與任何親朋好友共享。", - "partner_page_no_more_users": "無需新增更多用戶", + "partner_page_no_more_users": "無需新增更多使用者", "partner_page_partner_add_failed": "新增親朋好友失敗", "partner_page_select_partner": "選擇親朋好友", "partner_page_shared_to_title": "共享給", @@ -1457,14 +1494,14 @@ "permanently_deleted_assets_count": "永久刪除的 {count, plural, one {# 個檔案} other {# 個檔案}}", "permission": "權限", "permission_empty": "權限不能為空", - "permission_onboarding_back": "返回", + "permission_onboarding_back": "上一頁", "permission_onboarding_continue_anyway": "確認繼續", "permission_onboarding_get_started": "開始使用", "permission_onboarding_go_to_settings": "前往設定", "permission_onboarding_permission_denied": "如要繼續,請允許 Immich 存取相片和影片權限。", "permission_onboarding_permission_granted": "已允許!一切就緒。", "permission_onboarding_permission_limited": "如要繼續,請允許 Immich 備份和管理您的相簿收藏,在設定中授予相片和影片權限。", - "permission_onboarding_request": "Immich 需要權限才能查看您的相片和短片。", + "permission_onboarding_request": "Immich 需要權限才能檢視您的相片和短片。", "person": "人物", "person_age_months": "{months, plural, one {# 個月} other {# 個月}}前", "person_age_year_months": "1 年 {months, plural, one {# 個月} other {# 個月}}前", @@ -1478,9 +1515,9 @@ "photos_from_previous_years": "往年的照片", "pick_a_location": "選擇位置", "pin_code_changed_successfully": "變更 PIN 碼成功", - "pin_code_reset_successfully": "重置 PIN 碼成功", + "pin_code_reset_successfully": "重設 PIN 碼成功", "pin_code_setup_successfully": "設定 PIN 碼成功", - "pin_verification": "PIN碼驗證", + "pin_verification": "PIN 碼驗證", "place": "地點", "places": "地點", "places_count": "{count, plural, one {{count, number} 個地點} other {{count, number} 個地點}}", @@ -1492,6 +1529,7 @@ "port": "埠口", "preferences_settings_subtitle": "管理應用程式偏好設定", "preferences_settings_title": "偏好設定", + "preparing": "準備", "preset": "預設", "preview": "預覽", "previous": "上一張", @@ -1504,13 +1542,13 @@ "privacy": "隱私", "profile": "帳戶設定", "profile_drawer_app_logs": "日誌", - "profile_drawer_client_out_of_date_major": "客戶端有大版本升級,請盡快升級至最新版。", - "profile_drawer_client_out_of_date_minor": "客戶端有小版本升級,請盡快升級至最新版。", - "profile_drawer_client_server_up_to_date": "客戶端和服務端都是最新的", + "profile_drawer_client_out_of_date_major": "用戶端有大版本升級,請盡快升級至最新版。", + "profile_drawer_client_out_of_date_minor": "用戶端有小版本升級,請盡快升級至最新版。", + "profile_drawer_client_server_up_to_date": "用戶端與伺服器端都是最新的", "profile_drawer_github": "GitHub", - "profile_drawer_readonly_mode": "唯讀模式已開啟。請長按使用者頭像圖示以退出。", - "profile_drawer_server_out_of_date_major": "服務端有大版本升級,請盡快升級至最新版。", - "profile_drawer_server_out_of_date_minor": "服務端有小版本升級,請盡快升級至最新版。", + "profile_drawer_readonly_mode": "唯讀模式已開啟。請長按使用者頭像圖示以結束。", + "profile_drawer_server_out_of_date_major": "伺服器端有大版本升級,請盡快升級至最新版。", + "profile_drawer_server_out_of_date_minor": "伺服器端有小版本升級,請盡快升級至最新版。", "profile_image_of_user": "{user} 的個人資料圖片", "profile_picture_set": "已設定個人資料圖片。", "public_album": "公開相簿", @@ -1535,7 +1573,7 @@ "purchase_lifetime_description": "終身購置", "purchase_option_title": "購置選項", "purchase_panel_info_1": "開發 Immich 可不是件容易的事,花了我們不少功夫。好在有一群全職工程師在背後默默努力,為的就是把它做到最好。我們的目標很簡單:讓開放原始碼軟體和正當的商業模式能成為開發者的長期飯碗,同時打造出重視隱私的生態系統,讓大家有個不被限制的雲端服務新選擇。", - "purchase_panel_info_2": "我們承諾不設付費牆,所以購置 Immich 並不會讓您獲得額外的功能。我們是依賴使用者們的支援來開發 Immich 的。", + "purchase_panel_info_2": "我們承諾不設付費牆,所以購置 Immich 並不會讓您獲得額外的功能。我們仰賴使用者們的支援來開發 Immich。", "purchase_panel_title": "支援這項專案", "purchase_per_server": "每臺伺服器", "purchase_per_user": "每位使用者", @@ -1547,7 +1585,7 @@ "purchase_server_description_2": "擁護者狀態", "purchase_server_title": "伺服器", "purchase_settings_server_activated": "伺服器產品金鑰是由管理者管理的", - "query_asset_id": "査詢資產ID", + "query_asset_id": "査詢資產 ID", "queue_status": "處理中 {count}/{total}", "rating": "評星", "rating_clear": "清除評等", @@ -1557,6 +1595,7 @@ "read_changelog": "閱覽變更日誌", "readonly_mode_disabled": "唯讀模式已關閉", "readonly_mode_enabled": "唯讀模式已開啟", + "ready_for_upload": "已準備好上傳", "reassign": "重新指定", "reassigned_assets_to_existing_person": "已將 {count, plural, other {# 個檔案}}重新指定給{name, select, null {現有的人} other {{name}}}", "reassigned_assets_to_new_person": "已將 {count, plural, other {# 個檔案}}重新指定給一位新人物", @@ -1581,6 +1620,7 @@ "regenerating_thumbnails": "重新產生縮圖中", "remote": "遠端", "remote_assets": "遠端項目", + "remote_media_summary": "遠端媒體摘要", "remove": "移除", "remove_assets_album_confirmation": "確定要從相簿中移除 {count, plural, other {# 個檔案}}嗎?", "remove_assets_shared_link_confirmation": "確定刪除共享連結中{count, plural, other {# 個項目}}嗎?", @@ -1592,13 +1632,13 @@ "remove_from_favorites": "從收藏中移除", "remove_from_lock_folder_action_prompt": "已從鎖定的資料夾中移除了 {count} 個項目", "remove_from_locked_folder": "從鎖定的資料夾中移除", - "remove_from_locked_folder_confirmation": "您確定要將這些照片和影片移出鎖定的資料夾嗎?這些內容將會顯示在您的圖庫中。", + "remove_from_locked_folder_confirmation": "您確定要將這些照片和影片移出鎖定的資料夾嗎?這些內容將會顯示在您的相簿中。", "remove_from_shared_link": "從共享連結中移除", "remove_memory": "移除記憶", "remove_photo_from_memory": "將圖片從此記憶中移除", "remove_tag": "移除標籤", "remove_url": "移除 URL", - "remove_user": "移除用戶", + "remove_user": "移除使用者", "removed_api_key": "已移除 API 金鑰:{name}", "removed_from_archive": "從封存中移除", "removed_from_favorites": "已從收藏中移除", @@ -1617,9 +1657,9 @@ "reset": "重設", "reset_password": "重設密碼", "reset_people_visibility": "重設人物可見性", - "reset_pin_code": "重置 PIN 碼", - "reset_pin_code_description": "若忘記了PIN 碼,閣下可要求系統伺服器管理員為您重置", - "reset_pin_code_success": "閣下已成功重設PIN碼", + "reset_pin_code": "重設 PIN 碼", + "reset_pin_code_description": "若忘記了 PIN 碼,閣下可要求系統伺服器管理員為您重設", + "reset_pin_code_success": "閣下已成功重設 PIN 碼", "reset_pin_code_with_password": "您可隨時使用您的密碼來重設 PIN 碼", "reset_sqlite": "重設 SQLite 資料庫", "reset_sqlite_confirmation": "確定要重設 SQLite 資料庫嗎?閣下需登出並重新登入才能重新同步資料", @@ -1636,19 +1676,19 @@ "resume_paused_jobs": "恢復 {count, plural, one {# 暫停的任務} other {# 暫停的任務}}", "retry_upload": "重新上傳", "review_duplicates": "檢視重複項目", - "review_large_files": "檢視大型文件", + "review_large_files": "檢視大型檔案", "role": "角色", "role_editor": "編輯者", "role_viewer": "檢視者", - "running": "運行中", + "running": "執行中", "save": "儲存", - "save_to_gallery": "儲存到圖庫", + "save_to_gallery": "儲存到相簿", "saved_api_key": "已儲存 API 金鑰", "saved_profile": "已儲存個人資料", "saved_settings": "已儲存設定", "say_something": "說說您的想法吧", "scaffold_body_error_occurred": "發生錯誤", - "scan_all_libraries": "掃描所有圖庫", + "scan_all_libraries": "掃描所有相簿", "scan_library": "掃描", "scan_settings": "掃描設定", "scanning_for_album": "掃描相簿中……", @@ -1687,11 +1727,11 @@ "search_page_motion_photos": "動態照片", "search_page_no_objects": "找不到物件資訊", "search_page_no_places": "找不到地點資訊", - "search_page_screenshots": "屏幕截圖", + "search_page_screenshots": "螢幕截圖", "search_page_search_photos_videos": "搜尋您的照片與影片", "search_page_selfies": "自拍", "search_page_things": "事物", - "search_page_view_all_button": "查看全部", + "search_page_view_all_button": "檢視全部", "search_page_your_activity": "您的活動", "search_page_your_map": "您的足跡", "search_people": "搜尋人物", @@ -1708,7 +1748,7 @@ "search_your_photos": "搜尋照片", "searching_locales": "搜尋區域…", "second": "秒", - "see_all_people": "查看所有人物", + "see_all_people": "檢視所有人物", "select": "選擇", "select_album_cover": "選擇相簿封面", "select_all": "選擇全部", @@ -1719,7 +1759,7 @@ "select_featured_photo": "選擇特色照片", "select_from_computer": "從電腦中選取", "select_keep_all": "全部保留", - "select_library_owner": "選擇圖庫擁有者", + "select_library_owner": "選擇相簿擁有者", "select_new_face": "選擇新臉孔", "select_person_to_tag": "選擇要標記的人物", "select_photos": "選照片", @@ -1727,12 +1767,12 @@ "select_user_for_sharing_page_err_album": "新增相簿失敗", "selected": "已選擇", "selected_count": "{count, plural, other {選了 # 項}}", - "selected_gps_coordinates": "已選擇的 GPS 座標", + "selected_gps_coordinates": "選定的 GPS 座標", "send_message": "傳訊息", "send_welcome_email": "傳送歡迎電子郵件", "server_endpoint": "伺服器端點", "server_info_box_app_version": "App 版本", - "server_info_box_server_url": "伺服器地址", + "server_info_box_server_url": "伺服器網址", "server_offline": "伺服器已離線", "server_online": "伺服器已上線", "server_privacy": "伺服器隱私", @@ -1743,13 +1783,13 @@ "set_as_featured_photo": "設為特色照片", "set_as_profile_picture": "設為個人資料圖片", "set_date_of_birth": "設定出生日期", - "set_profile_picture": "設置個人資料圖片", + "set_profile_picture": "設定個人資料圖片", "set_slideshow_to_fullscreen": "以全螢幕放映幻燈片", - "set_stack_primary_asset": "設置堆疊的首要項目", - "setting_image_viewer_help": "詳細資訊查看器首先載入小縮圖,然後載入中等大小的預覽圖(若啓用),最後載入原始圖片。", - "setting_image_viewer_original_subtitle": "啓用以載入原圖,禁用以減少數據使用量(包括網絡和裝置緩存)。", + "set_stack_primary_asset": "設定堆疊的首要項目", + "setting_image_viewer_help": "詳細資訊檢視器首先載入小縮圖,然後載入中等大小的預覽圖(若啟用),最後載入原始圖片。", + "setting_image_viewer_original_subtitle": "啟用以載入原圖,停用以減少資料使用量(包括網路和裝置快取)。", "setting_image_viewer_original_title": "載入原圖", - "setting_image_viewer_preview_subtitle": "啓用以載入中等質量的圖片,禁用以載入原圖或縮圖。", + "setting_image_viewer_preview_subtitle": "啟用以載入中等品質的圖片,停用以載入原圖或縮圖。", "setting_image_viewer_preview_title": "載入預覽圖", "setting_image_viewer_title": "圖片", "setting_languages_apply": "套用", @@ -1763,13 +1803,13 @@ "setting_notifications_single_progress_subtitle": "每項的詳細上傳進度資訊", "setting_notifications_single_progress_title": "顯示背景備份詳細進度", "setting_notifications_subtitle": "調整通知選項", - "setting_notifications_total_progress_subtitle": "總體上傳進度(已完成/總計)", + "setting_notifications_total_progress_subtitle": "總體上傳進度 (已完成/總計)", "setting_notifications_total_progress_title": "顯示背景備份總進度", - "setting_video_viewer_looping_title": "循環播放", + "setting_video_viewer_looping_title": "迴圈播放", "setting_video_viewer_original_video_subtitle": "從伺服器串流影片時,優先播放原始畫質(即使有轉檔的版本可用)。這可能會導致播放時出現緩衝情況。若影片已儲存在本機,則一律以原始畫質播放,與此設定無關。", "setting_video_viewer_original_video_title": "一律播放原始影片", "settings": "設定", - "settings_require_restart": "請重啓 Immich 以使設定生效", + "settings_require_restart": "請重啟 Immich 以使設定生效", "settings_saved": "設定已儲存", "setup_pin_code": "設定 PIN 碼", "share": "分享", @@ -1779,35 +1819,35 @@ "share_dialog_preparing": "正在準備...", "share_link": "分享連結", "shared": "共享", - "shared_album_activities_input_disable": "已禁用評論", + "shared_album_activities_input_disable": "已停用評論", "shared_album_activity_remove_content": "您確定要刪除此活動嗎?", "shared_album_activity_remove_title": "刪除活動", - "shared_album_section_people_action_error": "退出/刪除相簿失敗", - "shared_album_section_people_action_leave": "從相簿中刪除用戶", - "shared_album_section_people_action_remove_user": "從相簿中刪除用戶", + "shared_album_section_people_action_error": "結束/刪除相簿失敗", + "shared_album_section_people_action_leave": "從相簿中刪除使用者", + "shared_album_section_people_action_remove_user": "從相簿中刪除使用者", "shared_album_section_people_title": "人物", "shared_by": "共享自", "shared_by_user": "由 {user} 分享", "shared_by_you": "由您分享", "shared_from_partner": "來自 {partner} 的照片", "shared_intent_upload_button_progress_text": "{current} / {total} 已上傳", - "shared_link_app_bar_title": "共享鏈接", - "shared_link_clipboard_copied_massage": "複製到剪貼板", + "shared_link_app_bar_title": "共享連結", + "shared_link_clipboard_copied_massage": "複製到剪貼簿", "shared_link_clipboard_text": "連結: {link}\n密碼: {password}", "shared_link_create_error": "新增共享連結時發生錯誤", - "shared_link_custom_url_description": "使用自定義的 URL 連結", + "shared_link_custom_url_description": "使用自訂 URL", "shared_link_edit_description_hint": "編輯共享描述", - "shared_link_edit_expire_after_option_day": "1天", + "shared_link_edit_expire_after_option_day": "1 天", "shared_link_edit_expire_after_option_days": "{count} 天", - "shared_link_edit_expire_after_option_hour": "1小時", + "shared_link_edit_expire_after_option_hour": "1 小時", "shared_link_edit_expire_after_option_hours": "{count} 小時", - "shared_link_edit_expire_after_option_minute": "1分鐘", + "shared_link_edit_expire_after_option_minute": "1 分鐘", "shared_link_edit_expire_after_option_minutes": "{count} 分鐘", "shared_link_edit_expire_after_option_months": "{count} 個月", "shared_link_edit_expire_after_option_year": "{count} 年", "shared_link_edit_password_hint": "輸入共享密碼", "shared_link_edit_submit_button": "更新連結", - "shared_link_error_server_url_fetch": "無法取得伺服器地址", + "shared_link_error_server_url_fetch": "無法取得伺服器網址", "shared_link_expires_day": "{count} 天後過期", "shared_link_expires_days": "{count} 天後過期", "shared_link_expires_hour": "{count} 小時後過期", @@ -1819,18 +1859,18 @@ "shared_link_expires_seconds": "將在 {count} 秒後過期", "shared_link_individual_shared": "個人共享", "shared_link_info_chip_metadata": "EXIF", - "shared_link_manage_links": "管理共享鏈接", + "shared_link_manage_links": "管理共享連結", "shared_link_options": "共享連結選項", - "shared_link_password_description": "要求在訪問此連結時提供密碼", + "shared_link_password_description": "要求在存取此連結時提供密碼", "shared_links": "共享連結", "shared_links_description": "以連結分享照片和影片", "shared_photos_and_videos_count": "{assetCount, plural, other {已分享 # 張照片及影片。}}", "shared_with_me": "與我共享", "shared_with_partner": "與 {partner} 共享", "sharing": "共享", - "sharing_enter_password": "要查看此頁面請輸入密碼。", + "sharing_enter_password": "要檢視此頁面請輸入密碼。", "sharing_page_album": "共享相簿", - "sharing_page_description": "新增共享相簿以與網絡中的人共享照片和短片。", + "sharing_page_description": "新增共享相簿以與網路中的人共享照片和短片。", "sharing_page_empty_list": "空白清單", "sharing_sidebar_description": "在側邊欄顯示共享連結", "sharing_silver_appbar_create_shared_album": "新增共享相簿", @@ -1840,7 +1880,7 @@ "show_albums": "顯示相簿", "show_all_people": "顯示所有人物", "show_and_hide_people": "顯示與隱藏人物", - "show_file_location": "顯示文件位置", + "show_file_location": "顯示檔案位置", "show_gallery": "顯示畫廊", "show_hidden_people": "顯示隱藏的人物", "show_in_timeline": "在時間軸中顯示", @@ -1856,9 +1896,10 @@ "show_slideshow_transition": "顯示幻燈片轉場", "show_supporter_badge": "擁護者徽章", "show_supporter_badge_description": "顯示擁護者徽章", + "show_text_search_menu": "顯示文字蒐索選單", "shuffle": "隨機排序", "sidebar": "側邊欄", - "sidebar_display_description": "在側邊欄中顯示鏈結", + "sidebar_display_description": "在側邊欄中顯示連結", "sign_out": "登出", "sign_up": "註冊", "size": "用量", @@ -1877,7 +1918,7 @@ "sort_recent": "最新的照片", "sort_title": "標題", "source": "來源", - "stack": "堆叠", + "stack": "堆疊", "stack_action_prompt": "已堆疊了{count} 個項目", "stack_duplicates": "堆疊重複項目", "stack_select_one_photo": "為堆疊選一張主要照片", @@ -1886,13 +1927,14 @@ "stacktrace": "堆疊追蹤", "start": "開始", "start_date": "開始日期", + "start_date_before_end_date": "開始日期必須早於結束日期", "state": "地區", "status": "狀態", - "stop_casting": "停止casting", + "stop_casting": "停止投放", "stop_motion_photo": "停止動態照片", "stop_photo_sharing": "要停止分享您的照片嗎?", - "stop_photo_sharing_description": "{partner} 將無法再訪問您的照片。", - "stop_sharing_photos_with_user": "停止與此用戶共享您的照片", + "stop_photo_sharing_description": "{partner} 將無法再存取您的照片。", + "stop_sharing_photos_with_user": "停止與此使用者共享您的照片", "storage": "儲存空間", "storage_label": "儲存標籤", "storage_quota": "儲存空間", @@ -1902,16 +1944,16 @@ "suggestions": "建議", "sunrise_on_the_beach": "日出的海灘", "support": "支援", - "support_and_feedback": "支持與回饋", - "support_third_party_description": "您安裝的 immich 是由第三方打包的。您遇到的問題可能是該軟體包造成的,所以請先使用下面的鏈結向他們提出問題。", + "support_and_feedback": "支援與回饋", + "support_third_party_description": "您安裝的 Immich 是由第三方打包的。您遇到的問題可能是該套件造成的,所以請先使用下面的連結向他們提出問題。", "swap_merge_direction": "交換合併方向", "sync": "同步", "sync_albums": "同步相簿", "sync_albums_manual_subtitle": "將所有上傳的短片和照片同步到選定的備份相簿", "sync_local": "同步本機", "sync_remote": "同步遠端", - "sync_status": "同步状态", - "sync_status_subtitle": "查看和管理同步系統", + "sync_status": "同步狀態", + "sync_status_subtitle": "檢視和管理同步系統", "sync_upload_album_setting_subtitle": "新增照片和短片並上傳到 Immich 上的選定相簿中", "tag": "標籤", "tag_assets": "標記檔案", @@ -1922,7 +1964,7 @@ "tag_updated": "已更新標籤:{tag}", "tagged_assets": "已標籤 {count, plural, one {# 個檔案} other {# 個檔案}}", "tags": "標籤", - "tap_to_run_job": "點擊以進行作業", + "tap_to_run_job": "點選以進行作業", "template": "模板", "theme": "主題", "theme_selection": "主題選項", @@ -1930,16 +1972,16 @@ "theme_setting_asset_list_storage_indicator_title": "在項目標題上顯示使用之儲存空間", "theme_setting_asset_list_tiles_per_row_title": "每行展示 {count} 項", "theme_setting_colorful_interface_subtitle": "套用主色調到背景。", - "theme_setting_colorful_interface_title": "彩色界面", - "theme_setting_image_viewer_quality_subtitle": "調整查看大圖時的圖片質量", - "theme_setting_image_viewer_quality_title": "圖片質量", + "theme_setting_colorful_interface_title": "彩色介面", + "theme_setting_image_viewer_quality_subtitle": "調整檢視大圖時的圖片品質", + "theme_setting_image_viewer_quality_title": "圖片品質", "theme_setting_primary_color_subtitle": "選擇顏色作為主色調。", "theme_setting_primary_color_title": "主色調", "theme_setting_system_primary_color_title": "使用系統顏色", "theme_setting_system_theme_switch": "自動(跟隨系統設定)", "theme_setting_theme_subtitle": "選擇套用主題", "theme_setting_three_stage_loading_subtitle": "三段式載入可能提升載入效能,但會大幅增加網路負載", - "theme_setting_three_stage_loading_title": "啓用三段式載入", + "theme_setting_three_stage_loading_title": "啟用三段式載入", "they_will_be_merged_together": "它們將會被合併在一起", "third_party_resources": "第三方資源", "time_based_memories": "依時間回憶", @@ -1951,7 +1993,7 @@ "to_login": "登入", "to_multi_select": "進行多選", "to_parent": "到上一級", - "to_select": "选择", + "to_select": "選擇", "to_trash": "垃圾桶", "toggle_settings": "切換設定", "total": "統計", @@ -1964,14 +2006,14 @@ "trash_emptied": "已清空回收桶", "trash_no_results_message": "垃圾桶中的照片和影片將顯示在這裡。", "trash_page_delete_all": "刪除全部", - "trash_page_empty_trash_dialog_content": "是否清空回收桶?這些項目將被從Immich中永久刪除", + "trash_page_empty_trash_dialog_content": "是否清空回收桶?這些項目將被從 Immich 中永久刪除", "trash_page_info": "回收桶中項目將在 {days} 天後永久刪除", "trash_page_no_assets": "暫無已刪除項目", "trash_page_restore_all": "恢復全部", "trash_page_select_assets_btn": "選擇項目", "trash_page_title": "垃圾桶 ({count})", "trashed_items_will_be_permanently_deleted_after": "垃圾桶中的項目會在 {days, plural, other {# 天}}後永久刪除。", - "troubleshoot": "疑难解答", + "troubleshoot": "疑難解答", "type": "類型", "unable_to_change_pin_code": "無法變更 PIN 碼", "unable_to_setup_pin_code": "無法設定 PIN 碼", @@ -1984,11 +2026,11 @@ "unhide_person": "取消隱藏人物", "unknown": "未知", "unknown_country": "未知國家", - "unknown_year": "不知年份", + "unknown_year": "未知年份", "unlimited": "不限制", - "unlink_motion_video": "取消鏈結動態影片", - "unlink_oauth": "取消連接 OAuth", - "unlinked_oauth_account": "已解除連接 OAuth 帳號", + "unlink_motion_video": "解除連結動態影片", + "unlink_oauth": "解除連結 OAuth", + "unlinked_oauth_account": "已解除連結 OAuth 帳號", "unmute_memories": "取消靜音回憶", "unnamed_album": "未命名相簿", "unnamed_album_delete_confirmation": "確定要刪除這本相簿嗎?", @@ -1997,7 +2039,7 @@ "unselect_all": "取消全選", "unselect_all_duplicates": "取消選取所有的重複項目", "unselect_all_in": "{group} 全不選", - "unstack": "取消堆叠", + "unstack": "取消堆疊", "unstack_action_prompt": "{count} 個取消堆疊", "unstacked_assets_count": "已解除堆疊 {count, plural, other {# 個檔案}}", "untagged": "無標籤", @@ -2011,14 +2053,14 @@ "upload_details": "上傳詳細資訊", "upload_dialog_info": "是否要將所選項目備份到伺服器?", "upload_dialog_title": "上傳項目", - "upload_errors": "上傳完成,但有 {count, plural, other {# 處時發生錯誤}},要查看新上傳的檔案請重新整理頁面。", + "upload_errors": "上傳完成,但有 {count, plural, other {# 處時發生錯誤}},要檢視新上傳的檔案請重新整理頁面。", "upload_finished": "上傳完成", "upload_progress": "剩餘 {remaining, number} - 已處理 {processed, number}/{total, number}", "upload_skipped_duplicates": "已略過 {count, plural, other {# 個重複的檔案}}", "upload_status_duplicates": "重複項目", "upload_status_errors": "錯誤", "upload_status_uploaded": "已上傳", - "upload_success": "上傳成功,要查看新上傳的檔案請重新整理頁面。", + "upload_success": "上傳成功,要檢視新上傳的檔案請重新整理頁面。", "upload_to_immich": "上傳至 Immich ({count})", "uploading": "上傳中", "uploading_media": "媒體上傳中", @@ -2028,7 +2070,7 @@ "use_current_connection": "使用目前的連線", "use_custom_date_range": "改用自訂日期範圍", "user": "使用者", - "user_has_been_deleted": "此用戶已被刪除。", + "user_has_been_deleted": "此使用者已被刪除。", "user_id": "使用者 ID", "user_liked": "{user} 喜歡了 {type, select, photo {這張照片} video {這段影片} asset {這個檔案} other {它}}", "user_pin_code_settings": "PIN 碼", @@ -2039,13 +2081,13 @@ "user_role_set": "設 {user} 為{role}", "user_usage_detail": "使用者用量詳細資訊", "user_usage_stats": "帳號使用量統計", - "user_usage_stats_description": "查看帳號使用量", + "user_usage_stats_description": "檢視帳號使用量", "username": "使用者名稱", "users": "admin", "users_added_to_album_count": "已在此相簿中新增了 {count, plural, one {# 個} other {# 個}} 使用者", "utilities": "工具", "validate": "驗證", - "validate_endpoint_error": "請輸入有效的連結", + "validate_endpoint_error": "請輸入有效的 URL", "variables": "變數", "version": "版本", "version_announcement_closing": "敬祝順心,Alex", @@ -2053,24 +2095,24 @@ "version_history": "版本紀錄", "version_history_item": "{date} 安裝了 {version}", "video": "影片", - "video_hover_setting": "游標停留時播放影片縮圖", + "video_hover_setting": "遊標停留時播放影片縮圖", "video_hover_setting_description": "當滑鼠停在項目上時播放影片縮圖。即使停用,將滑鼠停在播放圖示上也可以播放。", "videos": "影片", "videos_count": "{count, plural, other {# 部影片}}", - "view": "查看", - "view_album": "查看相簿", + "view": "檢視", + "view_album": "檢視相簿", "view_all": "瀏覽全部", - "view_all_users": "查看所有使用者", + "view_all_users": "檢視所有使用者", "view_details": "檢視詳細資訊", - "view_in_timeline": "在時間軸中查看", - "view_link": "查看連結", - "view_links": "檢視鏈結", + "view_in_timeline": "在時間軸中檢視", + "view_link": "檢視連結", + "view_links": "檢視連結", "view_name": "檢視分類", - "view_next_asset": "查看下一項", - "view_previous_asset": "查看上一項", - "view_qr_code": "查看 QR code", - "view_similar_photos": "查看相似照片", - "view_stack": "查看堆疊", + "view_next_asset": "檢視下一項", + "view_previous_asset": "檢視上一項", + "view_qr_code": "檢視 QR code", + "view_similar_photos": "檢視相似照片", + "view_stack": "檢視堆疊", "view_user": "顯示使用者", "viewer_remove_from_stack": "從堆疊中移除", "viewer_stack_use_as_main_asset": "作為主項目使用", @@ -2088,5 +2130,6 @@ "yes": "是", "you_dont_have_any_shared_links": "您沒有任何共享連結", "your_wifi_name": "您的 Wi-Fi 名稱", - "zoom_image": "縮放圖片" + "zoom_image": "縮放圖片", + "zoom_to_bounds": "縮放到邊界" } diff --git a/i18n/zh_SIMPLIFIED.json b/i18n/zh_SIMPLIFIED.json index 5b29640d63..fc575d44e5 100644 --- a/i18n/zh_SIMPLIFIED.json +++ b/i18n/zh_SIMPLIFIED.json @@ -28,10 +28,12 @@ "add_to_album": "添加到相册", "add_to_album_bottom_sheet_added": "添加到相册 “{album}”", "add_to_album_bottom_sheet_already_exists": "已在相册“ {album} ” 中", + "add_to_album_bottom_sheet_some_local_assets": "某些本地资产无法添加到相册", "add_to_album_toggle": "选择相册 {album}", "add_to_albums": "添加到相册", "add_to_albums_count": "添加到相册({count}个)", "add_to_shared_album": "添加到共享相册", + "add_upload_to_stack": "上传项目至堆叠", "add_url": "添加 URL", "added_to_archive": "添加到归档", "added_to_favorites": "添加到收藏", @@ -464,7 +466,7 @@ "api_key_description": "该应用密钥只会显示一次。请确保在关闭窗口前复制下来。", "api_key_empty": "API 密钥名称不可为空", "api_keys": "API 密钥", - "app_bar_signout_dialog_content": "确定退出登录?", + "app_bar_signout_dialog_content": "您确定要退出吗?", "app_bar_signout_dialog_ok": "是", "app_bar_signout_dialog_title": "退出登录", "app_settings": "应用设置", @@ -598,6 +600,7 @@ "backup_controller_page_turn_on": "开启前台备份", "backup_controller_page_uploading_file_info": "正在上传中的文件信息", "backup_err_only_album": "不能移除唯一的一个相册", + "backup_error_sync_failed": "同步失败。无法处理备份。", "backup_info_card_assets": "项", "backup_manual_cancelled": "已取消", "backup_manual_in_progress": "上传正在进行中,请稍后再试", @@ -1373,7 +1376,7 @@ "new_person": "新人物", "new_pin_code": "新的PIN码", "new_pin_code_subtitle": "这是您第一次访问此锁定文件夹。创建一个PIN码以安全访问此页面", - "new_timeline": "新建时间轴", + "new_timeline": "切换到新版时间线", "new_user_created": "已创建新用户", "new_version_available": "有新版本发布啦", "newest_first": "最新优先", @@ -1898,7 +1901,7 @@ "shuffle": "随机", "sidebar": "侧边栏", "sidebar_display_description": "在侧边栏中显示链接", - "sign_out": "注销", + "sign_out": "退出登录", "sign_up": "注册", "size": "大小", "skip_to_content": "跳转到内容", diff --git a/machine-learning/Dockerfile b/machine-learning/Dockerfile index e4ed643375..f913fd3c78 100644 --- a/machine-learning/Dockerfile +++ b/machine-learning/Dockerfile @@ -70,7 +70,8 @@ RUN if [ "$DEVICE" = "rocm" ]; then \ FROM python:3.11-slim-bookworm@sha256:873f91540d53b36327ed4fb018c9669107a4e2a676719720edb4209c4b15d029 AS prod-cpu -ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 +ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 \ + MACHINE_LEARNING_MODEL_ARENA=false FROM python:3.11-slim-bookworm@sha256:873f91540d53b36327ed4fb018c9669107a4e2a676719720edb4209c4b15d029 AS prod-openvino @@ -88,7 +89,8 @@ RUN apt-get update && \ FROM nvidia/cuda:12.2.2-runtime-ubuntu22.04@sha256:94c1577b2cd9dd6c0312dc04dff9cb2fdce2b268018abc3d7c2dbcacf1155000 AS prod-cuda -ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 +ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 \ + MACHINE_LEARNING_MODEL_ARENA=false RUN apt-get update && \ apt-get install --no-install-recommends -yqq libcudnn9-cuda-12 && \ @@ -104,7 +106,8 @@ FROM rocm/dev-ubuntu-22.04:6.4.3-complete@sha256:1f7e92ca7e3a3785680473329ed1091 FROM prod-cpu AS prod-armnn ENV LD_LIBRARY_PATH=/opt/armnn \ - LD_PRELOAD=/usr/lib/libmimalloc.so.2 + LD_PRELOAD=/usr/lib/libmimalloc.so.2 \ + MACHINE_LEARNING_MODEL_ARENA=false RUN apt-get update && apt-get install -y --no-install-recommends ocl-icd-libopencl1 mesa-opencl-icd libgomp1 && \ rm -rf /var/lib/apt/lists/* && \ @@ -127,7 +130,8 @@ FROM prod-cpu AS prod-rknn # renovate: datasource=github-tags depName=airockchip/rknn-toolkit2 ARG RKNN_TOOLKIT_VERSION="v2.3.0" -ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 +ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 \ + MACHINE_LEARNING_MODEL_ARENA=false ADD --checksum=sha256:73993ed4b440460825f21611731564503cc1d5a0c123746477da6cd574f34885 "https://github.com/airockchip/rknn-toolkit2/raw/refs/tags/${RKNN_TOOLKIT_VERSION}/rknpu2/runtime/Linux/librknn_api/aarch64/librknnrt.so" /usr/lib/ diff --git a/machine-learning/immich_ml/__main__.py b/machine-learning/immich_ml/__main__.py index d15b0fb321..8d575a58d5 100644 --- a/machine-learning/immich_ml/__main__.py +++ b/machine-learning/immich_ml/__main__.py @@ -1,6 +1,7 @@ import os import signal import subprocess +from ipaddress import ip_address from pathlib import Path from .config import log, non_prefixed_settings, settings @@ -12,6 +13,19 @@ else: module_dir = Path(__file__).parent + +def is_ipv6(host: str) -> bool: + try: + return ip_address(host).version == 6 + except ValueError: + return False + + +bind_host = non_prefixed_settings.immich_host +if is_ipv6(bind_host): + bind_host = f"[{bind_host}]" +bind_address = f"{bind_host}:{non_prefixed_settings.immich_port}" + try: with subprocess.Popen( [ @@ -24,7 +38,7 @@ try: "-c", module_dir / "gunicorn_conf.py", "-b", - f"{non_prefixed_settings.immich_host}:{non_prefixed_settings.immich_port}", + bind_address, "-w", str(settings.workers), "-t", diff --git a/machine-learning/immich_ml/config.py b/machine-learning/immich_ml/config.py index 939afbc98b..d9edd88a8c 100644 --- a/machine-learning/immich_ml/config.py +++ b/machine-learning/immich_ml/config.py @@ -61,6 +61,7 @@ class Settings(BaseSettings): request_threads: int = os.cpu_count() or 4 model_inter_op_threads: int = 0 model_intra_op_threads: int = 0 + model_arena: bool = True ann: bool = True ann_fp16_turbo: bool = False ann_tuning_level: int = 2 diff --git a/machine-learning/immich_ml/models/constants.py b/machine-learning/immich_ml/models/constants.py index 41b0990f71..b15b75b7d1 100644 --- a/machine-learning/immich_ml/models/constants.py +++ b/machine-learning/immich_ml/models/constants.py @@ -79,6 +79,7 @@ SUPPORTED_PROVIDERS = [ "CUDAExecutionProvider", "ROCMExecutionProvider", "OpenVINOExecutionProvider", + "CoreMLExecutionProvider", "CPUExecutionProvider", ] diff --git a/machine-learning/immich_ml/sessions/ort.py b/machine-learning/immich_ml/sessions/ort.py index e7d8635876..d18aae751a 100644 --- a/machine-learning/immich_ml/sessions/ort.py +++ b/machine-learning/immich_ml/sessions/ort.py @@ -96,6 +96,14 @@ class OrtSession: "precision": "FP32", "cache_dir": (self.model_path.parent / "openvino").as_posix(), } + case "CoreMLExecutionProvider": + options = { + "ModelFormat": "MLProgram", + "MLComputeUnits": "ALL", + "SpecializationStrategy": "FastPrediction", + "AllowLowPrecisionAccumulationOnGPU": "1", + "ModelCacheDirectory": (self.model_path.parent / "coreml").as_posix(), + } case _: options = {} provider_options.append(options) @@ -115,7 +123,7 @@ class OrtSession: @property def _sess_options_default(self) -> ort.SessionOptions: sess_options = ort.SessionOptions() - sess_options.enable_cpu_mem_arena = False + sess_options.enable_cpu_mem_arena = settings.model_arena # avoid thread contention between models if settings.model_inter_op_threads > 0: diff --git a/machine-learning/pyproject.toml b/machine-learning/pyproject.toml index f0f08b20b6..683d8578a3 100644 --- a/machine-learning/pyproject.toml +++ b/machine-learning/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "immich-ml" -version = "1.129.0" +version = "2.1.0" description = "" authors = [{ name = "Hau Tran", email = "alex.tran1502@gmail.com" }] requires-python = ">=3.10,<4.0" diff --git a/machine-learning/scripts/healthcheck.py b/machine-learning/scripts/healthcheck.py index 82c6cad790..38c0a522f1 100644 --- a/machine-learning/scripts/healthcheck.py +++ b/machine-learning/scripts/healthcheck.py @@ -1,12 +1,22 @@ import os import sys +from ipaddress import ip_address import requests port = os.getenv("IMMICH_PORT", 3003) host = os.getenv("IMMICH_HOST", "0.0.0.0") + +def is_ipv6(host: str) -> bool: + try: + return ip_address(host).version == 6 + except ValueError: + return False + + host = "localhost" if host == "0.0.0.0" else host +host = f"[{host}]" if is_ipv6(host) else host try: response = requests.get(f"http://{host}:{port}/ping", timeout=2) diff --git a/machine-learning/test_main.py b/machine-learning/test_main.py index eeafd01062..582a05a950 100644 --- a/machine-learning/test_main.py +++ b/machine-learning/test_main.py @@ -180,6 +180,7 @@ class TestOrtSession: CUDA_EP_OUT_OF_ORDER = ["CPUExecutionProvider", "CUDAExecutionProvider"] TRT_EP = ["TensorrtExecutionProvider", "CUDAExecutionProvider", "CPUExecutionProvider"] ROCM_EP = ["ROCMExecutionProvider", "CPUExecutionProvider"] + COREML_EP = ["CoreMLExecutionProvider", "CPUExecutionProvider"] @pytest.mark.providers(CPU_EP) def test_sets_cpu_provider(self, providers: list[str]) -> None: @@ -225,6 +226,12 @@ class TestOrtSession: assert session.providers == self.ROCM_EP + @pytest.mark.providers(COREML_EP) + def test_uses_coreml(self, providers: list[str]) -> None: + session = OrtSession("ViT-B-32__openai") + + assert session.providers == self.COREML_EP + def test_sets_provider_kwarg(self) -> None: providers = ["CUDAExecutionProvider"] session = OrtSession("ViT-B-32__openai", providers=providers) @@ -284,7 +291,6 @@ class TestOrtSession: assert session.sess_options.execution_mode == ort.ExecutionMode.ORT_SEQUENTIAL assert session.sess_options.inter_op_num_threads == 1 assert session.sess_options.intra_op_num_threads == 2 - assert session.sess_options.enable_cpu_mem_arena is False def test_sets_default_sess_options_does_not_set_threads_if_non_cpu_and_default_threads(self) -> None: session = OrtSession("ViT-B-32__openai", providers=["CUDAExecutionProvider", "CPUExecutionProvider"]) @@ -302,6 +308,26 @@ class TestOrtSession: assert session.sess_options.inter_op_num_threads == 2 assert session.sess_options.intra_op_num_threads == 4 + def test_uses_arena_if_enabled(self, mocker: MockerFixture) -> None: + mock_settings = mocker.patch("immich_ml.sessions.ort.settings", autospec=True) + mock_settings.model_inter_op_threads = 0 + mock_settings.model_intra_op_threads = 0 + mock_settings.model_arena = True + + session = OrtSession("ViT-B-32__openai", providers=["CPUExecutionProvider"]) + + assert session.sess_options.enable_cpu_mem_arena + + def test_does_not_use_arena_if_disabled(self, mocker: MockerFixture) -> None: + mock_settings = mocker.patch("immich_ml.sessions.ort.settings", autospec=True) + mock_settings.model_inter_op_threads = 0 + mock_settings.model_intra_op_threads = 0 + mock_settings.model_arena = False + + session = OrtSession("ViT-B-32__openai", providers=["CPUExecutionProvider"]) + + assert not session.sess_options.enable_cpu_mem_arena + def test_sets_sess_options_kwarg(self) -> None: sess_options = ort.SessionOptions() session = OrtSession( diff --git a/misc/release/archive-version.js b/misc/release/archive-version.js index 3ef4f58b1e..1a66963dad 100755 --- a/misc/release/archive-version.js +++ b/misc/release/archive-version.js @@ -10,7 +10,7 @@ if (!nextVersion) { const filename = './docs/static/archived-versions.json'; const oldVersions = JSON.parse(readFileSync(filename)); const newVersions = [ - { label: `v${nextVersion}`, url: `https://v${nextVersion}.archive.immich.app` }, + { label: `v${nextVersion}`, url: `https://docs.v${nextVersion}.archive.immich.app` }, ...oldVersions, ]; diff --git a/misc/release/pump-version.sh b/misc/release/pump-version.sh index 35ce9a1f33..65a2e70e50 100755 --- a/misc/release/pump-version.sh +++ b/misc/release/pump-version.sh @@ -80,7 +80,7 @@ if [ "$CURRENT_SERVER" != "$NEXT_SERVER" ]; then jq --arg version "$NEXT_SERVER" '.version = $version' e2e/package.json > e2e/package.json.tmp && mv e2e/package.json.tmp e2e/package.json pnpm install --frozen-lockfile --prefix e2e - uvx --from=toml-cli toml set --toml-path=pyproject.toml project.version "$SERVER_PUMP" + uvx --from=toml-cli toml set --toml-path=machine-learning/pyproject.toml project.version "$NEXT_SERVER" fi if [ "$CURRENT_MOBILE" != "$NEXT_MOBILE" ]; then diff --git a/mise.toml b/mise.toml index 51dc3b1606..275098ab55 100644 --- a/mise.toml +++ b/mise.toml @@ -1,7 +1,7 @@ [tools] -node = "22.19.0" -flutter = "3.35.4" -pnpm = "10.15.1" +node = "22.20.0" +flutter = "3.35.5" +pnpm = "10.18.1" [tools."github:CQLabs/homebrew-dcm"] version = "1.30.0" @@ -278,12 +278,7 @@ run = "prettier --write ." [tasks."web:lint"] env._.path = "web/node_modules/.bin" dir = "web" -run = "eslint . --max-warnings 0" - -[tasks."web:lint-p"] -env._.path = "web/node_modules/.bin" -dir = "web" -run = "eslint-p . --max-warnings 0 --concurrency=4" +run = "eslint . --max-warnings 0 --concurrency 4" [tasks."web:lint-fix"] run = "mise run web:lint --fix" @@ -315,7 +310,12 @@ run = [ alias = "mobile:codegen" description = "Execute build_runner to auto-generate dart code" dir = "mobile" -sources = ["pubspec.yaml", "build.yaml", "lib/**/*.dart"] +sources = [ + "pubspec.yaml", + "build.yaml", + "lib/**/*.dart", + "infrastructure/**/*.drift", +] outputs = { auto = true } run = "dart run build_runner build --delete-conflicting-outputs" diff --git a/mobile/README.md b/mobile/README.md index 436b0a4c34..59b2d9340c 100644 --- a/mobile/README.md +++ b/mobile/README.md @@ -84,4 +84,4 @@ Below is how your code needs to be structured: ## Contributing -Please refer to the [architecture](https://immich.app/docs/developer/architecture/) for contributing to the mobile app! +Please refer to the [architecture](https://docs.immich.app/developer/architecture/) for contributing to the mobile app! diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/ImmichApp.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/ImmichApp.kt index 5a3b0e1f3d..4474c63e09 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/ImmichApp.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/ImmichApp.kt @@ -1,8 +1,11 @@ package app.alextran.immich import android.app.Application +import android.os.Handler +import android.os.Looper import androidx.work.Configuration import androidx.work.WorkManager +import app.alextran.immich.background.BackgroundEngineLock import app.alextran.immich.background.BackgroundWorkerApiImpl class ImmichApp : Application() { @@ -17,6 +20,11 @@ class ImmichApp : Application() { // As a workaround, we also run a backup check when initializing the application ContentObserverWorker.startBackupWorker(context = this, delayMilliseconds = 0) - BackgroundWorkerApiImpl.enqueueBackgroundWorker(this) + Handler(Looper.getMainLooper()).postDelayed({ + // We can only check the engine count and not the status of the lock here, + // as the previous start might have been killed without unlocking. + if (BackgroundEngineLock.connectEngines > 0) return@postDelayed + BackgroundWorkerApiImpl.enqueueBackgroundWorker(this) + }, 5000) } } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundEngineLock.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundEngineLock.kt index d8afe32b5c..504267a4e5 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundEngineLock.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundEngineLock.kt @@ -8,43 +8,46 @@ import java.util.concurrent.atomic.AtomicInteger private const val TAG = "BackgroundEngineLock" class BackgroundEngineLock(context: Context) : BackgroundWorkerLockApi, FlutterPlugin { - private val ctx: Context = context.applicationContext + private val ctx: Context = context.applicationContext - companion object { + companion object { - private var engineCount = AtomicInteger(0) + private var engineCount = AtomicInteger(0) - private fun checkAndEnforceBackgroundLock(ctx: Context) { - // work manager task is running while the main app is opened, cancel the worker - if (BackgroundWorkerPreferences(ctx).isLocked() && - engineCount.get() > 1 && - BackgroundWorkerApiImpl.isBackgroundWorkerRunning() - ) { - Log.i(TAG, "Background worker is locked, cancelling the background worker") - BackgroundWorkerApiImpl.cancelBackgroundWorker(ctx) - } - } + val connectEngines: Int + get() = engineCount.get() + + private fun checkAndEnforceBackgroundLock(ctx: Context) { + // work manager task is running while the main app is opened, cancel the worker + if (BackgroundWorkerPreferences(ctx).isLocked() && + connectEngines > 1 && + BackgroundWorkerApiImpl.isBackgroundWorkerRunning() + ) { + Log.i(TAG, "Background worker is locked, cancelling the background worker") + BackgroundWorkerApiImpl.cancelBackgroundWorker(ctx) + } } + } - override fun lock() { - BackgroundWorkerPreferences(ctx).setLocked(true) - checkAndEnforceBackgroundLock(ctx) - Log.i(TAG, "Background worker is locked") - } + override fun lock() { + BackgroundWorkerPreferences(ctx).setLocked(true) + checkAndEnforceBackgroundLock(ctx) + Log.i(TAG, "Background worker is locked") + } - override fun unlock() { - BackgroundWorkerPreferences(ctx).setLocked(false) - Log.i(TAG, "Background worker is unlocked") - } + override fun unlock() { + BackgroundWorkerPreferences(ctx).setLocked(false) + Log.i(TAG, "Background worker is unlocked") + } - override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { - checkAndEnforceBackgroundLock(binding.applicationContext) - engineCount.incrementAndGet() - Log.i(TAG, "Flutter engine attached. Attached Engines count: $engineCount") - } + override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { + checkAndEnforceBackgroundLock(binding.applicationContext) + engineCount.incrementAndGet() + Log.i(TAG, "Flutter engine attached. Attached Engines count: $engineCount") + } - override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { - engineCount.decrementAndGet() - Log.i(TAG, "Flutter engine detached. Attached Engines count: $engineCount") - } + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + engineCount.decrementAndGet() + Log.i(TAG, "Flutter engine detached. Attached Engines count: $engineCount") + } } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt index 052395c172..5857453ad3 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt @@ -136,6 +136,7 @@ private open class BackgroundWorkerPigeonCodec : StandardMessageCodec() { /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface BackgroundWorkerFgHostApi { fun enable() + fun saveNotificationMessage(title: String, body: String) fun configure(settings: BackgroundWorkerSettings) fun disable() @@ -164,6 +165,25 @@ interface BackgroundWorkerFgHostApi { channel.setMessageHandler(null) } } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.saveNotificationMessage$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val titleArg = args[0] as String + val bodyArg = args[1] as String + val wrapped: List = try { + api.saveNotificationMessage(titleArg, bodyArg) + listOf(null) + } catch (exception: Throwable) { + BackgroundWorkerPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.configure$separatedMessageChannelSuffix", codec) if (api != null) { @@ -204,7 +224,6 @@ interface BackgroundWorkerFgHostApi { /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface BackgroundWorkerBgHostApi { fun onInitialized() - fun showNotification(title: String, content: String) fun close() companion object { @@ -232,25 +251,6 @@ interface BackgroundWorkerBgHostApi { channel.setMessageHandler(null) } } - run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.showNotification$separatedMessageChannelSuffix", codec) - if (api != null) { - channel.setMessageHandler { message, reply -> - val args = message as List - val titleArg = args[0] as String - val contentArg = args[1] as String - val wrapped: List = try { - api.showNotification(titleArg, contentArg) - listOf(null) - } catch (exception: Throwable) { - BackgroundWorkerPigeonUtils.wrapError(exception) - } - reply.reply(wrapped) - } - } else { - channel.setMessageHandler(null) - } - } run { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.close$separatedMessageChannelSuffix", codec) if (api != null) { diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.kt index 71d9f5ffe3..e59cee2c16 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.kt @@ -73,6 +73,8 @@ class BackgroundWorker(context: Context, params: WorkerParameters) : NotificationManager.IMPORTANCE_LOW ) notificationManager.createNotificationChannel(notificationChannel) + val notificationConfig = BackgroundWorkerPreferences(ctx).getNotificationConfig() + showNotification(notificationConfig.first, notificationConfig.second) loader.ensureInitializationCompleteAsync(ctx, null, Handler(Looper.getMainLooper())) { engine = FlutterEngine(ctx) @@ -109,7 +111,7 @@ class BackgroundWorker(context: Context, params: WorkerParameters) : } // TODO: Move this to a separate NotificationManager class - override fun showNotification(title: String, content: String) { + private fun showNotification(title: String, content: String) { val notification = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL_ID) .setSmallIcon(R.drawable.notification_icon) .setOnlyAlertOnce(true) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerApiImpl.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerApiImpl.kt index 78f2e9e461..a78db3c5ea 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerApiImpl.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerApiImpl.kt @@ -20,6 +20,10 @@ class BackgroundWorkerApiImpl(context: Context) : BackgroundWorkerFgHostApi { enqueueMediaObserver(ctx) } + override fun saveNotificationMessage(title: String, body: String) { + BackgroundWorkerPreferences(ctx).updateNotificationConfig(title, body) + } + override fun configure(settings: BackgroundWorkerSettings) { BackgroundWorkerPreferences(ctx).updateSettings(settings) enqueueMediaObserver(ctx) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerPreferences.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerPreferences.kt index cfceb06c1d..450113e5b0 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerPreferences.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerPreferences.kt @@ -10,9 +10,13 @@ class BackgroundWorkerPreferences(private val ctx: Context) { private const val SHARED_PREF_MIN_DELAY_KEY = "BackgroundWorker::minDelaySeconds" private const val SHARED_PREF_REQUIRE_CHARGING_KEY = "BackgroundWorker::requireCharging" private const val SHARED_PREF_LOCK_KEY = "BackgroundWorker::isLocked" + private const val SHARED_PREF_NOTIF_TITLE_KEY = "BackgroundWorker::notificationTitle" + private const val SHARED_PREF_NOTIF_MSG_KEY = "BackgroundWorker::notificationMessage" private const val DEFAULT_MIN_DELAY_SECONDS = 30L private const val DEFAULT_REQUIRE_CHARGING = false + private const val DEFAULT_NOTIF_TITLE = "Uploading media" + private const val DEFAULT_NOTIF_MSG = "Checking for new assets…" } private val sp: SharedPreferences by lazy { @@ -38,6 +42,20 @@ class BackgroundWorkerPreferences(private val ctx: Context) { ) } + fun updateNotificationConfig(title: String, message: String) { + sp.edit { + putString(SHARED_PREF_NOTIF_TITLE_KEY, title) + putString(SHARED_PREF_NOTIF_MSG_KEY, message) + } + } + + fun getNotificationConfig(): Pair { + val title = + sp.getString(SHARED_PREF_NOTIF_TITLE_KEY, DEFAULT_NOTIF_TITLE) ?: DEFAULT_NOTIF_TITLE + val message = sp.getString(SHARED_PREF_NOTIF_MSG_KEY, DEFAULT_NOTIF_MSG) ?: DEFAULT_NOTIF_MSG + return Pair(title, message) + } + fun setLocked(paused: Boolean) { sp.edit { putBoolean(SHARED_PREF_LOCK_KEY, paused) diff --git a/mobile/android/build.gradle b/mobile/android/build.gradle index bcf3daa1c8..719c946bd6 100644 --- a/mobile/android/build.gradle +++ b/mobile/android/build.gradle @@ -1,5 +1,5 @@ allprojects { - ext.kotlin_version = '2.0.20' + ext.kotlin_version = '2.2.20' repositories { google() @@ -16,8 +16,8 @@ subprojects { if (project.plugins.hasPlugin("com.android.application") || project.plugins.hasPlugin("com.android.library")) { project.android { - compileSdkVersion 35 - buildToolsVersion "35.0.0" + compileSdkVersion 36 + buildToolsVersion "36.0.0" } } } diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index 1ce506c73d..4c1df0d700 100644 --- a/mobile/android/fastlane/Fastfile +++ b/mobile/android/fastlane/Fastfile @@ -35,8 +35,8 @@ platform :android do task: 'bundle', build_type: 'Release', properties: { - "android.injected.version.code" => 3017, - "android.injected.version.name" => "1.143.1", + "android.injected.version.code" => 3022, + "android.injected.version.name" => "2.1.0", } ) upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab') diff --git a/mobile/android/gradle/wrapper/gradle-wrapper.properties b/mobile/android/gradle/wrapper/gradle-wrapper.properties index dedd5d1e69..ed4c299adb 100644 --- a/mobile/android/gradle/wrapper/gradle-wrapper.properties +++ b/mobile/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/mobile/android/settings.gradle b/mobile/android/settings.gradle index 29c3a7c056..fbed55a3e3 100644 --- a/mobile/android/settings.gradle +++ b/mobile/android/settings.gradle @@ -18,10 +18,10 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version '8.7.2' apply false - id "org.jetbrains.kotlin.android" version "2.0.20" apply false + id "com.android.application" version '8.11.2' apply false + id "org.jetbrains.kotlin.android" version "2.2.20" apply false id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.22' apply false - id 'com.google.devtools.ksp' version '2.0.20-1.0.24' apply false + id 'com.google.devtools.ksp' version '2.2.20-2.0.3' apply false } include ":app" diff --git a/mobile/drift_schemas/main/drift_schema_v12.json b/mobile/drift_schemas/main/drift_schema_v12.json new file mode 100644 index 0000000000..1c100ab37f --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v12.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"avatar_color","getter_name":"avatarColor","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AvatarColor.values)","dart_type_name":"AvatarColor"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"library_id","getter_name":"libraryId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[4],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"linked_remote_album_id","getter_name":"linkedRemoteAlbumId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":6,"references":[3,5],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":7,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)","unique":false,"columns":[]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_owner_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)","unique":false,"columns":[]}},{"id":9,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum\nON remote_asset_entity (owner_id, checksum)\nWHERE (library_id IS NULL);\n","unique":true,"columns":[]}},{"id":10,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_library_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum\nON remote_asset_entity (owner_id, library_id, checksum)\nWHERE (library_id IS NOT NULL);\n","unique":true,"columns":[]}},{"id":11,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)","unique":false,"columns":[]}},{"id":12,"references":[],"type":"table","data":{"name":"auth_user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"avatar_color","getter_name":"avatarColor","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AvatarColor.values)","dart_type_name":"AvatarColor"}},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"pin_code","getter_name":"pinCode","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":14,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":15,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":16,"references":[1,4],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":17,"references":[4,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":18,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":19,"references":[1,18],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":20,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":21,"references":[1,20],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":22,"references":[],"type":"table","data":{"name":"store_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"string_value","getter_name":"stringValue","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"int_value","getter_name":"intValue","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":23,"references":[15],"type":"index","data":{"on":15,"name":"idx_lat_lng","sql":"CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)","unique":false,"columns":[]}}]} \ No newline at end of file diff --git a/mobile/ios/Podfile.lock b/mobile/ios/Podfile.lock index 502fd9008f..9bff8cd8e2 100644 --- a/mobile/ios/Podfile.lock +++ b/mobile/ios/Podfile.lock @@ -64,7 +64,7 @@ PODS: - Flutter - integration_test (0.0.1): - Flutter - - isar_flutter_libs (1.0.0): + - isar_community_flutter_libs (1.0.0): - Flutter - local_auth_darwin (0.0.1): - Flutter @@ -149,7 +149,7 @@ DEPENDENCIES: - home_widget (from `.symlinks/plugins/home_widget/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - integration_test (from `.symlinks/plugins/integration_test/ios`) - - isar_flutter_libs (from `.symlinks/plugins/isar_flutter_libs/ios`) + - isar_community_flutter_libs (from `.symlinks/plugins/isar_community_flutter_libs/ios`) - local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`) - maplibre_gl (from `.symlinks/plugins/maplibre_gl/ios`) - native_video_player (from `.symlinks/plugins/native_video_player/ios`) @@ -210,8 +210,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/image_picker_ios/ios" integration_test: :path: ".symlinks/plugins/integration_test/ios" - isar_flutter_libs: - :path: ".symlinks/plugins/isar_flutter_libs/ios" + isar_community_flutter_libs: + :path: ".symlinks/plugins/isar_community_flutter_libs/ios" local_auth_darwin: :path: ".symlinks/plugins/local_auth_darwin/darwin" maplibre_gl: @@ -264,7 +264,7 @@ SPEC CHECKSUMS: home_widget: f169fc41fd807b4d46ab6615dc44d62adbf9f64f image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e - isar_flutter_libs: bc909e72c3d756c2759f14c8776c13b5b0556e26 + isar_community_flutter_libs: bede843185a61a05ff364a05c9b23209523f7e0d local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391 MapLibre: 69e572367f4ef6287e18246cfafc39c80cdcabcd maplibre_gl: 3c924e44725147b03dda33430ad216005b40555f diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj index 48826a20f1..3f00b6c6aa 100644 --- a/mobile/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/ios/Runner.xcodeproj/project.pbxproj @@ -131,6 +131,13 @@ /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ + B231F52D2E93A44A00BC45D1 /* Core */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + ); + path = Core; + sourceTree = ""; + }; B2CF7F8C2DDE4EBB00744BF6 /* Sync */ = { isa = PBXFileSystemSynchronizedRootGroup; exceptions = ( @@ -247,6 +254,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + B231F52D2E93A44A00BC45D1 /* Core */, B25D37792E72CA15008B6CA7 /* Connectivity */, B21E34A62E5AF9760031FDB9 /* Background */, B2CF7F8C2DDE4EBB00744BF6 /* Sync */, @@ -331,6 +339,7 @@ F0B57D482DF764BE00DC5BCC /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( + B231F52D2E93A44A00BC45D1 /* Core */, B2CF7F8C2DDE4EBB00744BF6 /* Sync */, ); name = Runner; @@ -705,7 +714,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 224; + CURRENT_PROJECT_VERSION = 231; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -849,7 +858,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 224; + CURRENT_PROJECT_VERSION = 231; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -879,7 +888,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 224; + CURRENT_PROJECT_VERSION = 231; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -913,7 +922,7 @@ CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 224; + CURRENT_PROJECT_VERSION = 231; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -956,7 +965,7 @@ CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 224; + CURRENT_PROJECT_VERSION = 231; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -996,7 +1005,7 @@ CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 224; + CURRENT_PROJECT_VERSION = 231; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -1035,7 +1044,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 224; + CURRENT_PROJECT_VERSION = 231; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -1079,7 +1088,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 224; + CURRENT_PROJECT_VERSION = 231; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -1120,7 +1129,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 224; + CURRENT_PROJECT_VERSION = 231; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; diff --git a/mobile/ios/Runner/AppDelegate.swift b/mobile/ios/Runner/AppDelegate.swift index 3476030923..4e4cb2ed13 100644 --- a/mobile/ios/Runner/AppDelegate.swift +++ b/mobile/ios/Runner/AppDelegate.swift @@ -20,7 +20,7 @@ import UIKit GeneratedPluginRegistrant.register(with: self) let controller: FlutterViewController = window?.rootViewController as! FlutterViewController - AppDelegate.registerPlugins(binaryMessenger: controller.binaryMessenger) + AppDelegate.registerPlugins(with: controller.engine) BackgroundServicePlugin.register(with: self.registrar(forPlugin: "BackgroundServicePlugin")!) BackgroundServicePlugin.registerBackgroundProcessing() @@ -51,9 +51,13 @@ import UIKit return super.application(application, didFinishLaunchingWithOptions: launchOptions) } - public static func registerPlugins(binaryMessenger: FlutterBinaryMessenger) { - NativeSyncApiSetup.setUp(binaryMessenger: binaryMessenger, api: NativeSyncApiImpl()) - ThumbnailApiSetup.setUp(binaryMessenger: binaryMessenger, api: ThumbnailApiImpl()) - BackgroundWorkerFgHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: BackgroundWorkerApiImpl()) + public static func registerPlugins(with engine: FlutterEngine) { + NativeSyncApiImpl.register(with: engine.registrar(forPlugin: NativeSyncApiImpl.name)!) + ThumbnailApiSetup.setUp(binaryMessenger: engine.binaryMessenger, api: ThumbnailApiImpl()) + BackgroundWorkerFgHostApiSetup.setUp(binaryMessenger: engine.binaryMessenger, api: BackgroundWorkerApiImpl()) + } + + public static func cancelPlugins(with engine: FlutterEngine) { + (engine.valuePublished(byPlugin: NativeSyncApiImpl.name) as? NativeSyncApiImpl)?.detachFromEngine() } } diff --git a/mobile/ios/Runner/Background/BackgroundWorker.g.swift b/mobile/ios/Runner/Background/BackgroundWorker.g.swift index ece5cd5f64..e339f150e7 100644 --- a/mobile/ios/Runner/Background/BackgroundWorker.g.swift +++ b/mobile/ios/Runner/Background/BackgroundWorker.g.swift @@ -182,6 +182,7 @@ class BackgroundWorkerPigeonCodec: FlutterStandardMessageCodec, @unchecked Senda /// Generated protocol from Pigeon that represents a handler of messages from Flutter. protocol BackgroundWorkerFgHostApi { func enable() throws + func saveNotificationMessage(title: String, body: String) throws func configure(settings: BackgroundWorkerSettings) throws func disable() throws } @@ -205,6 +206,22 @@ class BackgroundWorkerFgHostApiSetup { } else { enableChannel.setMessageHandler(nil) } + let saveNotificationMessageChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.saveNotificationMessage\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + saveNotificationMessageChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let titleArg = args[0] as! String + let bodyArg = args[1] as! String + do { + try api.saveNotificationMessage(title: titleArg, body: bodyArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + saveNotificationMessageChannel.setMessageHandler(nil) + } let configureChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.configure\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { configureChannel.setMessageHandler { message, reply in @@ -238,7 +255,6 @@ class BackgroundWorkerFgHostApiSetup { /// Generated protocol from Pigeon that represents a handler of messages from Flutter. protocol BackgroundWorkerBgHostApi { func onInitialized() throws - func showNotification(title: String, content: String) throws func close() throws } @@ -261,22 +277,6 @@ class BackgroundWorkerBgHostApiSetup { } else { onInitializedChannel.setMessageHandler(nil) } - let showNotificationChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.showNotification\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - showNotificationChannel.setMessageHandler { message, reply in - let args = message as! [Any?] - let titleArg = args[0] as! String - let contentArg = args[1] as! String - do { - try api.showNotification(title: titleArg, content: contentArg) - reply(wrapResult(nil)) - } catch { - reply(wrapError(error)) - } - } - } else { - showNotificationChannel.setMessageHandler(nil) - } let closeChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.close\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { closeChannel.setMessageHandler { _, reply in diff --git a/mobile/ios/Runner/Background/BackgroundWorker.swift b/mobile/ios/Runner/Background/BackgroundWorker.swift index c3268b4a2b..7dc450d76e 100644 --- a/mobile/ios/Runner/Background/BackgroundWorker.swift +++ b/mobile/ios/Runner/Background/BackgroundWorker.swift @@ -95,7 +95,7 @@ class BackgroundWorker: BackgroundWorkerBgHostApi { // Register plugins in the new engine GeneratedPluginRegistrant.register(with: engine) // Register custom plugins - AppDelegate.registerPlugins(binaryMessenger: engine.binaryMessenger) + AppDelegate.registerPlugins(with: engine) flutterApi = BackgroundWorkerFlutterApi(binaryMessenger: engine.binaryMessenger) BackgroundWorkerBgHostApiSetup.setUp(binaryMessenger: engine.binaryMessenger, api: self) @@ -119,10 +119,6 @@ class BackgroundWorker: BackgroundWorkerBgHostApi { }) } - func showNotification(title: String, content: String) throws { - // No-op on iOS for the time being - } - /** * Cancels the currently running background task, either due to timeout or external request. * Sends a cancel signal to the Flutter side and sets up a fallback timer to ensure @@ -172,6 +168,7 @@ class BackgroundWorker: BackgroundWorkerBgHostApi { } isComplete = true + AppDelegate.cancelPlugins(with: engine) engine.destroyContext() flutterApi = nil completionHandler(success) diff --git a/mobile/ios/Runner/Background/BackgroundWorkerApiImpl.swift b/mobile/ios/Runner/Background/BackgroundWorkerApiImpl.swift index f7f8f69989..a7bbc31ceb 100644 --- a/mobile/ios/Runner/Background/BackgroundWorkerApiImpl.swift +++ b/mobile/ios/Runner/Background/BackgroundWorkerApiImpl.swift @@ -12,6 +12,10 @@ class BackgroundWorkerApiImpl: BackgroundWorkerFgHostApi { // Android only } + func saveNotificationMessage(title: String, body: String) throws { + // Android only + } + func disable() throws { BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: BackgroundWorkerApiImpl.refreshTaskID); BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: BackgroundWorkerApiImpl.processingTaskID); diff --git a/mobile/ios/Runner/Core/ImmichPlugin.swift b/mobile/ios/Runner/Core/ImmichPlugin.swift new file mode 100644 index 0000000000..db10b7a75d --- /dev/null +++ b/mobile/ios/Runner/Core/ImmichPlugin.swift @@ -0,0 +1,17 @@ +class ImmichPlugin: NSObject { + var detached: Bool + + override init() { + detached = false + super.init() + } + + func detachFromEngine() { + self.detached = true + } + + func completeWhenActive(for completion: @escaping (T) -> Void, with value: T) { + guard !self.detached else { return } + completion(value) + } +} diff --git a/mobile/ios/Runner/Info.plist b/mobile/ios/Runner/Info.plist index c1e6ec212d..1dc55468da 100644 --- a/mobile/ios/Runner/Info.plist +++ b/mobile/ios/Runner/Info.plist @@ -80,7 +80,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.142.1 + 2.1.0 CFBundleSignature ???? CFBundleURLTypes @@ -107,7 +107,7 @@ CFBundleVersion - 224 + 231 FLTEnableImpeller ITSAppUsesNonExemptEncryption diff --git a/mobile/ios/Runner/Sync/MessagesImpl.swift b/mobile/ios/Runner/Sync/MessagesImpl.swift index bb23bae6b6..75981fb7ea 100644 --- a/mobile/ios/Runner/Sync/MessagesImpl.swift +++ b/mobile/ios/Runner/Sync/MessagesImpl.swift @@ -17,13 +17,25 @@ struct AssetWrapper: Hashable, Equatable { } } -class NativeSyncApiImpl: NativeSyncApi { +class NativeSyncApiImpl: ImmichPlugin, NativeSyncApi, FlutterPlugin { + static let name = "NativeSyncApi" + + static func register(with registrar: any FlutterPluginRegistrar) { + let instance = NativeSyncApiImpl() + NativeSyncApiSetup.setUp(binaryMessenger: registrar.messenger(), api: instance) + registrar.publish(instance) + } + + func detachFromEngine(for registrar: any FlutterPluginRegistrar) { + super.detachFromEngine() + } + private let defaults: UserDefaults private let changeTokenKey = "immich:changeToken" private let albumTypes: [PHAssetCollectionType] = [.album, .smartAlbum] private let recoveredAlbumSubType = 1000000219 - private var hashTask: Task? + private var hashTask: Task? private static let hashCancelledCode = "HASH_CANCELLED" private static let hashCancelled = Result<[HashResult], Error>.failure(PigeonError(code: hashCancelledCode, message: "Hashing cancelled", details: nil)) @@ -91,7 +103,9 @@ class NativeSyncApiImpl: NativeSyncApi { let options = PHFetchOptions() options.sortDescriptors = [NSSortDescriptor(key: "modificationDate", ascending: false)] options.includeHiddenAssets = false - let assets = PHAsset.fetchAssets(in: album, options: options) + + let assets = getAssetsFromAlbum(in: album, options: options) + let isCloud = album.assetCollectionSubtype == .albumCloudShared || album.assetCollectionSubtype == .albumMyPhotoStream var domainAlbum = PlatformAlbum( @@ -189,7 +203,7 @@ class NativeSyncApiImpl: NativeSyncApi { let options = PHFetchOptions() options.predicate = NSPredicate(format: "localIdentifier IN %@", assets.map(\.id)) options.includeHiddenAssets = false - let result = PHAsset.fetchAssets(in: album, options: options) + let result = self.getAssetsFromAlbum(in: album, options: options) result.enumerateObjects { (asset, _, _) in albumAssets[asset.localIdentifier, default: []].append(album.localIdentifier) } @@ -207,7 +221,7 @@ class NativeSyncApiImpl: NativeSyncApi { var ids: [String] = [] let options = PHFetchOptions() options.includeHiddenAssets = false - let assets = PHAsset.fetchAssets(in: album, options: options) + let assets = getAssetsFromAlbum(in: album, options: options) assets.enumerateObjects { (asset, _, _) in ids.append(asset.localIdentifier) } @@ -224,7 +238,7 @@ class NativeSyncApiImpl: NativeSyncApi { let options = PHFetchOptions() options.predicate = NSPredicate(format: "creationDate > %@ OR modificationDate > %@", date, date) options.includeHiddenAssets = false - let assets = PHAsset.fetchAssets(in: album, options: options) + let assets = getAssetsFromAlbum(in: album, options: options) return Int64(assets.count) } @@ -241,7 +255,7 @@ class NativeSyncApiImpl: NativeSyncApi { options.predicate = NSPredicate(format: "creationDate > %@ OR modificationDate > %@", date, date) } - let result = PHAsset.fetchAssets(in: album, options: options) + let result = getAssetsFromAlbum(in: album, options: options) if(result.count == 0) { return [] } @@ -272,7 +286,7 @@ class NativeSyncApiImpl: NativeSyncApi { } if Task.isCancelled { - return completion(Self.hashCancelled) + return self?.completeWhenActive(for: completion, with: Self.hashCancelled) } await withTaskGroup(of: HashResult?.self) { taskGroup in @@ -280,7 +294,7 @@ class NativeSyncApiImpl: NativeSyncApi { results.reserveCapacity(assets.count) for asset in assets { if Task.isCancelled { - return completion(Self.hashCancelled) + return self?.completeWhenActive(for: completion, with: Self.hashCancelled) } taskGroup.addTask { guard let self = self else { return nil } @@ -290,7 +304,7 @@ class NativeSyncApiImpl: NativeSyncApi { for await result in taskGroup { guard let result = result else { - return completion(Self.hashCancelled) + return self?.completeWhenActive(for: completion, with: Self.hashCancelled) } results.append(result) } @@ -299,7 +313,7 @@ class NativeSyncApiImpl: NativeSyncApi { results.append(HashResult(assetId: missing, error: "Asset not found in library", hash: nil)) } - completion(.success(results)) + return self?.completeWhenActive(for: completion, with: .success(results)) } } } @@ -363,4 +377,13 @@ class NativeSyncApiImpl: NativeSyncApi { PHAssetResourceManager.default().cancelDataRequest(requestId) }) } + + private func getAssetsFromAlbum(in album: PHAssetCollection, options: PHFetchOptions) -> PHFetchResult { + // Ensure to actually getting all assets for the Recents album + if (album.assetCollectionSubtype == .smartAlbumUserLibrary) { + return PHAsset.fetchAssets(with: options) + } else { + return PHAsset.fetchAssets(in: album, options: options) + } + } } diff --git a/mobile/ios/WidgetExtension/widgets/MemoryWidget.swift b/mobile/ios/WidgetExtension/widgets/MemoryWidget.swift index d0a3e8c29d..22414fbec4 100644 --- a/mobile/ios/WidgetExtension/widgets/MemoryWidget.swift +++ b/mobile/ios/WidgetExtension/widgets/MemoryWidget.swift @@ -114,7 +114,7 @@ struct ImmichMemoryProvider: TimelineProvider { } } - // If we didnt add any memory images (some failure occured or no images in memory), + // If we didn't add any memory images (some failure occurred or no images in memory), // default to 12 hours of random photos if entries.count == 0 { // this must be a do/catch since we need to diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index 998f78f768..57c853e751 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -22,7 +22,7 @@ platform :ios do path: "./Runner.xcodeproj", ) increment_version_number( - version_number: "1.143.1" + version_number: "2.1.0" ) increment_build_number( build_number: latest_testflight_build_number + 1, diff --git a/mobile/lib/domain/models/asset/base_asset.model.dart b/mobile/lib/domain/models/asset/base_asset.model.dart index 4d40be2d32..5774a13c90 100644 --- a/mobile/lib/domain/models/asset/base_asset.model.dart +++ b/mobile/lib/domain/models/asset/base_asset.model.dart @@ -56,6 +56,8 @@ sealed class BaseAsset { // Overridden in subclasses AssetState get storage; + String? get localId; + String? get remoteId; String get heroTag; @override diff --git a/mobile/lib/domain/models/asset/local_asset.model.dart b/mobile/lib/domain/models/asset/local_asset.model.dart index 9cd20acb0a..6f2f4c06ba 100644 --- a/mobile/lib/domain/models/asset/local_asset.model.dart +++ b/mobile/lib/domain/models/asset/local_asset.model.dart @@ -2,12 +2,12 @@ part of 'base_asset.model.dart'; class LocalAsset extends BaseAsset { final String id; - final String? remoteId; + final String? remoteAssetId; final int orientation; const LocalAsset({ required this.id, - this.remoteId, + String? remoteId, required super.name, super.checksum, required super.type, @@ -19,7 +19,13 @@ class LocalAsset extends BaseAsset { super.isFavorite = false, super.livePhotoVideoId, this.orientation = 0, - }); + }) : remoteAssetId = remoteId; + + @override + String? get localId => id; + + @override + String? get remoteId => remoteAssetId; @override AssetState get storage => remoteId == null ? AssetState.local : AssetState.merged; diff --git a/mobile/lib/domain/models/asset/remote_asset.model.dart b/mobile/lib/domain/models/asset/remote_asset.model.dart index 8648255167..4974dc9118 100644 --- a/mobile/lib/domain/models/asset/remote_asset.model.dart +++ b/mobile/lib/domain/models/asset/remote_asset.model.dart @@ -5,7 +5,7 @@ enum AssetVisibility { timeline, hidden, archive, locked } // Model for an asset stored in the server class RemoteAsset extends BaseAsset { final String id; - final String? localId; + final String? localAssetId; final String? thumbHash; final AssetVisibility visibility; final String ownerId; @@ -13,7 +13,7 @@ class RemoteAsset extends BaseAsset { const RemoteAsset({ required this.id, - this.localId, + String? localId, required super.name, required this.ownerId, required super.checksum, @@ -28,7 +28,13 @@ class RemoteAsset extends BaseAsset { this.visibility = AssetVisibility.timeline, super.livePhotoVideoId, this.stackId, - }); + }) : localAssetId = localId; + + @override + String? get localId => localAssetId; + + @override + String? get remoteId => id; @override AssetState get storage => localId == null ? AssetState.remote : AssetState.merged; diff --git a/mobile/lib/domain/models/search_result.model.dart b/mobile/lib/domain/models/search_result.model.dart index bae8b8e821..947bc6192f 100644 --- a/mobile/lib/domain/models/search_result.model.dart +++ b/mobile/lib/domain/models/search_result.model.dart @@ -3,27 +3,30 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; class SearchResult { final List assets; + final double scrollOffset; final int? nextPage; - const SearchResult({required this.assets, this.nextPage}); + const SearchResult({required this.assets, this.scrollOffset = 0.0, this.nextPage}); - int get totalAssets => assets.length; - - SearchResult copyWith({List? assets, int? nextPage}) { - return SearchResult(assets: assets ?? this.assets, nextPage: nextPage ?? this.nextPage); + SearchResult copyWith({List? assets, int? nextPage, double? scrollOffset}) { + return SearchResult( + assets: assets ?? this.assets, + nextPage: nextPage ?? this.nextPage, + scrollOffset: scrollOffset ?? this.scrollOffset, + ); } @override - String toString() => 'SearchResult(assets: $assets, nextPage: $nextPage)'; + String toString() => 'SearchResult(assets: ${assets.length}, nextPage: $nextPage, scrollOffset: $scrollOffset)'; @override bool operator ==(covariant SearchResult other) { if (identical(this, other)) return true; final listEquals = const DeepCollectionEquality().equals; - return listEquals(other.assets, assets) && other.nextPage == nextPage; + return listEquals(other.assets, assets) && other.nextPage == nextPage && other.scrollOffset == scrollOffset; } @override - int get hashCode => assets.hashCode ^ nextPage.hashCode; + int get hashCode => assets.hashCode ^ nextPage.hashCode ^ scrollOffset.hashCode; } diff --git a/mobile/lib/domain/models/setting.model.dart b/mobile/lib/domain/models/setting.model.dart index f427d93285..2c46507331 100644 --- a/mobile/lib/domain/models/setting.model.dart +++ b/mobile/lib/domain/models/setting.model.dart @@ -6,6 +6,7 @@ enum Setting { showStorageIndicator(StoreKey.storageIndicator, true), loadOriginal(StoreKey.loadOriginal, false), loadOriginalVideo(StoreKey.loadOriginalVideo, false), + autoPlayVideo(StoreKey.autoPlayVideo, true), preferRemoteImage(StoreKey.preferRemoteImage, false), advancedTroubleshooting(StoreKey.advancedTroubleshooting, false), enableBackup(StoreKey.enableBackup, false); diff --git a/mobile/lib/domain/models/store.model.dart b/mobile/lib/domain/models/store.model.dart index efccc9bccd..d8404db409 100644 --- a/mobile/lib/domain/models/store.model.dart +++ b/mobile/lib/domain/models/store.model.dart @@ -70,6 +70,8 @@ enum StoreKey { // Read-only Mode settings readonlyModeEnabled._(138), + autoPlayVideo._(139), + // Experimental stuff photoManagerCustomFilter._(1000), betaPromptShown._(1001), diff --git a/mobile/lib/domain/services/asset.service.dart b/mobile/lib/domain/services/asset.service.dart index 84f675f68b..7f8ade313c 100644 --- a/mobile/lib/domain/services/asset.service.dart +++ b/mobile/lib/domain/services/asset.service.dart @@ -84,8 +84,8 @@ class AssetService { return 1.0; } - Future> getPlaces() { - return _remoteAssetRepository.getPlaces(); + Future> getPlaces(String userId) { + return _remoteAssetRepository.getPlaces(userId); } Future<(int local, int remote)> getAssetCounts() async { diff --git a/mobile/lib/domain/services/background_worker.service.dart b/mobile/lib/domain/services/background_worker.service.dart index 942581633f..78ba5b7088 100644 --- a/mobile/lib/domain/services/background_worker.service.dart +++ b/mobile/lib/domain/services/background_worker.service.dart @@ -11,8 +11,6 @@ import 'package:immich_mobile/domain/services/log.service.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/network_capability_extensions.dart'; import 'package:immich_mobile/extensions/platform_extensions.dart'; -import 'package:immich_mobile/extensions/translate_extensions.dart'; -import 'package:immich_mobile/generated/intl_keys.g.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart'; import 'package:immich_mobile/platform/background_worker_api.g.dart'; @@ -28,7 +26,6 @@ import 'package:immich_mobile/repositories/file_media.repository.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/services/auth.service.dart'; import 'package:immich_mobile/services/localization.service.dart'; -import 'package:immich_mobile/services/server_info.service.dart'; import 'package:immich_mobile/services/upload.service.dart'; import 'package:immich_mobile/utils/bootstrap.dart'; import 'package:immich_mobile/utils/debug_print.dart'; @@ -45,6 +42,9 @@ class BackgroundWorkerFgService { // TODO: Move this call to native side once old timeline is removed Future enable() => _foregroundHostApi.enable(); + Future saveNotificationMessage(String title, String body) => + _foregroundHostApi.saveNotificationMessage(title, body); + Future configure({int? minimumDelaySeconds, bool? requireCharging}) => _foregroundHostApi.configure( BackgroundWorkerSettings( minimumDelaySeconds: @@ -113,13 +113,6 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { configureFileDownloaderNotifications(); - if (Platform.isAndroid) { - await _backgroundHostApi.showNotification( - IntlKeys.uploading_media.t(), - IntlKeys.backup_background_service_in_progress_notification.t(), - ); - } - // Notify the host that the background worker service has been initialized and is ready to use _backgroundHostApi.onInitialized(); } catch (error, stack) { @@ -130,30 +123,33 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { @override Future onAndroidUpload() async { + _logger.info('Android background processing started'); + final sw = Stopwatch()..start(); try { - _logger.info('Android background processing started'); - final sw = Stopwatch()..start(); - - await _syncAssets(hashTimeout: Duration(minutes: _isBackupEnabled ? 3 : 6)); + if (!await _syncAssets(hashTimeout: Duration(minutes: _isBackupEnabled ? 3 : 6))) { + _logger.warning("Remote sync did not complete successfully, skipping backup"); + return; + } await _handleBackup(); - - sw.stop(); - _logger.info("Android background processing completed in ${sw.elapsed.inSeconds}s"); } catch (error, stack) { _logger.severe("Failed to complete Android background processing", error, stack); } finally { + sw.stop(); + _logger.info("Android background processing completed in ${sw.elapsed.inSeconds}s"); await _cleanup(); } } @override Future onIosUpload(bool isRefresh, int? maxSeconds) async { + _logger.info('iOS background upload started with maxSeconds: ${maxSeconds}s'); + final sw = Stopwatch()..start(); try { - _logger.info('iOS background upload started with maxSeconds: ${maxSeconds}s'); - final sw = Stopwatch()..start(); - final timeout = isRefresh ? const Duration(seconds: 5) : Duration(minutes: _isBackupEnabled ? 3 : 6); - await _syncAssets(hashTimeout: timeout); + if (!await _syncAssets(hashTimeout: timeout)) { + _logger.warning("Remote sync did not complete successfully, skipping backup"); + return; + } final backupFuture = _handleBackup(); if (maxSeconds != null) { @@ -161,12 +157,11 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { } else { await backupFuture; } - - sw.stop(); - _logger.info("iOS background upload completed in ${sw.elapsed.inSeconds}s"); } catch (error, stack) { _logger.severe("Failed to complete iOS background upload", error, stack); } finally { + sw.stop(); + _logger.info("iOS background upload completed in ${sw.elapsed.inSeconds}s"); await _cleanup(); } } @@ -197,6 +192,7 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { _cancellationToken.cancel(); _logger.info("Cleaning up background worker"); final cleanupFutures = [ + nativeSyncApi?.cancelHashing(), workerManager.dispose().catchError((_) async { // Discard any errors on the dispose call return; @@ -206,7 +202,6 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { _drift.close(), _driftLogger.close(), backgroundSyncManager?.cancel(), - nativeSyncApi?.cancelHashing(), ]; if (_isar.isOpen) { @@ -227,29 +222,20 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { } if (!_isBackupEnabled) { - _logger.info("[_handleBackup 1] Backup is disabled. Skipping backup routine"); + _logger.info("Backup is disabled. Skipping backup routine"); return; } - _logger.info("[_handleBackup 2] Enqueuing assets for backup from the background service"); - final currentUser = _ref?.read(currentUserProvider); if (currentUser == null) { - _logger.warning("[_handleBackup 3] No current user found. Skipping backup from background"); + _logger.warning("No current user found. Skipping backup from background"); return; } - _logger.info("[_handleBackup 4] Resume backup from background"); if (Platform.isIOS) { return _ref?.read(driftBackupProvider.notifier).handleBackupResume(currentUser.id); } - final canPing = await _ref?.read(serverInfoServiceProvider).ping() ?? false; - if (!canPing) { - _logger.warning("[_handleBackup 5] Server is not reachable. Skipping backup from background"); - return; - } - final networkCapabilities = await _ref?.read(connectivityApiProvider).getCapabilities() ?? []; return _ref ?.read(uploadServiceProvider) @@ -261,15 +247,15 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { ); } - Future _syncAssets({Duration? hashTimeout}) async { + Future _syncAssets({Duration? hashTimeout}) async { await _ref?.read(backgroundSyncProvider).syncLocal(); if (_isCleanedUp) { - return; + return false; } - await _ref?.read(backgroundSyncProvider).syncRemote(); + final isSuccess = await _ref?.read(backgroundSyncProvider).syncRemote() ?? false; if (_isCleanedUp) { - return; + return isSuccess; } var hashFuture = _ref?.read(backgroundSyncProvider).hashAssets(); @@ -283,6 +269,7 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { } await hashFuture; + return isSuccess; } } diff --git a/mobile/lib/domain/services/local_sync.service.dart b/mobile/lib/domain/services/local_sync.service.dart index d333af7481..ca356c80d8 100644 --- a/mobile/lib/domain/services/local_sync.service.dart +++ b/mobile/lib/domain/services/local_sync.service.dart @@ -281,7 +281,7 @@ extension on Iterable { (e) => LocalAlbum( id: e.id, name: e.name, - updatedAt: tryFromSecondsSinceEpoch(e.updatedAt) ?? DateTime.now(), + updatedAt: tryFromSecondsSinceEpoch(e.updatedAt, isUtc: true) ?? DateTime.timestamp(), assetCount: e.assetCount, ), ).toList(); @@ -296,8 +296,8 @@ extension on Iterable { name: e.name, checksum: null, type: AssetType.values.elementAtOrNull(e.type) ?? AssetType.other, - createdAt: tryFromSecondsSinceEpoch(e.createdAt) ?? DateTime.now(), - updatedAt: tryFromSecondsSinceEpoch(e.updatedAt) ?? DateTime.now(), + createdAt: tryFromSecondsSinceEpoch(e.createdAt, isUtc: true) ?? DateTime.timestamp(), + updatedAt: tryFromSecondsSinceEpoch(e.updatedAt, isUtc: true) ?? DateTime.timestamp(), width: e.width, height: e.height, durationInSeconds: e.durationInSeconds, diff --git a/mobile/lib/domain/services/remote_album.service.dart b/mobile/lib/domain/services/remote_album.service.dart index cc28dfafd5..13dfadb8d8 100644 --- a/mobile/lib/domain/services/remote_album.service.dart +++ b/mobile/lib/domain/services/remote_album.service.dart @@ -120,6 +120,10 @@ class RemoteAlbumService { return _repository.getSharedUsers(albumId); } + Future getUserRole(String albumId, String userId) { + return _repository.getUserRole(albumId, userId); + } + Future> getAssets(String albumId) { return _repository.getAssets(albumId); } diff --git a/mobile/lib/domain/services/store.service.dart b/mobile/lib/domain/services/store.service.dart index f9b4a0aa81..0098c3d262 100644 --- a/mobile/lib/domain/services/store.service.dart +++ b/mobile/lib/domain/services/store.service.dart @@ -86,7 +86,7 @@ class StoreService { _cache.remove(key.id); } - /// Clears all values from thw store (cache and DB) + /// Clears all values from the store (cache and DB) Future clear() async { await _storeRepository.deleteAll(); _cache.clear(); diff --git a/mobile/lib/domain/services/sync_stream.service.dart b/mobile/lib/domain/services/sync_stream.service.dart index 6c8e444d50..bec7e6afda 100644 --- a/mobile/lib/domain/services/sync_stream.service.dart +++ b/mobile/lib/domain/services/sync_stream.service.dart @@ -23,7 +23,7 @@ class SyncStreamService { bool get isCancelled => _cancelChecker?.call() ?? false; - Future sync() async { + Future sync() async { _logger.info("Remote sync request for user"); // Start the sync stream and handle events bool shouldReset = false; @@ -32,6 +32,7 @@ class SyncStreamService { _logger.info("Resetting sync state as requested by server"); await _syncApiRepository.streamChanges(_handleEvents); } + return true; } Future _handleEvents(List events, Function() abort, Function() reset) async { diff --git a/mobile/lib/domain/services/timeline.service.dart b/mobile/lib/domain/services/timeline.service.dart index ab623720b7..85fc5fc55d 100644 --- a/mobile/lib/domain/services/timeline.service.dart +++ b/mobile/lib/domain/services/timeline.service.dart @@ -16,7 +16,24 @@ typedef TimelineAssetSource = Future> Function(int index, int co typedef TimelineBucketSource = Stream> Function(); -typedef TimelineQuery = ({TimelineAssetSource assetSource, TimelineBucketSource bucketSource}); +typedef TimelineQuery = ({TimelineAssetSource assetSource, TimelineBucketSource bucketSource, TimelineOrigin origin}); + +enum TimelineOrigin { + main, + localAlbum, + remoteAlbum, + remoteAssets, + favorite, + trash, + archive, + lockedFolder, + video, + place, + person, + map, + search, + deepLink, +} class TimelineFactory { final DriftTimelineRepository _timelineRepository; @@ -57,14 +74,17 @@ class TimelineFactory { TimelineService person(String userId, String personId) => TimelineService(_timelineRepository.person(userId, personId, groupBy)); - TimelineService fromAssets(List assets) => TimelineService(_timelineRepository.fromAssets(assets)); + TimelineService fromAssets(List assets, TimelineOrigin type) => + TimelineService(_timelineRepository.fromAssets(assets, type)); - TimelineService map(LatLngBounds bounds) => TimelineService(_timelineRepository.map(bounds, groupBy)); + TimelineService map(String userId, LatLngBounds bounds) => + TimelineService(_timelineRepository.map(userId, bounds, groupBy)); } class TimelineService { final TimelineAssetSource _assetSource; final TimelineBucketSource _bucketSource; + final TimelineOrigin origin; final AsyncMutex _mutex = AsyncMutex(); int _bufferOffset = 0; List _buffer = []; @@ -73,11 +93,15 @@ class TimelineService { int _totalAssets = 0; int get totalAssets => _totalAssets; - TimelineService(TimelineQuery query) : this._(assetSource: query.assetSource, bucketSource: query.bucketSource); + TimelineService(TimelineQuery query) + : this._(assetSource: query.assetSource, bucketSource: query.bucketSource, origin: query.origin); - TimelineService._({required TimelineAssetSource assetSource, required TimelineBucketSource bucketSource}) - : _assetSource = assetSource, - _bucketSource = bucketSource { + TimelineService._({ + required TimelineAssetSource assetSource, + required TimelineBucketSource bucketSource, + required this.origin, + }) : _assetSource = assetSource, + _bucketSource = bucketSource { _bucketSubscription = _bucketSource().listen((buckets) { _mutex.run(() async { final totalAssets = buckets.fold(0, (acc, bucket) => acc + bucket.assetCount); @@ -202,7 +226,7 @@ class TimelineService { Future dispose() async { await _bucketSubscription?.cancel(); _bucketSubscription = null; - _buffer.clear(); + _buffer = []; _bufferOffset = 0; } } diff --git a/mobile/lib/domain/utils/background_sync.dart b/mobile/lib/domain/utils/background_sync.dart index ffbb020345..38e249b9f1 100644 --- a/mobile/lib/domain/utils/background_sync.dart +++ b/mobile/lib/domain/utils/background_sync.dart @@ -6,11 +6,12 @@ import 'package:immich_mobile/utils/isolate.dart'; import 'package:worker_manager/worker_manager.dart'; typedef SyncCallback = void Function(); +typedef SyncCallbackWithResult = void Function(T result); typedef SyncErrorCallback = void Function(String error); class BackgroundSyncManager { final SyncCallback? onRemoteSyncStart; - final SyncCallback? onRemoteSyncComplete; + final SyncCallbackWithResult? onRemoteSyncComplete; final SyncErrorCallback? onRemoteSyncError; final SyncCallback? onLocalSyncStart; @@ -21,7 +22,7 @@ class BackgroundSyncManager { final SyncCallback? onHashingComplete; final SyncErrorCallback? onHashingError; - Cancelable? _syncTask; + Cancelable? _syncTask; Cancelable? _syncWebsocketTask; Cancelable? _deviceAlbumSyncTask; Cancelable? _linkedAlbumSyncTask; @@ -144,9 +145,9 @@ class BackgroundSyncManager { }); } - Future syncRemote() { + Future syncRemote() { if (_syncTask != null) { - return _syncTask!.future; + return _syncTask!.future.then((result) => result ?? false).catchError((_) => false); } onRemoteSyncStart?.call(); @@ -156,13 +157,18 @@ class BackgroundSyncManager { debugLabel: 'remote-sync', ); return _syncTask! - .whenComplete(() { - onRemoteSyncComplete?.call(); - _syncTask = null; + .then((result) { + final success = result ?? false; + onRemoteSyncComplete?.call(success); + return success; }) .catchError((error) { onRemoteSyncError?.call(error.toString()); _syncTask = null; + return false; + }) + .whenComplete(() { + _syncTask = null; }); } diff --git a/mobile/lib/entities/album.entity.g.dart b/mobile/lib/entities/album.entity.g.dart index e6ecde7f9a..ecbbab48c2 100644 --- a/mobile/lib/entities/album.entity.g.dart +++ b/mobile/lib/entities/album.entity.g.dart @@ -132,7 +132,7 @@ const AlbumSchema = CollectionSchema( getId: _albumGetId, getLinks: _albumGetLinks, attach: _albumAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _albumEstimateSize( diff --git a/mobile/lib/entities/android_device_asset.entity.g.dart b/mobile/lib/entities/android_device_asset.entity.g.dart index 9034709b8e..f8b1e32c72 100644 --- a/mobile/lib/entities/android_device_asset.entity.g.dart +++ b/mobile/lib/entities/android_device_asset.entity.g.dart @@ -47,7 +47,7 @@ const AndroidDeviceAssetSchema = CollectionSchema( getId: _androidDeviceAssetGetId, getLinks: _androidDeviceAssetGetLinks, attach: _androidDeviceAssetAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _androidDeviceAssetEstimateSize( diff --git a/mobile/lib/entities/asset.entity.g.dart b/mobile/lib/entities/asset.entity.g.dart index be5b427d01..db6bc72331 100644 --- a/mobile/lib/entities/asset.entity.g.dart +++ b/mobile/lib/entities/asset.entity.g.dart @@ -168,7 +168,7 @@ const AssetSchema = CollectionSchema( getId: _assetGetId, getLinks: _assetGetLinks, attach: _assetAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _assetEstimateSize( diff --git a/mobile/lib/entities/backup_album.entity.g.dart b/mobile/lib/entities/backup_album.entity.g.dart index ed98503119..583aa55c4d 100644 --- a/mobile/lib/entities/backup_album.entity.g.dart +++ b/mobile/lib/entities/backup_album.entity.g.dart @@ -43,7 +43,7 @@ const BackupAlbumSchema = CollectionSchema( getId: _backupAlbumGetId, getLinks: _backupAlbumGetLinks, attach: _backupAlbumAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _backupAlbumEstimateSize( diff --git a/mobile/lib/entities/duplicated_asset.entity.g.dart b/mobile/lib/entities/duplicated_asset.entity.g.dart index 6cf08ad9cc..80d2f344e6 100644 --- a/mobile/lib/entities/duplicated_asset.entity.g.dart +++ b/mobile/lib/entities/duplicated_asset.entity.g.dart @@ -32,7 +32,7 @@ const DuplicatedAssetSchema = CollectionSchema( getId: _duplicatedAssetGetId, getLinks: _duplicatedAssetGetLinks, attach: _duplicatedAssetAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _duplicatedAssetEstimateSize( diff --git a/mobile/lib/entities/etag.entity.g.dart b/mobile/lib/entities/etag.entity.g.dart index b1abba6bb7..03b4ea9918 100644 --- a/mobile/lib/entities/etag.entity.g.dart +++ b/mobile/lib/entities/etag.entity.g.dart @@ -52,7 +52,7 @@ const ETagSchema = CollectionSchema( getId: _eTagGetId, getLinks: _eTagGetLinks, attach: _eTagAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _eTagEstimateSize( diff --git a/mobile/lib/entities/ios_device_asset.entity.g.dart b/mobile/lib/entities/ios_device_asset.entity.g.dart index 8d8fec945b..252fe127bb 100644 --- a/mobile/lib/entities/ios_device_asset.entity.g.dart +++ b/mobile/lib/entities/ios_device_asset.entity.g.dart @@ -60,7 +60,7 @@ const IOSDeviceAssetSchema = CollectionSchema( getId: _iOSDeviceAssetGetId, getLinks: _iOSDeviceAssetGetLinks, attach: _iOSDeviceAssetAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _iOSDeviceAssetEstimateSize( diff --git a/mobile/lib/extensions/string_extensions.dart b/mobile/lib/extensions/string_extensions.dart index 6cd6e1e4b4..ae31565044 100644 --- a/mobile/lib/extensions/string_extensions.dart +++ b/mobile/lib/extensions/string_extensions.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + extension StringExtension on String { String capitalize() { return split(" ").map((str) => str.isEmpty ? str : str[0].toUpperCase() + str.substring(1)).join(" "); @@ -23,3 +25,11 @@ extension DurationExtension on String { return int.parse(this); } } + +Map? tryJsonDecode(dynamic json) { + try { + return jsonDecode(json) as Map; + } catch (e) { + return null; + } +} diff --git a/mobile/lib/infrastructure/entities/device_asset.entity.g.dart b/mobile/lib/infrastructure/entities/device_asset.entity.g.dart index 87ae54ad40..b6c30aca6f 100644 --- a/mobile/lib/infrastructure/entities/device_asset.entity.g.dart +++ b/mobile/lib/infrastructure/entities/device_asset.entity.g.dart @@ -65,7 +65,7 @@ const DeviceAssetEntitySchema = CollectionSchema( getId: _deviceAssetEntityGetId, getLinks: _deviceAssetEntityGetLinks, attach: _deviceAssetEntityAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _deviceAssetEntityEstimateSize( diff --git a/mobile/lib/infrastructure/entities/exif.entity.g.dart b/mobile/lib/infrastructure/entities/exif.entity.g.dart index d2f9ebda27..ffbfd0d8f0 100644 --- a/mobile/lib/infrastructure/entities/exif.entity.g.dart +++ b/mobile/lib/infrastructure/entities/exif.entity.g.dart @@ -68,7 +68,7 @@ const ExifInfoSchema = CollectionSchema( getId: _exifInfoGetId, getLinks: _exifInfoGetLinks, attach: _exifInfoAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _exifInfoEstimateSize( diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.dart b/mobile/lib/infrastructure/entities/local_asset.entity.dart index 337a6d728d..8b253f83a3 100644 --- a/mobile/lib/infrastructure/entities/local_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/local_asset.entity.dart @@ -21,7 +21,7 @@ class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { } extension LocalAssetEntityDataDomainExtension on LocalAssetEntityData { - LocalAsset toDto() => LocalAsset( + LocalAsset toDto({String? remoteId}) => LocalAsset( id: id, name: name, checksum: checksum, @@ -32,7 +32,7 @@ extension LocalAssetEntityDataDomainExtension on LocalAssetEntityData { isFavorite: isFavorite, height: height, width: width, - remoteId: null, + remoteId: remoteId, orientation: orientation, ); } diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.dart index 4426974413..dcc885a2a9 100644 --- a/mobile/lib/infrastructure/entities/remote_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/remote_asset.entity.dart @@ -49,7 +49,7 @@ class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin } extension RemoteAssetEntityDataDomainEx on RemoteAssetEntityData { - RemoteAsset toDto() => RemoteAsset( + RemoteAsset toDto({String? localId}) => RemoteAsset( id: id, name: name, ownerId: ownerId, @@ -64,7 +64,7 @@ extension RemoteAssetEntityDataDomainEx on RemoteAssetEntityData { thumbHash: thumbHash, visibility: visibility, livePhotoVideoId: livePhotoVideoId, - localId: null, + localId: localId, stackId: stackId, ); } diff --git a/mobile/lib/infrastructure/entities/store.entity.g.dart b/mobile/lib/infrastructure/entities/store.entity.g.dart index 7da92cf778..626c3084fe 100644 --- a/mobile/lib/infrastructure/entities/store.entity.g.dart +++ b/mobile/lib/infrastructure/entities/store.entity.g.dart @@ -37,7 +37,7 @@ const StoreValueSchema = CollectionSchema( getId: _storeValueGetId, getLinks: _storeValueGetLinks, attach: _storeValueAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _storeValueEstimateSize( diff --git a/mobile/lib/infrastructure/entities/user.entity.g.dart b/mobile/lib/infrastructure/entities/user.entity.g.dart index bb87051731..7e0af41b77 100644 --- a/mobile/lib/infrastructure/entities/user.entity.g.dart +++ b/mobile/lib/infrastructure/entities/user.entity.g.dart @@ -95,7 +95,7 @@ const UserSchema = CollectionSchema( getId: _userGetId, getLinks: _userGetLinks, attach: _userAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _userEstimateSize( diff --git a/mobile/lib/infrastructure/repositories/backup.repository.dart b/mobile/lib/infrastructure/repositories/backup.repository.dart index cc6fd7dfe3..0241711d4b 100644 --- a/mobile/lib/infrastructure/repositories/backup.repository.dart +++ b/mobile/lib/infrastructure/repositories/backup.repository.dart @@ -81,7 +81,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { ); } - Future> getCandidates(String userId) async { + Future> getCandidates(String userId, {bool onlyHashed = true}) async { final selectedAlbumIds = _db.localAlbumEntity.selectOnly(distinct: true) ..addColumns([_db.localAlbumEntity.id]) ..where(_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected)); @@ -89,7 +89,6 @@ class DriftBackupRepository extends DriftDatabaseRepository { final query = _db.localAssetEntity.select() ..where( (lae) => - lae.checksum.isNotNull() & existsQuery( _db.localAlbumAssetEntity.selectOnly() ..addColumns([_db.localAlbumAssetEntity.assetId]) @@ -109,6 +108,10 @@ class DriftBackupRepository extends DriftDatabaseRepository { ) ..orderBy([(localAsset) => OrderingTerm.desc(localAsset.createdAt)]); + if (onlyHashed) { + query.where((lae) => lae.checksum.isNotNull()); + } + return query.map((localAsset) => localAsset.toDto()).get(); } } diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 65d26d9747..7291c3a97b 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -93,7 +93,7 @@ class Drift extends $Drift implements IDatabaseRepository { } @override - int get schemaVersion => 11; + int get schemaVersion => 12; @override MigrationStrategy get migration => MigrationStrategy( @@ -159,6 +159,25 @@ class Drift extends $Drift implements IDatabaseRepository { from10To11: (m, v11) async { await m.addColumn(v11.localAlbumAssetEntity, v11.localAlbumAssetEntity.marker_); }, + from11To12: (m, v12) async { + final localToUTCMapping = { + v12.localAssetEntity: [v12.localAssetEntity.createdAt, v12.localAssetEntity.updatedAt], + v12.localAlbumEntity: [v12.localAlbumEntity.updatedAt], + }; + + for (final entry in localToUTCMapping.entries) { + final table = entry.key; + await m.alterTable( + TableMigration( + table, + columnTransformer: { + for (final column in entry.value) + column: column.modify(const DateTimeModifier.utc()).strftime('%Y-%m-%dT%H:%M:%fZ'), + }, + ), + ); + } + }, ), ); diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index 7910d9fcee..c973cd6f13 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.steps.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.steps.dart @@ -4659,6 +4659,384 @@ class Shape22 extends i0.VersionedTable { columnsByName['marker']! as i1.GeneratedColumn; } +final class Schema12 extends i0.VersionedSchema { + Schema12({required super.database}) : super(version: 12); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + remoteAlbumEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + authUserEntity, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + storeEntity, + idxLatLng, + ]; + late final Shape20 userEntity = Shape20( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_3, + _column_84, + _column_85, + _column_91, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape17 remoteAssetEntity = Shape17( + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + _column_86, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_75], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape2 localAssetEntity = Shape2( + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape9 remoteAlbumEntity = Shape9( + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape19 localAlbumEntity = Shape19( + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_90, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape22 localAlbumAssetEntity = Shape22( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35, _column_33], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index idxRemoteAssetOwnerChecksum = i1.Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + final i1.Index uQRemoteAssetsOwnerChecksum = i1.Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + final i1.Index uQRemoteAssetsOwnerLibraryChecksum = i1.Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final Shape21 authUserEntity = Shape21( + source: i0.VersionedTable( + entityName: 'auth_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_3, + _column_2, + _column_84, + _column_85, + _column_92, + _column_93, + _column_7, + _column_94, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape4 userMetadataEntity = Shape4( + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape5 partnerEntity = Shape5( + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape8 remoteExifEntity = Shape8( + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 remoteAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape10 remoteAlbumUserEntity = Shape10( + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape11 memoryEntity = Shape11( + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape12 memoryAssetEntity = Shape12( + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape14 personEntity = Shape14( + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape15 assetFaceEntity = Shape15( + source: i0.VersionedTable( + entityName: 'asset_face_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_36, + _column_76, + _column_77, + _column_78, + _column_79, + _column_80, + _column_81, + _column_82, + _column_83, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape18 storeEntity = Shape18( + source: i0.VersionedTable( + entityName: 'store_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_87, _column_88, _column_89], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLatLng = i1.Index( + 'idx_lat_lng', + 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', + ); +} + i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, @@ -4670,6 +5048,7 @@ i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema9 schema) from8To9, required Future Function(i1.Migrator m, Schema10 schema) from9To10, required Future Function(i1.Migrator m, Schema11 schema) from10To11, + required Future Function(i1.Migrator m, Schema12 schema) from11To12, }) { return (currentVersion, database) async { switch (currentVersion) { @@ -4723,6 +5102,11 @@ i0.MigrationStepWithVersion migrationSteps({ final migrator = i1.Migrator(database, schema); await from10To11(migrator, schema); return 11; + case 11: + final schema = Schema12(database: database); + final migrator = i1.Migrator(database, schema); + await from11To12(migrator, schema); + return 12; default: throw ArgumentError.value('Unknown migration from $currentVersion'); } @@ -4740,6 +5124,7 @@ i1.OnUpgrade stepByStep({ required Future Function(i1.Migrator m, Schema9 schema) from8To9, required Future Function(i1.Migrator m, Schema10 schema) from9To10, required Future Function(i1.Migrator m, Schema11 schema) from10To11, + required Future Function(i1.Migrator m, Schema12 schema) from11To12, }) => i0.VersionedSchema.stepByStepHelper( step: migrationSteps( from1To2: from1To2, @@ -4752,5 +5137,6 @@ i1.OnUpgrade stepByStep({ from8To9: from8To9, from9To10: from9To10, from10To11: from10To11, + from11To12: from11To12, ), ); diff --git a/mobile/lib/infrastructure/repositories/remote_album.repository.dart b/mobile/lib/infrastructure/repositories/remote_album.repository.dart index 5dfe4ac9b3..0526cfb7aa 100644 --- a/mobile/lib/infrastructure/repositories/remote_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_album.repository.dart @@ -62,7 +62,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { .toDto( assetCount: row.read(assetCount) ?? 0, ownerName: row.read(_db.userEntity.name)!, - isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 2, + isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0, ), ) .get(); @@ -107,7 +107,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { .toDto( assetCount: row.read(assetCount) ?? 0, ownerName: row.read(_db.userEntity.name)!, - isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 2, + isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0, ), ) .getSingleOrNull(); @@ -221,6 +221,15 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { .get(); } + Future getUserRole(String albumId, String userId) async { + final query = _db.remoteAlbumUserEntity.select() + ..where((row) => row.albumId.equals(albumId) & row.userId.equals(userId)) + ..limit(1); + + final result = await query.getSingleOrNull(); + return result?.role; + } + Future> getAssets(String albumId) { final query = _db.remoteAlbumAssetEntity.select().join([ innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId)), @@ -305,8 +314,9 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { .readTable(_db.remoteAlbumEntity) .toDto( ownerName: row.read(_db.userEntity.name)!, - isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 2, + isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0, ); + return album; }).watchSingleOrNull(); } diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 092bb728d9..be55c21afc 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -81,9 +81,11 @@ class RemoteAssetRepository extends DriftDatabaseRepository { .getSingleOrNull(); } - Future> getPlaces() { + Future> getPlaces(String userId) { final asset = Subquery( - _db.remoteAssetEntity.select()..orderBy([(row) => OrderingTerm.desc(row.createdAt)]), + _db.remoteAssetEntity.select() + ..where((row) => row.ownerId.equals(userId)) + ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]), "asset", ); diff --git a/mobile/lib/infrastructure/repositories/sync_api.repository.dart b/mobile/lib/infrastructure/repositories/sync_api.repository.dart index 3969286d28..8bf2e80579 100644 --- a/mobile/lib/infrastructure/repositories/sync_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_api.repository.dart @@ -110,7 +110,6 @@ class SyncApiRepository { await onData(_parseLines(lines), abort, reset); } } catch (error, stack) { - _logger.severe("Error processing stream", error, stack); return Future.error(error, stack); } finally { client.close(); diff --git a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart index f4720fb110..3f74fe25d1 100644 --- a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart @@ -51,6 +51,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { await _db.remoteAssetEntity.deleteAll(); await _db.remoteExifEntity.deleteAll(); await _db.stackEntity.deleteAll(); + await _db.authUserEntity.deleteAll(); await _db.userEntity.deleteAll(); await _db.userMetadataEntity.deleteAll(); }); diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index 86f68c397e..1fc0ee43e5 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -35,6 +35,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { TimelineQuery main(List userIds, GroupAssetsBy groupBy) => ( bucketSource: () => _watchMainBucket(userIds, groupBy: groupBy), assetSource: (offset, count) => _getMainBucketAssets(userIds, offset: offset, count: count), + origin: TimelineOrigin.main, ); Stream> _watchMainBucket(List userIds, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { @@ -43,7 +44,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { } return _db.mergedAssetDrift.mergedBucket(userIds: userIds, groupBy: groupBy.index).map((row) { - final date = row.bucketDate.dateFmt(groupBy); + final date = row.bucketDate.truncateDate(groupBy); return TimeBucket(date: date, assetCount: row.assetCount); }).watch(); } @@ -91,6 +92,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { TimelineQuery localAlbum(String albumId, GroupAssetsBy groupBy) => ( bucketSource: () => _watchLocalAlbumBucket(albumId, groupBy: groupBy), assetSource: (offset, count) => _getLocalAlbumBucketAssets(albumId, offset: offset, count: count), + origin: TimelineOrigin.localAlbum, ); Stream> _watchLocalAlbumBucket(String albumId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { @@ -123,7 +125,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { ..orderBy([OrderingTerm.desc(dateExp)]); return query.map((row) { - final timeline = row.read(dateExp)!.dateFmt(groupBy); + final timeline = row.read(dateExp)!.truncateDate(groupBy); final assetCount = row.read(assetCountExp)!; return TimeBucket(date: timeline, assetCount: assetCount); }).watch(); @@ -148,15 +150,15 @@ class DriftTimelineRepository extends DriftDatabaseRepository { ..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)]) ..limit(count, offset: offset); - return query.map((row) { - final asset = row.readTable(_db.localAssetEntity).toDto(); - return asset.copyWith(remoteId: row.read(_db.remoteAssetEntity.id)); - }).get(); + return query + .map((row) => row.readTable(_db.localAssetEntity).toDto(remoteId: row.read(_db.remoteAssetEntity.id))) + .get(); } TimelineQuery remoteAlbum(String albumId, GroupAssetsBy groupBy) => ( bucketSource: () => _watchRemoteAlbumBucket(albumId, groupBy: groupBy), assetSource: (offset, count) => _getRemoteAlbumBucketAssets(albumId, offset: offset, count: count), + origin: TimelineOrigin.remoteAlbum, ); Stream> _watchRemoteAlbumBucket(String albumId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { @@ -165,17 +167,15 @@ class DriftTimelineRepository extends DriftDatabaseRepository { .count(where: (row) => row.albumId.equals(albumId)) .map(_generateBuckets) .watch() - .map((results) => results.isNotEmpty ? results.first : []) - .handleError((error) { - return []; - }); + .map((results) => results.isNotEmpty ? results.first : const []) + .handleError((error) => const []); } return (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId))) .watch() .switchMap((albums) { if (albums.isEmpty) { - return Stream.value([]); + return Stream.value(const []); } final album = albums.first; @@ -202,15 +202,13 @@ class DriftTimelineRepository extends DriftDatabaseRepository { } return query.map((row) { - final timeline = row.read(dateExp)!.dateFmt(groupBy); + final timeline = row.read(dateExp)!.truncateDate(groupBy); final assetCount = row.read(assetCountExp)!; return TimeBucket(date: timeline, assetCount: assetCount); }).watch(); }) - .handleError((error) { - // If there's an error (e.g., album was deleted), return empty buckets - return []; - }); + // If there's an error (e.g., album was deleted), return empty buckets + .handleError((error) => const []); } Future> _getRemoteAlbumBucketAssets(String albumId, {required int offset, required int count}) async { @@ -218,17 +216,22 @@ class DriftTimelineRepository extends DriftDatabaseRepository { // If album doesn't exist (was deleted), return empty list if (albumData == null) { - return []; + return const []; } final isAscending = albumData.order == AlbumAssetOrder.asc; - final query = _db.remoteAssetEntity.select().join([ + final query = _db.remoteAssetEntity.select().addColumns([_db.localAssetEntity.id]).join([ innerJoin( _db.remoteAlbumAssetEntity, _db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id), useColumns: false, ), + leftOuterJoin( + _db.localAssetEntity, + _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), + useColumns: false, + ), ])..where(_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId)); if (isAscending) { @@ -239,18 +242,22 @@ class DriftTimelineRepository extends DriftDatabaseRepository { query.limit(count, offset: offset); - return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); + return query + .map((row) => row.readTable(_db.remoteAssetEntity).toDto(localId: row.read(_db.localAssetEntity.id))) + .get(); } - TimelineQuery fromAssets(List assets) => ( + TimelineQuery fromAssets(List assets, TimelineOrigin origin) => ( bucketSource: () => Stream.value(_generateBuckets(assets.length)), - assetSource: (offset, count) => Future.value(assets.skip(offset).take(count).toList()), + assetSource: (offset, count) => Future.value(assets.skip(offset).take(count).toList(growable: false)), + origin: origin, ); TimelineQuery remote(String ownerId, GroupAssetsBy groupBy) => _remoteQueryBuilder( filter: (row) => row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.timeline) & row.ownerId.equals(ownerId), groupBy: groupBy, + origin: TimelineOrigin.remoteAssets, ); TimelineQuery favorite(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( @@ -260,11 +267,13 @@ class DriftTimelineRepository extends DriftDatabaseRepository { row.ownerId.equals(userId) & row.visibility.equalsValue(AssetVisibility.timeline), groupBy: groupBy, + origin: TimelineOrigin.favorite, ); TimelineQuery trash(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( filter: (row) => row.deletedAt.isNotNull() & row.ownerId.equals(userId), groupBy: groupBy, + origin: TimelineOrigin.trash, joinLocal: true, ); @@ -272,11 +281,13 @@ class DriftTimelineRepository extends DriftDatabaseRepository { filter: (row) => row.deletedAt.isNull() & row.ownerId.equals(userId) & row.visibility.equalsValue(AssetVisibility.archive), groupBy: groupBy, + origin: TimelineOrigin.archive, ); TimelineQuery locked(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( filter: (row) => row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.locked) & row.ownerId.equals(userId), + origin: TimelineOrigin.lockedFolder, groupBy: groupBy, ); @@ -286,17 +297,20 @@ class DriftTimelineRepository extends DriftDatabaseRepository { row.type.equalsValue(AssetType.video) & row.visibility.equalsValue(AssetVisibility.timeline) & row.ownerId.equals(userId), + origin: TimelineOrigin.video, groupBy: groupBy, ); TimelineQuery place(String place, GroupAssetsBy groupBy) => ( bucketSource: () => _watchPlaceBucket(place, groupBy: groupBy), assetSource: (offset, count) => _getPlaceBucketAssets(place, offset: offset, count: count), + origin: TimelineOrigin.place, ); TimelineQuery person(String userId, String personId, GroupAssetsBy groupBy) => ( bucketSource: () => _watchPersonBucket(userId, personId, groupBy: groupBy), assetSource: (offset, count) => _getPersonBucketAssets(userId, personId, offset: offset, count: count), + origin: TimelineOrigin.person, ); Stream> _watchPlaceBucket(String place, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { @@ -326,7 +340,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { ..orderBy([OrderingTerm.desc(dateExp)]); return query.map((row) { - final timeline = row.read(dateExp)!.dateFmt(groupBy); + final timeline = row.read(dateExp)!.truncateDate(groupBy); final assetCount = row.read(assetCountExp)!; return TimeBucket(date: timeline, assetCount: assetCount); }).watch(); @@ -397,7 +411,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { ..orderBy([OrderingTerm.desc(dateExp)]); return query.map((row) { - final timeline = row.read(dateExp)!.dateFmt(groupBy); + final timeline = row.read(dateExp)!.truncateDate(groupBy); final assetCount = row.read(assetCountExp)!; return TimeBucket(date: timeline, assetCount: assetCount); }).watch(); @@ -429,12 +443,17 @@ class DriftTimelineRepository extends DriftDatabaseRepository { return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); } - TimelineQuery map(LatLngBounds bounds, GroupAssetsBy groupBy) => ( - bucketSource: () => _watchMapBucket(bounds, groupBy: groupBy), - assetSource: (offset, count) => _getMapBucketAssets(bounds, offset: offset, count: count), + TimelineQuery map(String userId, LatLngBounds bounds, GroupAssetsBy groupBy) => ( + bucketSource: () => _watchMapBucket(userId, bounds, groupBy: groupBy), + assetSource: (offset, count) => _getMapBucketAssets(userId, bounds, offset: offset, count: count), + origin: TimelineOrigin.map, ); - Stream> _watchMapBucket(LatLngBounds bounds, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { + Stream> _watchMapBucket( + String userId, + LatLngBounds bounds, { + GroupAssetsBy groupBy = GroupAssetsBy.day, + }) { if (groupBy == GroupAssetsBy.none) { // TODO: Support GroupAssetsBy.none throw UnsupportedError("GroupAssetsBy.none is not supported for _watchMapBucket"); @@ -453,7 +472,8 @@ class DriftTimelineRepository extends DriftDatabaseRepository { ), ]) ..where( - _db.remoteExifEntity.inBounds(bounds) & + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteExifEntity.inBounds(bounds) & _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & _db.remoteAssetEntity.deletedAt.isNull(), ) @@ -461,13 +481,18 @@ class DriftTimelineRepository extends DriftDatabaseRepository { ..orderBy([OrderingTerm.desc(dateExp)]); return query.map((row) { - final timeline = row.read(dateExp)!.dateFmt(groupBy); + final timeline = row.read(dateExp)!.truncateDate(groupBy); final assetCount = row.read(assetCountExp)!; return TimeBucket(date: timeline, assetCount: assetCount); }).watch(); } - Future> _getMapBucketAssets(LatLngBounds bounds, {required int offset, required int count}) { + Future> _getMapBucketAssets( + String userId, + LatLngBounds bounds, { + required int offset, + required int count, + }) { final query = _db.remoteAssetEntity.select().join([ innerJoin( @@ -477,7 +502,8 @@ class DriftTimelineRepository extends DriftDatabaseRepository { ), ]) ..where( - _db.remoteExifEntity.inBounds(bounds) & + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteExifEntity.inBounds(bounds) & _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & _db.remoteAssetEntity.deletedAt.isNull(), ) @@ -486,8 +512,10 @@ class DriftTimelineRepository extends DriftDatabaseRepository { return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); } + @pragma('vm:prefer-inline') TimelineQuery _remoteQueryBuilder({ required Expression Function($RemoteAssetEntityTable row) filter, + required TimelineOrigin origin, GroupAssetsBy groupBy = GroupAssetsBy.day, bool joinLocal = false, }) { @@ -495,6 +523,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { bucketSource: () => _watchRemoteBucket(filter: filter, groupBy: groupBy), assetSource: (offset, count) => _getRemoteAssets(filter: filter, offset: offset, count: count, joinLocal: joinLocal), + origin: origin, ); } @@ -517,12 +546,13 @@ class DriftTimelineRepository extends DriftDatabaseRepository { ..orderBy([OrderingTerm.desc(dateExp)]); return query.map((row) { - final timeline = row.read(dateExp)!.dateFmt(groupBy); + final timeline = row.read(dateExp)!.truncateDate(groupBy); final assetCount = row.read(assetCountExp)!; return TimeBucket(date: timeline, assetCount: assetCount); }).watch(); } + @pragma('vm:prefer-inline') Future> _getRemoteAssets({ required Expression Function($RemoteAssetEntityTable row) filter, required int offset, @@ -543,11 +573,9 @@ class DriftTimelineRepository extends DriftDatabaseRepository { ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) ..limit(count, offset: offset); - return query.map((row) { - final asset = row.readTable(_db.remoteAssetEntity).toDto(); - final localId = row.read(_db.localAssetEntity.id); - return asset.copyWith(localId: localId); - }).get(); + return query + .map((row) => row.readTable(_db.remoteAssetEntity).toDto(localId: row.read(_db.localAssetEntity.id))) + .get(); } else { final query = _db.remoteAssetEntity.select() ..where(filter) @@ -560,12 +588,12 @@ class DriftTimelineRepository extends DriftDatabaseRepository { } List _generateBuckets(int count) { - final buckets = List.generate( - (count / kTimelineNoneSegmentSize).floor(), - (_) => const Bucket(assetCount: kTimelineNoneSegmentSize), + final buckets = List.filled( + (count / kTimelineNoneSegmentSize).ceil(), + const Bucket(assetCount: kTimelineNoneSegmentSize), ); if (count % kTimelineNoneSegmentSize != 0) { - buckets.add(Bucket(assetCount: count % kTimelineNoneSegmentSize)); + buckets[buckets.length - 1] = Bucket(assetCount: count % kTimelineNoneSegmentSize); } return buckets; } @@ -584,16 +612,12 @@ extension on Expression { } extension on String { - DateTime dateFmt(GroupAssetsBy groupBy) { + DateTime truncateDate(GroupAssetsBy groupBy) { final format = switch (groupBy) { GroupAssetsBy.day || GroupAssetsBy.auto => "y-M-d", GroupAssetsBy.month => "y-M", GroupAssetsBy.none => throw ArgumentError("GroupAssetsBy.none is not supported for date formatting"), }; - try { - return DateFormat(format, 'en').parse(this); - } catch (e) { - throw FormatException("Invalid date format: $this", e); - } + return DateFormat(format, 'en').parse(this); } } diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 712ee0bd83..263a5ef769 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -15,7 +15,9 @@ import 'package:immich_mobile/constants/locales.dart'; import 'package:immich_mobile/domain/services/background_worker.service.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/generated/codegen_loader.g.dart'; +import 'package:immich_mobile/generated/intl_keys.g.dart'; import 'package:immich_mobile/platform/background_worker_lock_api.g.dart'; import 'package:immich_mobile/providers/app_life_cycle.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/share_intent_upload.provider.dart'; @@ -210,6 +212,14 @@ class ImmichAppState extends ConsumerState with WidgetsBindingObserve if (Store.isBetaTimelineEnabled) { ref.read(backgroundServiceProvider).disableService(); ref.read(backgroundWorkerFgServiceProvider).enable(); + if (Platform.isAndroid) { + ref + .read(backgroundWorkerFgServiceProvider) + .saveNotificationMessage( + IntlKeys.uploading_media.t(), + IntlKeys.backup_background_service_default_notification.t(), + ); + } } else { ref.read(backgroundWorkerFgServiceProvider).disable(); ref.read(backgroundServiceProvider).resumeServiceIfEnabled(); diff --git a/mobile/lib/pages/backup/drift_backup.page.dart b/mobile/lib/pages/backup/drift_backup.page.dart index 30782726e2..2e7c3e946c 100644 --- a/mobile/lib/pages/backup/drift_backup.page.dart +++ b/mobile/lib/pages/backup/drift_backup.page.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; @@ -8,6 +10,7 @@ import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/generated/intl_keys.g.dart'; import 'package:immich_mobile/presentation/widgets/backup/backup_toggle_button.widget.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; @@ -16,8 +19,7 @@ import 'package:immich_mobile/providers/sync_status.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/backup/backup_info_card.dart'; -import 'dart:async'; - +import 'package:logging/logging.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; @RoutePage() @@ -29,6 +31,8 @@ class DriftBackupPage extends ConsumerStatefulWidget { } class _DriftBackupPageState extends ConsumerState { + bool? syncSuccess; + @override void initState() { super.initState(); @@ -42,7 +46,10 @@ class _DriftBackupPageState extends ConsumerState { WidgetsBinding.instance.addPostFrameCallback((_) async { await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); - await ref.read(backgroundSyncProvider).syncRemote(); + + ref.read(driftBackupProvider.notifier).updateSyncing(true); + syncSuccess = await ref.read(backgroundSyncProvider).syncRemote(); + ref.read(driftBackupProvider.notifier).updateSyncing(false); if (mounted) { await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); @@ -63,7 +70,10 @@ class _DriftBackupPageState extends ConsumerState { .where((album) => album.backupSelection == BackupSelection.selected) .toList(); + final error = ref.watch(driftBackupProvider.select((p) => p.error)); + final backupNotifier = ref.read(driftBackupProvider.notifier); + final backupSyncManager = ref.read(backgroundSyncProvider); Future startBackup() async { final currentUser = Store.tryGet(StoreKey.currentUser); @@ -71,7 +81,18 @@ class _DriftBackupPageState extends ConsumerState { return; } + if (syncSuccess == null) { + ref.read(driftBackupProvider.notifier).updateSyncing(true); + syncSuccess = await backupSyncManager.syncRemote(); + ref.read(driftBackupProvider.notifier).updateSyncing(false); + } + await backupNotifier.getBackupStatus(currentUser.id); + + if (syncSuccess == false) { + Logger("DriftBackupPage").warning("Remote sync did not complete successfully, skipping backup"); + return; + } await backupNotifier.startBackup(currentUser.id); } @@ -113,7 +134,33 @@ class _DriftBackupPageState extends ConsumerState { const _BackupCard(), const _RemainderCard(), const Divider(), - BackupToggleButton(onStart: () async => await startBackup(), onStop: () async => await stopBackup()), + BackupToggleButton( + onStart: () async => await startBackup(), + onStop: () async { + syncSuccess = null; + await stopBackup(); + }, + ), + switch (error) { + BackupError.none => const SizedBox.shrink(), + BackupError.syncFailed => Padding( + padding: const EdgeInsets.only(top: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + Icon(Icons.warning_rounded, color: context.colorScheme.error, fill: 1), + const SizedBox(width: 8), + Text( + IntlKeys.backup_error_sync_failed.t(), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.error), + textAlign: TextAlign.center, + ), + ], + ), + ), + }, TextButton.icon( icon: const Icon(Icons.info_outline_rounded), onPressed: () => context.pushRoute(const DriftUploadDetailRoute()), diff --git a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart index 368341f24a..cae9f0a408 100644 --- a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart @@ -18,6 +18,7 @@ import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart'; import 'package:immich_mobile/widgets/common/search_field.dart'; +import 'package:logging/logging.dart'; @RoutePage() class DriftBackupAlbumSelectionPage extends ConsumerStatefulWidget { @@ -112,7 +113,17 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState backgroundSync.hashAssets())); if (isBackupEnabled) { - unawaited(backupNotifier.cancel().whenComplete(() => backupNotifier.startBackup(user.id))); + unawaited( + backupNotifier.cancel().whenComplete( + () => backgroundSync.syncRemote().then((success) { + if (success) { + return backupNotifier.startBackup(user.id); + } else { + Logger('DriftBackupAlbumSelectionPage').warning('Background sync failed, not starting backup'); + } + }), + ), + ); } } diff --git a/mobile/lib/pages/backup/drift_backup_options.page.dart b/mobile/lib/pages/backup/drift_backup_options.page.dart index 92f911ae1e..1e5c326478 100644 --- a/mobile/lib/pages/backup/drift_backup_options.page.dart +++ b/mobile/lib/pages/backup/drift_backup_options.page.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -5,10 +7,12 @@ import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/widgets/settings/backup_settings/drift_backup_settings.dart'; +import 'package:logging/logging.dart'; @RoutePage() class DriftBackupOptionsPage extends ConsumerWidget { @@ -54,9 +58,18 @@ class DriftBackupOptionsPage extends ConsumerWidget { ); final backupNotifier = ref.read(driftBackupProvider.notifier); - backupNotifier.cancel().then((_) { - backupNotifier.startBackup(currentUser.id); - }); + final backgroundSync = ref.read(backgroundSyncProvider); + unawaited( + backupNotifier.cancel().whenComplete( + () => backgroundSync.syncRemote().then((success) { + if (success) { + return backupNotifier.startBackup(currentUser.id); + } else { + Logger('DriftBackupOptionsPage').warning('Background sync failed, not starting backup'); + } + }), + ), + ); } }, child: Scaffold( diff --git a/mobile/lib/pages/backup/drift_upload_detail.page.dart b/mobile/lib/pages/backup/drift_upload_detail.page.dart index bececddc7f..80956b708f 100644 --- a/mobile/lib/pages/backup/drift_upload_detail.page.dart +++ b/mobile/lib/pages/backup/drift_upload_detail.page.dart @@ -1,12 +1,12 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; -import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; -import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/utils/bytes_units.dart'; import 'package:path/path.dart' as path; @@ -82,6 +82,7 @@ class DriftUploadDetailPage extends ConsumerWidget { Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, + spacing: 4, children: [ Text( path.basename(item.filename), @@ -89,7 +90,13 @@ class DriftUploadDetailPage extends ConsumerWidget { maxLines: 1, overflow: TextOverflow.ellipsis, ), - const SizedBox(height: 4), + if (item.error != null) + Text( + item.error!, + style: context.textTheme.bodySmall?.copyWith( + color: context.colorScheme.onErrorContainer.withValues(alpha: 0.6), + ), + ), Text( 'Tap for more details', style: context.textTheme.bodySmall?.copyWith( diff --git a/mobile/lib/pages/common/native_video_viewer.page.dart b/mobile/lib/pages/common/native_video_viewer.page.dart index d8b6db2276..7f39d07ec0 100644 --- a/mobile/lib/pages/common/native_video_viewer.page.dart +++ b/mobile/lib/pages/common/native_video_viewer.page.dart @@ -190,7 +190,10 @@ class NativeVideoViewerPage extends HookConsumerWidget { isVideoReady.value = true; try { - await videoController.play(); + final autoPlayVideo = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.autoPlayVideo); + if (autoPlayVideo) { + await videoController.play(); + } await videoController.setVolume(0.9); } catch (error) { log.severe('Error playing video: $error'); diff --git a/mobile/lib/pages/common/settings.page.dart b/mobile/lib/pages/common/settings.page.dart index b23c420971..0fe2ccec09 100644 --- a/mobile/lib/pages/common/settings.page.dart +++ b/mobile/lib/pages/common/settings.page.dart @@ -141,14 +141,9 @@ class SettingsSubPage extends StatelessWidget { @override Widget build(BuildContext context) { context.locale; - return SafeArea( - bottom: true, - top: false, - right: true, - child: Scaffold( - appBar: AppBar(centerTitle: false, title: Text(section.title).tr()), - body: section.widget, - ), + return Scaffold( + appBar: AppBar(centerTitle: false, title: Text(section.title).tr()), + body: section.widget, ); } } diff --git a/mobile/lib/pages/common/splash_screen.page.dart b/mobile/lib/pages/common/splash_screen.page.dart index aa4d142381..29b3dcd3be 100644 --- a/mobile/lib/pages/common/splash_screen.page.dart +++ b/mobile/lib/pages/common/splash_screen.page.dart @@ -62,14 +62,23 @@ class SplashScreenPageState extends ConsumerState { infoProvider.getServerInfo(); if (Store.isBetaTimelineEnabled) { - await Future.wait([backgroundManager.syncLocal(), backgroundManager.syncRemote()]); + bool syncSuccess = false; await Future.wait([ - backgroundManager.hashAssets().then((_) { - _resumeBackup(backupProvider); - }), - _resumeBackup(backupProvider), + backgroundManager.syncLocal(), + backgroundManager.syncRemote().then((success) => syncSuccess = success), ]); + if (syncSuccess) { + await Future.wait([ + backgroundManager.hashAssets().then((_) { + _resumeBackup(backupProvider); + }), + _resumeBackup(backupProvider), + ]); + } else { + await backgroundManager.hashAssets(); + } + if (Store.get(StoreKey.syncAlbums, false)) { await backgroundManager.syncLinkedAlbum(); } diff --git a/mobile/lib/platform/background_worker_api.g.dart b/mobile/lib/platform/background_worker_api.g.dart index af7c78fd4b..22325603c0 100644 --- a/mobile/lib/platform/background_worker_api.g.dart +++ b/mobile/lib/platform/background_worker_api.g.dart @@ -138,6 +138,29 @@ class BackgroundWorkerFgHostApi { } } + Future saveNotificationMessage(String title, String body) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.saveNotificationMessage$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([title, body]); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + Future configure(BackgroundWorkerSettings settings) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.configure$pigeonVar_messageChannelSuffix'; @@ -221,29 +244,6 @@ class BackgroundWorkerBgHostApi { } } - Future showNotification(String title, String content) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.showNotification$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([title, content]); - final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } - } - Future close() async { final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.close$pigeonVar_messageChannelSuffix'; diff --git a/mobile/lib/presentation/pages/drift_map.page.dart b/mobile/lib/presentation/pages/drift_map.page.dart index 30da6410b5..de8dde7714 100644 --- a/mobile/lib/presentation/pages/drift_map.page.dart +++ b/mobile/lib/presentation/pages/drift_map.page.dart @@ -25,9 +25,10 @@ class DriftMapPage extends StatelessWidget { onPressed: () => context.pop(), icon: const Icon(Icons.arrow_back_ios_new_rounded), style: IconButton.styleFrom( - shape: const CircleBorder(side: BorderSide(width: 1, color: Colors.black26)), padding: const EdgeInsets.all(8), - backgroundColor: Colors.indigo.withValues(alpha: 0.7), + backgroundColor: Colors.indigo, + shadowColor: Colors.black26, + elevation: 4, ), ), ), diff --git a/mobile/lib/presentation/pages/drift_remote_album.page.dart b/mobile/lib/presentation/pages/drift_remote_album.page.dart index e0fe5ee62b..23d82dcb92 100644 --- a/mobile/lib/presentation/pages/drift_remote_album.page.dart +++ b/mobile/lib/presentation/pages/drift_remote_album.page.dart @@ -169,9 +169,11 @@ class _RemoteAlbumPageState extends ConsumerState { context.pushRoute(const DriftActivitiesRoute()); } - void showOptionSheet(BuildContext context) { + Future showOptionSheet(BuildContext context) async { final user = ref.watch(currentUserProvider); final isOwner = user != null ? user.id == _album.ownerId : false; + final canAddPhotos = + await ref.read(remoteAlbumServiceProvider).getUserRole(_album.id, user?.id ?? '') == AlbumUserRole.editor; showModalBottomSheet( context: context, @@ -193,22 +195,30 @@ class _RemoteAlbumPageState extends ConsumerState { context.pop(); } : null, - onAddPhotos: () async { - await addAssets(context); - context.pop(); - }, - onToggleAlbumOrder: () async { - await toggleAlbumOrder(); - context.pop(); - }, - onEditAlbum: () async { - context.pop(); - await showEditTitleAndDescription(context); - }, - onCreateSharedLink: () async { - context.pop(); - context.pushRoute(SharedLinkEditRoute(albumId: _album.id)); - }, + onAddPhotos: isOwner || canAddPhotos + ? () async { + await addAssets(context); + context.pop(); + } + : null, + onToggleAlbumOrder: isOwner + ? () async { + await toggleAlbumOrder(); + context.pop(); + } + : null, + onEditAlbum: isOwner + ? () async { + context.pop(); + await showEditTitleAndDescription(context); + } + : null, + onCreateSharedLink: isOwner + ? () async { + context.pop(); + context.pushRoute(SharedLinkEditRoute(albumId: _album.id)); + } + : null, onShowOptions: () { context.pop(); context.pushRoute(const DriftAlbumOptionsRoute()); @@ -220,6 +230,9 @@ class _RemoteAlbumPageState extends ConsumerState { @override Widget build(BuildContext context) { + final user = ref.watch(currentUserProvider); + final isOwner = user != null ? user.id == _album.ownerId : false; + return PopScope( onPopInvokedWithResult: (didPop, _) { if (didPop) { @@ -243,8 +256,8 @@ class _RemoteAlbumPageState extends ConsumerState { appBar: RemoteAlbumSliverAppBar( icon: Icons.photo_album_outlined, onShowOptions: () => showOptionSheet(context), - onToggleAlbumOrder: () => toggleAlbumOrder(), - onEditTitle: () => showEditTitleAndDescription(context), + onToggleAlbumOrder: isOwner ? () => toggleAlbumOrder() : null, + onEditTitle: isOwner ? () => showEditTitleAndDescription(context) : null, onActivity: () => showActivity(context), ), bottomSheet: RemoteAlbumBottomSheet(album: _album), @@ -300,7 +313,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { await ref .read(remoteAlbumProvider.notifier) - .updateAlbum(widget.album.id, name: newTitle, description: newDescription.isEmpty ? null : newDescription); + .updateAlbum(widget.album.id, name: newTitle, description: newDescription); if (mounted) { Navigator.of( diff --git a/mobile/lib/presentation/pages/drift_trash.page.dart b/mobile/lib/presentation/pages/drift_trash.page.dart index 43e9217cdf..8713166027 100644 --- a/mobile/lib/presentation/pages/drift_trash.page.dart +++ b/mobile/lib/presentation/pages/drift_trash.page.dart @@ -44,10 +44,7 @@ class DriftTrashPage extends StatelessWidget { return SliverPadding( padding: const EdgeInsets.all(16.0), sliver: SliverToBoxAdapter( - child: SizedBox( - height: 24.0, - child: const Text("trash_page_info").t(context: context, args: {"days": "$trashDays"}), - ), + child: const Text("trash_page_info").t(context: context, args: {"days": "$trashDays"}), ), ); }, diff --git a/mobile/lib/presentation/pages/editing/drift_edit.page.dart b/mobile/lib/presentation/pages/editing/drift_edit.page.dart index da62d49b49..e24a1967f2 100644 --- a/mobile/lib/presentation/pages/editing/drift_edit.page.dart +++ b/mobile/lib/presentation/pages/editing/drift_edit.page.dart @@ -50,6 +50,11 @@ class DriftEditImagePage extends ConsumerWidget { return completer.future; } + void _exitEditing(BuildContext context) { + // this assumes that the only way to get to this page is from the AssetViewerRoute + context.navigator.popUntil((route) => route.data?.name == AssetViewerRoute.name); + } + Future _saveEditedImage(BuildContext context, BaseAsset asset, Image image, WidgetRef ref) async { try { final Uint8List imageData = await _imageToUint8List(image); @@ -66,7 +71,7 @@ class DriftEditImagePage extends ConsumerWidget { } ref.read(backgroundSyncProvider).syncLocal(full: true); - context.navigator.popUntil((route) => route.isFirst); + _exitEditing(context); ImmichToast.show(durationInSecond: 3, context: context, msg: 'Image Saved!'); if (localAsset == null) { @@ -91,7 +96,7 @@ class DriftEditImagePage extends ConsumerWidget { backgroundColor: context.scaffoldBackgroundColor, leading: IconButton( icon: Icon(Icons.close_rounded, color: context.primaryColor, size: 24), - onPressed: () => context.navigator.popUntil((route) => route.isFirst), + onPressed: () => _exitEditing(context), ), actions: [ TextButton( diff --git a/mobile/lib/presentation/pages/search/drift_search.page.dart b/mobile/lib/presentation/pages/search/drift_search.page.dart index 7e70ebf8ff..425c4985e9 100644 --- a/mobile/lib/presentation/pages/search/drift_search.page.dart +++ b/mobile/lib/presentation/pages/search/drift_search.page.dart @@ -8,6 +8,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/person.model.dart'; import 'package:immich_mobile/domain/models/timeline.model.dart'; +import 'package:immich_mobile/domain/services/timeline.service.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; @@ -599,9 +600,9 @@ class _SearchResultGrid extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final searchResult = ref.watch(paginatedSearchProvider); + final assets = ref.watch(paginatedSearchProvider.select((s) => s.assets)); - if (searchResult.totalAssets == 0) { + if (assets.isEmpty) { return const _SearchEmptyContent(); } @@ -615,6 +616,7 @@ class _SearchResultGrid extends ConsumerWidget { if (metrics.pixels >= metrics.maxScrollExtent && isVerticalScroll && !isBottomSheetNotification) { onScrollEnd(); + ref.read(paginatedSearchProvider.notifier).setScrollOffset(metrics.maxScrollExtent); } return true; @@ -623,17 +625,18 @@ class _SearchResultGrid extends ConsumerWidget { child: ProviderScope( overrides: [ timelineServiceProvider.overrideWith((ref) { - final timelineService = ref.watch(timelineFactoryProvider).fromAssets(searchResult.assets); + final timelineService = ref.watch(timelineFactoryProvider).fromAssets(assets, TimelineOrigin.search); ref.onDispose(timelineService.dispose); return timelineService; }), ], child: Timeline( - key: ValueKey(searchResult.totalAssets), + key: ValueKey(assets.length), groupBy: GroupAssetsBy.none, appBar: null, bottomSheet: const GeneralBottomSheet(minChildSize: 0.20), snapToMonth: false, + initialScrollOffset: ref.read(paginatedSearchProvider.select((s) => s.scrollOffset)), ), ), ), diff --git a/mobile/lib/presentation/pages/search/paginated_search.provider.dart b/mobile/lib/presentation/pages/search/paginated_search.provider.dart index 718a241ba4..c0c822198d 100644 --- a/mobile/lib/presentation/pages/search/paginated_search.provider.dart +++ b/mobile/lib/presentation/pages/search/paginated_search.provider.dart @@ -24,12 +24,20 @@ class PaginatedSearchNotifier extends StateNotifier { return false; } - state = SearchResult(assets: [...state.assets, ...result.assets], nextPage: result.nextPage); + state = SearchResult( + assets: [...state.assets, ...result.assets], + nextPage: result.nextPage, + scrollOffset: state.scrollOffset, + ); return true; } + void setScrollOffset(double offset) { + state = state.copyWith(scrollOffset: offset); + } + clear() { - state = const SearchResult(assets: [], nextPage: 1); + state = const SearchResult(assets: [], nextPage: 1, scrollOffset: 0.0); } } diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart index cccdee9b3a..9445daa2ad 100644 --- a/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart @@ -8,6 +8,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_bu import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/asset_grid/delete_dialog.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; /// This delete action has the following behavior: @@ -22,7 +23,17 @@ class DeleteLocalActionButton extends ConsumerWidget { return; } - final result = await ref.read(actionProvider.notifier).deleteLocal(source); + bool? backedUpOnly = await showDialog( + context: context, + builder: (BuildContext context) => DeleteLocalOnlyDialog(onDeleteLocal: (_) {}), + ); + + if (backedUpOnly == null) { + // User cancelled the dialog + return; + } + + final result = await ref.read(actionProvider.notifier).deleteLocal(source, backedUpOnly); ref.read(multiSelectProvider.notifier).reset(); if (source == ActionSource.viewer) { diff --git a/mobile/lib/presentation/widgets/action_buttons/edit_image_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/edit_image_action_button.widget.dart index cde0db8e18..4c7b6ffbdc 100644 --- a/mobile/lib/presentation/widgets/action_buttons/edit_image_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/edit_image_action_button.widget.dart @@ -1,11 +1,11 @@ +import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; -import 'package:immich_mobile/presentation/pages/editing/drift_edit.page.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; class EditImageActionButton extends ConsumerWidget { const EditImageActionButton({super.key}); @@ -20,12 +20,7 @@ class EditImageActionButton extends ConsumerWidget { } final image = Image(image: getFullImageProvider(currentAsset)); - - context.navigator.push( - MaterialPageRoute( - builder: (context) => DriftEditImagePage(asset: currentAsset, image: image, isEdited: false), - ), - ); + context.pushRoute(DriftEditImageRoute(asset: currentAsset, image: image, isEdited: false)); } return BaseActionButton( diff --git a/mobile/lib/presentation/widgets/action_buttons/unstack_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/unstack_action_button.widget.dart index ecc8a39c74..a07803ace5 100644 --- a/mobile/lib/presentation/widgets/action_buttons/unstack_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/unstack_action_button.widget.dart @@ -36,7 +36,7 @@ class UnStackActionButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return BaseActionButton( - iconData: Icons.filter_none_rounded, + iconData: Icons.layers_clear_outlined, label: "unstack".t(context: context), onPressed: () => _onTap(context, ref), ); diff --git a/mobile/lib/presentation/widgets/album/album_selector.widget.dart b/mobile/lib/presentation/widgets/album/album_selector.widget.dart index f79b4bd7b1..bffe3d3941 100644 --- a/mobile/lib/presentation/widgets/album/album_selector.widget.dart +++ b/mobile/lib/presentation/widgets/album/album_selector.widget.dart @@ -512,7 +512,7 @@ class _AlbumList extends ConsumerWidget { } return SliverPadding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), + padding: const EdgeInsets.only(left: 16.0, right: 16, bottom: 64), sliver: SliverList.builder( itemBuilder: (_, index) { final album = albums[index]; diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart index 7ac8cf34d5..e3106c7f7f 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart @@ -221,10 +221,8 @@ class _AssetViewerState extends ConsumerState { context.scaffoldMessenger.hideCurrentSnackBar(); // send image to casting if the server has it - if (asset.hasRemote) { - final remoteAsset = asset as RemoteAsset; - - ref.read(castProvider.notifier).loadMedia(remoteAsset, false); + if (asset is RemoteAsset) { + ref.read(castProvider.notifier).loadMedia(asset, false); } else { // casting cannot show local assets context.scaffoldMessenger.clearSnackBars(); diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart index 3111512823..44660e440c 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart @@ -43,7 +43,7 @@ class ViewerBottomBar extends ConsumerWidget { final actions = [ const ShareActionButton(source: ActionSource.viewer), if (asset.isLocalOnly) const UploadActionButton(source: ActionSource.viewer), - if (asset.type == AssetType.image) const EditImageActionButton(), + if (asset.type == AssetType.image && isOwner) const EditImageActionButton(), if (isOwner) ...[ if (asset.hasRemote && isOwner && isArchived) const UnArchiveActionButton(source: ActionSource.viewer) diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index 7431290ad8..9d29b19bff 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -51,6 +51,7 @@ class AssetDetailBottomSheet extends ConsumerWidget { isArchived: isArchived, isTrashEnabled: isTrashEnable, isInLockedView: isInLockedView, + isStacked: asset is RemoteAsset && asset.stackId != null, currentAlbum: currentAlbum, advancedTroubleshooting: advancedTroubleshooting, source: ActionSource.viewer, @@ -139,6 +140,7 @@ class _AssetDetailBottomSheet extends ConsumerWidget { final exifInfo = ref.watch(currentAssetExifProvider).valueOrNull; final cameraTitle = _getCameraInfoTitle(exifInfo); + final isOwner = ref.watch(currentUserProvider)?.id == (asset is RemoteAsset ? asset.ownerId : null); return SliverList.list( children: [ @@ -146,10 +148,10 @@ class _AssetDetailBottomSheet extends ConsumerWidget { _SheetTile( title: _getDateTime(context, asset), titleStyle: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600), - trailing: asset.hasRemote ? const Icon(Icons.edit, size: 18) : null, - onTap: asset.hasRemote ? () async => await _editDateTime(context, ref) : null, + trailing: asset.hasRemote && isOwner ? const Icon(Icons.edit, size: 18) : null, + onTap: asset.hasRemote && isOwner ? () async => await _editDateTime(context, ref) : null, ), - if (exifInfo != null) _SheetAssetDescription(exif: exifInfo), + if (exifInfo != null) _SheetAssetDescription(exif: exifInfo, isEditable: isOwner), const SheetPeopleDetails(), const SheetLocationDetails(), // Details header @@ -185,6 +187,7 @@ class _AssetDetailBottomSheet extends ConsumerWidget { color: context.textTheme.bodyMedium?.color?.withAlpha(155), ), ), + const SizedBox(height: 64), ], ); } @@ -263,8 +266,9 @@ class _SheetTile extends ConsumerWidget { class _SheetAssetDescription extends ConsumerStatefulWidget { final ExifInfo exif; + final bool isEditable; - const _SheetAssetDescription({required this.exif}); + const _SheetAssetDescription({required this.exif, this.isEditable = true}); @override ConsumerState<_SheetAssetDescription> createState() => _SheetAssetDescriptionState(); @@ -310,27 +314,33 @@ class _SheetAssetDescriptionState extends ConsumerState<_SheetAssetDescription> // Update controller text when EXIF data changes final currentDescription = currentExifInfo?.description ?? ''; + final hintText = (widget.isEditable ? 'exif_bottom_sheet_description' : 'exif_bottom_sheet_no_description').t( + context: context, + ); if (_controller.text != currentDescription && !_descriptionFocus.hasFocus) { _controller.text = currentDescription; } return Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8), - child: TextField( - controller: _controller, - keyboardType: TextInputType.multiline, - focusNode: _descriptionFocus, - maxLines: null, // makes it grow as text is added - decoration: InputDecoration( - hintText: 'exif_bottom_sheet_description'.t(context: context), - border: InputBorder.none, - enabledBorder: InputBorder.none, - focusedBorder: InputBorder.none, - disabledBorder: InputBorder.none, - errorBorder: InputBorder.none, - focusedErrorBorder: InputBorder.none, + child: IgnorePointer( + ignoring: !widget.isEditable, + child: TextField( + controller: _controller, + keyboardType: TextInputType.multiline, + focusNode: _descriptionFocus, + maxLines: null, // makes it grow as text is added + decoration: InputDecoration( + hintText: hintText, + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + disabledBorder: InputBorder.none, + errorBorder: InputBorder.none, + focusedErrorBorder: InputBorder.none, + ), + onTapOutside: (_) => saveDescription(currentExifInfo?.description), ), - onTapOutside: (_) => saveDescription(currentExifInfo?.description), ), ); } diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart index fee34bca1b..64f22eca92 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart @@ -11,8 +11,8 @@ import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/api.service.dart'; -import 'package:immich_mobile/utils/people.utils.dart'; import 'package:immich_mobile/utils/image_url_builder.dart'; +import 'package:immich_mobile/utils/people.utils.dart'; class SheetPeopleDetails extends ConsumerStatefulWidget { const SheetPeopleDetails({super.key}); @@ -158,11 +158,14 @@ class _PeopleAvatar extends StatelessWidget { maxLines: 1, ), if (person.birthDate != null) - Text( - formatAge(person.birthDate!, assetFileCreatedAt), - textAlign: TextAlign.center, - style: context.textTheme.bodyMedium?.copyWith( - color: context.textTheme.bodyMedium?.color?.withAlpha(175), + FittedBox( + fit: BoxFit.scaleDown, + child: Text( + formatAge(person.birthDate!, assetFileCreatedAt), + textAlign: TextAlign.center, + style: context.textTheme.bodyMedium?.copyWith( + color: context.textTheme.bodyMedium?.color?.withAlpha(175), + ), ), ), ], diff --git a/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart index 0159e04c4e..38ad9d17de 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart @@ -4,6 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/timeline.model.dart'; +import 'package:immich_mobile/domain/services/timeline.service.dart'; import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; @@ -17,8 +18,8 @@ import 'package:immich_mobile/providers/cast.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/routes.provider.dart'; -import 'package:immich_mobile/providers/tab.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/routing/router.dart'; @@ -39,12 +40,14 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget { final isInLockedView = ref.watch(inLockedViewProvider); final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); - final previousRouteName = ref.watch(previousRouteNameProvider); - final tabRoute = ref.watch(tabProvider); + final timelineOrigin = ref.read(timelineServiceProvider).origin; final showViewInTimelineButton = - (previousRouteName != TabShellRoute.name || tabRoute == TabEnum.search) && - previousRouteName != AssetViewerRoute.name && - previousRouteName != null; + timelineOrigin != TimelineOrigin.main && + timelineOrigin != TimelineOrigin.deepLink && + timelineOrigin != TimelineOrigin.trash && + timelineOrigin != TimelineOrigin.archive && + timelineOrigin != TimelineOrigin.localAlbum && + isOwner; final isShowingSheet = ref.watch(assetViewerProvider.select((state) => state.showingBottomSheet)); int opacity = ref.watch(assetViewerProvider.select((state) => state.backgroundOpacity)); @@ -57,7 +60,7 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget { final isCasting = ref.watch(castProvider.select((c) => c.isCasting)); final actions = [ - if (asset.hasRemote) const DownloadActionButton(source: ActionSource.viewer, menuItem: true), + if (asset.isRemoteOnly) const DownloadActionButton(source: ActionSource.viewer, menuItem: true), if (isCasting || (asset.hasRemote)) const CastActionButton(menuItem: true), if (album != null && album.isActivityEnabled && album.isShared) IconButton( diff --git a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart index fa7f204596..3af31950ad 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart @@ -7,6 +7,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/services/setting.service.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; @@ -88,10 +89,18 @@ class NativeVideoViewer extends HookConsumerWidget { } final videoAsset = await ref.read(assetServiceProvider).getAsset(asset) ?? asset; + if (!context.mounted) { + return null; + } + try { if (videoAsset.hasLocal && videoAsset.livePhotoVideoId == null) { final id = videoAsset is LocalAsset ? videoAsset.id : (videoAsset as RemoteAsset).localId!; final file = await const StorageRepository().getFileForAsset(id); + if (!context.mounted) { + return null; + } + if (file == null) { throw Exception('No file found for the video'); } @@ -210,7 +219,10 @@ class NativeVideoViewer extends HookConsumerWidget { } try { - await videoController.play(); + final autoPlayVideo = AppSetting.get(Setting.autoPlayVideo); + if (autoPlayVideo) { + await videoController.play(); + } await videoController.setVolume(0.9); } catch (error) { log.severe('Error playing video: $error'); @@ -289,7 +301,7 @@ class NativeVideoViewer extends HookConsumerWidget { ref.read(videoPlaybackValueProvider.notifier).reset(); final source = await videoSource; - if (source == null) { + if (source == null || !context.mounted) { return; } @@ -314,6 +326,9 @@ class NativeVideoViewer extends HookConsumerWidget { removeListeners(playerController); } + if (value != null) { + isVisible.value = _isCurrentAsset(value, asset); + } final curAsset = currentAsset.value; if (curAsset == asset) { return; diff --git a/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart b/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart index a74c169224..8d374f74ff 100644 --- a/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart +++ b/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart @@ -65,7 +65,9 @@ class BackupToggleButtonState extends ConsumerState with Sin final uploadTasks = ref.watch(driftBackupProvider.select((state) => state.uploadItems)); - final isUploading = uploadTasks.isNotEmpty; + final isSyncing = ref.watch(driftBackupProvider.select((state) => state.isSyncing)); + + final isProcessing = uploadTasks.isNotEmpty || isSyncing; return AnimatedBuilder( animation: _animationController, @@ -129,7 +131,7 @@ class BackupToggleButtonState extends ConsumerState with Sin ], ), ), - child: isUploading + child: isProcessing ? const SizedBox(width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2)) : Icon(Icons.cloud_upload_outlined, color: context.primaryColor, size: 24), ), diff --git a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart index 558df4e496..f40e189e18 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart @@ -13,6 +13,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_act import 'package:immich_mobile/presentation/widgets/action_buttons/stack_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/unarchive_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/unstack_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; @@ -44,6 +45,7 @@ class ArchiveBottomSheet extends ConsumerWidget { const EditLocationActionButton(source: ActionSource.timeline), const MoveToLockFolderActionButton(source: ActionSource.timeline), if (multiselect.selectedAssets.length > 1) const StackActionButton(source: ActionSource.timeline), + if (multiselect.hasStacked) const UnStackActionButton(source: ActionSource.timeline), ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), diff --git a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart index a162dbbfb2..b2502127d4 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/models/album/album.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; @@ -13,10 +16,14 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_act import 'package:immich_mobile/presentation/widgets/action_buttons/stack_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/unstack_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; class FavoriteBottomSheet extends ConsumerWidget { const FavoriteBottomSheet({super.key}); @@ -26,9 +33,42 @@ class FavoriteBottomSheet extends ConsumerWidget { final multiselect = ref.watch(multiSelectProvider); final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); + Future addAssetsToAlbum(RemoteAlbum album) async { + final selectedAssets = multiselect.selectedAssets; + if (selectedAssets.isEmpty) { + return; + } + + final remoteAssets = selectedAssets.whereType(); + final addedCount = await ref + .read(remoteAlbumProvider.notifier) + .addAssets(album.id, remoteAssets.map((e) => e.id).toList()); + + if (selectedAssets.length != remoteAssets.length) { + ImmichToast.show( + context: context, + msg: 'add_to_album_bottom_sheet_some_local_assets'.t(context: context), + ); + } + + if (addedCount != remoteAssets.length) { + ImmichToast.show( + context: context, + msg: 'add_to_album_bottom_sheet_already_exists'.t(args: {"album": album.name}), + ); + } else { + ImmichToast.show( + context: context, + msg: 'add_to_album_bottom_sheet_added'.t(args: {"album": album.name}), + ); + } + + ref.read(multiSelectProvider.notifier).reset(); + } + return BaseBottomSheet( - initialChildSize: 0.25, - maxChildSize: 0.4, + initialChildSize: 0.4, + maxChildSize: 0.7, shouldCloseOnMinExtent: false, actions: [ const ShareActionButton(source: ActionSource.timeline), @@ -44,12 +84,16 @@ class FavoriteBottomSheet extends ConsumerWidget { const EditLocationActionButton(source: ActionSource.timeline), const MoveToLockFolderActionButton(source: ActionSource.timeline), if (multiselect.selectedAssets.length > 1) const StackActionButton(source: ActionSource.timeline), + if (multiselect.hasStacked) const UnStackActionButton(source: ActionSource.timeline), ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), const UploadActionButton(source: ActionSource.timeline), ], ], + slivers: multiselect.hasRemote + ? [const AddToAlbumHeader(), AlbumSelector(onAlbumSelected: addAssetsToAlbum)] + : [], ); } } diff --git a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart index 73ebf60067..9436707c84 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart @@ -5,6 +5,7 @@ import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/setting.model.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; @@ -19,6 +20,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_b import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/stack_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/unstack_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; @@ -62,11 +64,19 @@ class _GeneralBottomSheetState extends ConsumerState { return; } + final remoteAssets = selectedAssets.whereType(); final addedCount = await ref .read(remoteAlbumProvider.notifier) - .addAssets(album.id, selectedAssets.map((e) => (e as RemoteAsset).id).toList()); + .addAssets(album.id, remoteAssets.map((e) => e.id).toList()); - if (addedCount != selectedAssets.length) { + if (selectedAssets.length != remoteAssets.length) { + ImmichToast.show( + context: context, + msg: 'add_to_album_bottom_sheet_some_local_assets'.t(context: context), + ); + } + + if (addedCount != remoteAssets.length) { ImmichToast.show( context: context, msg: 'add_to_album_bottom_sheet_already_exists'.tr(namedArgs: {"album": album.name}), @@ -108,15 +118,18 @@ class _GeneralBottomSheetState extends ConsumerState { const EditLocationActionButton(source: ActionSource.timeline), const MoveToLockFolderActionButton(source: ActionSource.timeline), if (multiselect.selectedAssets.length > 1) const StackActionButton(source: ActionSource.timeline), + if (multiselect.hasStacked) const UnStackActionButton(source: ActionSource.timeline), const DeleteActionButton(source: ActionSource.timeline), ], if (multiselect.hasLocal || multiselect.hasMerged) const DeleteLocalActionButton(source: ActionSource.timeline), if (multiselect.hasLocal) const UploadActionButton(source: ActionSource.timeline), ], - slivers: [ - const AddToAlbumHeader(), - AlbumSelector(onAlbumSelected: addAssetsToAlbum, onKeyboardExpanded: onKeyboardExpand), - ], + slivers: multiselect.hasRemote + ? [ + const AddToAlbumHeader(), + AlbumSelector(onAlbumSelected: addAssetsToAlbum, onKeyboardExpanded: onKeyboardExpand), + ] + : [], ); } } diff --git a/mobile/lib/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart index 19cce3392f..ac3772a02b 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart @@ -1,22 +1,25 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; import 'package:immich_mobile/presentation/widgets/map/map.state.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; class MapBottomSheet extends StatelessWidget { const MapBottomSheet({super.key}); @override Widget build(BuildContext context) { - return const BaseBottomSheet( + return BaseBottomSheet( initialChildSize: 0.25, maxChildSize: 0.9, shouldCloseOnMinExtent: false, resizeOnScroll: false, actions: [], - slivers: [SliverFillRemaining(hasScrollBody: false, child: _ScopedMapTimeline())], + backgroundColor: context.themeData.colorScheme.surface, + slivers: [const SliverFillRemaining(hasScrollBody: false, child: _ScopedMapTimeline())], ); } } @@ -30,8 +33,13 @@ class _ScopedMapTimeline extends StatelessWidget { return ProviderScope( overrides: [ timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access archive'); + } + final bounds = ref.watch(mapStateProvider).bounds; - final timelineService = ref.watch(timelineFactoryProvider).map(bounds); + final timelineService = ref.watch(timelineFactoryProvider).map(user.id, bounds); ref.onDispose(timelineService.dispose); return timelineService; }), diff --git a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart index 1dcc52f349..7db8a80af2 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart @@ -17,12 +17,14 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_b import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/stack_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/unstack_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; class RemoteAlbumBottomSheet extends ConsumerStatefulWidget { @@ -52,6 +54,7 @@ class _RemoteAlbumBottomSheetState extends ConsumerState Widget build(BuildContext context) { final multiselect = ref.watch(multiSelectProvider); final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); + final ownsAlbum = ref.watch(currentUserProvider)?.id == widget.album.ownerId; Future addAssetsToAlbum(RemoteAlbum album) async { final selectedAssets = multiselect.selectedAssets; @@ -92,27 +95,35 @@ class _RemoteAlbumBottomSheetState extends ConsumerState const ShareActionButton(source: ActionSource.timeline), if (multiselect.hasRemote) ...[ const ShareLinkActionButton(source: ActionSource.timeline), - const ArchiveActionButton(source: ActionSource.timeline), - const FavoriteActionButton(source: ActionSource.timeline), + + if (ownsAlbum) ...[ + const ArchiveActionButton(source: ActionSource.timeline), + const FavoriteActionButton(source: ActionSource.timeline), + ], const DownloadActionButton(source: ActionSource.timeline), - isTrashEnable - ? const TrashActionButton(source: ActionSource.timeline) - : const DeletePermanentActionButton(source: ActionSource.timeline), - const EditDateTimeActionButton(source: ActionSource.timeline), - const EditLocationActionButton(source: ActionSource.timeline), - const MoveToLockFolderActionButton(source: ActionSource.timeline), - if (multiselect.selectedAssets.length > 1) const StackActionButton(source: ActionSource.timeline), + if (ownsAlbum) ...[ + isTrashEnable + ? const TrashActionButton(source: ActionSource.timeline) + : const DeletePermanentActionButton(source: ActionSource.timeline), + const EditDateTimeActionButton(source: ActionSource.timeline), + const EditLocationActionButton(source: ActionSource.timeline), + const MoveToLockFolderActionButton(source: ActionSource.timeline), + if (multiselect.selectedAssets.length > 1) const StackActionButton(source: ActionSource.timeline), + if (multiselect.hasStacked) const UnStackActionButton(source: ActionSource.timeline), + ], ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), const UploadActionButton(source: ActionSource.timeline), ], - RemoveFromAlbumActionButton(source: ActionSource.timeline, albumId: widget.album.id), - ], - slivers: [ - const AddToAlbumHeader(), - AlbumSelector(onAlbumSelected: addAssetsToAlbum, onKeyboardExpanded: onKeyboardExpand), + if (ownsAlbum) RemoveFromAlbumActionButton(source: ActionSource.timeline, albumId: widget.album.id), ], + slivers: ownsAlbum + ? [ + const AddToAlbumHeader(), + AlbumSelector(onAlbumSelected: addAssetsToAlbum, onKeyboardExpanded: onKeyboardExpand), + ] + : null, ); } } diff --git a/mobile/lib/presentation/widgets/images/image_provider.dart b/mobile/lib/presentation/widgets/images/image_provider.dart index ab5ead5ca5..810340aeb8 100644 --- a/mobile/lib/presentation/widgets/images/image_provider.dart +++ b/mobile/lib/presentation/widgets/images/image_provider.dart @@ -123,28 +123,14 @@ ImageProvider getFullImageProvider(BaseAsset asset, {Size size = const Size(1080 return provider; } -ImageProvider getThumbnailImageProvider({BaseAsset? asset, String? remoteId, Size size = kThumbnailResolution}) { - assert(asset != null || remoteId != null, 'Either asset or remoteId must be provided'); - - if (remoteId != null) { - return RemoteThumbProvider(assetId: remoteId); - } - - if (_shouldUseLocalAsset(asset!)) { +ImageProvider? getThumbnailImageProvider(BaseAsset asset, {Size size = kThumbnailResolution}) { + if (_shouldUseLocalAsset(asset)) { final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).localId!; return LocalThumbProvider(id: id, size: size, assetType: asset.type); } - final String assetId; - if (asset is LocalAsset && asset.hasRemote) { - assetId = asset.remoteId!; - } else if (asset is RemoteAsset) { - assetId = asset.id; - } else { - throw ArgumentError("Unsupported asset type: ${asset.runtimeType}"); - } - - return RemoteThumbProvider(assetId: assetId); + final assetId = asset is RemoteAsset ? asset.id : (asset as LocalAsset).remoteId; + return assetId != null ? RemoteThumbProvider(assetId: assetId) : null; } bool _shouldUseLocalAsset(BaseAsset asset) => diff --git a/mobile/lib/presentation/widgets/images/thumbnail.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail.widget.dart index 3832029702..92b1bb2544 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail.widget.dart @@ -5,7 +5,6 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; -import 'package:immich_mobile/presentation/widgets/images/local_image_provider.dart'; import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart'; import 'package:immich_mobile/presentation/widgets/images/thumb_hash_provider.dart'; import 'package:immich_mobile/presentation/widgets/timeline/constants.dart'; @@ -39,14 +38,7 @@ class Thumbnail extends StatefulWidget { ), _ => null, }, - imageProvider = switch (asset) { - RemoteAsset() => - asset.localId == null - ? RemoteThumbProvider(assetId: asset.id) - : LocalThumbProvider(id: asset.localId!, size: size, assetType: asset.type), - LocalAsset() => LocalThumbProvider(id: asset.id, size: size, assetType: asset.type), - _ => null, - }; + imageProvider = asset == null ? null : getThumbnailImageProvider(asset, size: size); @override State createState() => _ThumbnailState(); diff --git a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart index cfcb7a8985..5359391261 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart @@ -16,7 +16,7 @@ class ThumbnailTile extends ConsumerWidget { this.asset, { this.size = kThumbnailResolution, this.fit = BoxFit.cover, - this.showStorageIndicator, + this.showStorageIndicator = false, this.lockSelection = false, this.heroOffset, super.key, @@ -25,7 +25,7 @@ class ThumbnailTile extends ConsumerWidget { final BaseAsset? asset; final Size size; final BoxFit fit; - final bool? showStorageIndicator; + final bool showStorageIndicator; final bool lockSelection; final int? heroOffset; @@ -54,10 +54,8 @@ class ThumbnailTile extends ConsumerWidget { ) : const BoxDecoration(); - final hasStack = asset is RemoteAsset && asset.stackId != null; - final bool storageIndicator = - showStorageIndicator ?? ref.watch(settingsProvider.select((s) => s.get(Setting.showStorageIndicator))); + ref.watch(settingsProvider.select((s) => s.get(Setting.showStorageIndicator))) && showStorageIndicator; return Stack( children: [ @@ -77,21 +75,10 @@ class ThumbnailTile extends ConsumerWidget { child: Thumbnail.fromAsset(asset: asset, size: size), ), ), - if (hasStack) + if (asset != null) Align( alignment: Alignment.topRight, - child: Padding( - padding: EdgeInsets.only(right: 10.0, top: asset.isVideo ? 24.0 : 6.0), - child: const _TileOverlayIcon(Icons.burst_mode_rounded), - ), - ), - if (asset != null && asset.isVideo) - Align( - alignment: Alignment.topRight, - child: Padding( - padding: const EdgeInsets.only(right: 10.0, top: 6.0), - child: _VideoIndicator(asset.duration), - ), + child: _AssetTypeIcons(asset: asset), ), if (storageIndicator && asset != null) switch (asset.storage) { @@ -214,3 +201,34 @@ class _TileOverlayIcon extends StatelessWidget { ); } } + +class _AssetTypeIcons extends StatelessWidget { + final BaseAsset asset; + + const _AssetTypeIcons({required this.asset}); + + @override + Widget build(BuildContext context) { + final hasStack = asset is RemoteAsset && (asset as RemoteAsset).stackId != null; + final isLivePhoto = asset is RemoteAsset && asset.livePhotoVideoId != null; + + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + if (asset.isVideo) + Padding(padding: const EdgeInsets.only(right: 10.0, top: 6.0), child: _VideoIndicator(asset.duration)), + if (hasStack) + const Padding( + padding: EdgeInsets.only(right: 10.0, top: 6.0), + child: _TileOverlayIcon(Icons.burst_mode_rounded), + ), + if (isLivePhoto) + const Padding( + padding: EdgeInsets.only(right: 10.0, top: 6.0), + child: _TileOverlayIcon(Icons.motion_photos_on_rounded), + ), + ], + ); + } +} diff --git a/mobile/lib/presentation/widgets/map/map.widget.dart b/mobile/lib/presentation/widgets/map/map.widget.dart index 0c3b37a3b4..c1d5bf6464 100644 --- a/mobile/lib/presentation/widgets/map/map.widget.dart +++ b/mobile/lib/presentation/widgets/map/map.widget.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:io'; +import 'dart:math'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; @@ -9,8 +10,8 @@ import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart'; -import 'package:immich_mobile/presentation/widgets/map/map_utils.dart'; import 'package:immich_mobile/presentation/widgets/map/map.state.dart'; +import 'package:immich_mobile/presentation/widgets/map/map_utils.dart'; import 'package:immich_mobile/utils/async_mutex.dart'; import 'package:immich_mobile/utils/debounce.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -184,9 +185,13 @@ class _Map extends StatelessWidget { initialCameraPosition: initialLocation == null ? const CameraPosition(target: LatLng(0, 0), zoom: 0) : CameraPosition(target: initialLocation, zoom: MapUtils.mapZoomToAssetLevel), + compassEnabled: false, + rotateGesturesEnabled: false, styleString: style, onMapCreated: onMapCreated, onStyleLoadedCallback: onMapReady, + attributionButtonPosition: AttributionButtonPosition.topRight, + attributionButtonMargins: Platform.isIOS ? const Point(40, 12) : const Point(40, 72), ), ), ); diff --git a/mobile/lib/presentation/widgets/timeline/timeline.state.dart b/mobile/lib/presentation/widgets/timeline/timeline.state.dart index b3aec23f7f..1e1d4130f7 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.state.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.state.dart @@ -14,7 +14,7 @@ class TimelineArgs { final double maxHeight; final double spacing; final int columnCount; - final bool? showStorageIndicator; + final bool showStorageIndicator; final bool withStack; final GroupAssetsBy? groupBy; @@ -23,7 +23,7 @@ class TimelineArgs { required this.maxHeight, this.spacing = kTimelineSpacing, this.columnCount = kTimelineColumnCount, - this.showStorageIndicator, + this.showStorageIndicator = false, this.withStack = false, this.groupBy, }); diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart index c38116c64e..5f1e7f27b0 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -36,10 +36,11 @@ class Timeline extends StatelessWidget { this.showStorageIndicator = false, this.withStack = false, this.appBar = const ImmichSliverAppBar(floating: true, pinned: false, snap: false), - this.bottomSheet = const GeneralBottomSheet(minChildSize: 0.18), + this.bottomSheet = const GeneralBottomSheet(minChildSize: 0.23), this.groupBy, this.withScrubber = true, this.snapToMonth = true, + this.initialScrollOffset, }); final Widget? topSliverWidget; @@ -51,6 +52,7 @@ class Timeline extends StatelessWidget { final GroupAssetsBy? groupBy; final bool withScrubber; final bool snapToMonth; + final double? initialScrollOffset; @override Widget build(BuildContext context) { @@ -78,6 +80,7 @@ class Timeline extends StatelessWidget { bottomSheet: bottomSheet, withScrubber: withScrubber, snapToMonth: snapToMonth, + initialScrollOffset: initialScrollOffset, ), ), ), @@ -93,6 +96,7 @@ class _SliverTimeline extends ConsumerStatefulWidget { this.bottomSheet, this.withScrubber = true, this.snapToMonth = true, + this.initialScrollOffset, }); final Widget? topSliverWidget; @@ -101,6 +105,7 @@ class _SliverTimeline extends ConsumerStatefulWidget { final Widget? bottomSheet; final bool withScrubber; final bool snapToMonth; + final double? initialScrollOffset; @override ConsumerState createState() => _SliverTimelineState(); @@ -124,7 +129,10 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { @override void initState() { super.initState(); - _scrollController = ScrollController(onAttach: _restoreScalePosition); + _scrollController = ScrollController( + initialScrollOffset: widget.initialScrollOffset ?? 0.0, + onAttach: _restoreScalePosition, + ); _eventSubscription = EventStream.shared.listen(_onEvent); final currentTilesPerRow = ref.read(settingsProvider).get(Setting.tilesPerRow); @@ -212,11 +220,14 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { if (fallbackSegment != null) { // Scroll to the segment with a small offset to show the header final targetOffset = fallbackSegment.startOffset - 50; - _scrollController.animateTo( - targetOffset.clamp(0.0, _scrollController.position.maxScrollExtent), - duration: const Duration(milliseconds: 500), - curve: Curves.easeInOut, - ); + ref.read(timelineStateProvider.notifier).setScrubbing(true); + _scrollController + .animateTo( + targetOffset.clamp(0.0, _scrollController.position.maxScrollExtent), + duration: const Duration(milliseconds: 500), + curve: Curves.easeInOut, + ) + .whenComplete(() => ref.read(timelineStateProvider.notifier).setScrubbing(false)); } }); } diff --git a/mobile/lib/providers/app_life_cycle.provider.dart b/mobile/lib/providers/app_life_cycle.provider.dart index ec6495440a..3b51874ab5 100644 --- a/mobile/lib/providers/app_life_cycle.provider.dart +++ b/mobile/lib/providers/app_life_cycle.provider.dart @@ -148,17 +148,21 @@ class AppLifeCycleNotifier extends StateNotifier { final isAlbumLinkedSyncEnable = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums); try { + bool syncSuccess = false; await Future.wait([ _safeRun(backgroundManager.syncLocal(), "syncLocal"), - _safeRun(backgroundManager.syncRemote(), "syncRemote"), - ]); - - await Future.wait([ - _safeRun(backgroundManager.hashAssets(), "hashAssets").then((_) { - _resumeBackup(); - }), - _resumeBackup(), + _safeRun(backgroundManager.syncRemote().then((success) => syncSuccess = success), "syncRemote"), ]); + if (syncSuccess) { + await Future.wait([ + _safeRun(backgroundManager.hashAssets(), "hashAssets").then((_) { + _resumeBackup(); + }), + _resumeBackup(), + ]); + } else { + await _safeRun(backgroundManager.hashAssets(), "hashAssets"); + } if (isAlbumLinkedSyncEnable) { await _safeRun(backgroundManager.syncLinkedAlbum(), "syncLinkedAlbum"); diff --git a/mobile/lib/providers/background_sync.provider.dart b/mobile/lib/providers/background_sync.provider.dart index e6e83b64df..a61cd93022 100644 --- a/mobile/lib/providers/background_sync.provider.dart +++ b/mobile/lib/providers/background_sync.provider.dart @@ -1,12 +1,21 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/utils/background_sync.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/providers/sync_status.provider.dart'; final backgroundSyncProvider = Provider((ref) { final syncStatusNotifier = ref.read(syncStatusProvider.notifier); + final backupProvider = ref.read(driftBackupProvider.notifier); + final manager = BackgroundSyncManager( - onRemoteSyncStart: syncStatusNotifier.startRemoteSync, - onRemoteSyncComplete: syncStatusNotifier.completeRemoteSync, + onRemoteSyncStart: () { + syncStatusNotifier.startRemoteSync(); + backupProvider.updateError(BackupError.none); + }, + onRemoteSyncComplete: (isSuccess) { + syncStatusNotifier.completeRemoteSync(); + backupProvider.updateError(isSuccess == true ? BackupError.none : BackupError.syncFailed); + }, onRemoteSyncError: syncStatusNotifier.errorRemoteSync, onLocalSyncStart: syncStatusNotifier.startLocalSync, onLocalSyncComplete: syncStatusNotifier.completeLocalSync, diff --git a/mobile/lib/providers/backup/drift_backup.provider.dart b/mobile/lib/providers/backup/drift_backup.provider.dart index 1ce14e5204..f52fc654f2 100644 --- a/mobile/lib/providers/backup/drift_backup.provider.dart +++ b/mobile/lib/providers/backup/drift_backup.provider.dart @@ -1,6 +1,5 @@ // ignore_for_file: public_member_api_docs, sort_constructors_first import 'dart:async'; -import 'dart:convert'; import 'package:background_downloader/background_downloader.dart'; import 'package:collection/collection.dart'; @@ -8,12 +7,13 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/extensions/string_extensions.dart'; import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/upload.service.dart'; -import 'package:logging/logging.dart'; import 'package:immich_mobile/utils/debug_print.dart'; +import 'package:logging/logging.dart'; class EnqueueStatus { final int enqueueCount; @@ -36,6 +36,7 @@ class DriftUploadStatus { final int fileSize; final String networkSpeedAsString; final bool? isFailed; + final String? error; const DriftUploadStatus({ required this.taskId, @@ -44,6 +45,7 @@ class DriftUploadStatus { required this.fileSize, required this.networkSpeedAsString, this.isFailed, + this.error, }); DriftUploadStatus copyWith({ @@ -53,6 +55,7 @@ class DriftUploadStatus { int? fileSize, String? networkSpeedAsString, bool? isFailed, + String? error, }) { return DriftUploadStatus( taskId: taskId ?? this.taskId, @@ -61,12 +64,13 @@ class DriftUploadStatus { fileSize: fileSize ?? this.fileSize, networkSpeedAsString: networkSpeedAsString ?? this.networkSpeedAsString, isFailed: isFailed ?? this.isFailed, + error: error ?? this.error, ); } @override String toString() { - return 'DriftUploadStatus(taskId: $taskId, filename: $filename, progress: $progress, fileSize: $fileSize, networkSpeedAsString: $networkSpeedAsString, isFailed: $isFailed)'; + return 'DriftUploadStatus(taskId: $taskId, filename: $filename, progress: $progress, fileSize: $fileSize, networkSpeedAsString: $networkSpeedAsString, isFailed: $isFailed, error: $error)'; } @override @@ -78,7 +82,8 @@ class DriftUploadStatus { other.progress == progress && other.fileSize == fileSize && other.networkSpeedAsString == networkSpeedAsString && - other.isFailed == isFailed; + other.isFailed == isFailed && + other.error == error; } @override @@ -88,37 +93,13 @@ class DriftUploadStatus { progress.hashCode ^ fileSize.hashCode ^ networkSpeedAsString.hashCode ^ - isFailed.hashCode; + isFailed.hashCode ^ + error.hashCode; } - - Map toMap() { - return { - 'taskId': taskId, - 'filename': filename, - 'progress': progress, - 'fileSize': fileSize, - 'networkSpeedAsString': networkSpeedAsString, - 'isFailed': isFailed, - }; - } - - factory DriftUploadStatus.fromMap(Map map) { - return DriftUploadStatus( - taskId: map['taskId'] as String, - filename: map['filename'] as String, - progress: map['progress'] as double, - fileSize: map['fileSize'] as int, - networkSpeedAsString: map['networkSpeedAsString'] as String, - isFailed: map['isFailed'] != null ? map['isFailed'] as bool : null, - ); - } - - String toJson() => json.encode(toMap()); - - factory DriftUploadStatus.fromJson(String source) => - DriftUploadStatus.fromMap(json.decode(source) as Map); } +enum BackupError { none, syncFailed } + class DriftBackupState { final int totalCount; final int backupCount; @@ -128,7 +109,9 @@ class DriftBackupState { final int enqueueCount; final int enqueueTotalCount; + final bool isSyncing; final bool isCanceling; + final BackupError error; final Map uploadItems; @@ -140,7 +123,9 @@ class DriftBackupState { required this.enqueueCount, required this.enqueueTotalCount, required this.isCanceling, + required this.isSyncing, required this.uploadItems, + this.error = BackupError.none, }); DriftBackupState copyWith({ @@ -151,7 +136,9 @@ class DriftBackupState { int? enqueueCount, int? enqueueTotalCount, bool? isCanceling, + bool? isSyncing, Map? uploadItems, + BackupError? error, }) { return DriftBackupState( totalCount: totalCount ?? this.totalCount, @@ -161,13 +148,15 @@ class DriftBackupState { enqueueCount: enqueueCount ?? this.enqueueCount, enqueueTotalCount: enqueueTotalCount ?? this.enqueueTotalCount, isCanceling: isCanceling ?? this.isCanceling, + isSyncing: isSyncing ?? this.isSyncing, uploadItems: uploadItems ?? this.uploadItems, + error: error ?? this.error, ); } @override String toString() { - return 'DriftBackupState(totalCount: $totalCount, backupCount: $backupCount, remainderCount: $remainderCount, processingCount: $processingCount, enqueueCount: $enqueueCount, enqueueTotalCount: $enqueueTotalCount, isCanceling: $isCanceling, uploadItems: $uploadItems)'; + return 'DriftBackupState(totalCount: $totalCount, backupCount: $backupCount, remainderCount: $remainderCount, processingCount: $processingCount, enqueueCount: $enqueueCount, enqueueTotalCount: $enqueueTotalCount, isCanceling: $isCanceling, isSyncing: $isSyncing, uploadItems: $uploadItems, error: $error)'; } @override @@ -182,7 +171,9 @@ class DriftBackupState { other.enqueueCount == enqueueCount && other.enqueueTotalCount == enqueueTotalCount && other.isCanceling == isCanceling && - mapEquals(other.uploadItems, uploadItems); + other.isSyncing == isSyncing && + mapEquals(other.uploadItems, uploadItems) && + other.error == error; } @override @@ -194,7 +185,9 @@ class DriftBackupState { enqueueCount.hashCode ^ enqueueTotalCount.hashCode ^ isCanceling.hashCode ^ - uploadItems.hashCode; + isSyncing.hashCode ^ + uploadItems.hashCode ^ + error.hashCode; } } @@ -213,7 +206,9 @@ class DriftBackupNotifier extends StateNotifier { enqueueCount: 0, enqueueTotalCount: 0, isCanceling: false, + isSyncing: false, uploadItems: {}, + error: BackupError.none, ), ) { { @@ -266,7 +261,24 @@ class DriftBackupNotifier extends StateNotifier { return; } - state = state.copyWith(uploadItems: {...state.uploadItems, taskId: currentItem.copyWith(isFailed: true)}); + String? error; + final exception = update.exception; + if (exception != null && exception is TaskHttpException) { + final message = tryJsonDecode(exception.description)?['message'] as String?; + if (message != null) { + final responseCode = exception.httpResponseCode; + error = "${exception.exceptionType}, response code $responseCode: $message"; + } + } + error ??= update.exception?.toString(); + + state = state.copyWith( + uploadItems: { + ...state.uploadItems, + taskId: currentItem.copyWith(isFailed: true, error: error), + }, + ); + _logger.fine("Upload failed for taskId: $taskId, exception: ${update.exception}"); break; case TaskStatus.canceled: @@ -330,7 +342,16 @@ class DriftBackupNotifier extends StateNotifier { ); } + void updateError(BackupError error) async { + state = state.copyWith(error: error); + } + + void updateSyncing(bool isSyncing) async { + state = state.copyWith(isSyncing: isSyncing); + } + Future startBackup(String userId) { + state = state.copyWith(error: BackupError.none); return _uploadService.startBackup(userId, _updateEnqueueCount); } @@ -340,7 +361,7 @@ class DriftBackupNotifier extends StateNotifier { Future cancel() async { dPrint(() => "Canceling backup tasks..."); - state = state.copyWith(enqueueCount: 0, enqueueTotalCount: 0, isCanceling: true); + state = state.copyWith(enqueueCount: 0, enqueueTotalCount: 0, isCanceling: true, error: BackupError.none); final activeTaskCount = await _uploadService.cancelBackup(); @@ -356,6 +377,7 @@ class DriftBackupNotifier extends StateNotifier { Future handleBackupResume(String userId) async { _logger.info("Resuming backup tasks..."); + state = state.copyWith(error: BackupError.none); final tasks = await _uploadService.getActiveTasks(kBackupGroup); _logger.info("Found ${tasks.length} tasks"); @@ -383,7 +405,7 @@ final driftBackupCandidateProvider = FutureProvider.autoDispose return []; } - return ref.read(backupRepositoryProvider).getCandidates(user.id); + return ref.read(backupRepositoryProvider).getCandidates(user.id, onlyHashed: false); }); final driftCandidateBackupAlbumInfoProvider = FutureProvider.autoDispose.family, String>(( diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index 9a343aa358..9f38f4b0ec 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -3,7 +3,10 @@ import 'package:background_downloader/background_downloader.dart'; import 'package:flutter/material.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/services/asset.service.dart'; import 'package:immich_mobile/models/download/livephotos_medatada.model.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; @@ -36,6 +39,7 @@ class ActionNotifier extends Notifier { late ActionService _service; late UploadService _uploadService; late DownloadService _downloadService; + late AssetService _assetService; ActionNotifier() : super(); @@ -43,6 +47,7 @@ class ActionNotifier extends Notifier { void build() { _uploadService = ref.watch(uploadServiceProvider); _service = ref.watch(actionServiceProvider); + _assetService = ref.watch(assetServiceProvider); _downloadService = ref.watch(downloadServiceProvider); _downloadService.onImageDownloadStatus = _downloadImageCallback; _downloadService.onVideoDownloadStatus = _downloadVideoCallback; @@ -72,11 +77,14 @@ class ActionNotifier extends Notifier { return _getAssets(source).whereType().toIds().toList(growable: false); } - List _getLocalIdsForSource(ActionSource source) { + List _getLocalIdsForSource(ActionSource source, {bool ignoreLocalOnly = false}) { final Set assets = _getAssets(source); final List localIds = []; for (final asset in assets) { + if (ignoreLocalOnly && asset.storage != AssetState.merged) { + continue; + } if (asset is LocalAsset) { localIds.add(asset.id); } else if (asset is RemoteAsset && asset.localId != null) { @@ -184,7 +192,7 @@ class ActionNotifier extends Notifier { Future moveToLockFolder(ActionSource source) async { final ids = _getOwnedRemoteIdsForSource(source); - final localIds = _getLocalIdsForSource(source); + final localIds = _getLocalIdsForSource(source, ignoreLocalOnly: true); try { await _service.moveToLockFolder(ids, localIds); return ActionResult(count: ids.length, success: true); @@ -252,8 +260,15 @@ class ActionNotifier extends Notifier { } } - Future deleteLocal(ActionSource source) async { - final ids = _getLocalIdsForSource(source); + Future deleteLocal(ActionSource source, bool backedUpOnly) async { + final List ids; + if (backedUpOnly) { + final assets = _getAssets(source); + ids = assets.where((asset) => asset.storage == AssetState.merged).map((asset) => asset.localId!).toList(); + } else { + ids = _getLocalIdsForSource(source); + } + try { final deletedCount = await _service.deleteLocal(ids); return ActionResult(count: deletedCount, success: true); @@ -335,6 +350,14 @@ class ActionNotifier extends Notifier { final assets = _getOwnedRemoteAssetsForSource(source); try { await _service.unStack(assets.map((e) => e.stackId).nonNulls.toList()); + if (source == ActionSource.viewer) { + final updatedParent = await _assetService.getRemoteAsset(assets.first.id); + if (updatedParent != null) { + ref.read(currentAssetNotifier.notifier).setAsset(updatedParent); + ref.read(assetViewerProvider.notifier).setAsset(updatedParent); + } + } + return ActionResult(count: assets.length, success: true); } catch (error, stack) { _logger.severe('Failed to unstack assets', error, stack); diff --git a/mobile/lib/providers/infrastructure/asset.provider.dart b/mobile/lib/providers/infrastructure/asset.provider.dart index 102e6aa60c..4b51ce33bd 100644 --- a/mobile/lib/providers/infrastructure/asset.provider.dart +++ b/mobile/lib/providers/infrastructure/asset.provider.dart @@ -3,6 +3,7 @@ import 'package:immich_mobile/domain/services/asset.service.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/remote_asset.repository.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; final localAssetRepository = Provider( (ref) => DriftLocalAssetRepository(ref.watch(driftProvider)), @@ -19,9 +20,13 @@ final assetServiceProvider = Provider( ), ); -final placesProvider = FutureProvider>( - (ref) => AssetService( - remoteAssetRepository: ref.watch(remoteAssetRepositoryProvider), - localAssetRepository: ref.watch(localAssetRepository), - ).getPlaces(), -); +final placesProvider = FutureProvider>((ref) { + final assetService = ref.watch(assetServiceProvider); + final auth = ref.watch(currentUserProvider); + + if (auth == null) { + return Future.value(const []); + } + + return assetService.getPlaces(auth.id); +}); diff --git a/mobile/lib/providers/timeline/multiselect.provider.dart b/mobile/lib/providers/timeline/multiselect.provider.dart index e225e0c98d..6949413cd9 100644 --- a/mobile/lib/providers/timeline/multiselect.provider.dart +++ b/mobile/lib/providers/timeline/multiselect.provider.dart @@ -28,6 +28,8 @@ class MultiSelectState { bool get hasRemote => selectedAssets.any((asset) => asset.storage == AssetState.remote || asset.storage == AssetState.merged); + bool get hasStacked => selectedAssets.any((asset) => asset is RemoteAsset && asset.stackId != null); + bool get hasLocal => selectedAssets.any((asset) => asset.storage == AssetState.local); bool get hasMerged => selectedAssets.any((asset) => asset.storage == AssetState.merged); diff --git a/mobile/lib/repositories/asset_media.repository.dart b/mobile/lib/repositories/asset_media.repository.dart index bfcf7060e0..f71c919373 100644 --- a/mobile/lib/repositories/asset_media.repository.dart +++ b/mobile/lib/repositories/asset_media.repository.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; @@ -8,6 +9,7 @@ import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart' as asset_entity; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/platform_extensions.dart'; import 'package:immich_mobile/extensions/response_extensions.dart'; import 'package:immich_mobile/repositories/asset_api.repository.dart'; import 'package:immich_mobile/utils/hash.dart'; @@ -20,11 +22,33 @@ final assetMediaRepositoryProvider = Provider((ref) => AssetMediaRepository(ref. class AssetMediaRepository { final AssetApiRepository _assetApiRepository; + static final Logger _log = Logger("AssetMediaRepository"); const AssetMediaRepository(this._assetApiRepository); - Future> deleteAll(List ids) => PhotoManager.editor.deleteWithIds(ids); + Future _androidSupportsTrash() async { + if (Platform.isAndroid) { + DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); + AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; + int sdkVersion = androidInfo.version.sdkInt; + return sdkVersion >= 31; + } + return false; + } + + Future> deleteAll(List ids) async { + if (CurrentPlatform.isAndroid) { + if (await _androidSupportsTrash()) { + return PhotoManager.editor.android.moveToTrash( + ids.map((e) => AssetEntity(id: e, width: 1, height: 1, typeInt: 0)).toList(), + ); + } else { + return PhotoManager.editor.deleteWithIds(ids); + } + } + return PhotoManager.editor.deleteWithIds(ids); + } Future get(String id) async { final entity = await AssetEntity.fromId(id); @@ -72,6 +96,7 @@ class AssetMediaRepository { // TODO: make this more efficient Future shareAssets(List assets, BuildContext context) async { final downloadedXFiles = []; + final tempFiles = []; for (var asset in assets) { final localId = (asset is LocalAsset) @@ -82,6 +107,9 @@ class AssetMediaRepository { if (localId != null) { File? f = await AssetEntity(id: localId, width: 1, height: 1, typeInt: 0).originFile; downloadedXFiles.add(XFile(f!.path)); + if (CurrentPlatform.isIOS) { + tempFiles.add(f); + } } else if (asset is RemoteAsset) { final tempDir = await getTemporaryDirectory(); final name = asset.name; @@ -95,6 +123,7 @@ class AssetMediaRepository { await tempFile.writeAsBytes(res.bodyBytes); downloadedXFiles.add(XFile(tempFile.path)); + tempFiles.add(tempFile); } else { _log.warning("Asset type not supported for sharing: $asset"); continue; @@ -113,9 +142,9 @@ class AssetMediaRepository { downloadedXFiles, sharePositionOrigin: Rect.fromPoints(Offset.zero, Offset(size.width / 3, size.height)), ).then((result) async { - for (var file in downloadedXFiles) { + for (var file in tempFiles) { try { - await File(file.path).delete(); + await file.delete(); } catch (e) { _log.warning("Failed to delete temporary file: ${file.path}", e); } diff --git a/mobile/lib/repositories/download.repository.dart b/mobile/lib/repositories/download.repository.dart index 1ac9410fc6..c578746a4c 100644 --- a/mobile/lib/repositories/download.repository.dart +++ b/mobile/lib/repositories/download.repository.dart @@ -121,7 +121,7 @@ class DownloadRepository { _dummyMetadata['part'] = LivePhotosPart.video.index; tasks[taskIndex++] = DownloadTask( taskId: livePhotoVideoId, - url: url, + url: getOriginalUrlForRemoteId(livePhotoVideoId), headers: headers, filename: asset.name.toUpperCase().replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'), updates: Updates.statusAndProgress, diff --git a/mobile/lib/services/app_settings.service.dart b/mobile/lib/services/app_settings.service.dart index 03d91328d1..7149408e8a 100644 --- a/mobile/lib/services/app_settings.service.dart +++ b/mobile/lib/services/app_settings.service.dart @@ -34,6 +34,7 @@ enum AppSettingsEnum { preferRemoteImage(StoreKey.preferRemoteImage, null, false), loopVideo(StoreKey.loopVideo, "loopVideo", true), loadOriginalVideo(StoreKey.loadOriginalVideo, "loadOriginalVideo", false), + autoPlayVideo(StoreKey.autoPlayVideo, "autoPlayVideo", true), mapThemeMode(StoreKey.mapThemeMode, null, 0), mapShowFavoriteOnly(StoreKey.mapShowFavoriteOnly, null, false), mapIncludeArchived(StoreKey.mapIncludeArchived, null, false), diff --git a/mobile/lib/services/deep_link.service.dart b/mobile/lib/services/deep_link.service.dart index 6226781919..7c8ddce265 100644 --- a/mobile/lib/services/deep_link.service.dart +++ b/mobile/lib/services/deep_link.service.dart @@ -8,8 +8,8 @@ import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.page.dart'; import 'package:immich_mobile/providers/album/current_album.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/asset.provider.dart' as beta_asset_provider; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart' as beta_asset_provider; import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; @@ -150,7 +150,10 @@ class DeepLinkService { } AssetViewer.setAsset(ref, asset); - return AssetViewerRoute(initialIndex: 0, timelineService: _betaTimelineFactory.fromAssets([asset])); + return AssetViewerRoute( + initialIndex: 0, + timelineService: _betaTimelineFactory.fromAssets([asset], TimelineOrigin.deepLink), + ); } else { // TODO: Remove this when beta is default final asset = await _assetService.getAssetByRemoteId(assetId); diff --git a/mobile/lib/services/server_info.service.dart b/mobile/lib/services/server_info.service.dart index 0bce9366d2..460e135421 100644 --- a/mobile/lib/services/server_info.service.dart +++ b/mobile/lib/services/server_info.service.dart @@ -14,15 +14,6 @@ class ServerInfoService { const ServerInfoService(this._apiService); - Future ping() async { - try { - await _apiService.serverInfoApi.pingServer().timeout(const Duration(seconds: 5)); - return true; - } catch (e) { - return false; - } - } - Future getDiskInfo() async { try { final dto = await _apiService.serverInfoApi.getStorage(); diff --git a/mobile/lib/services/upload.service.dart b/mobile/lib/services/upload.service.dart index b1130dab80..e8e98562f7 100644 --- a/mobile/lib/services/upload.service.dart +++ b/mobile/lib/services/upload.service.dart @@ -9,6 +9,7 @@ import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/extensions/platform_extensions.dart'; import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; @@ -19,9 +20,9 @@ import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; import 'package:immich_mobile/repositories/upload.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as p; -import 'package:immich_mobile/utils/debug_print.dart'; final uploadServiceProvider = Provider((ref) { final service = UploadService( @@ -205,10 +206,20 @@ class UploadService { return _uploadRepository.start(); } - void _handleTaskStatusUpdate(TaskStatusUpdate update) { + void _handleTaskStatusUpdate(TaskStatusUpdate update) async { switch (update.status) { case TaskStatus.complete: _handleLivePhoto(update); + + if (CurrentPlatform.isIOS) { + try { + final path = await update.task.filePath(); + await File(path).delete(); + } catch (e) { + _logger.severe('Error deleting file path for iOS: $e'); + } + } + break; default: diff --git a/mobile/lib/utils/action_button.utils.dart b/mobile/lib/utils/action_button.utils.dart index 090aeeeaa7..c5a2583531 100644 --- a/mobile/lib/utils/action_button.utils.dart +++ b/mobile/lib/utils/action_button.utils.dart @@ -16,6 +16,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_b import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/unarchive_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/unstack_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; class ActionButtonContext { @@ -24,6 +25,7 @@ class ActionButtonContext { final bool isArchived; final bool isTrashEnabled; final bool isInLockedView; + final bool isStacked; final RemoteAlbum? currentAlbum; final bool advancedTroubleshooting; final ActionSource source; @@ -33,6 +35,7 @@ class ActionButtonContext { required this.isOwner, required this.isArchived, required this.isTrashEnabled, + required this.isStacked, required this.isInLockedView, required this.currentAlbum, required this.advancedTroubleshooting, @@ -55,6 +58,7 @@ enum ActionButtonType { deleteLocal, upload, removeFromAlbum, + unstack, likeActivity; bool shouldShow(ActionButtonContext context) { @@ -110,6 +114,10 @@ enum ActionButtonType { context.isOwner && // !context.isInLockedView && // context.currentAlbum != null, + ActionButtonType.unstack => + context.isOwner && // + !context.isInLockedView && // + context.isStacked, ActionButtonType.likeActivity => !context.isInLockedView && context.currentAlbum != null && @@ -138,28 +146,13 @@ enum ActionButtonType { source: context.source, ), ActionButtonType.likeActivity => const LikeActivityActionButton(), + ActionButtonType.unstack => UnStackActionButton(source: context.source), }; } } class ActionButtonBuilder { - static const List _actionTypes = [ - ActionButtonType.advancedInfo, - ActionButtonType.share, - ActionButtonType.shareLink, - ActionButtonType.likeActivity, - ActionButtonType.archive, - ActionButtonType.unarchive, - ActionButtonType.download, - ActionButtonType.trash, - ActionButtonType.deletePermanent, - ActionButtonType.delete, - ActionButtonType.moveToLockFolder, - ActionButtonType.removeFromLockFolder, - ActionButtonType.deleteLocal, - ActionButtonType.upload, - ActionButtonType.removeFromAlbum, - ]; + static const List _actionTypes = ActionButtonType.values; static List build(ActionButtonContext context) { return _actionTypes.where((type) => type.shouldShow(context)).map((type) => type.buildButton(context)).toList(); diff --git a/mobile/lib/utils/datetime_helpers.dart b/mobile/lib/utils/datetime_helpers.dart index 829f71c37e..c13c8ca312 100644 --- a/mobile/lib/utils/datetime_helpers.dart +++ b/mobile/lib/utils/datetime_helpers.dart @@ -1,7 +1,7 @@ const int _maxMillisecondsSinceEpoch = 8640000000000000; // 275760-09-13 const int _minMillisecondsSinceEpoch = -62135596800000; // 0001-01-01 -DateTime? tryFromSecondsSinceEpoch(int? secondsSinceEpoch) { +DateTime? tryFromSecondsSinceEpoch(int? secondsSinceEpoch, {bool isUtc = false}) { if (secondsSinceEpoch == null) { return null; } @@ -12,7 +12,7 @@ DateTime? tryFromSecondsSinceEpoch(int? secondsSinceEpoch) { } try { - return DateTime.fromMillisecondsSinceEpoch(milliSeconds); + return DateTime.fromMillisecondsSinceEpoch(milliSeconds, isUtc: isUtc); } catch (e) { return null; } diff --git a/mobile/lib/utils/isolate.dart b/mobile/lib/utils/isolate.dart index e8b7d410f4..1ccf00d58b 100644 --- a/mobile/lib/utils/isolate.dart +++ b/mobile/lib/utils/isolate.dart @@ -32,6 +32,7 @@ Cancelable runInIsolateGentle({ } return workerManager.executeGentle((cancelledChecker) async { + T? result; await runZonedGuarded( () async { BackgroundIsolateBinaryMessenger.ensureInitialized(token); @@ -53,7 +54,7 @@ Cancelable runInIsolateGentle({ try { HttpSSLOptions.apply(applyNative: false); - return await computation(ref); + result = await computation(ref); } on CanceledError { log.warning("Computation cancelled ${debugLabel == null ? '' : ' for $debugLabel'}"); } catch (error, stack) { @@ -83,12 +84,11 @@ Cancelable runInIsolateGentle({ await Future.delayed(const Duration(seconds: 2)); } } - return null; }, (error, stack) { dPrint(() => "Error in isolate $debugLabel zone: $error, $stack"); }, ); - return null; + return result; }); } diff --git a/mobile/lib/widgets/asset_grid/delete_dialog.dart b/mobile/lib/widgets/asset_grid/delete_dialog.dart index e7c7775e54..adb22889a8 100644 --- a/mobile/lib/widgets/asset_grid/delete_dialog.dart +++ b/mobile/lib/widgets/asset_grid/delete_dialog.dart @@ -22,12 +22,12 @@ class DeleteLocalOnlyDialog extends StatelessWidget { @override Widget build(BuildContext context) { void onDeleteBackedUpOnly() { - context.pop(); + context.pop(true); onDeleteLocal(true); } void onForceDelete() { - context.pop(); + context.pop(false); onDeleteLocal(false); } @@ -36,26 +36,44 @@ class DeleteLocalOnlyDialog extends StatelessWidget { title: const Text("delete_dialog_title").tr(), content: const Text("delete_dialog_alert_local_non_backed_up").tr(), actions: [ - TextButton( - onPressed: () => context.pop(), - child: Text( - "cancel", - style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), - ).tr(), + SizedBox( + width: double.infinity, + height: 48, + child: FilledButton( + onPressed: () => context.pop(), + style: FilledButton.styleFrom( + backgroundColor: context.colorScheme.surfaceDim, + foregroundColor: context.primaryColor, + ), + child: const Text("cancel", style: TextStyle(fontWeight: FontWeight.bold)).tr(), + ), ), - TextButton( - onPressed: onDeleteBackedUpOnly, - child: Text( - "delete_local_dialog_ok_backed_up_only", - style: TextStyle(color: context.colorScheme.tertiary, fontWeight: FontWeight.bold), - ).tr(), + const SizedBox(height: 8), + SizedBox( + width: double.infinity, + height: 48, + + child: FilledButton( + onPressed: onDeleteBackedUpOnly, + style: FilledButton.styleFrom( + backgroundColor: context.colorScheme.errorContainer, + foregroundColor: context.colorScheme.onErrorContainer, + ), + child: const Text( + "delete_local_dialog_ok_backed_up_only", + style: TextStyle(fontWeight: FontWeight.bold), + ).tr(), + ), ), - TextButton( - onPressed: onForceDelete, - child: Text( - "delete_local_dialog_ok_force", - style: TextStyle(color: Colors.red[400], fontWeight: FontWeight.bold), - ).tr(), + const SizedBox(height: 8), + SizedBox( + width: double.infinity, + height: 48, + child: FilledButton( + onPressed: onForceDelete, + style: FilledButton.styleFrom(backgroundColor: Colors.red[400], foregroundColor: Colors.white), + child: const Text("delete_local_dialog_ok_force", style: TextStyle(fontWeight: FontWeight.bold)).tr(), + ), ), ], ); diff --git a/mobile/lib/widgets/common/immich_app_bar.dart b/mobile/lib/widgets/common/immich_app_bar.dart index 7eaedd27b5..28b5c535d2 100644 --- a/mobile/lib/widgets/common/immich_app_bar.dart +++ b/mobile/lib/widgets/common/immich_app_bar.dart @@ -129,19 +129,24 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { title: Builder( builder: (BuildContext context) { return Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Builder( - builder: (context) { - return Padding( - padding: const EdgeInsets.only(top: 3.0), - child: SvgPicture.asset( - context.isDarkTheme - ? 'assets/immich-logo-inline-dark.svg' - : 'assets/immich-logo-inline-light.svg', - height: 40, - ), - ); - }, + Padding( + padding: const EdgeInsets.only(top: 3.0), + child: SvgPicture.asset( + context.isDarkTheme ? 'assets/immich-logo-inline-dark.svg' : 'assets/immich-logo-inline-light.svg', + height: 40, + ), + ), + const Tooltip( + triggerMode: TooltipTriggerMode.tap, + showDuration: Duration(seconds: 4), + message: + "The old timeline is deprecated and will be removed in a future release. Kindly switch to the new timeline under Advanced Settings.", + child: Padding( + padding: EdgeInsets.only(top: 3.0), + child: Icon(Icons.error_rounded, fill: 1, color: Colors.amber, size: 20), + ), ), ], ); diff --git a/mobile/lib/widgets/common/immich_sliver_app_bar.dart b/mobile/lib/widgets/common/immich_sliver_app_bar.dart index 09c0518a23..90c213599c 100644 --- a/mobile/lib/widgets/common/immich_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/immich_sliver_app_bar.dart @@ -9,8 +9,8 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/models/server_info/server_info.model.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/providers/cast.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/sync_status.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; @@ -162,40 +162,32 @@ class _ProfileIndicator extends ConsumerWidget { } } +const double _kBadgeWidgetSize = 30.0; + class _BackupIndicator extends ConsumerWidget { const _BackupIndicator(); @override Widget build(BuildContext context, WidgetRef ref) { - const widgetSize = 30.0; final indicatorIcon = _getBackupBadgeIcon(context, ref); - final badgeBackground = context.colorScheme.surfaceContainer; return InkWell( onTap: () => context.pushRoute(const DriftBackupRoute()), borderRadius: const BorderRadius.all(Radius.circular(12)), child: Badge( - label: Container( - width: widgetSize / 2, - height: widgetSize / 2, - decoration: BoxDecoration( - color: badgeBackground, - border: Border.all(color: context.colorScheme.outline.withValues(alpha: .3)), - borderRadius: BorderRadius.circular(widgetSize / 2), - ), - child: indicatorIcon, - ), + label: indicatorIcon, backgroundColor: Colors.transparent, alignment: Alignment.bottomRight, isLabelVisible: indicatorIcon != null, offset: const Offset(-2, -12), - child: Icon(Icons.backup_rounded, size: widgetSize, color: context.primaryColor), + child: Icon(Icons.backup_rounded, size: _kBadgeWidgetSize, color: context.primaryColor), ), ); } Widget? _getBackupBadgeIcon(BuildContext context, WidgetRef ref) { final backupStateStream = ref.watch(settingsProvider).watch(Setting.enableBackup); + final hasError = ref.watch(driftBackupProvider.select((state) => state.error != BackupError.none)); final isDarkTheme = context.isDarkTheme; final iconColor = isDarkTheme ? Colors.white : Colors.black; final isUploading = ref.watch(driftBackupProvider.select((state) => state.uploadItems.isNotEmpty)); @@ -207,42 +199,76 @@ class _BackupIndicator extends ConsumerWidget { final backupEnabled = snapshot.data ?? false; if (!backupEnabled) { - return Icon( - Icons.cloud_off_rounded, - size: 9, - color: iconColor, - semanticLabel: 'backup_controller_page_backup'.tr(), + return _BadgeLabel( + Icon( + Icons.cloud_off_rounded, + size: 9, + color: iconColor, + semanticLabel: 'backup_controller_page_backup'.tr(), + ), + ); + } + + if (hasError) { + return _BadgeLabel( + Icon( + Icons.warning_rounded, + size: 12, + color: context.colorScheme.error, + semanticLabel: 'backup_controller_page_backup'.tr(), + ), + backgroundColor: context.colorScheme.errorContainer, ); } if (isUploading) { - return Container( - padding: const EdgeInsets.all(3.5), - child: Theme( - data: context.themeData.copyWith( - progressIndicatorTheme: context.themeData.progressIndicatorTheme.copyWith(year2023: true), - ), - child: CircularProgressIndicator( - strokeWidth: 2, - strokeCap: StrokeCap.round, - valueColor: AlwaysStoppedAnimation(iconColor), - semanticsLabel: 'backup_controller_page_backup'.tr(), + return _BadgeLabel( + Container( + padding: const EdgeInsets.all(3.5), + child: Theme( + data: context.themeData.copyWith( + progressIndicatorTheme: context.themeData.progressIndicatorTheme.copyWith(year2023: true), + ), + child: CircularProgressIndicator( + strokeWidth: 2, + strokeCap: StrokeCap.round, + valueColor: AlwaysStoppedAnimation(iconColor), + semanticsLabel: 'backup_controller_page_backup'.tr(), + ), ), ), ); } - return Icon( - Icons.check_outlined, - size: 9, - color: iconColor, - semanticLabel: 'backup_controller_page_backup'.tr(), + return _BadgeLabel( + Icon(Icons.check_outlined, size: 9, color: iconColor, semanticLabel: 'backup_controller_page_backup'.tr()), ); }, ); } } +class _BadgeLabel extends StatelessWidget { + final Widget indicator; + final Color? backgroundColor; + + const _BadgeLabel(this.indicator, {this.backgroundColor}); + + @override + Widget build(BuildContext context) { + return Container( + width: _kBadgeWidgetSize / 2, + height: _kBadgeWidgetSize / 2, + decoration: BoxDecoration( + color: backgroundColor ?? context.colorScheme.surfaceContainer, + border: Border.all(color: context.colorScheme.outline.withValues(alpha: .3)), + borderRadius: BorderRadius.circular(_kBadgeWidgetSize / 2), + ), + child: indicator, + ); + } +} + class _SyncStatusIndicator extends ConsumerStatefulWidget { const _SyncStatusIndicator(); diff --git a/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart b/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart index 1d8d9812be..9a89b7e1e3 100644 --- a/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart +++ b/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart @@ -14,11 +14,18 @@ class VideoViewerSettings extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final useLoopVideo = useAppSettingsState(AppSettingsEnum.loopVideo); final useOriginalVideo = useAppSettingsState(AppSettingsEnum.loadOriginalVideo); + final useAutoPlayVideo = useAppSettingsState(AppSettingsEnum.autoPlayVideo); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SettingsSubTitle(title: "videos".tr()), + SettingsSwitchListTile( + valueNotifier: useAutoPlayVideo, + title: "setting_video_viewer_auto_play_title".tr(), + subtitle: "setting_video_viewer_auto_play_subtitle".tr(), + onChanged: (_) => ref.invalidate(appSettingsServiceProvider), + ), SettingsSwitchListTile( valueNotifier: useLoopVideo, title: "setting_video_viewer_looping_title".tr(), diff --git a/mobile/lib/widgets/settings/beta_sync_settings/sync_status_and_actions.dart b/mobile/lib/widgets/settings/beta_sync_settings/sync_status_and_actions.dart index 56506072c5..a5bca24f81 100644 --- a/mobile/lib/widgets/settings/beta_sync_settings/sync_status_and_actions.dart +++ b/mobile/lib/widgets/settings/beta_sync_settings/sync_status_and_actions.dart @@ -42,7 +42,12 @@ class SyncStatusAndActions extends HookConsumerWidget { await dbFile.copy(exportFile.path); - await Share.shareXFiles([XFile(exportFile.path)], text: 'Immich Database Export'); + final size = MediaQuery.of(context).size; + await Share.shareXFiles( + [XFile(exportFile.path)], + text: 'Immich Database Export', + sharePositionOrigin: Rect.fromPoints(Offset.zero, Offset(size.width / 3, size.height)), + ); Future.delayed(const Duration(seconds: 30), () async { if (await exportFile.exists()) { diff --git a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart index 326d6c49d1..1fefb3dcfa 100644 --- a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart +++ b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart @@ -1,12 +1,10 @@ import 'package:auto_route/auto_route.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; -import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; @@ -16,10 +14,9 @@ class BetaTimelineListTile extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final betaTimelineValue = ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.betaTimeline); - final serverInfo = ref.watch(serverInfoProvider); final auth = ref.watch(authProvider); - if (!auth.isAuthenticated || (serverInfo.serverVersion.minor < 136 && kReleaseMode)) { + if (!auth.isAuthenticated) { return const SizedBox.shrink(); } diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 665bcb7e62..698b9774da 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -3,7 +3,7 @@ Immich API This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: -- API version: 1.143.1 +- API version: 2.1.0 - Generator version: 7.8.0 - Build package: org.openapitools.codegen.languages.DartClientCodegen @@ -359,6 +359,7 @@ Class | Method | HTTP request | Description - [CheckExistingAssetsDto](doc//CheckExistingAssetsDto.md) - [CheckExistingAssetsResponseDto](doc//CheckExistingAssetsResponseDto.md) - [Colorspace](doc//Colorspace.md) + - [ContributorCountResponseDto](doc//ContributorCountResponseDto.md) - [CreateAlbumDto](doc//CreateAlbumDto.md) - [CreateLibraryDto](doc//CreateLibraryDto.md) - [CreateProfileImageResponseDto](doc//CreateProfileImageResponseDto.md) diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index df2c2226b1..a3117ede86 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -130,6 +130,7 @@ part 'model/change_password_dto.dart'; part 'model/check_existing_assets_dto.dart'; part 'model/check_existing_assets_response_dto.dart'; part 'model/colorspace.dart'; +part 'model/contributor_count_response_dto.dart'; part 'model/create_album_dto.dart'; part 'model/create_library_dto.dart'; part 'model/create_profile_image_response_dto.dart'; diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index 06d27593c9..33056cf14e 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -314,6 +314,8 @@ class ApiClient { return CheckExistingAssetsResponseDto.fromJson(value); case 'Colorspace': return ColorspaceTypeTransformer().decode(value); + case 'ContributorCountResponseDto': + return ContributorCountResponseDto.fromJson(value); case 'CreateAlbumDto': return CreateAlbumDto.fromJson(value); case 'CreateLibraryDto': diff --git a/mobile/openapi/lib/model/album_response_dto.dart b/mobile/openapi/lib/model/album_response_dto.dart index 547a6a70fd..2f53706e7a 100644 --- a/mobile/openapi/lib/model/album_response_dto.dart +++ b/mobile/openapi/lib/model/album_response_dto.dart @@ -18,6 +18,7 @@ class AlbumResponseDto { this.albumUsers = const [], required this.assetCount, this.assets = const [], + this.contributorCounts = const [], required this.createdAt, required this.description, this.endDate, @@ -43,6 +44,8 @@ class AlbumResponseDto { List assets; + List contributorCounts; + DateTime createdAt; String description; @@ -100,6 +103,7 @@ class AlbumResponseDto { _deepEquality.equals(other.albumUsers, albumUsers) && other.assetCount == assetCount && _deepEquality.equals(other.assets, assets) && + _deepEquality.equals(other.contributorCounts, contributorCounts) && other.createdAt == createdAt && other.description == description && other.endDate == endDate && @@ -122,6 +126,7 @@ class AlbumResponseDto { (albumUsers.hashCode) + (assetCount.hashCode) + (assets.hashCode) + + (contributorCounts.hashCode) + (createdAt.hashCode) + (description.hashCode) + (endDate == null ? 0 : endDate!.hashCode) + @@ -137,7 +142,7 @@ class AlbumResponseDto { (updatedAt.hashCode); @override - String toString() => 'AlbumResponseDto[albumName=$albumName, albumThumbnailAssetId=$albumThumbnailAssetId, albumUsers=$albumUsers, assetCount=$assetCount, assets=$assets, createdAt=$createdAt, description=$description, endDate=$endDate, hasSharedLink=$hasSharedLink, id=$id, isActivityEnabled=$isActivityEnabled, lastModifiedAssetTimestamp=$lastModifiedAssetTimestamp, order=$order, owner=$owner, ownerId=$ownerId, shared=$shared, startDate=$startDate, updatedAt=$updatedAt]'; + String toString() => 'AlbumResponseDto[albumName=$albumName, albumThumbnailAssetId=$albumThumbnailAssetId, albumUsers=$albumUsers, assetCount=$assetCount, assets=$assets, contributorCounts=$contributorCounts, createdAt=$createdAt, description=$description, endDate=$endDate, hasSharedLink=$hasSharedLink, id=$id, isActivityEnabled=$isActivityEnabled, lastModifiedAssetTimestamp=$lastModifiedAssetTimestamp, order=$order, owner=$owner, ownerId=$ownerId, shared=$shared, startDate=$startDate, updatedAt=$updatedAt]'; Map toJson() { final json = {}; @@ -150,6 +155,7 @@ class AlbumResponseDto { json[r'albumUsers'] = this.albumUsers; json[r'assetCount'] = this.assetCount; json[r'assets'] = this.assets; + json[r'contributorCounts'] = this.contributorCounts; json[r'createdAt'] = this.createdAt.toUtc().toIso8601String(); json[r'description'] = this.description; if (this.endDate != null) { @@ -196,6 +202,7 @@ class AlbumResponseDto { albumUsers: AlbumUserResponseDto.listFromJson(json[r'albumUsers']), assetCount: mapValueOfType(json, r'assetCount')!, assets: AssetResponseDto.listFromJson(json[r'assets']), + contributorCounts: ContributorCountResponseDto.listFromJson(json[r'contributorCounts']), createdAt: mapDateTime(json, r'createdAt', r'')!, description: mapValueOfType(json, r'description')!, endDate: mapDateTime(json, r'endDate', r''), diff --git a/mobile/openapi/lib/model/contributor_count_response_dto.dart b/mobile/openapi/lib/model/contributor_count_response_dto.dart new file mode 100644 index 0000000000..e0e16ee427 --- /dev/null +++ b/mobile/openapi/lib/model/contributor_count_response_dto.dart @@ -0,0 +1,107 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class ContributorCountResponseDto { + /// Returns a new [ContributorCountResponseDto] instance. + ContributorCountResponseDto({ + required this.assetCount, + required this.userId, + }); + + int assetCount; + + String userId; + + @override + bool operator ==(Object other) => identical(this, other) || other is ContributorCountResponseDto && + other.assetCount == assetCount && + other.userId == userId; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (assetCount.hashCode) + + (userId.hashCode); + + @override + String toString() => 'ContributorCountResponseDto[assetCount=$assetCount, userId=$userId]'; + + Map toJson() { + final json = {}; + json[r'assetCount'] = this.assetCount; + json[r'userId'] = this.userId; + return json; + } + + /// Returns a new [ContributorCountResponseDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static ContributorCountResponseDto? fromJson(dynamic value) { + upgradeDto(value, "ContributorCountResponseDto"); + if (value is Map) { + final json = value.cast(); + + return ContributorCountResponseDto( + assetCount: mapValueOfType(json, r'assetCount')!, + userId: mapValueOfType(json, r'userId')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = ContributorCountResponseDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = ContributorCountResponseDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of ContributorCountResponseDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = ContributorCountResponseDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'assetCount', + 'userId', + }; +} + diff --git a/mobile/openapi/lib/model/notification_type.dart b/mobile/openapi/lib/model/notification_type.dart index 436d2d190f..b5885aa441 100644 --- a/mobile/openapi/lib/model/notification_type.dart +++ b/mobile/openapi/lib/model/notification_type.dart @@ -26,6 +26,8 @@ class NotificationType { static const jobFailed = NotificationType._(r'JobFailed'); static const backupFailed = NotificationType._(r'BackupFailed'); static const systemMessage = NotificationType._(r'SystemMessage'); + static const albumInvite = NotificationType._(r'AlbumInvite'); + static const albumUpdate = NotificationType._(r'AlbumUpdate'); static const custom = NotificationType._(r'Custom'); /// List of all possible values in this [enum][NotificationType]. @@ -33,6 +35,8 @@ class NotificationType { jobFailed, backupFailed, systemMessage, + albumInvite, + albumUpdate, custom, ]; @@ -75,6 +79,8 @@ class NotificationTypeTypeTransformer { case r'JobFailed': return NotificationType.jobFailed; case r'BackupFailed': return NotificationType.backupFailed; case r'SystemMessage': return NotificationType.systemMessage; + case r'AlbumInvite': return NotificationType.albumInvite; + case r'AlbumUpdate': return NotificationType.albumUpdate; case r'Custom': return NotificationType.custom; default: if (!allowNull) { diff --git a/mobile/openapi/lib/model/system_config_smtp_transport_dto.dart b/mobile/openapi/lib/model/system_config_smtp_transport_dto.dart index bdaaa426c5..46307046b4 100644 --- a/mobile/openapi/lib/model/system_config_smtp_transport_dto.dart +++ b/mobile/openapi/lib/model/system_config_smtp_transport_dto.dart @@ -17,6 +17,7 @@ class SystemConfigSmtpTransportDto { required this.ignoreCert, required this.password, required this.port, + required this.secure, required this.username, }); @@ -30,6 +31,8 @@ class SystemConfigSmtpTransportDto { /// Maximum value: 65535 num port; + bool secure; + String username; @override @@ -38,6 +41,7 @@ class SystemConfigSmtpTransportDto { other.ignoreCert == ignoreCert && other.password == password && other.port == port && + other.secure == secure && other.username == username; @override @@ -47,10 +51,11 @@ class SystemConfigSmtpTransportDto { (ignoreCert.hashCode) + (password.hashCode) + (port.hashCode) + + (secure.hashCode) + (username.hashCode); @override - String toString() => 'SystemConfigSmtpTransportDto[host=$host, ignoreCert=$ignoreCert, password=$password, port=$port, username=$username]'; + String toString() => 'SystemConfigSmtpTransportDto[host=$host, ignoreCert=$ignoreCert, password=$password, port=$port, secure=$secure, username=$username]'; Map toJson() { final json = {}; @@ -58,6 +63,7 @@ class SystemConfigSmtpTransportDto { json[r'ignoreCert'] = this.ignoreCert; json[r'password'] = this.password; json[r'port'] = this.port; + json[r'secure'] = this.secure; json[r'username'] = this.username; return json; } @@ -75,6 +81,7 @@ class SystemConfigSmtpTransportDto { ignoreCert: mapValueOfType(json, r'ignoreCert')!, password: mapValueOfType(json, r'password')!, port: num.parse('${json[r'port']}'), + secure: mapValueOfType(json, r'secure')!, username: mapValueOfType(json, r'username')!, ); } @@ -127,6 +134,7 @@ class SystemConfigSmtpTransportDto { 'ignoreCert', 'password', 'port', + 'secure', 'username', }; } diff --git a/mobile/pigeon/background_worker_api.dart b/mobile/pigeon/background_worker_api.dart index 6f6c781de2..a40d290199 100644 --- a/mobile/pigeon/background_worker_api.dart +++ b/mobile/pigeon/background_worker_api.dart @@ -22,6 +22,8 @@ class BackgroundWorkerSettings { abstract class BackgroundWorkerFgHostApi { void enable(); + void saveNotificationMessage(String title, String body); + void configure(BackgroundWorkerSettings settings); void disable(); @@ -33,8 +35,6 @@ abstract class BackgroundWorkerBgHostApi { // required platform channels to notify the native side to start the background upload void onInitialized(); - void showNotification(String title, String content); - // Called from the background flutter engine to request the native side to cleanup void close(); } diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index d0dc8e64d3..125e4d46e2 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -317,10 +317,10 @@ packages: dependency: "direct main" description: name: connectivity_plus - sha256: "04bf81bb0b77de31557b58d052b24b3eee33f09a6e7a8c68a3e247c7df19ec27" + sha256: b5e72753cf63becce2c61fd04dfe0f1c430cc5278b53a1342dc5ad839eab29ec url: "https://pub.dev" source: hosted - version: "6.1.3" + version: "6.1.5" connectivity_plus_platform_interface: dependency: transitive description: @@ -437,18 +437,18 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: "306b78788d1bb569edb7c55d622953c2414ca12445b41c9117963e03afc5c513" + sha256: "49413c8ca514dea7633e8def233b25efdf83ec8522955cc2c0e3ad802927e7c6" url: "https://pub.dev" source: hosted - version: "11.3.3" + version: "12.1.0" device_info_plus_platform_interface: dependency: transitive description: name: device_info_plus_platform_interface - sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2" + sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f url: "https://pub.dev" source: hosted - version: "7.0.2" + version: "7.0.3" drift: dependency: "direct main" description: @@ -1022,25 +1022,34 @@ packages: isar: dependency: "direct main" description: - name: isar - sha256: e17a9555bc7f22ff26568b8c64d019b4ffa2dc6bd4cb1c8d9b269aefd32e53ad - url: "https://pub.isar-community.dev" - source: hosted + path: "packages/isar" + ref: bb1dca40fe87a001122e5d43bc6254718cb49f3a + resolved-ref: bb1dca40fe87a001122e5d43bc6254718cb49f3a + url: "https://github.com/immich-app/isar" + source: git version: "3.1.8" - isar_flutter_libs: + isar_community: + dependency: transitive + description: + name: isar_community + sha256: "28f59e54636c45ba0bb1b3b7f2656f1c50325f740cea6efcd101900be3fba546" + url: "https://pub.dev" + source: hosted + version: "3.3.0-dev.3" + isar_community_flutter_libs: dependency: "direct main" description: - name: isar_flutter_libs - sha256: "78710781e658ce4bff59b3f38c5b2735e899e627f4e926e1221934e77b95231a" - url: "https://pub.isar-community.dev" + name: isar_community_flutter_libs + sha256: c2934fe755bb3181cb67602fd5df0d080b3d3eb52799f98623aa4fc5acbea010 + url: "https://pub.dev" source: hosted - version: "3.1.8" + version: "3.3.0-dev.3" isar_generator: dependency: "direct dev" description: path: "packages/isar_generator" - ref: v3 - resolved-ref: ad574f60ed6f39d2995cd16fc7dc3de9a646ef30 + ref: bb1dca40fe87a001122e5d43bc6254718cb49f3a + resolved-ref: bb1dca40fe87a001122e5d43bc6254718cb49f3a url: "https://github.com/immich-app/isar" source: git version: "3.1.8" @@ -1208,8 +1217,8 @@ packages: dependency: "direct main" description: path: "." - ref: "5459d54" - resolved-ref: "5459d54cdc1cf4d99e2193b310052f1ebb5dcf43" + ref: "893894b" + resolved-ref: "893894b98b832be8a995a8d5d4c2289d0ad2d246" url: "https://github.com/immich-app/native_video_player" source: git version: "1.3.1" diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 75c992436a..82ce321954 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -2,14 +2,12 @@ name: immich_mobile description: Immich - selfhosted backup media file on mobile phone publish_to: 'none' -version: 1.143.1+3017 +version: 2.1.0+3022 environment: sdk: '>=3.8.0 <4.0.0' flutter: 3.35.4 -isar_version: &isar_version 3.1.8 - dependencies: flutter: sdk: flutter @@ -24,7 +22,7 @@ dependencies: connectivity_plus: ^6.1.3 crop_image: ^1.0.16 crypto: ^3.0.6 - device_info_plus: ^11.3.3 + device_info_plus: ^12.0.0 dynamic_color: ^1.7.0 easy_image_viewer: ^1.5.1 easy_localization: ^3.0.7+1 @@ -77,15 +75,15 @@ dependencies: native_video_player: git: url: https://github.com/immich-app/native_video_player - ref: '5459d54' + ref: '893894b' openapi: path: openapi isar: - version: *isar_version - hosted: https://pub.isar-community.dev/ - isar_flutter_libs: # contains Isar Core - version: *isar_version - hosted: https://pub.isar-community.dev/ + git: + url: https://github.com/immich-app/isar + ref: 'bb1dca40fe87a001122e5d43bc6254718cb49f3a' + path: packages/isar/ + isar_community_flutter_libs: 3.3.0-dev.3 # DB drift: ^2.23.1 drift_flutter: ^0.2.4 @@ -101,7 +99,7 @@ dev_dependencies: isar_generator: git: url: https://github.com/immich-app/isar - ref: v3 + ref: 'bb1dca40fe87a001122e5d43bc6254718cb49f3a' path: packages/isar_generator/ integration_test: sdk: flutter diff --git a/mobile/test/drift/main/generated/schema.dart b/mobile/test/drift/main/generated/schema.dart index 1d78a44317..073a86078f 100644 --- a/mobile/test/drift/main/generated/schema.dart +++ b/mobile/test/drift/main/generated/schema.dart @@ -14,6 +14,7 @@ import 'schema_v8.dart' as v8; import 'schema_v9.dart' as v9; import 'schema_v10.dart' as v10; import 'schema_v11.dart' as v11; +import 'schema_v12.dart' as v12; class GeneratedHelper implements SchemaInstantiationHelper { @override @@ -41,10 +42,12 @@ class GeneratedHelper implements SchemaInstantiationHelper { return v10.DatabaseAtV10(db); case 11: return v11.DatabaseAtV11(db); + case 12: + return v12.DatabaseAtV12(db); default: throw MissingSchemaException(version, versions); } } - static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; + static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; } diff --git a/mobile/test/drift/main/generated/schema_v12.dart b/mobile/test/drift/main/generated/schema_v12.dart new file mode 100644 index 0000000000..c42df284ec --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v12.dart @@ -0,0 +1,7198 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = + GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn avatarColor = GeneratedColumn( + 'avatar_color', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + id, + name, + email, + hasProfileImage, + profileChangedAt, + avatarColor, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + avatarColor: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}avatar_color'], + )!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final String email; + final bool hasProfileImage; + final DateTime profileChangedAt; + final int avatarColor; + const UserEntityData({ + required this.id, + required this.name, + required this.email, + required this.hasProfileImage, + required this.profileChangedAt, + required this.avatarColor, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['email'] = Variable(email); + map['has_profile_image'] = Variable(hasProfileImage); + map['profile_changed_at'] = Variable(profileChangedAt); + map['avatar_color'] = Variable(avatarColor); + return map; + } + + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + email: serializer.fromJson(json['email']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + avatarColor: serializer.fromJson(json['avatarColor']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'email': serializer.toJson(email), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'avatarColor': serializer.toJson(avatarColor), + }; + } + + UserEntityData copyWith({ + String? id, + String? name, + String? email, + bool? hasProfileImage, + DateTime? profileChangedAt, + int? avatarColor, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + avatarColor: avatarColor ?? this.avatarColor, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + email: data.email.present ? data.email.value : this.email, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + avatarColor: data.avatarColor.present + ? data.avatarColor.value + : this.avatarColor, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('avatarColor: $avatarColor') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + email, + hasProfileImage, + profileChangedAt, + avatarColor, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.email == this.email && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.avatarColor == this.avatarColor); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value email; + final Value hasProfileImage; + final Value profileChangedAt; + final Value avatarColor; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.email = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.avatarColor = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + required String email, + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.avatarColor = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? email, + Expression? hasProfileImage, + Expression? profileChangedAt, + Expression? avatarColor, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (email != null) 'email': email, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (avatarColor != null) 'avatar_color': avatarColor, + }); + } + + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? email, + Value? hasProfileImage, + Value? profileChangedAt, + Value? avatarColor, + }) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + avatarColor: avatarColor ?? this.avatarColor, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = Variable(profileChangedAt.value); + } + if (avatarColor.present) { + map['avatar_color'] = Variable(avatarColor.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('avatarColor: $avatarColor') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn libraryId = GeneratedColumn( + 'library_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + libraryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}library_id'], + ), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + final String? libraryId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + this.libraryId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + if (!nullToAbsent || libraryId != null) { + map['library_id'] = Variable(libraryId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + libraryId: serializer.fromJson(json['libraryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + 'libraryId': serializer.toJson(libraryId), + }; + } + + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + Value libraryId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + libraryId: libraryId.present ? libraryId.value : this.libraryId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + libraryId: data.libraryId.present ? data.libraryId.value : this.libraryId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId && + other.libraryId == this.libraryId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + final Value libraryId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + Expression? libraryId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + if (libraryId != null) 'library_id': libraryId, + }); + } + + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + Value? libraryId, + }) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + libraryId: libraryId ?? this.libraryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + if (libraryId.present) { + map['library_id'] = Variable(libraryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn linkedRemoteAlbumId = + GeneratedColumn( + 'linked_remote_album_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + linkedRemoteAlbumId, + marker_, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + linkedRemoteAlbumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}linked_remote_album_id'], + ), + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final String? linkedRemoteAlbumId; + final bool? marker_; + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.linkedRemoteAlbumId, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || linkedRemoteAlbumId != null) { + map['linked_remote_album_id'] = Variable(linkedRemoteAlbumId); + } + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + linkedRemoteAlbumId: serializer.fromJson( + json['linkedRemoteAlbumId'], + ), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'linkedRemoteAlbumId': serializer.toJson(linkedRemoteAlbumId), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value linkedRemoteAlbumId = const Value.absent(), + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + linkedRemoteAlbumId: linkedRemoteAlbumId.present + ? linkedRemoteAlbumId.value + : this.linkedRemoteAlbumId, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + linkedRemoteAlbumId: data.linkedRemoteAlbumId.present + ? data.linkedRemoteAlbumId.value + : this.linkedRemoteAlbumId, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('linkedRemoteAlbumId: $linkedRemoteAlbumId, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + linkedRemoteAlbumId, + marker_, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.linkedRemoteAlbumId == this.linkedRemoteAlbumId && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value linkedRemoteAlbumId; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.linkedRemoteAlbumId = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.linkedRemoteAlbumId = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? linkedRemoteAlbumId, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (linkedRemoteAlbumId != null) + 'linked_remote_album_id': linkedRemoteAlbumId, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? linkedRemoteAlbumId, + Value? marker_, + }) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + linkedRemoteAlbumId: linkedRemoteAlbumId ?? this.linkedRemoteAlbumId, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (linkedRemoteAlbumId.present) { + map['linked_remote_album_id'] = Variable( + linkedRemoteAlbumId.value, + ); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('linkedRemoteAlbumId: $linkedRemoteAlbumId, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [assetId, albumId, marker_]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + final bool? marker_; + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumAssetEntityData copyWith({ + String? assetId, + String? albumId, + Value marker_ = const Value.absent(), + }) => LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId, marker_); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId && + other.marker_ == this.marker_); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + final Value marker_; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + this.marker_ = const Value.absent(), + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + Value? marker_, + }) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class AuthUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AuthUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = + GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn avatarColor = GeneratedColumn( + 'avatar_color', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn pinCode = GeneratedColumn( + 'pin_code', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + name, + email, + isAdmin, + hasProfileImage, + profileChangedAt, + avatarColor, + quotaSizeInBytes, + quotaUsageInBytes, + pinCode, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'auth_user_entity'; + @override + Set get $primaryKey => {id}; + @override + AuthUserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AuthUserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + avatarColor: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}avatar_color'], + )!, + quotaSizeInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + )!, + quotaUsageInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, + pinCode: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}pin_code'], + ), + ); + } + + @override + AuthUserEntity createAlias(String alias) { + return AuthUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AuthUserEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String email; + final bool isAdmin; + final bool hasProfileImage; + final DateTime profileChangedAt; + final int avatarColor; + final int quotaSizeInBytes; + final int quotaUsageInBytes; + final String? pinCode; + const AuthUserEntityData({ + required this.id, + required this.name, + required this.email, + required this.isAdmin, + required this.hasProfileImage, + required this.profileChangedAt, + required this.avatarColor, + required this.quotaSizeInBytes, + required this.quotaUsageInBytes, + this.pinCode, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['email'] = Variable(email); + map['is_admin'] = Variable(isAdmin); + map['has_profile_image'] = Variable(hasProfileImage); + map['profile_changed_at'] = Variable(profileChangedAt); + map['avatar_color'] = Variable(avatarColor); + map['quota_size_in_bytes'] = Variable(quotaSizeInBytes); + map['quota_usage_in_bytes'] = Variable(quotaUsageInBytes); + if (!nullToAbsent || pinCode != null) { + map['pin_code'] = Variable(pinCode); + } + return map; + } + + factory AuthUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AuthUserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + email: serializer.fromJson(json['email']), + isAdmin: serializer.fromJson(json['isAdmin']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + avatarColor: serializer.fromJson(json['avatarColor']), + quotaSizeInBytes: serializer.fromJson(json['quotaSizeInBytes']), + quotaUsageInBytes: serializer.fromJson(json['quotaUsageInBytes']), + pinCode: serializer.fromJson(json['pinCode']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'email': serializer.toJson(email), + 'isAdmin': serializer.toJson(isAdmin), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'avatarColor': serializer.toJson(avatarColor), + 'quotaSizeInBytes': serializer.toJson(quotaSizeInBytes), + 'quotaUsageInBytes': serializer.toJson(quotaUsageInBytes), + 'pinCode': serializer.toJson(pinCode), + }; + } + + AuthUserEntityData copyWith({ + String? id, + String? name, + String? email, + bool? isAdmin, + bool? hasProfileImage, + DateTime? profileChangedAt, + int? avatarColor, + int? quotaSizeInBytes, + int? quotaUsageInBytes, + Value pinCode = const Value.absent(), + }) => AuthUserEntityData( + id: id ?? this.id, + name: name ?? this.name, + email: email ?? this.email, + isAdmin: isAdmin ?? this.isAdmin, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + avatarColor: avatarColor ?? this.avatarColor, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + pinCode: pinCode.present ? pinCode.value : this.pinCode, + ); + AuthUserEntityData copyWithCompanion(AuthUserEntityCompanion data) { + return AuthUserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + email: data.email.present ? data.email.value : this.email, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + avatarColor: data.avatarColor.present + ? data.avatarColor.value + : this.avatarColor, + quotaSizeInBytes: data.quotaSizeInBytes.present + ? data.quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: data.quotaUsageInBytes.present + ? data.quotaUsageInBytes.value + : this.quotaUsageInBytes, + pinCode: data.pinCode.present ? data.pinCode.value : this.pinCode, + ); + } + + @override + String toString() { + return (StringBuffer('AuthUserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('email: $email, ') + ..write('isAdmin: $isAdmin, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('avatarColor: $avatarColor, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes, ') + ..write('pinCode: $pinCode') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + email, + isAdmin, + hasProfileImage, + profileChangedAt, + avatarColor, + quotaSizeInBytes, + quotaUsageInBytes, + pinCode, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AuthUserEntityData && + other.id == this.id && + other.name == this.name && + other.email == this.email && + other.isAdmin == this.isAdmin && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.avatarColor == this.avatarColor && + other.quotaSizeInBytes == this.quotaSizeInBytes && + other.quotaUsageInBytes == this.quotaUsageInBytes && + other.pinCode == this.pinCode); +} + +class AuthUserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value email; + final Value isAdmin; + final Value hasProfileImage; + final Value profileChangedAt; + final Value avatarColor; + final Value quotaSizeInBytes; + final Value quotaUsageInBytes; + final Value pinCode; + const AuthUserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.email = const Value.absent(), + this.isAdmin = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.avatarColor = const Value.absent(), + this.quotaSizeInBytes = const Value.absent(), + this.quotaUsageInBytes = const Value.absent(), + this.pinCode = const Value.absent(), + }); + AuthUserEntityCompanion.insert({ + required String id, + required String name, + required String email, + this.isAdmin = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + required int avatarColor, + this.quotaSizeInBytes = const Value.absent(), + this.quotaUsageInBytes = const Value.absent(), + this.pinCode = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email), + avatarColor = Value(avatarColor); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? email, + Expression? isAdmin, + Expression? hasProfileImage, + Expression? profileChangedAt, + Expression? avatarColor, + Expression? quotaSizeInBytes, + Expression? quotaUsageInBytes, + Expression? pinCode, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (email != null) 'email': email, + if (isAdmin != null) 'is_admin': isAdmin, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (avatarColor != null) 'avatar_color': avatarColor, + if (quotaSizeInBytes != null) 'quota_size_in_bytes': quotaSizeInBytes, + if (quotaUsageInBytes != null) 'quota_usage_in_bytes': quotaUsageInBytes, + if (pinCode != null) 'pin_code': pinCode, + }); + } + + AuthUserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? email, + Value? isAdmin, + Value? hasProfileImage, + Value? profileChangedAt, + Value? avatarColor, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes, + Value? pinCode, + }) { + return AuthUserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + email: email ?? this.email, + isAdmin: isAdmin ?? this.isAdmin, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + avatarColor: avatarColor ?? this.avatarColor, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + pinCode: pinCode ?? this.pinCode, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = Variable(profileChangedAt.value); + } + if (avatarColor.present) { + map['avatar_color'] = Variable(avatarColor.value); + } + if (quotaSizeInBytes.present) { + map['quota_size_in_bytes'] = Variable(quotaSizeInBytes.value); + } + if (quotaUsageInBytes.present) { + map['quota_usage_in_bytes'] = Variable(quotaUsageInBytes.value); + } + if (pinCode.present) { + map['pin_code'] = Variable(pinCode.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AuthUserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('email: $email, ') + ..write('isAdmin: $isAdmin, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('avatarColor: $avatarColor, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes, ') + ..write('pinCode: $pinCode') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class AssetFaceEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetFaceEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetFaceEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = Variable(personId); + } + map['image_width'] = Variable(imageWidth); + map['image_height'] = Variable(imageHeight); + map['bounding_box_x1'] = Variable(boundingBoxX1); + map['bounding_box_y1'] = Variable(boundingBoxY1); + map['bounding_box_x2'] = Variable(boundingBoxX2); + map['bounding_box_y2'] = Variable(boundingBoxY2); + map['source_type'] = Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + AssetFaceEntityData copyWith({ + String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value personId; + final Value imageWidth; + final Value imageHeight; + final Value boundingBoxX1; + final Value boundingBoxY1; + final Value boundingBoxX2; + final Value boundingBoxY2; + final Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.personId = const Value.absent(), + this.imageWidth = const Value.absent(), + this.imageHeight = const Value.absent(), + this.boundingBoxX1 = const Value.absent(), + this.boundingBoxY1 = const Value.absent(), + this.boundingBoxX2 = const Value.absent(), + this.boundingBoxY2 = const Value.absent(), + this.sourceType = const Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? personId, + Expression? imageWidth, + Expression? imageHeight, + Expression? boundingBoxX1, + Expression? boundingBoxY1, + Expression? boundingBoxX2, + Expression? boundingBoxY2, + Expression? sourceType, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + }) { + return AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} + +class StoreEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StoreEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stringValue = GeneratedColumn( + 'string_value', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn intValue = GeneratedColumn( + 'int_value', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + @override + List get $columns => [id, stringValue, intValue]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'store_entity'; + @override + Set get $primaryKey => {id}; + @override + StoreEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StoreEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + stringValue: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}string_value'], + ), + intValue: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}int_value'], + ), + ); + } + + @override + StoreEntity createAlias(String alias) { + return StoreEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StoreEntityData extends DataClass implements Insertable { + final int id; + final String? stringValue; + final int? intValue; + const StoreEntityData({required this.id, this.stringValue, this.intValue}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + if (!nullToAbsent || stringValue != null) { + map['string_value'] = Variable(stringValue); + } + if (!nullToAbsent || intValue != null) { + map['int_value'] = Variable(intValue); + } + return map; + } + + factory StoreEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StoreEntityData( + id: serializer.fromJson(json['id']), + stringValue: serializer.fromJson(json['stringValue']), + intValue: serializer.fromJson(json['intValue']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'stringValue': serializer.toJson(stringValue), + 'intValue': serializer.toJson(intValue), + }; + } + + StoreEntityData copyWith({ + int? id, + Value stringValue = const Value.absent(), + Value intValue = const Value.absent(), + }) => StoreEntityData( + id: id ?? this.id, + stringValue: stringValue.present ? stringValue.value : this.stringValue, + intValue: intValue.present ? intValue.value : this.intValue, + ); + StoreEntityData copyWithCompanion(StoreEntityCompanion data) { + return StoreEntityData( + id: data.id.present ? data.id.value : this.id, + stringValue: data.stringValue.present + ? data.stringValue.value + : this.stringValue, + intValue: data.intValue.present ? data.intValue.value : this.intValue, + ); + } + + @override + String toString() { + return (StringBuffer('StoreEntityData(') + ..write('id: $id, ') + ..write('stringValue: $stringValue, ') + ..write('intValue: $intValue') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, stringValue, intValue); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StoreEntityData && + other.id == this.id && + other.stringValue == this.stringValue && + other.intValue == this.intValue); +} + +class StoreEntityCompanion extends UpdateCompanion { + final Value id; + final Value stringValue; + final Value intValue; + const StoreEntityCompanion({ + this.id = const Value.absent(), + this.stringValue = const Value.absent(), + this.intValue = const Value.absent(), + }); + StoreEntityCompanion.insert({ + required int id, + this.stringValue = const Value.absent(), + this.intValue = const Value.absent(), + }) : id = Value(id); + static Insertable custom({ + Expression? id, + Expression? stringValue, + Expression? intValue, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (stringValue != null) 'string_value': stringValue, + if (intValue != null) 'int_value': intValue, + }); + } + + StoreEntityCompanion copyWith({ + Value? id, + Value? stringValue, + Value? intValue, + }) { + return StoreEntityCompanion( + id: id ?? this.id, + stringValue: stringValue ?? this.stringValue, + intValue: intValue ?? this.intValue, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (stringValue.present) { + map['string_value'] = Variable(stringValue.value); + } + if (intValue.present) { + map['int_value'] = Variable(intValue.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StoreEntityCompanion(') + ..write('id: $id, ') + ..write('stringValue: $stringValue, ') + ..write('intValue: $intValue') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV12 extends GeneratedDatabase { + DatabaseAtV12(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index idxRemoteAssetOwnerChecksum = Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + late final Index uQRemoteAssetsOwnerChecksum = Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + late final Index uQRemoteAssetsOwnerLibraryChecksum = Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final AuthUserEntity authUserEntity = AuthUserEntity(this); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); + late final StoreEntity storeEntity = StoreEntity(this); + late final Index idxLatLng = Index( + 'idx_lat_lng', + 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', + ); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + remoteAlbumEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + authUserEntity, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + storeEntity, + idxLatLng, + ]; + @override + int get schemaVersion => 12; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} diff --git a/mobile/test/utils/action_button_utils_test.dart b/mobile/test/utils/action_button_utils_test.dart index f8c51173d7..274176ae88 100644 --- a/mobile/test/utils/action_button_utils_test.dart +++ b/mobile/test/utils/action_button_utils_test.dart @@ -82,6 +82,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -112,6 +113,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -127,6 +129,7 @@ void main() { isInLockedView: true, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -145,6 +148,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -161,6 +165,7 @@ void main() { isInLockedView: true, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -177,6 +182,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -195,6 +201,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -211,6 +218,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -227,6 +235,7 @@ void main() { isInLockedView: true, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -243,6 +252,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -259,6 +269,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -277,6 +288,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -293,6 +305,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -309,6 +322,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -327,6 +341,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -343,6 +358,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -359,6 +375,7 @@ void main() { isInLockedView: true, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -377,6 +394,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -393,6 +411,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -411,6 +430,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -427,6 +447,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -445,6 +466,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -463,6 +485,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -481,6 +504,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -497,6 +521,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -512,6 +537,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -530,6 +556,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -548,6 +575,7 @@ void main() { isInLockedView: false, currentAlbum: album, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -563,6 +591,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -581,6 +610,7 @@ void main() { isInLockedView: false, currentAlbum: album, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -597,6 +627,7 @@ void main() { isInLockedView: false, currentAlbum: album, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -613,6 +644,7 @@ void main() { isInLockedView: false, currentAlbum: album, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -628,6 +660,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -645,6 +678,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: true, + isStacked: false, source: ActionSource.timeline, ); @@ -660,6 +694,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -668,6 +703,59 @@ void main() { }); }); + group('unstack button', () { + test('should show when owner, not locked, has remote, and is stacked', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: true, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.unstack.shouldShow(context), isTrue); + }); + + test('should not show when not stacked', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.unstack.shouldShow(context), isFalse); + }); + + test('should not show when not owner', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: false, + isArchived: true, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.unstack.shouldShow(context), isFalse); + }); + }); + group('ActionButtonType.buildButton', () { late BaseAsset asset; late ActionButtonContext context; @@ -682,6 +770,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); }); @@ -698,6 +787,22 @@ void main() { isInLockedView: false, currentAlbum: album, advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + final widget = buttonType.buildButton(contextWithAlbum); + expect(widget, isA()); + } else if (buttonType == ActionButtonType.unstack) { + final album = createRemoteAlbum(); + final contextWithAlbum = ActionButtonContext( + asset: asset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: album, + advancedTroubleshooting: false, + isStacked: true, source: ActionSource.timeline, ); final widget = buttonType.buildButton(contextWithAlbum); @@ -721,6 +826,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -741,6 +847,7 @@ void main() { isInLockedView: false, currentAlbum: album, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -759,6 +866,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -778,6 +886,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); @@ -791,6 +900,7 @@ void main() { isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, + isStacked: false, source: ActionSource.timeline, ); diff --git a/open-api/bin/generate-open-api.sh b/open-api/bin/generate-open-api.sh index 1ce33b96e6..43292089d7 100755 --- a/open-api/bin/generate-open-api.sh +++ b/open-api/bin/generate-open-api.sh @@ -15,7 +15,7 @@ function dart { patch --no-backup-if-mismatch -u api.mustache =10.0.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7032720c81..269b246de9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,7 +7,7 @@ settings: overrides: canvas: 2.11.2 - sharp: ^0.34.3 + sharp: ^0.34.4 packageExtensionsChecksum: sha256-DAYr0FTkvKYnvBH4muAER9UE1FVGKhqfRU4/QwA2xPQ= @@ -43,7 +43,7 @@ importers: devDependencies: '@eslint/js': specifier: ^9.8.0 - version: 9.35.0 + version: 9.37.0 '@immich/sdk': specifier: file:../open-api/typescript-sdk version: link:../open-api/typescript-sdk @@ -63,11 +63,11 @@ importers: specifier: ^4.13.1 version: 4.13.4 '@types/node': - specifier: ^22.18.1 - version: 22.18.5 + specifier: ^22.18.8 + version: 22.18.10 '@vitest/coverage-v8': specifier: ^3.0.0 - version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) byte-size: specifier: ^9.0.0 version: 9.0.1 @@ -79,16 +79,16 @@ importers: version: 12.1.0 eslint: specifier: ^9.14.0 - version: 9.35.0(jiti@2.5.1) + version: 9.37.0(jiti@2.6.1) eslint-config-prettier: specifier: ^10.1.8 - version: 10.1.8(eslint@9.35.0(jiti@2.5.1)) + version: 10.1.8(eslint@9.37.0(jiti@2.6.1)) eslint-plugin-prettier: specifier: ^5.1.3 - version: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.35.0(jiti@2.5.1)))(eslint@9.35.0(jiti@2.5.1))(prettier@3.6.2) + version: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1))(prettier@3.6.2) eslint-plugin-unicorn: specifier: ^60.0.0 - version: 60.0.0(eslint@9.35.0(jiti@2.5.1)) + version: 60.0.0(eslint@9.37.0(jiti@2.6.1)) globals: specifier: ^16.0.0 version: 16.4.0 @@ -100,25 +100,25 @@ importers: version: 3.6.2 prettier-plugin-organize-imports: specifier: ^4.0.0 - version: 4.2.0(prettier@3.6.2)(typescript@5.9.2) + version: 4.3.0(prettier@3.6.2)(typescript@5.9.3) typescript: specifier: ^5.3.3 - version: 5.9.2 + version: 5.9.3 typescript-eslint: specifier: ^8.28.0 - version: 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + version: 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) vite: specifier: ^7.0.0 - version: 7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + version: 7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite-tsconfig-paths: specifier: ^5.0.0 - version: 5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 5.1.4(typescript@5.9.3)(vite@7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) vitest: specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vitest-fetch-mock: specifier: ^0.4.0 - version: 0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) yaml: specifier: ^2.3.1 version: 2.8.1 @@ -126,14 +126,14 @@ importers: docs: dependencies: '@docusaurus/core': - specifier: ~3.8.0 - version: 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + specifier: ~3.9.0 + version: 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) '@docusaurus/preset-classic': - specifier: ~3.8.0 - version: 3.8.1(@algolia/client-search@5.29.0)(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(@types/react@19.1.13)(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3)(typescript@5.9.2) + specifier: ~3.9.0 + version: 3.9.1(@algolia/client-search@5.40.0)(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3)(typescript@5.9.3) '@docusaurus/theme-common': - specifier: ~3.8.0 - version: 3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ~3.9.0 + version: 3.9.1(@docusaurus/plugin-content-docs@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mdi/js': specifier: ^7.3.67 version: 7.4.47 @@ -142,16 +142,13 @@ importers: version: 1.6.1 '@mdx-js/react': specifier: ^3.0.0 - version: 3.1.1(@types/react@19.1.13)(react@18.3.1) + version: 3.1.1(@types/react@19.2.2)(react@18.3.1) autoprefixer: specifier: ^10.4.17 version: 10.4.21(postcss@8.5.6) docusaurus-lunr-search: specifier: ^3.3.2 - version: 3.6.0(@docusaurus/core@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - docusaurus-preset-openapi: - specifier: ^0.7.5 - version: 0.7.6(@algolia/client-search@5.29.0)(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(@types/react@19.1.13)(acorn@8.15.0)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(redux@4.2.1)(search-insights@2.17.3)(typescript@5.9.2) + version: 3.6.0(@docusaurus/core@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) lunr: specifier: ^2.3.9 version: 2.3.9 @@ -163,7 +160,7 @@ importers: version: 2.4.1(react@18.3.1) raw-loader: specifier: ^4.0.2 - version: 4.0.2(webpack@5.100.2) + version: 4.0.2(webpack@5.102.1) react: specifier: ^18.0.0 version: 18.3.1 @@ -172,32 +169,32 @@ importers: version: 18.3.1(react@18.3.1) tailwindcss: specifier: ^3.2.4 - version: 3.4.17 + version: 3.4.18(yaml@2.8.1) url: specifier: ^0.11.0 version: 0.11.4 devDependencies: '@docusaurus/module-type-aliases': - specifier: ~3.8.0 - version: 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ~3.9.0 + version: 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@docusaurus/tsconfig': specifier: ^3.7.0 - version: 3.8.1 + version: 3.9.1 '@docusaurus/types': specifier: ^3.7.0 - version: 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) prettier: specifier: ^3.2.4 version: 3.6.2 typescript: specifier: ^5.1.6 - version: 5.9.2 + version: 5.9.3 e2e: devDependencies: '@eslint/js': specifier: ^9.8.0 - version: 9.35.0 + version: 9.37.0 '@immich/cli': specifier: file:../cli version: link:../cli @@ -206,7 +203,7 @@ importers: version: link:../open-api/typescript-sdk '@playwright/test': specifier: ^1.44.1 - version: 1.55.0 + version: 1.56.0 '@socket.io/component-emitter': specifier: ^3.1.2 version: 3.1.2 @@ -214,8 +211,8 @@ importers: specifier: ^3.4.2 version: 3.7.1 '@types/node': - specifier: ^22.18.1 - version: 22.18.5 + specifier: ^22.18.8 + version: 22.18.10 '@types/oidc-provider': specifier: ^9.0.0 version: 9.5.0 @@ -230,16 +227,16 @@ importers: version: 6.0.3 eslint: specifier: ^9.14.0 - version: 9.35.0(jiti@2.5.1) + version: 9.37.0(jiti@2.6.1) eslint-config-prettier: specifier: ^10.1.8 - version: 10.1.8(eslint@9.35.0(jiti@2.5.1)) + version: 10.1.8(eslint@9.37.0(jiti@2.6.1)) eslint-plugin-prettier: specifier: ^5.1.3 - version: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.35.0(jiti@2.5.1)))(eslint@9.35.0(jiti@2.5.1))(prettier@3.6.2) + version: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1))(prettier@3.6.2) eslint-plugin-unicorn: specifier: ^60.0.0 - version: 60.0.0(eslint@9.35.0(jiti@2.5.1)) + version: 60.0.0(eslint@9.37.0(jiti@2.6.1)) exiftool-vendored: specifier: ^28.3.1 version: 28.8.0 @@ -266,10 +263,10 @@ importers: version: 3.6.2 prettier-plugin-organize-imports: specifier: ^4.0.0 - version: 4.2.0(prettier@3.6.2)(typescript@5.9.2) + version: 4.3.0(prettier@3.6.2)(typescript@5.9.3) sharp: - specifier: ^0.34.3 - version: 0.34.3 + specifier: ^0.34.4 + version: 0.34.4 socket.io-client: specifier: ^4.7.4 version: 4.8.1 @@ -278,16 +275,16 @@ importers: version: 7.1.4 typescript: specifier: ^5.3.3 - version: 5.9.2 + version: 5.9.3 typescript-eslint: specifier: ^8.28.0 - version: 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + version: 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) utimes: specifier: ^5.2.1 version: 5.2.1(encoding@0.1.13) vitest: specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) open-api/typescript-sdk: dependencies: @@ -296,17 +293,17 @@ importers: version: 1.0.4 devDependencies: '@types/node': - specifier: ^22.18.1 - version: 22.18.5 + specifier: ^22.18.8 + version: 22.18.10 typescript: specifier: ^5.3.3 - version: 5.9.2 + version: 5.9.3 server: dependencies: '@nestjs/bullmq': specifier: ^11.0.1 - version: 11.0.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(bullmq@5.58.5) + version: 11.0.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(bullmq@5.61.0) '@nestjs/common': specifier: ^11.0.4 version: 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -321,7 +318,7 @@ importers: version: 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.6)(rxjs@7.8.2) '@nestjs/schedule': specifier: ^6.0.0 - version: 6.0.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6) + version: 6.0.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6) '@nestjs/swagger': specifier: ^11.0.2 version: 11.2.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2) @@ -335,20 +332,20 @@ importers: specifier: ^2.0.0 version: 2.1.0(@opentelemetry/api@1.9.0) '@opentelemetry/exporter-prometheus': - specifier: ^0.205.0 - version: 0.205.0(@opentelemetry/api@1.9.0) + specifier: ^0.206.0 + version: 0.206.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-http': - specifier: ^0.205.0 - version: 0.205.0(@opentelemetry/api@1.9.0) + specifier: ^0.206.0 + version: 0.206.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-ioredis': + specifier: ^0.54.0 + version: 0.54.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-nestjs-core': specifier: ^0.53.0 version: 0.53.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-nestjs-core': - specifier: ^0.51.0 - version: 0.51.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-pg': - specifier: ^0.58.0 - version: 0.58.0(@opentelemetry/api@1.9.0) + specifier: ^0.59.0 + version: 0.59.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': specifier: ^2.0.1 version: 2.1.0(@opentelemetry/api@1.9.0) @@ -356,17 +353,17 @@ importers: specifier: ^2.0.1 version: 2.1.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-node': - specifier: ^0.205.0 - version: 0.205.0(@opentelemetry/api@1.9.0) + specifier: ^0.206.0 + version: 0.206.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': specifier: ^1.34.0 version: 1.37.0 '@react-email/components': specifier: ^0.5.0 - version: 0.5.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 0.5.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@react-email/render': specifier: ^1.1.2 - version: 1.2.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 1.3.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@socket.io/redis-adapter': specifier: ^8.3.0 version: 8.3.0(socket.io-adapter@2.5.5) @@ -384,7 +381,7 @@ importers: version: 2.2.0 bullmq: specifier: ^5.51.0 - version: 5.58.5 + version: 5.61.0 chokidar: specifier: ^4.0.3 version: 4.0.3 @@ -404,8 +401,8 @@ importers: specifier: ^1.4.7 version: 1.4.7 cron: - specifier: 4.3.0 - version: 4.3.0 + specifier: 4.3.3 + version: 4.3.3 exiftool-vendored: specifier: ^28.8.0 version: 28.8.0 @@ -429,7 +426,7 @@ importers: version: 7.14.0 ioredis: specifier: ^5.3.2 - version: 5.7.0 + version: 5.8.1 js-yaml: specifier: ^4.1.0 version: 4.1.0 @@ -437,8 +434,8 @@ importers: specifier: 0.28.2 version: 0.28.2 kysely-postgres-js: - specifier: ^2.0.0 - version: 2.0.0(kysely@0.28.2)(postgres@3.4.7) + specifier: ^3.0.0 + version: 3.0.0(kysely@0.28.2)(postgres@3.4.7) lodash: specifier: ^4.17.21 version: 4.17.21 @@ -453,22 +450,22 @@ importers: version: 2.0.2 nest-commander: specifier: ^3.16.0 - version: 3.19.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@types/inquirer@8.2.11)(@types/node@22.18.5)(typescript@5.9.2) + version: 3.20.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@types/inquirer@8.2.11)(@types/node@22.18.10)(typescript@5.9.3) nestjs-cls: specifier: ^5.0.0 version: 5.4.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) nestjs-kysely: - specifier: ^3.0.0 + specifier: 3.0.0 version: 3.0.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(kysely@0.28.2)(reflect-metadata@0.2.2) nestjs-otel: specifier: ^7.0.0 version: 7.0.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6) nodemailer: specifier: ^7.0.0 - version: 7.0.6 + version: 7.0.9 openid-client: specifier: ^6.3.3 - version: 6.8.0 + version: 6.8.1 pg: specifier: ^8.11.3 version: 8.16.3 @@ -483,13 +480,13 @@ importers: version: 3.4.7 react: specifier: ^19.0.0 - version: 19.1.1 + version: 19.2.0 react-dom: specifier: ^19.0.0 - version: 19.1.1(react@19.1.1) + version: 19.2.0(react@19.2.0) react-email: specifier: ^4.0.0 - version: 4.2.11 + version: 4.3.0 reflect-metadata: specifier: ^0.2.0 version: 0.2.2 @@ -504,10 +501,10 @@ importers: version: 2.17.0 semver: specifier: ^7.6.2 - version: 7.7.2 + version: 7.7.3 sharp: - specifier: ^0.34.3 - version: 0.34.3 + specifier: ^0.34.4 + version: 0.34.4 sirv: specifier: ^3.0.0 version: 3.0.2 @@ -516,7 +513,7 @@ importers: version: 4.8.1 tailwindcss-preset-email: specifier: ^1.4.0 - version: 1.4.0(tailwindcss@3.4.17) + version: 1.4.0(tailwindcss@3.4.18(yaml@2.8.1)) thumbhash: specifier: ^0.1.1 version: 0.1.1 @@ -532,13 +529,13 @@ importers: devDependencies: '@eslint/js': specifier: ^9.8.0 - version: 9.35.0 + version: 9.37.0 '@nestjs/cli': specifier: ^11.0.2 - version: 11.0.10(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.18.5) + version: 11.0.10(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.18.10) '@nestjs/schematics': specifier: ^11.0.0 - version: 11.0.7(chokidar@4.0.3)(typescript@5.9.2) + version: 11.0.8(chokidar@4.0.3)(typescript@5.9.3) '@nestjs/testing': specifier: ^11.0.4 version: 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@nestjs/platform-express@11.1.6) @@ -585,11 +582,11 @@ importers: specifier: ^2.0.0 version: 2.0.0 '@types/node': - specifier: ^22.18.1 - version: 22.18.5 + specifier: ^22.18.8 + version: 22.18.10 '@types/nodemailer': specifier: ^7.0.0 - version: 7.0.1 + version: 7.0.2 '@types/picomatch': specifier: ^4.0.0 version: 4.0.2 @@ -598,7 +595,7 @@ importers: version: 6.0.5 '@types/react': specifier: ^19.0.0 - version: 19.1.13 + version: 19.2.2 '@types/sanitize-html': specifier: ^2.13.0 version: 2.16.0 @@ -616,19 +613,19 @@ importers: version: 13.15.3 '@vitest/coverage-v8': specifier: ^3.0.0 - version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) eslint: specifier: ^9.14.0 - version: 9.35.0(jiti@2.5.1) + version: 9.37.0(jiti@2.6.1) eslint-config-prettier: specifier: ^10.1.8 - version: 10.1.8(eslint@9.35.0(jiti@2.5.1)) + version: 10.1.8(eslint@9.37.0(jiti@2.6.1)) eslint-plugin-prettier: specifier: ^5.1.3 - version: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.35.0(jiti@2.5.1)))(eslint@9.35.0(jiti@2.5.1))(prettier@3.6.2) + version: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1))(prettier@3.6.2) eslint-plugin-unicorn: specifier: ^60.0.0 - version: 60.0.0(eslint@9.35.0(jiti@2.5.1)) + version: 60.0.0(eslint@9.37.0(jiti@2.6.1)) globals: specifier: ^16.0.0 version: 16.4.0 @@ -646,46 +643,46 @@ importers: version: 3.6.2 prettier-plugin-organize-imports: specifier: ^4.0.0 - version: 4.2.0(prettier@3.6.2)(typescript@5.9.2) + version: 4.3.0(prettier@3.6.2)(typescript@5.9.3) sql-formatter: specifier: ^15.0.0 - version: 15.6.9 + version: 15.6.10 supertest: specifier: ^7.1.0 version: 7.1.4 tailwindcss: specifier: ^3.4.0 - version: 3.4.17 + version: 3.4.18(yaml@2.8.1) testcontainers: specifier: ^11.0.0 - version: 11.5.1 + version: 11.7.1 typescript: specifier: ^5.9.2 - version: 5.9.2 + version: 5.9.3 typescript-eslint: specifier: ^8.28.0 - version: 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + version: 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) unplugin-swc: specifier: ^1.4.5 - version: 1.5.7(@swc/core@1.13.5(@swc/helpers@0.5.17))(rollup@4.50.1) + version: 1.5.7(@swc/core@1.13.5(@swc/helpers@0.5.17))(rollup@4.52.4) vite-tsconfig-paths: specifier: ^5.0.0 - version: 5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 5.1.4(typescript@5.9.3)(vite@7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) vitest: specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) web: dependencies: '@formatjs/icu-messageformat-parser': specifier: ^2.9.8 - version: 2.11.2 + version: 2.11.3 '@immich/sdk': specifier: file:../open-api/typescript-sdk version: link:../open-api/typescript-sdk '@immich/ui': - specifier: ^0.29.0 - version: 0.29.0(@internationalized/date@3.8.2)(svelte@5.38.10) + specifier: ^0.34.0 + version: 0.34.2(@internationalized/date@3.8.2)(svelte@5.39.11) '@mapbox/mapbox-gl-rtl-text': specifier: 0.2.3 version: 0.2.3(mapbox-gl@1.13.3) @@ -712,10 +709,10 @@ importers: version: 7946.0.16 '@zoom-image/core': specifier: ^0.41.0 - version: 0.41.0 + version: 0.41.3 '@zoom-image/svelte': specifier: ^0.3.0 - version: 0.3.4(svelte@5.38.10) + version: 0.3.7(svelte@5.39.11) async-mutex: specifier: ^0.5.0 version: 0.5.0 @@ -735,11 +732,11 @@ importers: specifier: ^4.7.8 version: 4.7.8 happy-dom: - specifier: ^18.0.1 - version: 18.0.1 + specifier: ^20.0.0 + version: 20.0.2 intl-messageformat: specifier: ^10.7.11 - version: 10.7.16 + version: 10.7.17 justified-layout: specifier: ^4.1.0 version: 4.1.0 @@ -751,7 +748,7 @@ importers: version: 3.7.2 maplibre-gl: specifier: ^5.6.2 - version: 5.7.1 + version: 5.8.0 pmtiles: specifier: ^4.3.0 version: 4.3.0 @@ -760,7 +757,7 @@ importers: version: 1.5.4 simple-icons: specifier: ^15.15.0 - version: 15.15.0 + version: 15.16.1 socket.io-client: specifier: ~4.8.0 version: 4.8.1 @@ -769,13 +766,13 @@ importers: version: 5.2.2 svelte-i18n: specifier: ^4.0.1 - version: 4.0.1(svelte@5.38.10) + version: 4.0.1(svelte@5.39.11) svelte-maplibre: specifier: ^1.2.0 - version: 1.2.1(svelte@5.38.10) + version: 1.2.1(svelte@5.39.11) svelte-persisted-store: specifier: ^0.12.0 - version: 0.12.0(svelte@5.38.10) + version: 0.12.0(svelte@5.39.11) tabbable: specifier: ^6.2.0 version: 6.2.0 @@ -784,38 +781,38 @@ importers: version: 0.1.1 devDependencies: '@eslint/js': - specifier: ^9.18.0 - version: 9.35.0 + specifier: ^9.36.0 + version: 9.37.0 '@faker-js/faker': specifier: ^10.0.0 version: 10.0.0 '@koddsson/eslint-plugin-tscompat': specifier: ^0.2.0 - version: 0.2.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + version: 0.2.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) '@socket.io/component-emitter': specifier: ^3.1.0 version: 3.1.2 '@sveltejs/adapter-static': specifier: ^3.0.8 - version: 3.0.9(@sveltejs/kit@2.38.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))) + version: 3.0.10(@sveltejs/kit@2.46.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))) '@sveltejs/enhanced-img': specifier: ^0.8.0 - version: 0.8.1(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.50.1)(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 0.8.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.52.4)(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@sveltejs/kit': specifier: ^2.27.1 - version: 2.38.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 2.46.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@sveltejs/vite-plugin-svelte': - specifier: 6.2.0 - version: 6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + specifier: 6.2.1 + version: 6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@tailwindcss/vite': specifier: ^4.1.7 - version: 4.1.13(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 4.1.14(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@testing-library/jest-dom': specifier: ^6.4.2 - version: 6.8.0 + version: 6.9.1 '@testing-library/svelte': specifier: ^5.2.8 - version: 5.2.8(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.5.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 5.2.8(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.7.2)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@testing-library/user-event': specifier: ^14.5.2 version: 14.6.1(@testing-library/dom@10.4.0) @@ -839,28 +836,25 @@ importers: version: 1.5.5 '@vitest/coverage-v8': specifier: ^3.0.0 - version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.5.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.7.2)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) dotenv: specifier: ^17.0.0 - version: 17.2.2 + version: 17.2.3 eslint: - specifier: ^9.18.0 - version: 9.35.0(jiti@2.5.1) + specifier: ^9.36.0 + version: 9.37.0(jiti@2.6.1) eslint-config-prettier: specifier: ^10.1.8 - version: 10.1.8(eslint@9.35.0(jiti@2.5.1)) - eslint-p: - specifier: ^0.26.0 - version: 0.26.0(jiti@2.5.1) + version: 10.1.8(eslint@9.37.0(jiti@2.6.1)) eslint-plugin-compat: specifier: ^6.0.2 - version: 6.0.2(eslint@9.35.0(jiti@2.5.1)) + version: 6.0.2(eslint@9.37.0(jiti@2.6.1)) eslint-plugin-svelte: - specifier: ^3.9.0 - version: 3.12.3(eslint@9.35.0(jiti@2.5.1))(svelte@5.38.10) + specifier: ^3.12.4 + version: 3.12.4(eslint@9.37.0(jiti@2.6.1))(svelte@5.39.11) eslint-plugin-unicorn: - specifier: ^60.0.0 - version: 60.0.0(eslint@9.35.0(jiti@2.5.1)) + specifier: ^61.0.2 + version: 61.0.2(eslint@9.37.0(jiti@2.6.1)) factory.ts: specifier: ^1.4.1 version: 1.4.2 @@ -872,46 +866,50 @@ importers: version: 3.6.2 prettier-plugin-organize-imports: specifier: ^4.0.0 - version: 4.2.0(prettier@3.6.2)(typescript@5.9.2) + version: 4.3.0(prettier@3.6.2)(typescript@5.9.3) prettier-plugin-sort-json: specifier: ^4.1.1 version: 4.1.1(prettier@3.6.2) prettier-plugin-svelte: specifier: ^3.3.3 - version: 3.4.0(prettier@3.6.2)(svelte@5.38.10) + version: 3.4.0(prettier@3.6.2)(svelte@5.39.11) rollup-plugin-visualizer: specifier: ^6.0.0 - version: 6.0.3(rollup@4.50.1) + version: 6.0.4(rollup@4.52.4) svelte: - specifier: 5.38.10 - version: 5.38.10 + specifier: 5.39.11 + version: 5.39.11 svelte-check: specifier: ^4.1.5 - version: 4.3.1(picomatch@4.0.3)(svelte@5.38.10)(typescript@5.9.2) + version: 4.3.3(picomatch@4.0.3)(svelte@5.39.11)(typescript@5.9.3) svelte-eslint-parser: - specifier: ^1.2.0 - version: 1.3.2(svelte@5.38.10) + specifier: ^1.3.3 + version: 1.3.3(svelte@5.39.11) tailwindcss: specifier: ^4.1.7 - version: 4.1.13 + version: 4.1.14 typescript: specifier: ^5.8.3 - version: 5.9.2 + version: 5.9.3 typescript-eslint: - specifier: ^8.28.0 - version: 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + specifier: ^8.45.0 + version: 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) vite: specifier: ^7.1.2 - version: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + version: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vitest: specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.5.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.7.2)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) packages: '@adobe/css-tools@4.4.4': resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} + '@algolia/abtesting@1.6.0': + resolution: {integrity: sha512-c4M/Z/KWkEG+RHpZsWKDTTlApXu3fe4vlABNcpankWBhdMe4oPZ/r4JxEr2zKUP6K+BT66tnp8UbHmgOd/vvqQ==} + engines: {node: '>= 14.0.0'} + '@algolia/autocomplete-core@1.17.9': resolution: {integrity: sha512-O7BxrpLDPJWWHv/DLA9DRFWs+iY1uOJZkqUwjS5HSZAGcl0hIVCQ97LTLewiZmZ402JYUrun+8NqFP+hCknlbQ==} @@ -932,59 +930,59 @@ packages: '@algolia/client-search': '>= 4.9.1 < 6' algoliasearch: '>= 4.9.1 < 6' - '@algolia/client-abtesting@5.29.0': - resolution: {integrity: sha512-AM/6LYMSTnZvAT5IarLEKjYWOdV+Fb+LVs8JRq88jn8HH6bpVUtjWdOZXqX1hJRXuCAY8SdQfb7F8uEiMNXdYQ==} + '@algolia/client-abtesting@5.40.0': + resolution: {integrity: sha512-qegVlgHtmiS8m9nEsuKUVhlw1FHsIshtt5nhNnA6EYz3g+tm9+xkVZZMzkrMLPP7kpoheHJZAwz2MYnHtwFa9A==} engines: {node: '>= 14.0.0'} - '@algolia/client-analytics@5.29.0': - resolution: {integrity: sha512-La34HJh90l0waw3wl5zETO8TuukeUyjcXhmjYZL3CAPLggmKv74mobiGRIb+mmBENybiFDXf/BeKFLhuDYWMMQ==} + '@algolia/client-analytics@5.40.0': + resolution: {integrity: sha512-Dw2c+6KGkw7mucnnxPyyMsIGEY8+hqv6oB+viYB612OMM3l8aNaWToBZMnNvXsyP+fArwq7XGR+k3boPZyV53A==} engines: {node: '>= 14.0.0'} - '@algolia/client-common@5.29.0': - resolution: {integrity: sha512-T0lzJH/JiCxQYtCcnWy7Jf1w/qjGDXTi2npyF9B9UsTvXB97GRC6icyfXxe21mhYvhQcaB1EQ/J2575FXxi2rA==} + '@algolia/client-common@5.40.0': + resolution: {integrity: sha512-dbE4+MJIDsTghG3hUYWBq7THhaAmqNqvW9g2vzwPf5edU4IRmuYpKtY3MMotes8/wdTasWG07XoaVhplJBlvdg==} engines: {node: '>= 14.0.0'} - '@algolia/client-insights@5.29.0': - resolution: {integrity: sha512-A39F1zmHY9aev0z4Rt3fTLcGN5AG1VsVUkVWy6yQG5BRDScktH+U5m3zXwThwniBTDV1HrPgiGHZeWb67GkR2Q==} + '@algolia/client-insights@5.40.0': + resolution: {integrity: sha512-SH6zlROyGUCDDWg71DlCnbbZ/zEHYPZC8k901EAaBVhvY43Ju8Wa6LAcMPC4tahcDBgkG2poBy8nJZXvwEWAlQ==} engines: {node: '>= 14.0.0'} - '@algolia/client-personalization@5.29.0': - resolution: {integrity: sha512-ibxmh2wKKrzu5du02gp8CLpRMeo+b/75e4ORct98CT7mIxuYFXowULwCd6cMMkz/R0LpKXIbTUl15UL5soaiUQ==} + '@algolia/client-personalization@5.40.0': + resolution: {integrity: sha512-EgHjJEEf7CbUL9gJHI1ULmAtAFeym2cFNSAi1uwHelWgLPcnLjYW2opruPxigOV7NcetkGu+t2pcWOWmZFuvKQ==} engines: {node: '>= 14.0.0'} - '@algolia/client-query-suggestions@5.29.0': - resolution: {integrity: sha512-VZq4/AukOoJC2WSwF6J5sBtt+kImOoBwQc1nH3tgI+cxJBg7B77UsNC+jT6eP2dQCwGKBBRTmtPLUTDDnHpMgA==} + '@algolia/client-query-suggestions@5.40.0': + resolution: {integrity: sha512-HvE1jtCag95DR41tDh7cGwrMk4X0aQXPOBIhZRmsBPolMeqRJz0kvfVw8VCKvA1uuoAkjFfTG0X0IZED+rKXoA==} engines: {node: '>= 14.0.0'} - '@algolia/client-search@5.29.0': - resolution: {integrity: sha512-cZ0Iq3OzFUPpgszzDr1G1aJV5UMIZ4VygJ2Az252q4Rdf5cQMhYEIKArWY/oUjMhQmosM8ygOovNq7gvA9CdCg==} + '@algolia/client-search@5.40.0': + resolution: {integrity: sha512-nlr/MMgoLNUHcfWC5Ns2ENrzKx9x51orPc6wJ8Ignv1DsrUmKm0LUih+Tj3J+kxYofzqQIQRU495d4xn3ozMbg==} engines: {node: '>= 14.0.0'} '@algolia/events@4.0.1': resolution: {integrity: sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==} - '@algolia/ingestion@1.29.0': - resolution: {integrity: sha512-scBXn0wO5tZCxmO6evfa7A3bGryfyOI3aoXqSQBj5SRvNYXaUlFWQ/iKI70gRe/82ICwE0ICXbHT/wIvxOW7vw==} + '@algolia/ingestion@1.40.0': + resolution: {integrity: sha512-OfHnhE+P0f+p3i90Kmshf9Epgesw5oPV1IEUOY4Mq1HV7cQk16gvklVN1EaY/T9sVavl+Vc3g4ojlfpIwZFA4g==} engines: {node: '>= 14.0.0'} - '@algolia/monitoring@1.29.0': - resolution: {integrity: sha512-FGWWG9jLFhsKB7YiDjM2dwQOYnWu//7Oxrb2vT96N7+s+hg1mdHHfHNRyEudWdxd4jkMhBjeqNA21VbTiOIPVg==} + '@algolia/monitoring@1.40.0': + resolution: {integrity: sha512-SWANV32PTKhBYvwKozeWP9HOnVabOixAuPdFFGoqtysTkkwutrtGI/rrh80tvG+BnQAmZX0vUmD/RqFZVfr/Yg==} engines: {node: '>= 14.0.0'} - '@algolia/recommend@5.29.0': - resolution: {integrity: sha512-xte5+mpdfEARAu61KXa4ewpjchoZuJlAlvQb8ptK6hgHlBHDnYooy1bmOFpokaAICrq/H9HpoqNUX71n+3249A==} + '@algolia/recommend@5.40.0': + resolution: {integrity: sha512-1Qxy9I5bSb3mrhPk809DllMa561zl5hLsMR6YhIqNkqQ0OyXXQokvJ2zApSxvd39veRZZnhN+oGe+XNoNwLgkw==} engines: {node: '>= 14.0.0'} - '@algolia/requester-browser-xhr@5.29.0': - resolution: {integrity: sha512-og+7Em75aPHhahEUScq2HQ3J7ULN63Levtd87BYMpn6Im5d5cNhaC4QAUsXu6LWqxRPgh4G+i+wIb6tVhDhg2A==} + '@algolia/requester-browser-xhr@5.40.0': + resolution: {integrity: sha512-MGt94rdHfkrVjfN/KwUfWcnaeohYbWGINrPs96f5J7ZyRYpVLF+VtPQ2FmcddFvK4gnKXSu8BAi81hiIhUpm3w==} engines: {node: '>= 14.0.0'} - '@algolia/requester-fetch@5.29.0': - resolution: {integrity: sha512-JCxapz7neAy8hT/nQpCvOrI5JO8VyQ1kPvBiaXWNC1prVq0UMYHEL52o1BsPvtXfdQ7BVq19OIq6TjOI06mV/w==} + '@algolia/requester-fetch@5.40.0': + resolution: {integrity: sha512-wXQ05JZZ10Dr642QVAkAZ4ZZlU+lh5r6dIBGmm9WElz+1EaQ6BNYtEOTV6pkXuFYsZpeJA89JpDOiwBOP9j24w==} engines: {node: '>= 14.0.0'} - '@algolia/requester-node-http@5.29.0': - resolution: {integrity: sha512-lVBD81RBW5VTdEYgnzCz7Pf9j2H44aymCP+/eHGJu4vhU+1O8aKf3TVBgbQr5UM6xoe8IkR/B112XY6YIG2vtg==} + '@algolia/requester-node-http@5.40.0': + resolution: {integrity: sha512-5qCRoySnzpbQVg2IPLGFCm4LF75pToxI5tdjOYgUMNL/um91aJ4dH3SVdBEuFlVsalxl8mh3bWPgkUmv6NpJiQ==} engines: {node: '>= 14.0.0'} '@alloc/quick-lru@5.2.0': @@ -1004,6 +1002,15 @@ packages: chokidar: optional: true + '@angular-devkit/core@19.2.17': + resolution: {integrity: sha512-Ah008x2RJkd0F+NLKqIpA34/vUGwjlprRCkvddjDopAWRzYn6xCkz1Tqwuhn0nR1Dy47wTLKYD999TYl5ONOAQ==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + chokidar: ^4.0.0 + peerDependenciesMeta: + chokidar: + optional: true + '@angular-devkit/schematics-cli@19.2.15': resolution: {integrity: sha512-1ESFmFGMpGQmalDB3t2EtmWDGv6gOFYBMxmHO2f1KI/UDl8UmZnCGL4mD3EWo8Hv0YIsZ9wOH9Q7ZHNYjeSpzg==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} @@ -1013,6 +1020,10 @@ packages: resolution: {integrity: sha512-kNOJ+3vekJJCQKWihNmxBkarJzNW09kP5a9E1SRNiQVNOUEeSwcRR0qYotM65nx821gNzjjhJXnAZ8OazWldrg==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + '@angular-devkit/schematics@19.2.17': + resolution: {integrity: sha512-ADfbaBsrG8mBF6Mfs+crKA/2ykB8AJI50Cv9tKmZfwcUcyAdmTr+vVvhsBCfvUAEokigSsgqgpYxfkJVxhJYeg==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + '@asamuzakjp/css-color@3.2.0': resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} @@ -1029,103 +1040,103 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-sesv2@3.890.0': - resolution: {integrity: sha512-AM9Lt4QkNet8xKgCwJxfkyqlorwG9S+tvtpSfHYCVq0j2Z6PbkDaUBnvwjGOMBV7Um5IzZ7yhvQXrBg7omZciQ==} + '@aws-sdk/client-sesv2@3.907.0': + resolution: {integrity: sha512-r0NV1gxN3OO5l3gnxdQPS+STQHh+q9CRoEf9EG1273nOb1TwzrOIE34A0LO0Ow88XL5amgx/e9MdfuopJH9j+g==} engines: {node: '>=18.0.0'} - '@aws-sdk/client-sso@3.890.0': - resolution: {integrity: sha512-vefYNwh/K5V5YiJpFJfoMPNqsoiRTqD7ZnkvR0cjJdwhOIwFnSKN1vz0OMjySTQmVMcG4JKGVul82ou7ErtOhQ==} + '@aws-sdk/client-sso@3.907.0': + resolution: {integrity: sha512-ANuu0duNTcQHv0g5YrEuWImT8o9t6li3A+MtAaKxIbTA3eFQnl6xHDxyrbsrU19FtKPg3CWhvfY04j6DaDvR8g==} engines: {node: '>=18.0.0'} - '@aws-sdk/core@3.890.0': - resolution: {integrity: sha512-CT+yjhytHdyKvV3Nh/fqBjnZ8+UiQZVz4NMm4LrPATgVSOdfygXHqrWxrPTVgiBtuJWkotg06DF7+pTd5ekLBw==} + '@aws-sdk/core@3.907.0': + resolution: {integrity: sha512-vuIHL8qUcA5oNi7IWSZauCMaXstWTcSsnK1iHcvg92ddGDo1LMd2kQNo0G9UANa8vOfc908+8xKO40gfL8+M7w==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-env@3.890.0': - resolution: {integrity: sha512-BtsUa2y0Rs8phmB2ScZ5RuPqZVmxJJXjGfeiXctmLFTxTwoayIK1DdNzOWx6SRMPVc3s2RBGN4vO7T1TwN+ajA==} + '@aws-sdk/credential-provider-env@3.907.0': + resolution: {integrity: sha512-orqT6djon57y09Ci5q0kezisrEvr78Z+7WvZbq0ZC0Ncul4RgJfCmhcgmzNPaWA18NEI0wGytaxYh3YFE7kIBQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-http@3.890.0': - resolution: {integrity: sha512-0sru3LVwsuGYyzbD90EC/d5HnCZ9PL4O9BA2LYT6b9XceC005Oj86uzE47LXb+mDhTAt3T6ZO0+ZcVQe0DDi8w==} + '@aws-sdk/credential-provider-http@3.907.0': + resolution: {integrity: sha512-CKG/0hT4o8K2aQKOe+xwGP3keSNOyryhZNmKuHPuMRVlsJfO6wNxlu37HcUPzihJ+S2pOmTVGUbeVMCxJVUJmw==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-ini@3.890.0': - resolution: {integrity: sha512-Mxv7ByftHKH7dE6YXu9gQ6ODXwO1iSO32t8tBrZLS3g8K1knWADIqDFv3yErQtJ8hp27IDxbAbVH/1RQdSkmhA==} + '@aws-sdk/credential-provider-ini@3.907.0': + resolution: {integrity: sha512-Clz1YdXrgQ5WIlcRE7odHbgM/INBxy49EA3csDITafHaDPtPRL39zkQtB5+Lwrrt/Gg0xBlyTbvP5Snan+0lqA==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-node@3.890.0': - resolution: {integrity: sha512-zbPz3mUtaBdch0KoH8/LouRDcYSzyT2ecyCOo5OAFVil7AxT1jvsn4vX78FlnSVpZ4mLuHY8pHTVGi235XiyBA==} + '@aws-sdk/credential-provider-node@3.907.0': + resolution: {integrity: sha512-w6Hhc4rV/CFaBliIh9Ph/T59xdGcTF6WmPGzzpykjl68+jcJyUem82hbTVIGaMCpvhx8VRqEr5AEXCXdbDbojw==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-process@3.890.0': - resolution: {integrity: sha512-dWZ54TI1Q+UerF5YOqGiCzY+x2YfHsSQvkyM3T4QDNTJpb/zjiVv327VbSOULOlI7gHKWY/G3tMz0D9nWI7YbA==} + '@aws-sdk/credential-provider-process@3.907.0': + resolution: {integrity: sha512-MBWpZqZtKkpM/LOGD5quXvlHJJN8YIP4GKo2ad8y1fEEVydwI8cggyXuauMPV7GllW8d0u3kQUs+4rxm1VaS4w==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-sso@3.890.0': - resolution: {integrity: sha512-ajYCZ6f2+98w8zG/IXcQ+NhWYoI5qPUDovw+gMqMWX/jL1cmZ9PFAwj2Vyq9cbjum5RNWwPLArWytTCgJex4AQ==} + '@aws-sdk/credential-provider-sso@3.907.0': + resolution: {integrity: sha512-F8I7xwIt0mhdg8NrC70HDmhDRx3ValBvmWH3YkWsjZltWIFozhQCCDISRPhanMkXVhSFmZY0FJ5Lo+B/SZvAAA==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-web-identity@3.890.0': - resolution: {integrity: sha512-qZ2Mx7BeYR1s0F/H6wePI0MAmkFswmBgrpgMCOt2S4b2IpQPnUa2JbxY3GwW2WqX3nV0KjPW08ctSLMmlq/tKA==} + '@aws-sdk/credential-provider-web-identity@3.907.0': + resolution: {integrity: sha512-1CmRE/M8LJ/joXm5vUsKkQS35MoWA4xvUH9J1jyCuL3J9A8M+bnTe6ER8fnNLgmEs6ikdmYEIdfijPpBjBpFig==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-host-header@3.887.0': - resolution: {integrity: sha512-ulzqXv6NNqdu/kr0sgBYupWmahISHY+azpJidtK6ZwQIC+vBUk9NdZeqQpy7KVhIk2xd4+5Oq9rxapPwPI21CA==} + '@aws-sdk/middleware-host-header@3.901.0': + resolution: {integrity: sha512-yWX7GvRmqBtbNnUW7qbre3GvZmyYwU0WHefpZzDTYDoNgatuYq6LgUIQ+z5C04/kCRoFkAFrHag8a3BXqFzq5A==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-logger@3.887.0': - resolution: {integrity: sha512-YbbgLI6jKp2qSoAcHnXrQ5jcuc5EYAmGLVFgMVdk8dfCfJLfGGSaOLxF4CXC7QYhO50s+mPPkhBYejCik02Kug==} + '@aws-sdk/middleware-logger@3.901.0': + resolution: {integrity: sha512-UoHebjE7el/tfRo8/CQTj91oNUm+5Heus5/a4ECdmWaSCHCS/hXTsU3PTTHAY67oAQR8wBLFPfp3mMvXjB+L2A==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-recursion-detection@3.887.0': - resolution: {integrity: sha512-tjrUXFtQnFLo+qwMveq5faxP5MQakoLArXtqieHphSqZTXm21wDJM73hgT4/PQQGTwgYjDKqnqsE1hvk0hcfDw==} + '@aws-sdk/middleware-recursion-detection@3.901.0': + resolution: {integrity: sha512-Wd2t8qa/4OL0v/oDpCHHYkgsXJr8/ttCxrvCKAt0H1zZe2LlRhY9gpDVKqdertfHrHDj786fOvEQA28G1L75Dg==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-sdk-s3@3.890.0': - resolution: {integrity: sha512-58P1lrE606zpp29xH9Keh3j2BWfa2ciGBtygJTpulRMlqPL3U1gFfU2g5nDYJbjKgRtCgNIBqfmtkL4eikCb9w==} + '@aws-sdk/middleware-sdk-s3@3.907.0': + resolution: {integrity: sha512-8VVxcZPmJOKI8P08v5ARvoXbLV41abpAIIkt388fp/lwtfzbnXt6sWhhAk/pHgvCR1NnuvkbGuXGVcux59648Q==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-user-agent@3.890.0': - resolution: {integrity: sha512-x4+gLrOFGN7PnfxCaQbs3QEF8bMQE4CVxcOp066UEJqr2Pn4yB12Q3O+YntOtESK5NcTxIh7JlhGss95EHzNng==} + '@aws-sdk/middleware-user-agent@3.907.0': + resolution: {integrity: sha512-j/h3lk4X6AAXvusx/h8rr0zlo7G0l0quZM4k4rS/9jzatI53HCsrMaiGu6YXbxuVqtfMqv0MAj0MVhaMsAIs4A==} engines: {node: '>=18.0.0'} - '@aws-sdk/nested-clients@3.890.0': - resolution: {integrity: sha512-D5qVNd+qlqdL8duJShzffAqPllGRA4tG7n/GEpL13eNfHChPvGkkUFBMrxSgCAETaTna13G6kq+dMO+SAdbm1A==} + '@aws-sdk/nested-clients@3.907.0': + resolution: {integrity: sha512-LycXsdC5sMIc+Az5z1Mo2eYShr2kLo2gUgx7Rja3udG0GdqgdR/NNJ6ArmDCeKk2O5RFS5EgEg89bT55ecl5Uw==} engines: {node: '>=18.0.0'} - '@aws-sdk/region-config-resolver@3.890.0': - resolution: {integrity: sha512-VfdT+tkF9groRYNzKvQCsCGDbOQdeBdzyB1d6hWiq22u13UafMIoskJ1ec0i0H1X29oT6mjTitfnvPq1UiKwzQ==} + '@aws-sdk/region-config-resolver@3.901.0': + resolution: {integrity: sha512-7F0N888qVLHo4CSQOsnkZ4QAp8uHLKJ4v3u09Ly5k4AEStrSlFpckTPyUx6elwGL+fxGjNE2aakK8vEgzzCV0A==} engines: {node: '>=18.0.0'} - '@aws-sdk/signature-v4-multi-region@3.890.0': - resolution: {integrity: sha512-il8kb2/wDLXhemN3p7v4MvbvqoMuo7Ug3ihuIUIhPtSVjcnn+BISJU0S+5YTl8TXf6qxML9VrfxL0pmuhO3BsA==} + '@aws-sdk/signature-v4-multi-region@3.907.0': + resolution: {integrity: sha512-f5XHRu6MTbjB/ud5RwBZzntYMgThRaDur5PJRZ1CaYAL8gZLNuEpJLzUA7o3queeSfE9JboO+cm1gZnOfnYJkg==} engines: {node: '>=18.0.0'} - '@aws-sdk/token-providers@3.890.0': - resolution: {integrity: sha512-+pK/0iQEpPmnztbAw0NNmb+B5pPy8VLu+Ab4SJLgVp41RE9NO13VQtrzUbh61TTAVMrzqWlLQ2qmAl2Fk4VNgw==} + '@aws-sdk/token-providers@3.907.0': + resolution: {integrity: sha512-HjPbNft1Ad8X1lHQG21QXy9pitdXA+OKH6NtcXg57A31002tM+SkyUmU6ty1jbsRBEScxziIVe5doI1NmkHheA==} engines: {node: '>=18.0.0'} - '@aws-sdk/types@3.887.0': - resolution: {integrity: sha512-fmTEJpUhsPsovQ12vZSpVTEP/IaRoJAMBGQXlQNjtCpkBp6Iq3KQDa/HDaPINE+3xxo6XvTdtibsNOd5zJLV9A==} + '@aws-sdk/types@3.901.0': + resolution: {integrity: sha512-FfEM25hLEs4LoXsLXQ/q6X6L4JmKkKkbVFpKD4mwfVHtRVQG6QxJiCPcrkcPISquiy6esbwK2eh64TWbiD60cg==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-arn-parser@3.873.0': - resolution: {integrity: sha512-qag+VTqnJWDn8zTAXX4wiVioa0hZDQMtbZcGRERVnLar4/3/VIKBhxX2XibNQXFu1ufgcRn4YntT/XEPecFWcg==} + '@aws-sdk/util-arn-parser@3.893.0': + resolution: {integrity: sha512-u8H4f2Zsi19DGnwj5FSZzDMhytYF/bCh37vAtBsn3cNDL3YG578X5oc+wSX54pM3tOxS+NY7tvOAo52SW7koUA==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-endpoints@3.890.0': - resolution: {integrity: sha512-nJ8v1x9ZQKzMRK4dS4oefOMIHqb6cguctTcx1RB9iTaFOR5pP7bvq+D4mvNZ6vBxiHg1dQGBUUgl5XJmdR7atQ==} + '@aws-sdk/util-endpoints@3.901.0': + resolution: {integrity: sha512-5nZP3hGA8FHEtKvEQf4Aww5QZOkjLW1Z+NixSd+0XKfHvA39Ah5sZboScjLx0C9kti/K3OGW1RCx5K9Zc3bZqg==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-locate-window@3.873.0': - resolution: {integrity: sha512-xcVhZF6svjM5Rj89T1WzkjQmrTF6dpR2UvIHPMTnSZoNe6CixejPZ6f0JJ2kAhO8H+dUHwNBlsUgOTIKiK/Syg==} + '@aws-sdk/util-locate-window@3.893.0': + resolution: {integrity: sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-user-agent-browser@3.887.0': - resolution: {integrity: sha512-X71UmVsYc6ZTH4KU6hA5urOzYowSXc3qvroagJNLJYU1ilgZ529lP4J9XOYfEvTXkLR1hPFSRxa43SrwgelMjA==} + '@aws-sdk/util-user-agent-browser@3.907.0': + resolution: {integrity: sha512-Hus/2YCQmtCEfr4Ls88d07Q99Ex59uvtktiPTV963Q7w7LHuIT/JBjrbwNxtSm2KlJR9PHNdqxwN+fSuNsMGMQ==} - '@aws-sdk/util-user-agent-node@3.890.0': - resolution: {integrity: sha512-s85NkCxKoAlUvx7UP7OelxLqwTi27Tps9/Q+4N+9rEUjThxEnDsqJSStJ1XiYhddz1xc/vxMvPjYN0qX6EKPtA==} + '@aws-sdk/util-user-agent-node@3.907.0': + resolution: {integrity: sha512-r2Bc8VCU6ymkuem+QWT6oDdGvaYnK0YHg77SGUF47k+JsztSt1kZR0Y0q8jRH97bOsXldThyEcYsNbqDERa1Uw==} engines: {node: '>=18.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -1133,8 +1144,8 @@ packages: aws-crt: optional: true - '@aws-sdk/xml-builder@3.887.0': - resolution: {integrity: sha512-lMwgWK1kNgUhHGfBvO/5uLe7TKhycwOn3eRCqsKPT9aPCx/HWuTlpcQp8oW2pCRGLS7qzcxqpQulcD+bbUL7XQ==} + '@aws-sdk/xml-builder@3.901.0': + resolution: {integrity: sha512-pxFCkuAP7Q94wMTNPAwi6hEtNrp/BdFf+HOrIEeFQsk4EoOmpKY3I6S+u6A9Wg295J80Kh74LqDWM22ux3z6Aw==} engines: {node: '>=18.0.0'} '@aws/lambda-invoke-store@0.0.1': @@ -2001,120 +2012,120 @@ packages: search-insights: optional: true - '@docusaurus/babel@3.8.1': - resolution: {integrity: sha512-3brkJrml8vUbn9aeoZUlJfsI/GqyFcDgQJwQkmBtclJgWDEQBKKeagZfOgx0WfUQhagL1sQLNW0iBdxnI863Uw==} - engines: {node: '>=18.0'} + '@docusaurus/babel@3.9.1': + resolution: {integrity: sha512-/uoi3oG+wvbVWNBRfPrzrEslOSeLxrQEyWMywK51TLDFTANqIRivzkMusudh5bdDty8fXzCYUT+tg5t697jYqg==} + engines: {node: '>=20.0'} - '@docusaurus/bundler@3.8.1': - resolution: {integrity: sha512-/z4V0FRoQ0GuSLToNjOSGsk6m2lQUG4FRn8goOVoZSRsTrU8YR2aJacX5K3RG18EaX9b+52pN4m1sL3MQZVsQA==} - engines: {node: '>=18.0'} + '@docusaurus/bundler@3.9.1': + resolution: {integrity: sha512-E1c9DgNmAz4NqbNtiJVp4UgjLtr8O01IgtXD/NDQ4PZaK8895cMiTOgb3k7mN0qX8A3lb8vqyrPJ842+yMpuUg==} + engines: {node: '>=20.0'} peerDependencies: '@docusaurus/faster': '*' peerDependenciesMeta: '@docusaurus/faster': optional: true - '@docusaurus/core@3.8.1': - resolution: {integrity: sha512-ENB01IyQSqI2FLtOzqSI3qxG2B/jP4gQPahl2C3XReiLebcVh5B5cB9KYFvdoOqOWPyr5gXK4sjgTKv7peXCrA==} - engines: {node: '>=18.0'} + '@docusaurus/core@3.9.1': + resolution: {integrity: sha512-FWDk1LIGD5UR5Zmm9rCrXRoxZUgbwuP6FBA7rc50DVfzqDOMkeMe3NyJhOsA2dF0zBE3VbHEIMmTjKwTZJwbaA==} + engines: {node: '>=20.0'} hasBin: true peerDependencies: '@mdx-js/react': ^3.0.0 react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/cssnano-preset@3.8.1': - resolution: {integrity: sha512-G7WyR2N6SpyUotqhGznERBK+x84uyhfMQM2MmDLs88bw4Flom6TY46HzkRkSEzaP9j80MbTN8naiL1fR17WQug==} - engines: {node: '>=18.0'} + '@docusaurus/cssnano-preset@3.9.1': + resolution: {integrity: sha512-2y7+s7RWQMqBg+9ejeKwvZs7Bdw/hHIVJIodwMXbs2kr+S48AhcmAfdOh6Cwm0unJb0hJUshN0ROwRoQMwl3xg==} + engines: {node: '>=20.0'} - '@docusaurus/logger@3.8.1': - resolution: {integrity: sha512-2wjeGDhKcExEmjX8k1N/MRDiPKXGF2Pg+df/bDDPnnJWHXnVEZxXj80d6jcxp1Gpnksl0hF8t/ZQw9elqj2+ww==} - engines: {node: '>=18.0'} + '@docusaurus/logger@3.9.1': + resolution: {integrity: sha512-C9iFzXwHzwvGlisE4bZx+XQE0JIqlGAYAd5LzpR7fEDgjctu7yL8bE5U4nTNywXKHURDzMt4RJK8V6+stFHVkA==} + engines: {node: '>=20.0'} - '@docusaurus/mdx-loader@3.8.1': - resolution: {integrity: sha512-DZRhagSFRcEq1cUtBMo4TKxSNo/W6/s44yhr8X+eoXqCLycFQUylebOMPseHi5tc4fkGJqwqpWJLz6JStU9L4w==} - engines: {node: '>=18.0'} + '@docusaurus/mdx-loader@3.9.1': + resolution: {integrity: sha512-/1PY8lqry8jCt0qZddJSpc0U2sH6XC27kVJZfpA7o2TiQ3mdBQyH5AVbj/B2m682B1ounE+XjI0LdpOkAQLPoA==} + engines: {node: '>=20.0'} peerDependencies: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/module-type-aliases@3.8.1': - resolution: {integrity: sha512-6xhvAJiXzsaq3JdosS7wbRt/PwEPWHr9eM4YNYqVlbgG1hSK3uQDXTVvQktasp3VO6BmfYWPozueLWuj4gB+vg==} + '@docusaurus/module-type-aliases@3.9.1': + resolution: {integrity: sha512-YBce3GbJGGcMbJTyHcnEOMvdXqg41pa5HsrMCGA5Rm4z0h0tHS6YtEldj0mlfQRhCG7Y0VD66t2tb87Aom+11g==} peerDependencies: react: '*' react-dom: '*' - '@docusaurus/plugin-content-blog@3.8.1': - resolution: {integrity: sha512-vNTpMmlvNP9n3hGEcgPaXyvTljanAKIUkuG9URQ1DeuDup0OR7Ltvoc8yrmH+iMZJbcQGhUJF+WjHLwuk8HSdw==} - engines: {node: '>=18.0'} + '@docusaurus/plugin-content-blog@3.9.1': + resolution: {integrity: sha512-vT6kIimpJLWvW9iuWzH4u7VpTdsGlmn4yfyhq0/Kb1h4kf9uVouGsTmrD7WgtYBUG1P+TSmQzUUQa+ALBSRTig==} + engines: {node: '>=20.0'} peerDependencies: '@docusaurus/plugin-content-docs': '*' react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/plugin-content-docs@3.8.1': - resolution: {integrity: sha512-oByRkSZzeGNQByCMaX+kif5Nl2vmtj2IHQI2fWjCfCootsdKZDPFLonhIp5s3IGJO7PLUfe0POyw0Xh/RrGXJA==} - engines: {node: '>=18.0'} + '@docusaurus/plugin-content-docs@3.9.1': + resolution: {integrity: sha512-DyLk9BIA6I9gPIuia8XIL+XIEbNnExam6AHzRsfrEq4zJr7k/DsWW7oi4aJMepDnL7jMRhpVcdsCxdjb0/A9xg==} + engines: {node: '>=20.0'} peerDependencies: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/plugin-content-pages@3.8.1': - resolution: {integrity: sha512-a+V6MS2cIu37E/m7nDJn3dcxpvXb6TvgdNI22vJX8iUTp8eoMoPa0VArEbWvCxMY/xdC26WzNv4wZ6y0iIni/w==} - engines: {node: '>=18.0'} + '@docusaurus/plugin-content-pages@3.9.1': + resolution: {integrity: sha512-/1wFzRnXYASI+Nv9ck9IVPIMw0O5BGQ8ZVhDzEwhkL+tl44ycvSnY6PIe6rW2HLxsw61Z3WFwAiU8+xMMtMZpg==} + engines: {node: '>=20.0'} peerDependencies: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/plugin-css-cascade-layers@3.8.1': - resolution: {integrity: sha512-VQ47xRxfNKjHS5ItzaVXpxeTm7/wJLFMOPo1BkmoMG4Cuz4nuI+Hs62+RMk1OqVog68Swz66xVPK8g9XTrBKRw==} - engines: {node: '>=18.0'} + '@docusaurus/plugin-css-cascade-layers@3.9.1': + resolution: {integrity: sha512-/QyW2gRCk/XE3ttCK/ERIgle8KJ024dBNKMu6U5SmpJvuT2il1n5jR/48Pp/9wEwut8WVml4imNm6X8JsL5A0Q==} + engines: {node: '>=20.0'} - '@docusaurus/plugin-debug@3.8.1': - resolution: {integrity: sha512-nT3lN7TV5bi5hKMB7FK8gCffFTBSsBsAfV84/v293qAmnHOyg1nr9okEw8AiwcO3bl9vije5nsUvP0aRl2lpaw==} - engines: {node: '>=18.0'} + '@docusaurus/plugin-debug@3.9.1': + resolution: {integrity: sha512-qPeAuk0LccC251d7jg2MRhNI+o7niyqa924oEM/AxnZJvIpMa596aAxkRImiAqNN6+gtLE1Hkrz/RHUH2HDGsA==} + engines: {node: '>=20.0'} peerDependencies: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/plugin-google-analytics@3.8.1': - resolution: {integrity: sha512-Hrb/PurOJsmwHAsfMDH6oVpahkEGsx7F8CWMjyP/dw1qjqmdS9rcV1nYCGlM8nOtD3Wk/eaThzUB5TSZsGz+7Q==} - engines: {node: '>=18.0'} + '@docusaurus/plugin-google-analytics@3.9.1': + resolution: {integrity: sha512-k4Qq2HphqOrIU/CevGPdEO1yJnWUI8m0zOJsYt5NfMJwNsIn/gDD6gv/DKD+hxHndQT5pacsfBd4BWHZVNVroQ==} + engines: {node: '>=20.0'} peerDependencies: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/plugin-google-gtag@3.8.1': - resolution: {integrity: sha512-tKE8j1cEZCh8KZa4aa80zpSTxsC2/ZYqjx6AAfd8uA8VHZVw79+7OTEP2PoWi0uL5/1Is0LF5Vwxd+1fz5HlKg==} - engines: {node: '>=18.0'} + '@docusaurus/plugin-google-gtag@3.9.1': + resolution: {integrity: sha512-n9BURBiQyJKI/Ecz35IUjXYwXcgNCSq7/eA07+ZYcDiSyH2p/EjPf8q/QcZG3CyEJPZ/SzGkDHePfcVPahY4Gg==} + engines: {node: '>=20.0'} peerDependencies: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/plugin-google-tag-manager@3.8.1': - resolution: {integrity: sha512-iqe3XKITBquZq+6UAXdb1vI0fPY5iIOitVjPQ581R1ZKpHr0qe+V6gVOrrcOHixPDD/BUKdYwkxFjpNiEN+vBw==} - engines: {node: '>=18.0'} + '@docusaurus/plugin-google-tag-manager@3.9.1': + resolution: {integrity: sha512-rZAQZ25ZuXaThBajxzLjXieTDUCMmBzfAA6ThElQ3o7Q+LEpOjCIrwGFau0KLY9HeG6x91+FwwsAM8zeApYDrg==} + engines: {node: '>=20.0'} peerDependencies: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/plugin-sitemap@3.8.1': - resolution: {integrity: sha512-+9YV/7VLbGTq8qNkjiugIelmfUEVkTyLe6X8bWq7K5qPvGXAjno27QAfFq63mYfFFbJc7z+pudL63acprbqGzw==} - engines: {node: '>=18.0'} + '@docusaurus/plugin-sitemap@3.9.1': + resolution: {integrity: sha512-k/bf5cXDxAJUYTzqatgFJwmZsLUbIgl6S8AdZMKGG2Mv2wcOHt+EQNN9qPyWZ5/9cFj+Q8f8DN+KQheBMYLong==} + engines: {node: '>=20.0'} peerDependencies: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/plugin-svgr@3.8.1': - resolution: {integrity: sha512-rW0LWMDsdlsgowVwqiMb/7tANDodpy1wWPwCcamvhY7OECReN3feoFwLjd/U4tKjNY3encj0AJSTxJA+Fpe+Gw==} - engines: {node: '>=18.0'} + '@docusaurus/plugin-svgr@3.9.1': + resolution: {integrity: sha512-TeZOXT2PSdTNR1OpDJMkYqFyX7MMhbd4t16hQByXksgZQCXNyw3Dio+KaDJ2Nj+LA4WkOvsk45bWgYG5MAaXSQ==} + engines: {node: '>=20.0'} peerDependencies: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/preset-classic@3.8.1': - resolution: {integrity: sha512-yJSjYNHXD8POMGc2mKQuj3ApPrN+eG0rO1UPgSx7jySpYU+n4WjBikbrA2ue5ad9A7aouEtMWUoiSRXTH/g7KQ==} - engines: {node: '>=18.0'} + '@docusaurus/preset-classic@3.9.1': + resolution: {integrity: sha512-ZHga2xsxxsyd0dN1BpLj8S889Eu9eMBuj2suqxdw/vaaXu/FjJ8KEGbcaeo6nHPo8VQcBBnPEdkBtSDm2TfMNw==} + engines: {node: '>=20.0'} peerDependencies: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 @@ -2124,52 +2135,52 @@ packages: peerDependencies: react: '*' - '@docusaurus/theme-classic@3.8.1': - resolution: {integrity: sha512-bqDUCNqXeYypMCsE1VcTXSI1QuO4KXfx8Cvl6rYfY0bhhqN6d2WZlRkyLg/p6pm+DzvanqHOyYlqdPyP0iz+iw==} - engines: {node: '>=18.0'} + '@docusaurus/theme-classic@3.9.1': + resolution: {integrity: sha512-LrAIu/mQ04nG6s1cssC0TMmICD8twFIIn/hJ5Pd9uIPQvtKnyAKEn12RefopAul5KfMo9kixPaqogV5jIJr26w==} + engines: {node: '>=20.0'} peerDependencies: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/theme-common@3.8.1': - resolution: {integrity: sha512-UswMOyTnPEVRvN5Qzbo+l8k4xrd5fTFu2VPPfD6FcW/6qUtVLmJTQCktbAL3KJ0BVXGm5aJXz/ZrzqFuZERGPw==} - engines: {node: '>=18.0'} + '@docusaurus/theme-common@3.9.1': + resolution: {integrity: sha512-j9adi961F+6Ps9d0jcb5BokMcbjXAAJqKkV43eo8nh4YgmDj7KUNDX4EnOh/MjTQeO06oPY5cxp3yUXdW/8Ggw==} + engines: {node: '>=20.0'} peerDependencies: '@docusaurus/plugin-content-docs': '*' react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/theme-search-algolia@3.8.1': - resolution: {integrity: sha512-NBFH5rZVQRAQM087aYSRKQ9yGEK9eHd+xOxQjqNpxMiV85OhJDD4ZGz6YJIod26Fbooy54UWVdzNU0TFeUUUzQ==} - engines: {node: '>=18.0'} + '@docusaurus/theme-search-algolia@3.9.1': + resolution: {integrity: sha512-WjM28bzlgfT6nHlEJemkwyGVpvGsZWPireV/w+wZ1Uo64xCZ8lNOb4xwQRukDaLSed3oPBN0gSnu06l5VuCXHg==} + engines: {node: '>=20.0'} peerDependencies: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/theme-translations@3.8.1': - resolution: {integrity: sha512-OTp6eebuMcf2rJt4bqnvuwmm3NVXfzfYejL+u/Y1qwKhZPrjPoKWfk1CbOP5xH5ZOPkiAsx4dHdQBRJszK3z2g==} - engines: {node: '>=18.0'} + '@docusaurus/theme-translations@3.9.1': + resolution: {integrity: sha512-mUQd49BSGKTiM6vP9+JFgRJL28lMIN3PUvXjF3rzuOHMByUZUBNwCt26Z23GkKiSIOrRkjKoaBNTipR/MHdYSQ==} + engines: {node: '>=20.0'} - '@docusaurus/tsconfig@3.8.1': - resolution: {integrity: sha512-XBWCcqhRHhkhfolnSolNL+N7gj3HVE3CoZVqnVjfsMzCoOsuQw2iCLxVVHtO+rePUUfouVZHURDgmqIySsF66A==} + '@docusaurus/tsconfig@3.9.1': + resolution: {integrity: sha512-stdzM1dNDgRO0OvxeznXlE3N1igUoeHPNJjiKqyffLizgpVgNXJBAWeG6fuoYiCH4udGUBqy2dyM+1+kG2/UPQ==} - '@docusaurus/types@3.8.1': - resolution: {integrity: sha512-ZPdW5AB+pBjiVrcLuw3dOS6BFlrG0XkS2lDGsj8TizcnREQg3J8cjsgfDviszOk4CweNfwo1AEELJkYaMUuOPg==} + '@docusaurus/types@3.9.1': + resolution: {integrity: sha512-ElekJ29sk39s5LTEZMByY1c2oH9FMtw7KbWFU3BtuQ1TytfIK39HhUivDEJvm5KCLyEnnfUZlvSNDXeyk0vzAA==} peerDependencies: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/utils-common@3.8.1': - resolution: {integrity: sha512-zTZiDlvpvoJIrQEEd71c154DkcriBecm4z94OzEE9kz7ikS3J+iSlABhFXM45mZ0eN5pVqqr7cs60+ZlYLewtg==} - engines: {node: '>=18.0'} + '@docusaurus/utils-common@3.9.1': + resolution: {integrity: sha512-4M1u5Q8Zn2CYL2TJ864M51FV4YlxyGyfC3x+7CLuR6xsyTVNBNU4QMcPgsTHRS9J2+X6Lq7MyH6hiWXyi/sXUQ==} + engines: {node: '>=20.0'} - '@docusaurus/utils-validation@3.8.1': - resolution: {integrity: sha512-gs5bXIccxzEbyVecvxg6upTwaUbfa0KMmTj7HhHzc016AGyxH2o73k1/aOD0IFrdCsfJNt37MqNI47s2MgRZMA==} - engines: {node: '>=18.0'} + '@docusaurus/utils-validation@3.9.1': + resolution: {integrity: sha512-5bzab5si3E1udrlZuVGR17857Lfwe8iFPoy5AvMP9PXqDfoyIKT7gDQgAmxdRDMurgHaJlyhXEHHdzDKkOxxZQ==} + engines: {node: '>=20.0'} - '@docusaurus/utils@3.8.1': - resolution: {integrity: sha512-P1ml0nvOmEFdmu0smSXOqTS1sxU5tqvnc0dA4MTKV39kye+bhQnjkIKEE18fNOvxjyB86k8esoCIFM3x4RykOQ==} - engines: {node: '>=18.0'} + '@docusaurus/utils@3.9.1': + resolution: {integrity: sha512-YAL4yhhWLl9DXuf5MVig260a6INz4MehrBGFU/CZu8yXmRiYEuQvRFWh9ZsjfAOyaG7za1MNmBVZ4VVAi/CiJA==} + engines: {node: '>=20.0'} '@emnapi/runtime@1.5.0': resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} @@ -2180,8 +2191,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.25.9': - resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} + '@esbuild/aix-ppc64@0.25.10': + resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -2192,8 +2203,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.25.9': - resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==} + '@esbuild/android-arm64@0.25.10': + resolution: {integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -2204,8 +2215,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.25.9': - resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==} + '@esbuild/android-arm@0.25.10': + resolution: {integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -2216,8 +2227,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.25.9': - resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==} + '@esbuild/android-x64@0.25.10': + resolution: {integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -2228,8 +2239,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.25.9': - resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==} + '@esbuild/darwin-arm64@0.25.10': + resolution: {integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -2240,8 +2251,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.25.9': - resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==} + '@esbuild/darwin-x64@0.25.10': + resolution: {integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -2252,8 +2263,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.25.9': - resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==} + '@esbuild/freebsd-arm64@0.25.10': + resolution: {integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -2264,8 +2275,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.9': - resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==} + '@esbuild/freebsd-x64@0.25.10': + resolution: {integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -2276,8 +2287,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.25.9': - resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==} + '@esbuild/linux-arm64@0.25.10': + resolution: {integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -2288,8 +2299,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.25.9': - resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==} + '@esbuild/linux-arm@0.25.10': + resolution: {integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -2300,8 +2311,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.25.9': - resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==} + '@esbuild/linux-ia32@0.25.10': + resolution: {integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -2312,8 +2323,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.25.9': - resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==} + '@esbuild/linux-loong64@0.25.10': + resolution: {integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -2324,8 +2335,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.25.9': - resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==} + '@esbuild/linux-mips64el@0.25.10': + resolution: {integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -2336,8 +2347,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.25.9': - resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==} + '@esbuild/linux-ppc64@0.25.10': + resolution: {integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -2348,8 +2359,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.25.9': - resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==} + '@esbuild/linux-riscv64@0.25.10': + resolution: {integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -2360,8 +2371,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.25.9': - resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==} + '@esbuild/linux-s390x@0.25.10': + resolution: {integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -2372,14 +2383,14 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.25.9': - resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==} + '@esbuild/linux-x64@0.25.10': + resolution: {integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.9': - resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==} + '@esbuild/netbsd-arm64@0.25.10': + resolution: {integrity: sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] @@ -2390,14 +2401,14 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.9': - resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==} + '@esbuild/netbsd-x64@0.25.10': + resolution: {integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.9': - resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==} + '@esbuild/openbsd-arm64@0.25.10': + resolution: {integrity: sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -2408,14 +2419,14 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.9': - resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==} + '@esbuild/openbsd-x64@0.25.10': + resolution: {integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.9': - resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==} + '@esbuild/openharmony-arm64@0.25.10': + resolution: {integrity: sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] @@ -2426,8 +2437,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.25.9': - resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==} + '@esbuild/sunos-x64@0.25.10': + resolution: {integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -2438,8 +2449,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.25.9': - resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==} + '@esbuild/win32-arm64@0.25.10': + resolution: {integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -2450,8 +2461,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.25.9': - resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==} + '@esbuild/win32-ia32@0.25.10': + resolution: {integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -2462,8 +2473,8 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.25.9': - resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==} + '@esbuild/win32-x64@0.25.10': + resolution: {integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -2482,20 +2493,24 @@ packages: resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-helpers@0.3.1': - resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==} + '@eslint/config-helpers@0.4.0': + resolution: {integrity: sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/core@0.15.2': resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/core@0.16.0': + resolution: {integrity: sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/eslintrc@3.3.1': resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.35.0': - resolution: {integrity: sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==} + '@eslint/js@9.37.0': + resolution: {integrity: sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.6': @@ -2506,17 +2521,14 @@ packages: resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@exodus/schemasafe@1.3.0': - resolution: {integrity: sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==} + '@eslint/plugin-kit@0.4.0': + resolution: {integrity: sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@faker-js/faker@10.0.0': resolution: {integrity: sha512-UollFEUkVXutsaP+Vndjxar40Gs5JL2HeLcl8xO1QAjJgOdhc3OmBFWyEylS+RddWaaBiAzH+5/17PLQJwDiLw==} engines: {node: ^20.19.0 || ^22.13.0 || ^23.5.0 || >=24.0.0, npm: '>=10'} - '@faker-js/faker@5.5.3': - resolution: {integrity: sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw==} - deprecated: Please update to a newer version. - '@fig/complete-commander@3.2.0': resolution: {integrity: sha512-1Holl3XtRiANVKURZwgpjCnPuV4RsHp+XC0MhgvyAX/avQwj7F2HUItYOvGi/bXjJCkEzgBZmVfCr0HBA+q+Bw==} peerDependencies: @@ -2531,29 +2543,29 @@ packages: '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} - '@formatjs/ecma402-abstract@2.3.4': - resolution: {integrity: sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA==} + '@formatjs/ecma402-abstract@2.3.5': + resolution: {integrity: sha512-1HTESOq1IUa23g1lFZEGIXsfZKZOwWmB9RROwGn+xariiQnd++wwTMvlRAbZ8wtXRHFUamJPxsKcxpSzeCvFWQ==} '@formatjs/fast-memoize@2.2.7': resolution: {integrity: sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==} - '@formatjs/icu-messageformat-parser@2.11.2': - resolution: {integrity: sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA==} + '@formatjs/icu-messageformat-parser@2.11.3': + resolution: {integrity: sha512-H/KfWSosaiDiOaW4nHe1Fn4Cgzm+oFQ8giTmB5RJzTBNSMmd+j2NVrvvZHAmlxJHcuOelzKBLjQ2EDcyH4NSWw==} - '@formatjs/icu-skeleton-parser@1.8.14': - resolution: {integrity: sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ==} + '@formatjs/icu-skeleton-parser@1.8.15': + resolution: {integrity: sha512-qNrKxWJmnWxin5U4A4Evy7C0rgRiNw3IqXu9OGuT31B8lDxBGl+OgT8kcq0ZVKK0gqA4l4SQB9x+SFAvLT5hcQ==} - '@formatjs/intl-localematcher@0.6.1': - resolution: {integrity: sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==} + '@formatjs/intl-localematcher@0.6.2': + resolution: {integrity: sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==} - '@golevelup/nestjs-discovery@4.0.3': - resolution: {integrity: sha512-8w3CsXHN7+7Sn2i419Eal1Iw/kOjAd6Kb55M/ZqKBBwACCMn4WiEuzssC71LpBMI1090CiDxuelfPRwwIrQK+A==} + '@golevelup/nestjs-discovery@5.0.0': + resolution: {integrity: sha512-NaIWLCLI+XvneUK05LH2idHLmLNITYT88YnpOuUQmllKtiJNIS3woSt7QXrMZ5k3qUWuZpehEVz1JtlX4I1KyA==} peerDependencies: - '@nestjs/common': ^10.x || ^11.0.0 - '@nestjs/core': ^10.x || ^11.0.0 + '@nestjs/common': ^11.0.20 + '@nestjs/core': ^11.0.20 - '@grpc/grpc-js@1.13.4': - resolution: {integrity: sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==} + '@grpc/grpc-js@1.14.0': + resolution: {integrity: sha512-N8Jx6PaYzcTRNzirReJCtADVoq4z7+1KQ4E70jTg/koQiMoUSN1kbNjPOqpPbhMFhfU1/l7ixspPl8dNY+FoUg==} engines: {node: '>=12.10.0'} '@grpc/proto-loader@0.7.15': @@ -2561,6 +2573,11 @@ packages: engines: {node: '>=6'} hasBin: true + '@grpc/proto-loader@0.8.0': + resolution: {integrity: sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==} + engines: {node: '>=6'} + hasBin: true + '@hapi/hoek@9.3.0': resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} @@ -2583,130 +2600,134 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@img/sharp-darwin-arm64@0.34.3': - resolution: {integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==} + '@img/colour@1.0.0': + resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.4': + resolution: {integrity: sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.34.3': - resolution: {integrity: sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==} + '@img/sharp-darwin-x64@0.34.4': + resolution: {integrity: sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.2.0': - resolution: {integrity: sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==} + '@img/sharp-libvips-darwin-arm64@1.2.3': + resolution: {integrity: sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.2.0': - resolution: {integrity: sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==} + '@img/sharp-libvips-darwin-x64@1.2.3': + resolution: {integrity: sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.2.0': - resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==} + '@img/sharp-libvips-linux-arm64@1.2.3': + resolution: {integrity: sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linux-arm@1.2.0': - resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==} + '@img/sharp-libvips-linux-arm@1.2.3': + resolution: {integrity: sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==} cpu: [arm] os: [linux] - '@img/sharp-libvips-linux-ppc64@1.2.0': - resolution: {integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==} + '@img/sharp-libvips-linux-ppc64@1.2.3': + resolution: {integrity: sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==} cpu: [ppc64] os: [linux] - '@img/sharp-libvips-linux-s390x@1.2.0': - resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==} + '@img/sharp-libvips-linux-s390x@1.2.3': + resolution: {integrity: sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==} cpu: [s390x] os: [linux] - '@img/sharp-libvips-linux-x64@1.2.0': - resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==} + '@img/sharp-libvips-linux-x64@1.2.3': + resolution: {integrity: sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==} cpu: [x64] os: [linux] - '@img/sharp-libvips-linuxmusl-arm64@1.2.0': - resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==} + '@img/sharp-libvips-linuxmusl-arm64@1.2.3': + resolution: {integrity: sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linuxmusl-x64@1.2.0': - resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==} + '@img/sharp-libvips-linuxmusl-x64@1.2.3': + resolution: {integrity: sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==} cpu: [x64] os: [linux] - '@img/sharp-linux-arm64@0.34.3': - resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==} + '@img/sharp-linux-arm64@0.34.4': + resolution: {integrity: sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - '@img/sharp-linux-arm@0.34.3': - resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==} + '@img/sharp-linux-arm@0.34.4': + resolution: {integrity: sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - '@img/sharp-linux-ppc64@0.34.3': - resolution: {integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==} + '@img/sharp-linux-ppc64@0.34.4': + resolution: {integrity: sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] - '@img/sharp-linux-s390x@0.34.3': - resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==} + '@img/sharp-linux-s390x@0.34.4': + resolution: {integrity: sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] - '@img/sharp-linux-x64@0.34.3': - resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==} + '@img/sharp-linux-x64@0.34.4': + resolution: {integrity: sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - '@img/sharp-linuxmusl-arm64@0.34.3': - resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==} + '@img/sharp-linuxmusl-arm64@0.34.4': + resolution: {integrity: sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - '@img/sharp-linuxmusl-x64@0.34.3': - resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==} + '@img/sharp-linuxmusl-x64@0.34.4': + resolution: {integrity: sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - '@img/sharp-wasm32@0.34.3': - resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==} + '@img/sharp-wasm32@0.34.4': + resolution: {integrity: sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] - '@img/sharp-win32-arm64@0.34.3': - resolution: {integrity: sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==} + '@img/sharp-win32-arm64@0.34.4': + resolution: {integrity: sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [win32] - '@img/sharp-win32-ia32@0.34.3': - resolution: {integrity: sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==} + '@img/sharp-win32-ia32@0.34.4': + resolution: {integrity: sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.34.3': - resolution: {integrity: sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==} + '@img/sharp-win32-x64@0.34.4': + resolution: {integrity: sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] - '@immich/ui@0.29.0': - resolution: {integrity: sha512-An9cf1L4nMO6+C1Tkktd+qjGmZvyGz/Un33cGsKQa2I7IdZHd67KbbC2v3wN3bQMiTjxtFJ8YR9EONohJ8jDtQ==} + '@immich/ui@0.34.2': + resolution: {integrity: sha512-tWjEV1prSZ9VLes69Ha9jnnZj6tbv/9+PdQjsK+5zK7sQok/l7kyzAobj5z4XRD11XtGk/cAqi/ZOlqRWvZilA==} peerDependencies: svelte: ^5.0.0 @@ -2852,8 +2873,8 @@ packages: '@internationalized/date@3.8.2': resolution: {integrity: sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA==} - '@ioredis/commands@1.3.0': - resolution: {integrity: sha512-M/T6Zewn7sDaBQEqIZ8Rb+i9y8qfGmq+5SDFSf9sA2lUZTmdDLVdOiQaeDp+Q4wElZ9HG1GAX5KhDaidp6LQsQ==} + '@ioredis/commands@1.4.0': + resolution: {integrity: sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==} '@isaacs/balanced-match@4.0.1': resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} @@ -2899,12 +2920,48 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/trace-mapping@0.3.30': - resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} '@js-sdsl/ordered-map@4.4.2': resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + '@jsonjoy.com/base64@1.1.2': + resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/buffers@1.2.0': + resolution: {integrity: sha512-6RX+W5a+ZUY/c/7J5s5jK9UinLfJo5oWKh84fb4X0yK2q4WXEWUWZWuEMjvCb1YNUQhEAhUfr5scEGOH7jC4YQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/codegen@1.0.0': + resolution: {integrity: sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pack@1.20.0': + resolution: {integrity: sha512-adcXFVorSQULtT4XDL0giRLr2EVGIcyWm6eQKZWTrRA4EEydGOY8QVQtL0PaITQpUyu+lOd/QOicw6vdy1v8QQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pointer@1.0.2': + resolution: {integrity: sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/util@1.9.0': + resolution: {integrity: sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + '@koa/cors@5.0.0': resolution: {integrity: sha512-x/iUDjcS90W69PryLDIMgFyV21YLTnG9zOpPXS7Bkt2b8AsY3zZsIpOLBkYr9fBcF3HbkKaER5hOBZLfpLgYNw==} engines: {node: '>= 14.0.0'} @@ -2976,8 +3033,8 @@ packages: resolution: {integrity: sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==} engines: {node: '>=6.0.0'} - '@maplibre/maplibre-gl-style-spec@23.3.0': - resolution: {integrity: sha512-IGJtuBbaGzOUgODdBRg66p8stnwj9iDXkgbYKoYcNiiQmaez5WVRfXm4b03MCDwmZyX93csbfHFWEJJYHnn5oA==} + '@maplibre/maplibre-gl-style-spec@24.2.0': + resolution: {integrity: sha512-cE80g83fRcBbZbQC70siOUxUK6YJ/5ZkClDZbmm+hzrUbv+J6yntkMmcpdz9DbOrWOM7FHKR5rruc6Q/hWx5cA==} hasBin: true '@maplibre/vt-pbf@4.0.3': @@ -2995,8 +3052,8 @@ packages: '@mdn/browser-compat-data@6.0.27': resolution: {integrity: sha512-s5kTuDih5Ysb7DS2T2MhvneFLvDS0NwnLY5Jv6dL+zBXbcNVcyFcGdjwn3rttiHvcbb/qF02HP9ywufdwHZbIw==} - '@mdx-js/mdx@3.1.0': - resolution: {integrity: sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw==} + '@mdx-js/mdx@3.1.1': + resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} '@mdx-js/react@3.1.1': resolution: {integrity: sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==} @@ -3007,16 +3064,6 @@ packages: '@microsoft/tsdoc@0.15.1': resolution: {integrity: sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==} - '@monaco-editor/loader@1.5.0': - resolution: {integrity: sha512-hKoGSM+7aAc7eRTRjpqAZucPmoNOC4UUbknb/VNoTkEIkCPhqV8LfbsgM1webRM7S/z21eHEx9Fkwx8Z/C/+Xw==} - - '@monaco-editor/react@4.7.0': - resolution: {integrity: sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==} - peerDependencies: - monaco-editor: '>= 0.25.0 < 1' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} cpu: [arm64] @@ -3133,14 +3180,14 @@ packages: '@nestjs/websockets': ^11.0.0 rxjs: ^7.1.0 - '@nestjs/schedule@6.0.0': - resolution: {integrity: sha512-aQySMw6tw2nhitELXd3EiRacQRgzUKD9mFcUZVOJ7jPLqIBvXOyvRWLsK9SdurGA+jjziAlMef7iB5ZEFFoQpw==} + '@nestjs/schedule@6.0.1': + resolution: {integrity: sha512-v3yO6cSPAoBSSyH67HWnXHzuhPhSNZhRmLY38JvCt2sqY8sPMOODpcU1D79iUMFf7k16DaMEbL4Mgx61ZhiC8Q==} peerDependencies: '@nestjs/common': ^10.0.0 || ^11.0.0 '@nestjs/core': ^10.0.0 || ^11.0.0 - '@nestjs/schematics@11.0.7': - resolution: {integrity: sha512-t8dNYYMwEeEsrlwc2jbkfwCfXczq4AeNEgx1KVQuJ6wYibXk0ZbXbPdfp8scnEAaQv1grpncNV5gWgzi7ZwbvQ==} + '@nestjs/schematics@11.0.8': + resolution: {integrity: sha512-HKunkzfBYLpNyL/qP5wu0OBKVPrISJLnrB4r6S53fT99pEvopDcJAeIuznSAD1Dx1njUqpbTR/uGyD0xL1y0nw==} peerDependencies: typescript: '>=4.8.2' @@ -3218,8 +3265,8 @@ packages: '@oazapfts/runtime@1.0.4': resolution: {integrity: sha512-7t6C2shug/6tZhQgkCa532oTYBLEnbASV/i1SG1rH2GB4h3aQQujYciYSPT92hvN4IwTe8S2hPkN/6iiOyTlCg==} - '@opentelemetry/api-logs@0.205.0': - resolution: {integrity: sha512-wBlPk1nFB37Hsm+3Qy73yQSobVn28F4isnWIBvKpd5IUH/eat8bwcL02H9yzmHyyPmukeccSl2mbN5sDQZYnPg==} + '@opentelemetry/api-logs@0.206.0': + resolution: {integrity: sha512-yIVDu9jX//nV5wSMLZLdHdb1SKHIMj9k+wQVFtln5Flcgdldz9BkHtavvExQiJqBZg2OpEEJEZmzQazYztdz2A==} engines: {node: '>=8.0.0'} '@opentelemetry/api@1.9.0': @@ -3238,62 +3285,62 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/exporter-logs-otlp-grpc@0.205.0': - resolution: {integrity: sha512-jQlw7OHbqZ8zPt+pOrW2KGN7T55P50e3NXBMr4ckPOF+DWDwSy4W7mkG09GpYWlQAQ5C9BXg5gfUlv5ldTgWsw==} + '@opentelemetry/exporter-logs-otlp-grpc@0.206.0': + resolution: {integrity: sha512-kJKxKBaGwqWop95d6tcluz260IWwIgOG0BH8oVm6429tg8LxY2PJb7Om8d5s+5vOFM8DkUYCnIpn9d/13/RcKQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/exporter-logs-otlp-http@0.205.0': - resolution: {integrity: sha512-5JteMyVWiro4ghF0tHQjfE6OJcF7UBUcoEqX3UIQ5jutKP1H+fxFdyhqjjpmeHMFxzOHaYuLlNR1Bn7FOjGyJg==} + '@opentelemetry/exporter-logs-otlp-http@0.206.0': + resolution: {integrity: sha512-VWcHEnS+1kN+sQTAdCgSn2anqHPxY1/e52fhpe2mcSnEaXI1srFf3RU5DAu7hzQO6T9DPQzOKG8kc76vCtyYDw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/exporter-logs-otlp-proto@0.205.0': - resolution: {integrity: sha512-q3VS9wS+lpZ01txKxiDGBtBpTNge3YhbVEFDgem9ZQR9eI3EZ68+9tVZH9zJcSxI37nZPJ6lEEZO58yEjYZsVA==} + '@opentelemetry/exporter-logs-otlp-proto@0.206.0': + resolution: {integrity: sha512-CsYNXJwkn1qCXJGE+/JvvYucAjL8rpaxa2hnl+tDP6M5E0O3UVa8zG4ZUEebjr5J5Nc32egvslEZx5rgNOp3lQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/exporter-metrics-otlp-grpc@0.205.0': - resolution: {integrity: sha512-1Vxlo4lUwqSKYX+phFkXHKYR3DolFHxCku6lVMP1H8sVE3oj4wwmwxMzDsJ7zF+sXd8M0FCr+ckK4SnNNKkV+w==} + '@opentelemetry/exporter-metrics-otlp-grpc@0.206.0': + resolution: {integrity: sha512-/qGIwVa27BSGqzULB34O+UtEVl4isc+gEd2ydMfZVidWpdkJYbMeZZYBWi4z6giD0w0Dep5k/NIJ/lSEZoKEIg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/exporter-metrics-otlp-http@0.205.0': - resolution: {integrity: sha512-fFxNQ/HbbpLmh1pgU6HUVbFD1kNIjrkoluoKJkh88+gnmpFD92kMQ8WFNjPnSbjg2mNVnEkeKXgCYEowNW+p1w==} + '@opentelemetry/exporter-metrics-otlp-http@0.206.0': + resolution: {integrity: sha512-u6ztHNbgNlFw3vCuOErGsiiEcDk9KB0pwd/Jv6aPnvV4oKJd/5K+lfiSjDpCFUh5UUFJdGmHtADxUgbRpYMPyA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/exporter-metrics-otlp-proto@0.205.0': - resolution: {integrity: sha512-qIbNnedw9QfFjwpx4NQvdgjK3j3R2kWH/2T+7WXAm1IfMFe9fwatYxE61i7li4CIJKf8HgUC3GS8Du0C3D+AuQ==} + '@opentelemetry/exporter-metrics-otlp-proto@0.206.0': + resolution: {integrity: sha512-mLlmXmVOSwMw8Cm9RKz5ZuTUWW28lQWWIcy5cpEUhIrJ5cuSuTZ/PKIaa3+CHmcKkkF2pWFdBkzOIar9aApzoQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/exporter-prometheus@0.205.0': - resolution: {integrity: sha512-xsot/Qm9VLDTag4GEwAunD1XR1U8eBHTLAgO7IZNo2JuD/c/vL7xmDP7mQIUr6Lk3gtj/yGGIR2h3vhTeVzv4w==} + '@opentelemetry/exporter-prometheus@0.206.0': + resolution: {integrity: sha512-sCzW+vE2KhlDZhH73f7drr+d0kvlsqG02t9xGtG40c6t4hjgpa5qEwEZTzFo9s4djtUrmDBjwJ5S3NYuCERVKg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/exporter-trace-otlp-grpc@0.205.0': - resolution: {integrity: sha512-ZBksUk84CcQOuDJB65yu5A4PORkC4qEsskNwCrPZxDLeWjPOFZNSWt0E0jQxKCY8PskLhjNXJYo12YaqsYvGFA==} + '@opentelemetry/exporter-trace-otlp-grpc@0.206.0': + resolution: {integrity: sha512-/q8sU9wj20tT9m+529VXN9E9DzpvNui5Cict8onY/gRGeXqTav21xHkvmv0FSpetb/29WS89drVjfI8/Mf2iWQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/exporter-trace-otlp-http@0.205.0': - resolution: {integrity: sha512-vr2bwwPCSc9u7rbKc74jR+DXFvyMFQo9o5zs+H/fgbK672Whw/1izUKVf+xfWOdJOvuwTnfWxy+VAY+4TSo74Q==} + '@opentelemetry/exporter-trace-otlp-http@0.206.0': + resolution: {integrity: sha512-xiEhJZxE9yDb13FVW4XaF7J56boLv1NALOGEVu3F8jMC24iZmX5TSVRJCNGLWyy1Xb3N27Yu31kdSsmEBCnxyw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/exporter-trace-otlp-proto@0.205.0': - resolution: {integrity: sha512-bGtFzqiENO2GpJk988mOBMe0MfeNpTQjbLm/LBijas6VRyEDQarUzdBHpFlu89A25k1+BCntdWGsWTa9Ai4FyA==} + '@opentelemetry/exporter-trace-otlp-proto@0.206.0': + resolution: {integrity: sha512-A3lGp39qqZGt6PU/9X682/4KdkDMu+jO+slnNCdm8Zki3AKEFulY2yYtz4rL0rah/liN+ENRsFQWI/B3xJiZGw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -3310,50 +3357,50 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-http@0.205.0': - resolution: {integrity: sha512-6fOgRlV7ypBuEzCQP7vXkLQxz3UL1FhE24rAlMRbwGvPAnZLvutcG/fq9FI/n+VU23dOpYexocYsXCf5oy/AXw==} + '@opentelemetry/instrumentation-http@0.206.0': + resolution: {integrity: sha512-U355U6PpRSj5NDLqI8uY2SPp4uapuGASSGFLPoQbrqphHQ6pJrHDFBTR72a+S5XvHJo1PlLS84k4IOl+tXYPsA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-ioredis@0.53.0': - resolution: {integrity: sha512-Ah2wU347vOJYbE563Tgm3UX2J3DAXoI8gsr8qH0OOO4uDuEv3kVS/eDCfXApt11bvvDDPlOoc60/TGn6m9IoPw==} + '@opentelemetry/instrumentation-ioredis@0.54.0': + resolution: {integrity: sha512-SkGBzieEHnpfeYB112R9wzshtpqZkQ0DUHm1DhminO4KYIOTyGS7NkaZlIcRUsmMg3/1Tk1HqEVjSdCO2rOXfA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-nestjs-core@0.51.0': - resolution: {integrity: sha512-Se/m4887W94OO12pjKMjI3398L7HCoWeCjcbwoPvNOWpSpMkljBOHA9vE/fyo63CaVG1XAM5xA4ad60wmJKl9A==} + '@opentelemetry/instrumentation-nestjs-core@0.53.0': + resolution: {integrity: sha512-8sDcdwPb6fJ2ayD7Q7H2Vxwj21ao8Atuvm+RtPzAdrOvqEWTigpO99LJaEsZRRphJTdRBejg07/oYQzJusPODQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-pg@0.58.0': - resolution: {integrity: sha512-WHntZAorf6CZ0n5a3oHlwGkSeu5Xa4AiCmXkNTKg24TbYSFWzJUtWvPQSkxePvQ3ku71lhAY/M20WgwHlvpZpQ==} + '@opentelemetry/instrumentation-pg@0.59.0': + resolution: {integrity: sha512-26Q6wwsx84K8KNX/CZ2q6ZRv2hIzPawRpL0og+Ro4BHN8Qin+ktkqFDlF2n83KPJn/tFstz3nErue+DAVXAGTw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation@0.205.0': - resolution: {integrity: sha512-cgvm7tvQdu9Qo7VurJP84wJ7ZV9F6WqDDGZpUc6rUEXwjV7/bXWs0kaYp9v+1Vh1+3TZCD3i6j/lUBcPhu8NhA==} + '@opentelemetry/instrumentation@0.206.0': + resolution: {integrity: sha512-anPU9GAn3vSH/0JFQZ4e626xRw8p8R21kxM7xammFk9BRhfDw1IpgqvFMllbb+1MSHHEX9EiUqYHJyWo/B6KGA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/otlp-exporter-base@0.205.0': - resolution: {integrity: sha512-2MN0C1IiKyo34M6NZzD6P9Nv9Dfuz3OJ3rkZwzFmF6xzjDfqqCTatc9v1EpNfaP55iDOCLHFyYNCgs61FFgtUQ==} + '@opentelemetry/otlp-exporter-base@0.206.0': + resolution: {integrity: sha512-Rv54oSNKMHYS5hv+H5EGksfBUtvPQWFTK+Dk6MjJun9tOijCsFJrhRFvAqg5d67TWSMn+ZQYRKIeXh5oLVrpAQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/otlp-grpc-exporter-base@0.205.0': - resolution: {integrity: sha512-AeuLfrciGYffqsp4EUTdYYc6Ee2BQS+hr08mHZk1C524SFWx0WnfcTnV0NFXbVURUNU6DZu1DhS89zRRrcx/hg==} + '@opentelemetry/otlp-grpc-exporter-base@0.206.0': + resolution: {integrity: sha512-IA8EDbrB8OKtidMqErBY8sUc9mh03LOXuNPwp4/rdPrxSt45g1gBuZMovRXdEWfRyKKbF2E7MdipT2m11bs6SQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/otlp-transformer@0.205.0': - resolution: {integrity: sha512-KmObgqPtk9k/XTlWPJHdMbGCylRAmMJNXIRh6VYJmvlRDMfe+DonH41G7eenG8t4FXn3fxOGh14o/WiMRR6vPg==} + '@opentelemetry/otlp-transformer@0.206.0': + resolution: {integrity: sha512-Li2Cik1WnmNbU2mmTnw7DxvRiXhMcnAuTfAclP8y/zy7h5+GrLDpTZ+Z0XUs+Q3MLkb/h3ry4uFrC/z+2a6X7g==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -3370,8 +3417,8 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/redis-common@0.38.0': - resolution: {integrity: sha512-4Wc0AWURII2cfXVVoZ6vDqK+s5n4K5IssdrlVrvGsx6OEOKdghKtJZqXAHWFiZv4nTDLH2/2fldjIHY8clMOjQ==} + '@opentelemetry/redis-common@0.38.2': + resolution: {integrity: sha512-1BCcU93iwSRZvDAgwUxC/DV4T/406SkMfxGqu5ojc3AvNI+I9GhV7v0J1HljsczuuhcnFLYqD5VmwVXfCGHzxA==} engines: {node: ^18.19.0 || >=20.6.0} '@opentelemetry/resources@2.1.0': @@ -3380,8 +3427,8 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/sdk-logs@0.205.0': - resolution: {integrity: sha512-nyqhNQ6eEzPWQU60Nc7+A5LIq8fz3UeIzdEVBQYefB4+msJZ2vuVtRuk9KxPMw1uHoHDtYEwkr2Ct0iG29jU8w==} + '@opentelemetry/sdk-logs@0.206.0': + resolution: {integrity: sha512-SQ2yTmqe4Mw9RI3a/glVkfjWPsXh6LySvnljXubiZq4zu+UP8NMJt2j82ZsYb+KpD7Eu+/41/7qlJnjdeVjz7Q==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.4.0 <1.10.0' @@ -3392,8 +3439,8 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.9.0 <1.10.0' - '@opentelemetry/sdk-node@0.205.0': - resolution: {integrity: sha512-Y4Wcs8scj/Wy1u61pX1ggqPXPtCsGaqx/UnFu7BtRQE1zCQR+b0h56K7I0jz7U2bRlPUZIFdnNLtoaJSMNzz2g==} + '@opentelemetry/sdk-node@0.206.0': + resolution: {integrity: sha512-t/2xYG1+pXK2M4jra48Gw6BS0E/hz4zYwuqC8G8JCTx9UQpCiFlaA3Yxbns79R1h6NbQz7NAe2kJO3/Jf9BoIA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' @@ -3414,8 +3461,8 @@ packages: resolution: {integrity: sha512-JD6DerIKdJGmRp4jQyX5FlrQjA4tjOw1cvfsPAZXfOOEErMUHjPcPSICS+6WnM0nB0efSFARh0KAZss+bvExOA==} engines: {node: '>=14'} - '@opentelemetry/sql-common@0.41.0': - resolution: {integrity: sha512-pmzXctVbEERbqSfiAgdes9Y63xjoOyXcD7B6IXBkVb+vbM7M9U98mn33nGXxPf4dfYR0M+vhcKRZmbSJ7HfqFA==} + '@opentelemetry/sql-common@0.41.2': + resolution: {integrity: sha512-4mhWm3Z8z+i508zQJ7r6Xi7y4mmoJpdvH0fZPFRkWrdp5fq7hhZ2HhYokEOLkfqSMgPR4Z9EyB3DBkbKGOqZiQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.1.0 @@ -3459,8 +3506,8 @@ packages: resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@playwright/test@1.55.0': - resolution: {integrity: sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ==} + '@playwright/test@1.56.0': + resolution: {integrity: sha512-Tzh95Twig7hUwwNe381/K3PggZBZblKUe2wv25oIpzWLr6Z0m4KgV1ZVIjnR6GM9ANEqjZD7XsZEa6JL/7YEgg==} engines: {node: '>=18'} hasBin: true @@ -3538,8 +3585,8 @@ packages: peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc - '@react-email/components@0.5.3': - resolution: {integrity: sha512-8G5vsoMehuGOT4cDqaYLdpagtqCYPl4vThXNylClxO6SrN2w9Mh1+i2RNGj/rdqh/woamHORjlXMYCA/kzDMew==} + '@react-email/components@0.5.6': + resolution: {integrity: sha512-3o9ellDaF3bBcVMWeos9HI0iUIT1zGygPRcn9WSfI5JREORiN6ViEJIvz5SKWEn1KPNZtw/iaW8ct7PpVyhomg==} engines: {node: '>=18.0.0'} peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc @@ -3603,8 +3650,8 @@ packages: peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc - '@react-email/render@1.2.3': - resolution: {integrity: sha512-qu3XYNkHGao3teJexVD5CrcgFkNLrzbZvpZN17a7EyQYUN3kHkTkE9saqY4VbvGx6QoNU3p8rsk/Xm++D/+pTw==} + '@react-email/render@1.3.2': + resolution: {integrity: sha512-oq8/BD/I/YspeuBjjdLJG6xaf9tsPYk+VWu8/mX9xWbRN0t0ExKSVm9sEBL6RsCpndQA2jbY2VgPEreIrzUgqw==} engines: {node: '>=18.0.0'} peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc @@ -3634,17 +3681,6 @@ packages: peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc - '@reduxjs/toolkit@1.9.7': - resolution: {integrity: sha512-t7v8ZPxhhKgOKtU+uyJT13lu4vL7az5aFi4IdoDs/eS548edn2M8Ik9h8fxgvMjGoAUVFSt6ZC1P5cWmQ014QQ==} - peerDependencies: - react: ^16.9.0 || ^17.0.0 || ^18 - react-redux: ^7.2.1 || ^8.0.2 - peerDependenciesMeta: - react: - optional: true - react-redux: - optional: true - '@rollup/pluginutils@5.3.0': resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} engines: {node: '>=14.0.0'} @@ -3654,108 +3690,113 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.50.1': - resolution: {integrity: sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==} + '@rollup/rollup-android-arm-eabi@4.52.4': + resolution: {integrity: sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.50.1': - resolution: {integrity: sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==} + '@rollup/rollup-android-arm64@4.52.4': + resolution: {integrity: sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.50.1': - resolution: {integrity: sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==} + '@rollup/rollup-darwin-arm64@4.52.4': + resolution: {integrity: sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.50.1': - resolution: {integrity: sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==} + '@rollup/rollup-darwin-x64@4.52.4': + resolution: {integrity: sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.50.1': - resolution: {integrity: sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==} + '@rollup/rollup-freebsd-arm64@4.52.4': + resolution: {integrity: sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.50.1': - resolution: {integrity: sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==} + '@rollup/rollup-freebsd-x64@4.52.4': + resolution: {integrity: sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.50.1': - resolution: {integrity: sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==} + '@rollup/rollup-linux-arm-gnueabihf@4.52.4': + resolution: {integrity: sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.50.1': - resolution: {integrity: sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==} + '@rollup/rollup-linux-arm-musleabihf@4.52.4': + resolution: {integrity: sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.50.1': - resolution: {integrity: sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==} + '@rollup/rollup-linux-arm64-gnu@4.52.4': + resolution: {integrity: sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.50.1': - resolution: {integrity: sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==} + '@rollup/rollup-linux-arm64-musl@4.52.4': + resolution: {integrity: sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.50.1': - resolution: {integrity: sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==} + '@rollup/rollup-linux-loong64-gnu@4.52.4': + resolution: {integrity: sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.50.1': - resolution: {integrity: sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==} + '@rollup/rollup-linux-ppc64-gnu@4.52.4': + resolution: {integrity: sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.50.1': - resolution: {integrity: sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==} + '@rollup/rollup-linux-riscv64-gnu@4.52.4': + resolution: {integrity: sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.50.1': - resolution: {integrity: sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==} + '@rollup/rollup-linux-riscv64-musl@4.52.4': + resolution: {integrity: sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.50.1': - resolution: {integrity: sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==} + '@rollup/rollup-linux-s390x-gnu@4.52.4': + resolution: {integrity: sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.50.1': - resolution: {integrity: sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==} + '@rollup/rollup-linux-x64-gnu@4.52.4': + resolution: {integrity: sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.50.1': - resolution: {integrity: sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==} + '@rollup/rollup-linux-x64-musl@4.52.4': + resolution: {integrity: sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.50.1': - resolution: {integrity: sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==} + '@rollup/rollup-openharmony-arm64@4.52.4': + resolution: {integrity: sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.50.1': - resolution: {integrity: sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==} + '@rollup/rollup-win32-arm64-msvc@4.52.4': + resolution: {integrity: sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.50.1': - resolution: {integrity: sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==} + '@rollup/rollup-win32-ia32-msvc@4.52.4': + resolution: {integrity: sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.50.1': - resolution: {integrity: sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==} + '@rollup/rollup-win32-x64-gnu@4.52.4': + resolution: {integrity: sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.52.4': + resolution: {integrity: sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==} cpu: [x64] os: [win32] @@ -3794,172 +3835,176 @@ packages: '@slorber/remark-comment@1.0.0': resolution: {integrity: sha512-RCE24n7jsOj1M0UPvIQCHTe7fI0sFL4S2nwKVWwHyVr/wI/H8GosgsJGyhnsZoGFnD/P2hLf1mSbrrgSLN93NA==} - '@smithy/abort-controller@4.1.1': - resolution: {integrity: sha512-vkzula+IwRvPR6oKQhMYioM3A/oX/lFCZiwuxkQbRhqJS2S4YRY2k7k/SyR2jMf3607HLtbEwlRxi0ndXHMjRg==} + '@smithy/abort-controller@4.2.0': + resolution: {integrity: sha512-PLUYa+SUKOEZtXFURBu/CNxlsxfaFGxSBPcStL13KpVeVWIfdezWyDqkz7iDLmwnxojXD0s5KzuB5HGHvt4Aeg==} engines: {node: '>=18.0.0'} - '@smithy/config-resolver@4.2.2': - resolution: {integrity: sha512-IT6MatgBWagLybZl1xQcURXRICvqz1z3APSCAI9IqdvfCkrA7RaQIEfgC6G/KvfxnDfQUDqFV+ZlixcuFznGBQ==} + '@smithy/config-resolver@4.3.0': + resolution: {integrity: sha512-9oH+n8AVNiLPK/iK/agOsoWfrKZ3FGP3502tkksd6SRsKMYiu7AFX0YXo6YBADdsAj7C+G/aLKdsafIJHxuCkQ==} engines: {node: '>=18.0.0'} - '@smithy/core@3.11.0': - resolution: {integrity: sha512-Abs5rdP1o8/OINtE49wwNeWuynCu0kme1r4RI3VXVrHr4odVDG7h7mTnw1WXXfN5Il+c25QOnrdL2y56USfxkA==} + '@smithy/core@3.15.0': + resolution: {integrity: sha512-VJWncXgt+ExNn0U2+Y7UywuATtRYaodGQKFo9mDyh70q+fJGedfrqi2XuKU1BhiLeXgg6RZrW7VEKfeqFhHAJA==} engines: {node: '>=18.0.0'} - '@smithy/credential-provider-imds@4.1.2': - resolution: {integrity: sha512-JlYNq8TShnqCLg0h+afqe2wLAwZpuoSgOyzhYvTgbiKBWRov+uUve+vrZEQO6lkdLOWPh7gK5dtb9dS+KGendg==} + '@smithy/credential-provider-imds@4.2.0': + resolution: {integrity: sha512-SOhFVvFH4D5HJZytb0bLKxCrSnwcqPiNlrw+S4ZXjMnsC+o9JcUQzbZOEQcA8yv9wJFNhfsUiIUKiEnYL68Big==} engines: {node: '>=18.0.0'} - '@smithy/fetch-http-handler@5.2.1': - resolution: {integrity: sha512-5/3wxKNtV3wO/hk1is+CZUhL8a1yy/U+9u9LKQ9kZTkMsHaQjJhc3stFfiujtMnkITjzWfndGA2f7g9Uh9vKng==} + '@smithy/fetch-http-handler@5.3.1': + resolution: {integrity: sha512-3AvYYbB+Dv5EPLqnJIAgYw/9+WzeBiUYS8B+rU0pHq5NMQMvrZmevUROS4V2GAt0jEOn9viBzPLrZE+riTNd5Q==} engines: {node: '>=18.0.0'} - '@smithy/hash-node@4.1.1': - resolution: {integrity: sha512-H9DIU9WBLhYrvPs9v4sYvnZ1PiAI0oc8CgNQUJ1rpN3pP7QADbTOUjchI2FB764Ub0DstH5xbTqcMJu1pnVqxA==} + '@smithy/hash-node@4.2.0': + resolution: {integrity: sha512-ugv93gOhZGysTctZh9qdgng8B+xO0cj+zN0qAZ+Sgh7qTQGPOJbMdIuyP89KNfUyfAqFSNh5tMvC+h2uCpmTtA==} engines: {node: '>=18.0.0'} - '@smithy/invalid-dependency@4.1.1': - resolution: {integrity: sha512-1AqLyFlfrrDkyES8uhINRlJXmHA2FkG+3DY8X+rmLSqmFwk3DJnvhyGzyByPyewh2jbmV+TYQBEfngQax8IFGg==} + '@smithy/invalid-dependency@4.2.0': + resolution: {integrity: sha512-ZmK5X5fUPAbtvRcUPtk28aqIClVhbfcmfoS4M7UQBTnDdrNxhsrxYVv0ZEl5NaPSyExsPWqL4GsPlRvtlwg+2A==} engines: {node: '>=18.0.0'} '@smithy/is-array-buffer@2.2.0': resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} engines: {node: '>=14.0.0'} - '@smithy/is-array-buffer@4.1.0': - resolution: {integrity: sha512-ePTYUOV54wMogio+he4pBybe8fwg4sDvEVDBU8ZlHOZXbXK3/C0XfJgUCu6qAZcawv05ZhZzODGUerFBPsPUDQ==} + '@smithy/is-array-buffer@4.2.0': + resolution: {integrity: sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==} engines: {node: '>=18.0.0'} - '@smithy/middleware-content-length@4.1.1': - resolution: {integrity: sha512-9wlfBBgTsRvC2JxLJxv4xDGNBrZuio3AgSl0lSFX7fneW2cGskXTYpFxCdRYD2+5yzmsiTuaAJD1Wp7gWt9y9w==} + '@smithy/middleware-content-length@4.2.0': + resolution: {integrity: sha512-6ZAnwrXFecrA4kIDOcz6aLBhU5ih2is2NdcZtobBDSdSHtE9a+MThB5uqyK4XXesdOCvOcbCm2IGB95birTSOQ==} engines: {node: '>=18.0.0'} - '@smithy/middleware-endpoint@4.2.2': - resolution: {integrity: sha512-M51KcwD+UeSOFtpALGf5OijWt915aQT5eJhqnMKJt7ZTfDfNcvg2UZgIgTZUoiORawb6o5lk4n3rv7vnzQXgsA==} + '@smithy/middleware-endpoint@4.3.1': + resolution: {integrity: sha512-JtM4SjEgImLEJVXdsbvWHYiJ9dtuKE8bqLlvkvGi96LbejDL6qnVpVxEFUximFodoQbg0Gnkyff9EKUhFhVJFw==} engines: {node: '>=18.0.0'} - '@smithy/middleware-retry@4.2.2': - resolution: {integrity: sha512-KZJueEOO+PWqflv2oGx9jICpHdBYXwCI19j7e2V3IMwKgFcXc9D9q/dsTf4B+uCnYxjNoS1jpyv6pGNGRsKOXA==} + '@smithy/middleware-retry@4.4.1': + resolution: {integrity: sha512-wXxS4ex8cJJteL0PPQmWYkNi9QKDWZIpsndr0wZI2EL+pSSvA/qqxXU60gBOJoIc2YgtZSWY/PE86qhKCCKP1w==} engines: {node: '>=18.0.0'} - '@smithy/middleware-serde@4.1.1': - resolution: {integrity: sha512-lh48uQdbCoj619kRouev5XbWhCwRKLmphAif16c4J6JgJ4uXjub1PI6RL38d3BLliUvSso6klyB/LTNpWSNIyg==} + '@smithy/middleware-serde@4.2.0': + resolution: {integrity: sha512-rpTQ7D65/EAbC6VydXlxjvbifTf4IH+sADKg6JmAvhkflJO2NvDeyU9qsWUNBelJiQFcXKejUHWRSdmpJmEmiw==} engines: {node: '>=18.0.0'} - '@smithy/middleware-stack@4.1.1': - resolution: {integrity: sha512-ygRnniqNcDhHzs6QAPIdia26M7e7z9gpkIMUe/pK0RsrQ7i5MblwxY8078/QCnGq6AmlUUWgljK2HlelsKIb/A==} + '@smithy/middleware-stack@4.2.0': + resolution: {integrity: sha512-G5CJ//eqRd9OARrQu9MK1H8fNm2sMtqFh6j8/rPozhEL+Dokpvi1Og+aCixTuwDAGZUkJPk6hJT5jchbk/WCyg==} engines: {node: '>=18.0.0'} - '@smithy/node-config-provider@4.2.2': - resolution: {integrity: sha512-SYGTKyPvyCfEzIN5rD8q/bYaOPZprYUPD2f5g9M7OjaYupWOoQFYJ5ho+0wvxIRf471i2SR4GoiZ2r94Jq9h6A==} + '@smithy/node-config-provider@4.3.0': + resolution: {integrity: sha512-5QgHNuWdT9j9GwMPPJCKxy2KDxZ3E5l4M3/5TatSZrqYVoEiqQrDfAq8I6KWZw7RZOHtVtCzEPdYz7rHZixwcA==} engines: {node: '>=18.0.0'} - '@smithy/node-http-handler@4.2.1': - resolution: {integrity: sha512-REyybygHlxo3TJICPF89N2pMQSf+p+tBJqpVe1+77Cfi9HBPReNjTgtZ1Vg73exq24vkqJskKDpfF74reXjxfw==} + '@smithy/node-http-handler@4.3.0': + resolution: {integrity: sha512-RHZ/uWCmSNZ8cneoWEVsVwMZBKy/8123hEpm57vgGXA3Irf/Ja4v9TVshHK2ML5/IqzAZn0WhINHOP9xl+Qy6Q==} engines: {node: '>=18.0.0'} - '@smithy/property-provider@4.1.1': - resolution: {integrity: sha512-gm3ZS7DHxUbzC2wr8MUCsAabyiXY0gaj3ROWnhSx/9sPMc6eYLMM4rX81w1zsMaObj2Lq3PZtNCC1J6lpEY7zg==} + '@smithy/property-provider@4.2.0': + resolution: {integrity: sha512-rV6wFre0BU6n/tx2Ztn5LdvEdNZ2FasQbPQmDOPfV9QQyDmsCkOAB0osQjotRCQg+nSKFmINhyda0D3AnjSBJw==} engines: {node: '>=18.0.0'} - '@smithy/protocol-http@5.2.1': - resolution: {integrity: sha512-T8SlkLYCwfT/6m33SIU/JOVGNwoelkrvGjFKDSDtVvAXj/9gOT78JVJEas5a+ETjOu4SVvpCstKgd0PxSu/aHw==} + '@smithy/protocol-http@5.3.0': + resolution: {integrity: sha512-6POSYlmDnsLKb7r1D3SVm7RaYW6H1vcNcTWGWrF7s9+2noNYvUsm7E4tz5ZQ9HXPmKn6Hb67pBDRIjrT4w/d7Q==} engines: {node: '>=18.0.0'} - '@smithy/querystring-builder@4.1.1': - resolution: {integrity: sha512-J9b55bfimP4z/Jg1gNo+AT84hr90p716/nvxDkPGCD4W70MPms0h8KF50RDRgBGZeL83/u59DWNqJv6tEP/DHA==} + '@smithy/querystring-builder@4.2.0': + resolution: {integrity: sha512-Q4oFD0ZmI8yJkiPPeGUITZj++4HHYCW3pYBYfIobUCkYpI6mbkzmG1MAQQ3lJYYWj3iNqfzOenUZu+jqdPQ16A==} engines: {node: '>=18.0.0'} - '@smithy/querystring-parser@4.1.1': - resolution: {integrity: sha512-63TEp92YFz0oQ7Pj9IuI3IgnprP92LrZtRAkE3c6wLWJxfy/yOPRt39IOKerVr0JS770olzl0kGafXlAXZ1vng==} + '@smithy/querystring-parser@4.2.0': + resolution: {integrity: sha512-BjATSNNyvVbQxOOlKse0b0pSezTWGMvA87SvoFoFlkRsKXVsN3bEtjCxvsNXJXfnAzlWFPaT9DmhWy1vn0sNEA==} engines: {node: '>=18.0.0'} - '@smithy/service-error-classification@4.1.1': - resolution: {integrity: sha512-Iam75b/JNXyDE41UvrlM6n8DNOa/r1ylFyvgruTUx7h2Uk7vDNV9AAwP1vfL1fOL8ls0xArwEGVcGZVd7IO/Cw==} + '@smithy/service-error-classification@4.2.0': + resolution: {integrity: sha512-Ylv1ttUeKatpR0wEOMnHf1hXMktPUMObDClSWl2TpCVT4DwtJhCeighLzSLbgH3jr5pBNM0LDXT5yYxUvZ9WpA==} engines: {node: '>=18.0.0'} - '@smithy/shared-ini-file-loader@4.2.0': - resolution: {integrity: sha512-OQTfmIEp2LLuWdxa8nEEPhZmiOREO6bcB6pjs0AySf4yiZhl6kMOfqmcwcY8BaBPX+0Tb+tG7/Ia/6mwpoZ7Pw==} + '@smithy/shared-ini-file-loader@4.3.0': + resolution: {integrity: sha512-VCUPPtNs+rKWlqqntX0CbVvWyjhmX30JCtzO+s5dlzzxrvSfRh5SY0yxnkirvc1c80vdKQttahL71a9EsdolSQ==} engines: {node: '>=18.0.0'} - '@smithy/signature-v4@5.2.1': - resolution: {integrity: sha512-M9rZhWQLjlQVCCR37cSjHfhriGRN+FQ8UfgrYNufv66TJgk+acaggShl3KS5U/ssxivvZLlnj7QH2CUOKlxPyA==} + '@smithy/signature-v4@5.3.0': + resolution: {integrity: sha512-MKNyhXEs99xAZaFhm88h+3/V+tCRDQ+PrDzRqL0xdDpq4gjxcMmf5rBA3YXgqZqMZ/XwemZEurCBQMfxZOWq/g==} engines: {node: '>=18.0.0'} - '@smithy/smithy-client@4.6.2': - resolution: {integrity: sha512-u82cjh/x7MlMat76Z38TRmEcG6JtrrxN4N2CSNG5o2v2S3hfLAxRgSgFqf0FKM3dglH41Evknt/HOX+7nfzZ3g==} + '@smithy/smithy-client@4.7.1': + resolution: {integrity: sha512-WXVbiyNf/WOS/RHUoFMkJ6leEVpln5ojCjNBnzoZeMsnCg3A0BRhLK3WYc4V7PmYcYPZh9IYzzAg9XcNSzYxYQ==} engines: {node: '>=18.0.0'} - '@smithy/types@4.5.0': - resolution: {integrity: sha512-RkUpIOsVlAwUIZXO1dsz8Zm+N72LClFfsNqf173catVlvRZiwPy0x2u0JLEA4byreOPKDZPGjmPDylMoP8ZJRg==} + '@smithy/types@4.6.0': + resolution: {integrity: sha512-4lI9C8NzRPOv66FaY1LL1O/0v0aLVrq/mXP/keUa9mJOApEeae43LsLd2kZRUJw91gxOQfLIrV3OvqPgWz1YsA==} engines: {node: '>=18.0.0'} - '@smithy/url-parser@4.1.1': - resolution: {integrity: sha512-bx32FUpkhcaKlEoOMbScvc93isaSiRM75pQ5IgIBaMkT7qMlIibpPRONyx/0CvrXHzJLpOn/u6YiDX2hcvs7Dg==} + '@smithy/url-parser@4.2.0': + resolution: {integrity: sha512-AlBmD6Idav2ugmoAL6UtR6ItS7jU5h5RNqLMZC7QrLCoITA9NzIN3nx9GWi8g4z1pfWh2r9r96SX/jHiNwPJ9A==} engines: {node: '>=18.0.0'} - '@smithy/util-base64@4.1.0': - resolution: {integrity: sha512-RUGd4wNb8GeW7xk+AY5ghGnIwM96V0l2uzvs/uVHf+tIuVX2WSvynk5CxNoBCsM2rQRSZElAo9rt3G5mJ/gktQ==} + '@smithy/util-base64@4.3.0': + resolution: {integrity: sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==} engines: {node: '>=18.0.0'} - '@smithy/util-body-length-browser@4.1.0': - resolution: {integrity: sha512-V2E2Iez+bo6bUMOTENPr6eEmepdY8Hbs+Uc1vkDKgKNA/brTJqOW/ai3JO1BGj9GbCeLqw90pbbH7HFQyFotGQ==} + '@smithy/util-body-length-browser@4.2.0': + resolution: {integrity: sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==} engines: {node: '>=18.0.0'} - '@smithy/util-body-length-node@4.1.0': - resolution: {integrity: sha512-BOI5dYjheZdgR9XiEM3HJcEMCXSoqbzu7CzIgYrx0UtmvtC3tC2iDGpJLsSRFffUpy8ymsg2ARMP5fR8mtuUQQ==} + '@smithy/util-body-length-node@4.2.1': + resolution: {integrity: sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==} engines: {node: '>=18.0.0'} '@smithy/util-buffer-from@2.2.0': resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} engines: {node: '>=14.0.0'} - '@smithy/util-buffer-from@4.1.0': - resolution: {integrity: sha512-N6yXcjfe/E+xKEccWEKzK6M+crMrlwaCepKja0pNnlSkm6SjAeLKKA++er5Ba0I17gvKfN/ThV+ZOx/CntKTVw==} + '@smithy/util-buffer-from@4.2.0': + resolution: {integrity: sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==} engines: {node: '>=18.0.0'} - '@smithy/util-config-provider@4.1.0': - resolution: {integrity: sha512-swXz2vMjrP1ZusZWVTB/ai5gK+J8U0BWvP10v9fpcFvg+Xi/87LHvHfst2IgCs1i0v4qFZfGwCmeD/KNCdJZbQ==} + '@smithy/util-config-provider@4.2.0': + resolution: {integrity: sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-browser@4.1.2': - resolution: {integrity: sha512-QKrOw01DvNHKgY+3p4r9Ut4u6EHLVZ01u6SkOMe6V6v5C+nRPXJeWh72qCT1HgwU3O7sxAIu23nNh+FOpYVZKA==} + '@smithy/util-defaults-mode-browser@4.3.0': + resolution: {integrity: sha512-H4MAj8j8Yp19Mr7vVtGgi7noJjvjJbsKQJkvNnLlrIFduRFT5jq5Eri1k838YW7rN2g5FTnXpz5ktKVr1KVgPQ==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-node@4.1.2': - resolution: {integrity: sha512-l2yRmSfx5haYHswPxMmCR6jGwgPs5LjHLuBwlj9U7nNBMS43YV/eevj+Xq1869UYdiynnMrCKtoOYQcwtb6lKg==} + '@smithy/util-defaults-mode-node@4.2.1': + resolution: {integrity: sha512-PuDcgx7/qKEMzV1QFHJ7E4/MMeEjaA7+zS5UNcHCLPvvn59AeZQ0DSDGMpqC2xecfa/1cNGm4l8Ec/VxCuY7Ug==} engines: {node: '>=18.0.0'} - '@smithy/util-endpoints@3.1.2': - resolution: {integrity: sha512-+AJsaaEGb5ySvf1SKMRrPZdYHRYSzMkCoK16jWnIMpREAnflVspMIDeCVSZJuj+5muZfgGpNpijE3mUNtjv01Q==} + '@smithy/util-endpoints@3.2.0': + resolution: {integrity: sha512-TXeCn22D56vvWr/5xPqALc9oO+LN+QpFjrSM7peG/ckqEPoI3zaKZFp+bFwfmiHhn5MGWPaLCqDOJPPIixk9Wg==} engines: {node: '>=18.0.0'} - '@smithy/util-hex-encoding@4.1.0': - resolution: {integrity: sha512-1LcueNN5GYC4tr8mo14yVYbh/Ur8jHhWOxniZXii+1+ePiIbsLZ5fEI0QQGtbRRP5mOhmooos+rLmVASGGoq5w==} + '@smithy/util-hex-encoding@4.2.0': + resolution: {integrity: sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==} engines: {node: '>=18.0.0'} - '@smithy/util-middleware@4.1.1': - resolution: {integrity: sha512-CGmZ72mL29VMfESz7S6dekqzCh8ZISj3B+w0g1hZFXaOjGTVaSqfAEFAq8EGp8fUL+Q2l8aqNmt8U1tglTikeg==} + '@smithy/util-middleware@4.2.0': + resolution: {integrity: sha512-u9OOfDa43MjagtJZ8AapJcmimP+K2Z7szXn8xbty4aza+7P1wjFmy2ewjSbhEiYQoW1unTlOAIV165weYAaowA==} engines: {node: '>=18.0.0'} - '@smithy/util-retry@4.1.1': - resolution: {integrity: sha512-jGeybqEZ/LIordPLMh5bnmnoIgsqnp4IEimmUp5c5voZ8yx+5kAlN5+juyr7p+f7AtZTgvhmInQk4Q0UVbrZ0Q==} + '@smithy/util-retry@4.2.0': + resolution: {integrity: sha512-BWSiuGbwRnEE2SFfaAZEX0TqaxtvtSYPM/J73PFVm+A29Fg1HTPiYFb8TmX1DXp4hgcdyJcNQmprfd5foeORsg==} engines: {node: '>=18.0.0'} - '@smithy/util-stream@4.3.1': - resolution: {integrity: sha512-khKkW/Jqkgh6caxMWbMuox9+YfGlsk9OnHOYCGVEdYQb/XVzcORXHLYUubHmmda0pubEDncofUrPNniS9d+uAA==} + '@smithy/util-stream@4.5.0': + resolution: {integrity: sha512-0TD5M5HCGu5diEvZ/O/WquSjhJPasqv7trjoqHyWjNh/FBeBl7a0ztl9uFMOsauYtRfd8jvpzIAQhDHbx+nvZw==} engines: {node: '>=18.0.0'} - '@smithy/util-uri-escape@4.1.0': - resolution: {integrity: sha512-b0EFQkq35K5NHUYxU72JuoheM6+pytEVUGlTwiFxWFpmddA+Bpz3LgsPRIpBk8lnPE47yT7AF2Egc3jVnKLuPg==} + '@smithy/util-uri-escape@4.2.0': + resolution: {integrity: sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==} engines: {node: '>=18.0.0'} '@smithy/util-utf8@2.3.0': resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} engines: {node: '>=14.0.0'} - '@smithy/util-utf8@4.1.0': - resolution: {integrity: sha512-mEu1/UIXAdNYuBcyEPbjScKi/+MQVXNIuY/7Cm5XLIWe319kDrT5SizBE95jqtmEXoDbGoZxKLCMttdZdqTZKQ==} + '@smithy/util-utf8@4.2.0': + resolution: {integrity: sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==} + engines: {node: '>=18.0.0'} + + '@smithy/uuid@1.1.0': + resolution: {integrity: sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==} engines: {node: '>=18.0.0'} '@socket.io/component-emitter@3.1.2': @@ -3974,25 +4019,25 @@ packages: '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} - '@sveltejs/acorn-typescript@1.0.5': - resolution: {integrity: sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==} + '@sveltejs/acorn-typescript@1.0.6': + resolution: {integrity: sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ==} peerDependencies: acorn: ^8.9.0 - '@sveltejs/adapter-static@3.0.9': - resolution: {integrity: sha512-aytHXcMi7lb9ljsWUzXYQ0p5X1z9oWud2olu/EpmH7aCu4m84h7QLvb5Wp+CFirKcwoNnYvYWhyP/L8Vh1ztdw==} + '@sveltejs/adapter-static@3.0.10': + resolution: {integrity: sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew==} peerDependencies: '@sveltejs/kit': ^2.0.0 - '@sveltejs/enhanced-img@0.8.1': - resolution: {integrity: sha512-Ibom8j6F9vdmOeR+ljQ4q7TEJV0FWN1kB5wJZ2S/GuDVgCrKYrsXBw5gpSKcHwGYpKA3o9EoijPhep/GHgRUWQ==} + '@sveltejs/enhanced-img@0.8.4': + resolution: {integrity: sha512-/L12VUQj+ANIhskHT620jtxAs9wUPUIutgo37rwx5NgLnxOpVsP27fMiSpdIMgERldbgr6uRJ1WZGGOkzm7Vcg==} peerDependencies: '@sveltejs/vite-plugin-svelte': ^6.0.0 svelte: ^5.0.0 vite: ^6.3.0 || >=7.0.0 - '@sveltejs/kit@2.38.1': - resolution: {integrity: sha512-5JJBPu3U2KXpRwc+e/D2Pl+DJM9oBcCl6XtWenrb6xc6H4lFa0XIJaSch4wMiADrhX512sVIUf13VnEp7aWO1w==} + '@sveltejs/kit@2.46.4': + resolution: {integrity: sha512-J1fd80WokLzIm6EAV7z7C2+/C02qVAX645LZomARARTRJkbbJSY1Jln3wtBZYibUB8c9/5Z6xqLAV39VdbtWCQ==} engines: {node: '>=18.13'} hasBin: true peerDependencies: @@ -4012,8 +4057,8 @@ packages: svelte: ^5.0.0 vite: ^6.3.0 || ^7.0.0 - '@sveltejs/vite-plugin-svelte@6.2.0': - resolution: {integrity: sha512-nJsV36+o7rZUDlrnSduMNl11+RoDE1cKqOI0yUEBCcqFoAZOk47TwD3dPKS2WmRutke9StXnzsPBslY7prDM9w==} + '@sveltejs/vite-plugin-svelte@6.2.1': + resolution: {integrity: sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==} engines: {node: ^20.19 || ^22.12 || >=24} peerDependencies: svelte: ^5.0.0 @@ -4179,65 +4224,65 @@ packages: resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} engines: {node: '>=14.16'} - '@tailwindcss/node@4.1.13': - resolution: {integrity: sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==} + '@tailwindcss/node@4.1.14': + resolution: {integrity: sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw==} - '@tailwindcss/oxide-android-arm64@4.1.13': - resolution: {integrity: sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==} + '@tailwindcss/oxide-android-arm64@4.1.14': + resolution: {integrity: sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ==} engines: {node: '>= 10'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.1.13': - resolution: {integrity: sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==} + '@tailwindcss/oxide-darwin-arm64@4.1.14': + resolution: {integrity: sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.1.13': - resolution: {integrity: sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==} + '@tailwindcss/oxide-darwin-x64@4.1.14': + resolution: {integrity: sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.1.13': - resolution: {integrity: sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==} + '@tailwindcss/oxide-freebsd-x64@4.1.14': + resolution: {integrity: sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw==} engines: {node: '>= 10'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13': - resolution: {integrity: sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14': + resolution: {integrity: sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.1.13': - resolution: {integrity: sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==} + '@tailwindcss/oxide-linux-arm64-gnu@4.1.14': + resolution: {integrity: sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-arm64-musl@4.1.13': - resolution: {integrity: sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==} + '@tailwindcss/oxide-linux-arm64-musl@4.1.14': + resolution: {integrity: sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-x64-gnu@4.1.13': - resolution: {integrity: sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==} + '@tailwindcss/oxide-linux-x64-gnu@4.1.14': + resolution: {integrity: sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-linux-x64-musl@4.1.13': - resolution: {integrity: sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==} + '@tailwindcss/oxide-linux-x64-musl@4.1.14': + resolution: {integrity: sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-wasm32-wasi@4.1.13': - resolution: {integrity: sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==} + '@tailwindcss/oxide-wasm32-wasi@4.1.14': + resolution: {integrity: sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ==} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -4248,24 +4293,24 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.1.13': - resolution: {integrity: sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==} + '@tailwindcss/oxide-win32-arm64-msvc@4.1.14': + resolution: {integrity: sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.1.13': - resolution: {integrity: sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==} + '@tailwindcss/oxide-win32-x64-msvc@4.1.14': + resolution: {integrity: sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.1.13': - resolution: {integrity: sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==} + '@tailwindcss/oxide@4.1.14': + resolution: {integrity: sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw==} engines: {node: '>= 10'} - '@tailwindcss/vite@4.1.13': - resolution: {integrity: sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ==} + '@tailwindcss/vite@4.1.14': + resolution: {integrity: sha512-BoFUoU0XqgCUS1UXWhmDJroKKhNXeDzD7/XwabjkDIAbMnc4ULn5e2FuEuBbhZ6ENZoSYzKlzvZ44Yr6EUDUSA==} peerDependencies: vite: ^5.2.0 || ^6 || ^7 @@ -4273,8 +4318,8 @@ packages: resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} engines: {node: '>=18'} - '@testing-library/jest-dom@6.8.0': - resolution: {integrity: sha512-WgXcWzVM6idy5JaftTVC8Vs83NKRmGJz4Hqs4oyOuO2J4r/y79vvKZsb+CaGyCSEbUPI6OsewfPd0G1A0/TUZQ==} + '@testing-library/jest-dom@6.9.1': + resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} '@testing-library/svelte@5.2.8': @@ -4397,8 +4442,8 @@ packages: '@types/docker-modem@3.0.6': resolution: {integrity: sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==} - '@types/dockerode@3.3.42': - resolution: {integrity: sha512-U1jqHMShibMEWHdxYhj3rCMNCiLx5f35i4e3CEUuW+JSSszc/tVqc6WCAPdhwBymG5R/vgbcceagK0St7Cq6Eg==} + '@types/dockerode@3.3.44': + resolution: {integrity: sha512-fUpIHlsbYpxAJb285xx3vp7q5wf5mjqSn3cYwl/MhiM+DB99OdO5sOCPlO0PjO+TyOtphPs7tMVLU/RtOo/JjA==} '@types/dom-to-image@2.6.7': resolution: {integrity: sha512-me5VbCv+fcXozblWwG13krNBvuEOm6kA5xoa4RrjDJCNFOZSWR3/QLtOXimBHk1Fisq69Gx3JtOoXtg1N1tijg==} @@ -4457,9 +4502,6 @@ packages: '@types/history@4.7.11': resolution: {integrity: sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==} - '@types/hoist-non-react-statics@3.3.6': - resolution: {integrity: sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==} - '@types/html-minifier-terser@6.1.0': resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==} @@ -4514,9 +4556,6 @@ packages: '@types/lodash@4.17.20': resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} - '@types/luxon@3.6.2': - resolution: {integrity: sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==} - '@types/luxon@3.7.1': resolution: {integrity: sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==} @@ -4550,20 +4589,20 @@ packages: '@types/node@17.0.45': resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} - '@types/node@18.19.126': - resolution: {integrity: sha512-8AXQlBfrGmtYJEJUPs63F/uZQqVeFiN9o6NUjbDJYfxNxFnArlZufANPw4h6dGhYGKxcyw+TapXFvEsguzIQow==} + '@types/node@18.19.130': + resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} - '@types/node@20.19.2': - resolution: {integrity: sha512-9pLGGwdzOUBDYi0GNjM97FIA+f92fqSke6joWeBjWXllfNxZBs7qeMF7tvtOIsbY45xkWkxrdwUfUf3MnQa9gA==} + '@types/node@20.19.21': + resolution: {integrity: sha512-CsGG2P3I5y48RPMfprQGfy4JPRZ6csfC3ltBZSRItG3ngggmNY/qs2uZKp4p9VbrpqNNSMzUZNFZKzgOGnd/VA==} - '@types/node@22.18.5': - resolution: {integrity: sha512-g9BpPfJvxYBXUWI9bV37j6d6LTMNQ88hPwdWWUeYZnMhlo66FIg9gCc1/DZb15QylJSKwOZjwrckvOTWpOiChg==} + '@types/node@22.18.10': + resolution: {integrity: sha512-anNG/V/Efn/YZY4pRzbACnKxNKoBng2VTFydVu8RRs5hQjikP8CQfaeAV59VFSCzKNp90mXiVXW2QzV56rwMrg==} - '@types/node@24.5.1': - resolution: {integrity: sha512-/SQdmUP2xa+1rdx7VwB9yPq8PaKej8TD5cQ+XfKDPWWC+VDJU4rvVVagXqKUzhKjtFoNA8rXDJAkCxQPAe00+Q==} + '@types/node@24.7.2': + resolution: {integrity: sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==} - '@types/nodemailer@7.0.1': - resolution: {integrity: sha512-UfHAghPmGZVzaL8x9y+mKZMWyHC399+iq0MOmya5tIyenWX3lcdSb60vOmp0DocR6gCDTYTozv/ULQnREyyjkg==} + '@types/nodemailer@7.0.2': + resolution: {integrity: sha512-Zo6uOA9157WRgBk/ZhMpTQ/iCWLMk7OIs/Q9jvHarMvrzUUP/MDdPHL2U1zpf57HrrWGv4nYQn5uIxna0xY3xw==} '@types/oidc-provider@9.5.0': resolution: {integrity: sha512-eEzCRVTSqIHD9Bo/qRJ4XQWQ5Z/zBcG+Z2cGJluRsSuWx1RJihqRyPxhIEpMXTwPzHYRTQkVp7hwisQOwzzSAg==} @@ -4595,9 +4634,6 @@ packages: '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - '@types/react-redux@7.1.34': - resolution: {integrity: sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==} - '@types/react-router-config@5.0.11': resolution: {integrity: sha512-WmSAg7WgqW7m4x8Mt4N6ZyKz0BubSj/2tVUMsAHp+Yd2AMwcSbeFq9WympT19p5heCFmF97R9eD5uUR/t4HEqw==} @@ -4607,14 +4643,14 @@ packages: '@types/react-router@5.1.20': resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==} - '@types/react@19.1.13': - resolution: {integrity: sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==} + '@types/react@19.2.2': + resolution: {integrity: sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==} '@types/readdir-glob@1.1.5': resolution: {integrity: sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==} - '@types/retry@0.12.0': - resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} + '@types/retry@0.12.2': + resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==} '@types/sanitize-html@2.16.0': resolution: {integrity: sha512-l6rX1MUXje5ztPT0cAFtUayXF06DqPhRyfVXareEN5gGCFaP/iwsxIyKODr9XDhfxPpN6vXUFNfo5kZMXCxBtw==} @@ -4667,9 +4703,6 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@types/uuid@9.0.8': - resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - '@types/validator@13.15.3': resolution: {integrity: sha512-7bcUmDyS6PN3EuD9SlGGOxM77F8WLVsrwkxyWxKnxzmXoequ6c7741QBrANq6htVRGOITJ7z72mTP6Z4XyuG+Q==} @@ -4685,63 +4718,63 @@ packages: '@types/yargs@17.0.33': resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - '@typescript-eslint/eslint-plugin@8.43.0': - resolution: {integrity: sha512-8tg+gt7ENL7KewsKMKDHXR1vm8tt9eMxjJBYINf6swonlWgkYn5NwyIgXpbbDxTNU5DgpDFfj95prcTq2clIQQ==} + '@typescript-eslint/eslint-plugin@8.46.0': + resolution: {integrity: sha512-hA8gxBq4ukonVXPy0OKhiaUh/68D0E88GSmtC1iAEnGaieuDi38LhS7jdCHRLi6ErJBNDGCzvh5EnzdPwUc0DA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.43.0 + '@typescript-eslint/parser': ^8.46.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.43.0': - resolution: {integrity: sha512-B7RIQiTsCBBmY+yW4+ILd6mF5h1FUwJsVvpqkrgpszYifetQ2Ke+Z4u6aZh0CblkUGIdR59iYVyXqqZGkZ3aBw==} + '@typescript-eslint/parser@8.46.0': + resolution: {integrity: sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.43.0': - resolution: {integrity: sha512-htB/+D/BIGoNTQYffZw4uM4NzzuolCoaA/BusuSIcC8YjmBYQioew5VUZAYdAETPjeed0hqCaW7EHg+Robq8uw==} + '@typescript-eslint/project-service@8.46.0': + resolution: {integrity: sha512-OEhec0mH+U5Je2NZOeK1AbVCdm0ChyapAyTeXVIYTPXDJ3F07+cu87PPXcGoYqZ7M9YJVvFnfpGg1UmCIqM+QQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.43.0': - resolution: {integrity: sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg==} + '@typescript-eslint/scope-manager@8.46.0': + resolution: {integrity: sha512-lWETPa9XGcBes4jqAMYD9fW0j4n6hrPtTJwWDmtqgFO/4HF4jmdH/Q6wggTw5qIT5TXjKzbt7GsZUBnWoO3dqw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.43.0': - resolution: {integrity: sha512-ALC2prjZcj2YqqL5X/bwWQmHA2em6/94GcbB/KKu5SX3EBDOsqztmmX1kMkvAJHzxk7TazKzJfFiEIagNV3qEA==} + '@typescript-eslint/tsconfig-utils@8.46.0': + resolution: {integrity: sha512-WrYXKGAHY836/N7zoK/kzi6p8tXFhasHh8ocFL9VZSAkvH956gfeRfcnhs3xzRy8qQ/dq3q44v1jvQieMFg2cw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.43.0': - resolution: {integrity: sha512-qaH1uLBpBuBBuRf8c1mLJ6swOfzCXryhKND04Igr4pckzSEW9JX5Aw9AgW00kwfjWJF0kk0ps9ExKTfvXfw4Qg==} + '@typescript-eslint/type-utils@8.46.0': + resolution: {integrity: sha512-hy+lvYV1lZpVs2jRaEYvgCblZxUoJiPyCemwbQZ+NGulWkQRy0HRPYAoef/CNSzaLt+MLvMptZsHXHlkEilaeg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.43.0': - resolution: {integrity: sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw==} + '@typescript-eslint/types@8.46.0': + resolution: {integrity: sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.43.0': - resolution: {integrity: sha512-7Vv6zlAhPb+cvEpP06WXXy/ZByph9iL6BQRBDj4kmBsW98AqEeQHlj/13X+sZOrKSo9/rNKH4Ul4f6EICREFdw==} + '@typescript-eslint/typescript-estree@8.46.0': + resolution: {integrity: sha512-ekDCUfVpAKWJbRfm8T1YRrCot1KFxZn21oV76v5Fj4tr7ELyk84OS+ouvYdcDAwZL89WpEkEj2DKQ+qg//+ucg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.43.0': - resolution: {integrity: sha512-S1/tEmkUeeswxd0GGcnwuVQPFWo8NzZTOMxCvw8BX7OMxnNae+i8Tm7REQen/SwUIPoPqfKn7EaZ+YLpiB3k9g==} + '@typescript-eslint/utils@8.46.0': + resolution: {integrity: sha512-nD6yGWPj1xiOm4Gk0k6hLSZz2XkNXhuYmyIrOWcHoPuAhjT9i5bAG+xbWPgFeNR8HPHHtpNKdYUXJl/D3x7f5g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.43.0': - resolution: {integrity: sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw==} + '@typescript-eslint/visitor-keys@8.46.0': + resolution: {integrity: sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.0': @@ -4836,11 +4869,11 @@ packages: '@xtuc/long@4.2.2': resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} - '@zoom-image/core@0.41.0': - resolution: {integrity: sha512-LAxGru91286gFmyiQB4RjM277YOWxJX+OZcwtIH/N0dyo73y4NfaAE1eGVdnhjxEYv7yVV3xToMyYnm+uQboTw==} + '@zoom-image/core@0.41.3': + resolution: {integrity: sha512-yW2sZRP6FnZZ7vREpYucDpSCsVFgMSWq22Sp1kabsMPz5+6LSs+sBPPDCVs/ahVYSHa9YOX8rlGHM5iiTFPZLA==} - '@zoom-image/svelte@0.3.4': - resolution: {integrity: sha512-8cPkFUjh+t3/eYkoT2krvz8hoFiXoiYZKpcHOnYCHLhEwaHr1yjgXg/ttWehotVH9V3Z51JQgIcGF3uhYWKB/Q==} + '@zoom-image/svelte@0.3.7': + resolution: {integrity: sha512-vCgA2ClH8J2JBTzpJEgzy03yTi5uJE0963cUjBwc5fi7q8g1wxbbj4/W7YHwQfDmdh9uqHxlX0VcmUQqV44d+w==} peerDependencies: svelte: ^3.0.0 || ^4.0.0 || ^5.0.0 @@ -4911,14 +4944,6 @@ packages: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} - ajv-draft-04@1.0.0: - resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} - peerDependencies: - ajv: ^8.5.0 - peerDependenciesMeta: - ajv: - optional: true - ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -4948,9 +4973,6 @@ packages: ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - ajv@8.11.0: - resolution: {integrity: sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==} - ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} @@ -4959,8 +4981,8 @@ packages: peerDependencies: algoliasearch: '>= 3.1 < 6' - algoliasearch@5.29.0: - resolution: {integrity: sha512-E2l6AlTWGznM2e7vEE6T6hzObvEyXukxMOlBmVlMyixZyK1umuO/CiVc6sDBbzVH0oEviCE5IfVY1oZBmccYPQ==} + algoliasearch@5.40.0: + resolution: {integrity: sha512-a9aIL2E3Z7uYUPMCmjMFFd5MWhn+ccTubEvnMy7rOTZCB62dXBJtz0R5BZ/TPuX3R9ocBsgWuAbGWQ+Ph4Fmlg==} engines: {node: '>= 14.0.0'} ansi-align@3.0.1: @@ -5087,12 +5109,6 @@ packages: async@0.2.10: resolution: {integrity: sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==} - async@3.2.2: - resolution: {integrity: sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==} - - async@3.2.4: - resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} - async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} @@ -5150,11 +5166,11 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - bare-events@2.6.1: - resolution: {integrity: sha512-AuTJkq9XmE6Vk0FJVNq5QxETrSA/vKHarWVBG5l/JbdCL1prJemiyJqUS0jrlXO0MftuPq4m3YVYhoNc5+aE/g==} + bare-events@2.7.0: + resolution: {integrity: sha512-b3N5eTW1g7vXkw+0CXh/HazGTcO5KYuu/RCNaJbDMPI6LHDi+7qe8EmxKUVe1sUbY2KZOVZFyj62x0OEz9qyAA==} - bare-fs@4.2.0: - resolution: {integrity: sha512-oRfrw7gwwBVAWx9S5zPMo2iiOjxyiZE12DmblmMQREgcogbNO0AFaZ+QBxxkEXiPspcpvO/Qtqn8LabUx4uYXg==} + bare-fs@4.4.7: + resolution: {integrity: sha512-huJQxUWc2d1T+6dxnC/FoYpBgEHzJp33mYZqFtQqTTPPyP9xPvmjC16VpR4wTte4ZKd5VxkFAcfDYi51iwWMcg==} engines: {bare: '>=1.16.0'} peerDependencies: bare-buffer: '*' @@ -5162,8 +5178,8 @@ packages: bare-buffer: optional: true - bare-os@3.6.1: - resolution: {integrity: sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g==} + bare-os@3.6.2: + resolution: {integrity: sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==} engines: {bare: '>=1.14.0'} bare-path@3.0.0: @@ -5180,6 +5196,9 @@ packages: bare-events: optional: true + bare-url@2.2.2: + resolution: {integrity: sha512-g+ueNGKkrjMazDG3elZO1pNs3HY5+mMmOet1jtKyhOaCnkLzitxf26z7hoAEkDNgdNmnc1KIlt/dw6Po6xZMpA==} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -5187,6 +5206,10 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} + baseline-browser-mapping@2.8.15: + resolution: {integrity: sha512-qsJ8/X+UypqxHXN75M7dF88jNK37dLBRW7LeUzCPz+TNs37G8cfWy9nWzS+LS//g600zrt2le9KuXt0rWfDz5Q==} + hasBin: true + batch-cluster@13.0.0: resolution: {integrity: sha512-EreW0Vi8TwovhYUHBXXRA5tthuU2ynGsZFlboyMJHCCUXYa2AjgwnE3ubBOJs2xJLcuXFJbi6c/8pH5+FVj8Og==} engines: {node: '>=14'} @@ -5256,8 +5279,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.25.3: - resolution: {integrity: sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==} + browserslist@4.26.3: + resolution: {integrity: sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -5282,8 +5305,12 @@ packages: resolution: {integrity: sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==} engines: {node: '>=18.20'} - bullmq@5.58.5: - resolution: {integrity: sha512-0A6Qjxdn8j7aOcxfRZY798vO/aMuwvoZwfE6a9EOXHb1pzpBVAogsc/OfRWeUf+5wMBoYB5nthstnJo/zrQOeQ==} + bullmq@5.61.0: + resolution: {integrity: sha512-khaTjc1JnzaYFl4FrUtsSsqugAW/urRrcZ9Q0ZE+REAw8W+gkHFqxbGlutOu6q7j7n91wibVaaNlOUMdiEvoSQ==} + + bundle-name@4.1.0: + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + engines: {node: '>=18'} busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} @@ -5338,9 +5365,6 @@ packages: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} - call-me-maybe@1.0.2: - resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} - callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -5367,8 +5391,8 @@ packages: caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} - caniuse-lite@1.0.30001735: - resolution: {integrity: sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==} + caniuse-lite@1.0.30001749: + resolution: {integrity: sha512-0rw2fJOmLfnzCRbkm8EyHL8SvI2Apu5UbnQuTsJ0ClgrH8hcwFooJ1s5R0EP8o8aVrFu8++ae29Kt9/gZAZp/Q==} canvas@2.11.2: resolution: {integrity: sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==} @@ -5411,10 +5435,6 @@ packages: chardet@2.1.0: resolution: {integrity: sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==} - charset@1.0.1: - resolution: {integrity: sha512-6dVyOOYjpfFcL1Y4qChrAoQLRHvj2ziyhcm0QJlhOcAhykL/k1kTUPbeo+87MNRTRdk2OIIsIXbuF3x2wi5EXg==} - engines: {node: '>=4.0.0'} - check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} @@ -5532,10 +5552,6 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} - clsx@1.2.1: - resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} - engines: {node: '>=6'} - clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -5554,17 +5570,10 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - color-string@1.9.1: - resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} - color-support@1.1.3: resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} hasBin: true - color@4.2.3: - resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} - engines: {node: '>=12.5.0'} - colord@2.9.3: resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} @@ -5620,8 +5629,8 @@ packages: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} - comment-json@4.2.5: - resolution: {integrity: sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==} + comment-json@4.4.1: + resolution: {integrity: sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==} engines: {node: '>= 6'} common-path-prefix@3.0.0: @@ -5642,12 +5651,6 @@ packages: resolution: {integrity: sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==} engines: {node: '>= 0.8.0'} - compute-gcd@1.2.1: - resolution: {integrity: sha512-TwMbxBNz0l71+8Sc4czv13h4kEqnchV9igQZBi6QUaz09dnz13juGnnaWWJTRsP3brxOoxeB4SA2WELLw1hCtg==} - - compute-lcm@1.1.2: - resolution: {integrity: sha512-OFNPdQAXnQhDSKioX8/XYT6sdUlXwpeMjfd6ApxMJfyZ4GxmLR1xvMERctlYhlHwIiz6CSpBc2+qYKjHGZw4TQ==} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -5729,10 +5732,6 @@ packages: resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==} engines: {node: '>= 0.8'} - copy-text-to-clipboard@3.2.0: - resolution: {integrity: sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q==} - engines: {node: '>=12'} - copy-webpack-plugin@11.0.0: resolution: {integrity: sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==} engines: {node: '>= 14.15.0'} @@ -5781,17 +5780,14 @@ packages: resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} engines: {node: '>=12.0.0'} - cron@4.3.0: - resolution: {integrity: sha512-ciiYNLfSlF9MrDqnbMdRWFiA6oizSF7kA1osPP9lRzNu0Uu+AWog1UKy7SkckiDY2irrNjeO6qLyKnXC8oxmrw==} + cron@4.3.3: + resolution: {integrity: sha512-B/CJj5yL3sjtlun6RtYHvoSB26EmQ2NUmhq9ZiJSyKIM4K/fqfh9aelDFlIayD2YMeFZqWLi9hHV+c+pq2Djkw==} engines: {node: '>=18.x'} cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - crypto-js@4.2.0: - resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} - crypto-random-string@4.0.0: resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} engines: {node: '>=12'} @@ -6026,9 +6022,13 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} - default-gateway@6.0.3: - resolution: {integrity: sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==} - engines: {node: '>= 10'} + default-browser-id@5.0.0: + resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==} + engines: {node: '>=18'} + + default-browser@5.2.1: + resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==} + engines: {node: '>=18'} defaults@1.0.4: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} @@ -6045,6 +6045,10 @@ packages: resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} engines: {node: '>=8'} + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + define-properties@1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} @@ -6079,17 +6083,13 @@ packages: detect-europe-js@0.1.2: resolution: {integrity: sha512-lgdERlL3u0aUdHocoouzT10d9I89VVhk0qNRmll7mXdGfJT1/wqZ2ZLA4oJAjeACPY5fT1wsbq2AT+GkuInsow==} - detect-libc@2.1.0: - resolution: {integrity: sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} detect-node@2.1.0: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} - detect-package-manager@3.0.2: - resolution: {integrity: sha512-8JFjJHutStYrfWwzfretQoyNGoZVW1Fsrp4JO9spa7h/fBfwgTMEIy4/LBzRDGsxwVPHU0q+T9YvwLDJoOApLQ==} - engines: {node: '>=12'} - detect-port@1.6.1: resolution: {integrity: sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==} engines: {node: '>= 4.0.0'} @@ -6131,16 +6131,16 @@ packages: resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} engines: {node: '>=6'} - docker-compose@1.2.0: - resolution: {integrity: sha512-wIU1eHk3Op7dFgELRdmOYlPYS4gP8HhH1ZmZa13QZF59y0fblzFDFmKPhyc05phCy2hze9OEvNZAsoljrs+72w==} + docker-compose@1.3.0: + resolution: {integrity: sha512-7Gevk/5eGD50+eMD+XDnFnOrruFkL0kSd7jEG4cjmqweDSUhB7i0g8is/nBdVpl+Bx338SqIB2GLKm32M+Vs6g==} engines: {node: '>= 6.0.0'} docker-modem@5.0.6: resolution: {integrity: sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==} engines: {node: '>= 8.0'} - dockerode@4.0.7: - resolution: {integrity: sha512-R+rgrSRTRdU5mH14PZTCPZtW/zw3HDWNTS/1ZAQpL/5Upe/ye5K9WQkIysu4wBoiMwKynsz0a8qWuGsHgEvSAA==} + dockerode@4.0.9: + resolution: {integrity: sha512-iND4mcOWhPaCNh54WmK/KoSb35AFqPAUWFMffTQcp52uQt36b5uNwEJTSXntJZBbeGad72Crbi/hvDIv6us/6Q==} engines: {node: '>= 8.0'} docusaurus-lunr-search@3.6.0: @@ -6151,31 +6151,6 @@ packages: react: ^16.8.4 || ^17 || ^18 || ^19 react-dom: ^16.8.4 || ^17 || ^18 || ^19 - docusaurus-plugin-openapi@0.7.6: - resolution: {integrity: sha512-LR8DI0gO9WFy8K+r0xrVgqDkKKA9zQtDgOnX9CatP3I3Oz5lKegfTJM2fVUIp5m25elzHL+vVKNHS12Jg7sWVA==} - engines: {node: '>=18'} - peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 - - docusaurus-plugin-proxy@0.7.6: - resolution: {integrity: sha512-MgjzMEsQOHMljwQGglXXoGjQvs0v1DklhRgzqNLKFwpHB9xLWJZ0KQ3GgbPerW/2vy8tWGJeVhKHy5cPrmweUw==} - engines: {node: '>=14'} - - docusaurus-preset-openapi@0.7.6: - resolution: {integrity: sha512-QnArH/3X0lePB7667FyNK3EeTS8ZP8V2PQxz5m+3BMO2kIzdXDwfTIQ37boB0BTqsDfUE0yCWTVjB0W/BA1UXA==} - engines: {node: '>=18'} - peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 - - docusaurus-theme-openapi@0.7.6: - resolution: {integrity: sha512-euoEh8tYX/ssQcMQxBOxt3wPttz3zvPu0l5lSe6exiIwMrORB4O2b8XRB7fVa/awF7xzdIkKHMH55uc5zVOKYA==} - engines: {node: '>=18'} - peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 - dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} @@ -6223,8 +6198,8 @@ packages: resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} engines: {node: '>=10'} - dotenv@17.2.2: - resolution: {integrity: sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==} + dotenv@17.2.3: + resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} engines: {node: '>=12'} dunder-proto@1.0.1: @@ -6246,11 +6221,11 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.207: - resolution: {integrity: sha512-mryFrrL/GXDTmAtIVMVf+eIXM09BBPlO5IQ7lUyKmK8d+A4VpRGG+M3ofoVef6qyF8s60rJei8ymlJxjUA8Faw==} + electron-to-chromium@1.5.233: + resolution: {integrity: sha512-iUdTQSf7EFXsDdQsp8MwJz5SVk4APEFqXU/S47OtQ0YLqacSwPXdZ5vRlMX3neb07Cy2vgioNuRnWUXFwuslkg==} - emoji-regex@10.5.0: - resolution: {integrity: sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==} + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -6344,9 +6319,6 @@ packages: es6-iterator@2.0.3: resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} - es6-promise@3.3.1: - resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} - es6-symbol@3.1.4: resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} engines: {node: '>=0.12'} @@ -6365,8 +6337,8 @@ packages: engines: {node: '>=12'} hasBin: true - esbuild@0.25.9: - resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} + esbuild@0.25.10: + resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==} engines: {node: '>=18'} hasBin: true @@ -6404,12 +6376,6 @@ packages: peerDependencies: eslint: '>=7.0.0' - eslint-p@0.26.0: - resolution: {integrity: sha512-Y5bDWKIFEUE7dZrbBbq5SiHWadYC4h3+Q+xBAUNNAqU1VMokleoGGfK92Qsmi+EBOLUBbxrtOCND5BSqQn8NaQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - deprecated: ESLint has built-in support for multithread linting now. This package is no longer needed. - hasBin: true - eslint-plugin-compat@6.0.2: resolution: {integrity: sha512-1ME+YfJjmOz1blH0nPZpHgjMGK4kjgEeoYqGCqoBPQ/mGu/dJzdoP0f1C8H2jcWZjzhZjAMccbM/VdXhPORIfA==} engines: {node: '>=18.x'} @@ -6430,8 +6396,8 @@ packages: eslint-config-prettier: optional: true - eslint-plugin-svelte@3.12.3: - resolution: {integrity: sha512-YVNhKsHZeXVvsjZcSMjnce9gO31frICu453p5JjFiXNszHoG9k8WvsA/LAoLi4K8T69G7DIrgg1AqasDJLpgoQ==} + eslint-plugin-svelte@3.12.4: + resolution: {integrity: sha512-hD7wPe+vrPgx3U2X2b/wyTMtWobm660PygMGKrWWYTc9lvtY8DpNFDaU2CJQn1szLjGbn/aJ3g8WiXuKakrEkw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.1 || ^9.0.0 @@ -6446,6 +6412,12 @@ packages: peerDependencies: eslint: '>=9.29.0' + eslint-plugin-unicorn@61.0.2: + resolution: {integrity: sha512-zLihukvneYT7f74GNbVJXfWIiNQmkc/a9vYBTE4qPkQZswolWNdu+Wsp9sIXno1JOzdn6OUwLPd19ekXVkahRA==} + engines: {node: ^20.10.0 || >=21.0.0} + peerDependencies: + eslint: '>=9.29.0' + eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} @@ -6462,8 +6434,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.35.0: - resolution: {integrity: sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==} + eslint@9.37.0: + resolution: {integrity: sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -6564,6 +6536,9 @@ packages: eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + events-universal@1.0.1: + resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} + events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} @@ -6644,8 +6619,8 @@ packages: fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - fast-uri@3.0.6: - resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} fast-xml-parser@5.2.5: resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==} @@ -6698,10 +6673,6 @@ packages: resolution: {integrity: sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg==} engines: {node: '>=20'} - file-type@3.9.0: - resolution: {integrity: sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==} - engines: {node: '>=0.10.0'} - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -6759,9 +6730,6 @@ packages: debug: optional: true - foreach@2.0.6: - resolution: {integrity: sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==} - foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -6785,9 +6753,6 @@ packages: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} - formidable@2.1.5: - resolution: {integrity: sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==} - formidable@3.5.4: resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==} engines: {node: '>=14.0.0'} @@ -6901,10 +6866,6 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-stdin@5.0.1: - resolution: {integrity: sha512-jZV7n6jGE3Gt7fgSTJoz91Ak5MuTLwMwkoYdjxuJ/AmjIsE1UC03y/IWkZCQGEvVNS9qoRNwy5BCqxImv0FVeA==} - engines: {node: '>=0.12.0'} - get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} @@ -6923,6 +6884,12 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob-to-regex.js@1.2.0: + resolution: {integrity: sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} @@ -6990,9 +6957,6 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - graphlib@2.1.8: - resolution: {integrity: sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==} - gray-matter@4.0.3: resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} engines: {node: '>=6.0'} @@ -7012,18 +6976,14 @@ packages: engines: {node: '>=0.4.7'} hasBin: true - happy-dom@18.0.1: - resolution: {integrity: sha512-qn+rKOW7KWpVTtgIUi6RVmTBZJSe2k0Db0vh1f7CWrWclkkc7/Q+FrOfkZIb2eiErLyqu5AXEzE7XthO9JVxRA==} + happy-dom@20.0.2: + resolution: {integrity: sha512-pYOyu624+6HDbY+qkjILpQGnpvZOusItCk+rvF5/V+6NkcgTKnbOldpIy22tBnxoaLtlM9nXgoqAcW29/B7CIw==} engines: {node: '>=20.0.0'} has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - has-own-prop@2.0.0: - resolution: {integrity: sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==} - engines: {node: '>=8'} - has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} @@ -7061,9 +7021,6 @@ packages: hast-util-parse-selector@2.2.5: resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==} - hast-util-parse-selector@3.1.1: - resolution: {integrity: sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==} - hast-util-parse-selector@4.0.0: resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} @@ -7097,9 +7054,6 @@ packages: hastscript@6.0.0: resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==} - hastscript@7.2.0: - resolution: {integrity: sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==} - hastscript@9.0.1: resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} @@ -7107,6 +7061,10 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + highlight.js@11.11.1: + resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} + engines: {node: '>=12.0.0'} + history@4.10.1: resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==} @@ -7128,9 +7086,6 @@ packages: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} - html-entities@2.6.0: - resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} - html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -7219,12 +7174,6 @@ packages: resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} engines: {node: '>=8.0.0'} - http-reasons@0.1.0: - resolution: {integrity: sha512-P6kYh0lKZ+y29T2Gqz+RlC9WBLhKe8kDmcJ+A+611jFfxdPsbMRQ5aNmFRM3lENqFkK+HTTL+tlQviAiv0AbLQ==} - - http2-client@1.3.5: - resolution: {integrity: sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==} - http2-wrapper@2.2.1: resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} engines: {node: '>=10.19.0'} @@ -7241,6 +7190,10 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} + hyperdyperid@1.2.0: + resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} + engines: {node: '>=10.18'} + i18n-iso-countries@7.14.0: resolution: {integrity: sha512-nXHJZYtNrfsi1UQbyRqm3Gou431elgLjKl//CYlnBGt5aTWdRPH1PiS2T/p/n8Q8LnqYqzQJik3Q7mkwvLokeg==} engines: {node: '>= 12'} @@ -7286,15 +7239,12 @@ packages: immediate@3.3.0: resolution: {integrity: sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==} - immer@9.0.21: - resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} - import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} - import-in-the-middle@1.14.2: - resolution: {integrity: sha512-5tCuY9BV8ujfOpwtAGgsTx9CGUapcFMEEyByLv1B+v2+6DhAcw+Zr0nhQT7uwaZ7DiourxFEscghOR8e1aPLQw==} + import-in-the-middle@1.14.4: + resolution: {integrity: sha512-eWjxh735SJLFJJDs5X82JQ2405OdJeAHDBnaoFCfdr5GVc7AWc9xU7KbrF+3Xd5F2ccP1aQFKtY+65X6EfKZ7A==} import-lazy@4.0.0: resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} @@ -7344,18 +7294,14 @@ packages: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} - interpret@1.4.0: - resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} - engines: {node: '>= 0.10'} - - intl-messageformat@10.7.16: - resolution: {integrity: sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==} + intl-messageformat@10.7.17: + resolution: {integrity: sha512-0Ugaf65B2J76rb31drgNF1l6bGEDkbIiYc2Glx6jaZINHnwa5kDRGy8KXYuA+/8P4G0c9prAFhfVhQJJfzUuvQ==} invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} - ioredis@5.7.0: - resolution: {integrity: sha512-NUcA93i1lukyXU+riqEyPtSEkyFq8tX90uL659J+qpCZ3rEdViB/APC58oAhIh3+bJln2hzdlZbBZsGNrlsR8g==} + ioredis@5.8.1: + resolution: {integrity: sha512-Qho8TgIamqEPdgiMadJwzRMW3TudIg6vpg4YONokGDudy4eqRIJtDbVX72pfLBcWxvbn3qm/40TyGUObdW4tLQ==} engines: {node: '>=12.22.0'} ip-address@10.0.1: @@ -7379,9 +7325,6 @@ packages: is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-arrayish@0.3.2: - resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -7410,6 +7353,11 @@ packages: engines: {node: '>=8'} hasBin: true + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + is-extendable@0.1.1: resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} engines: {node: '>=0.10.0'} @@ -7429,6 +7377,11 @@ packages: is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + is-installed-globally@0.4.0: resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} engines: {node: '>=10'} @@ -7441,6 +7394,10 @@ packages: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} + is-network-error@1.3.0: + resolution: {integrity: sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw==} + engines: {node: '>=16'} + is-npm@6.0.0: resolution: {integrity: sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -7523,6 +7480,10 @@ packages: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} + is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + engines: {node: '>=16'} + is-yarn-global@0.4.1: resolution: {integrity: sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==} engines: {node: '>=12'} @@ -7591,8 +7552,8 @@ packages: resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} hasBin: true - jiti@2.5.1: - resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true joi@17.13.3: @@ -7652,25 +7613,6 @@ packages: json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-pointer@0.6.2: - resolution: {integrity: sha512-vLWcKbOaXlO+jvRy4qNd+TI1QUPZzfJj1tpJ3vAXDych5XJf93ftpUKe5pKCrzyIIwgBJcOcCVRUfqQP25afBw==} - - json-refs@3.0.15: - resolution: {integrity: sha512-0vOQd9eLNBL18EGl5yYaO44GhixmImes2wiYn9Z3sag3QnehWrYWlB9AFtMxCL2Bj3fyxgDYkxGFEU/chlYssw==} - engines: {node: '>=0.8'} - hasBin: true - - json-schema-compare@0.2.2: - resolution: {integrity: sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ==} - - json-schema-merge-allof@0.8.1: - resolution: {integrity: sha512-CTUKmIlPJbsWfzRRnOXz+0MjIqvnleIXwFTzz+t9T86HnYX/Rozria6ZVGLktAU9e+NygNljveP+yxqtQp/Q4w==} - engines: {node: '>=12.0.0'} - - json-schema-resolve-allof@1.5.0: - resolution: {integrity: sha512-Jgn6BQGSLDp3D7bTYrmCbP/p7SRFz5BfpeEJ9A7sXuVADMc14aaDN1a49zqk9D26wwJlcNvjRpT63cz1VgFZeg==} - hasBin: true - json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -7735,11 +7677,15 @@ packages: resolution: {integrity: sha512-oDxVkRwPOHhGlxKIDiDB2h+/l05QPtefD7nSqRgDfZt8P+QVYFWjfeK8jANf5O2YXjk8egd7KntvXKYx82wOag==} engines: {node: '>= 18'} - kysely-postgres-js@2.0.0: - resolution: {integrity: sha512-R1tWx6/x3tSatWvsmbHJxpBZYhNNxcnMw52QzZaHKg7ZOWtHib4iZyEaw4gb2hNKVctWQ3jfMxZT/ZaEMK6kBQ==} + kysely-postgres-js@3.0.0: + resolution: {integrity: sha512-o2t/xNSYJQDW6rVGGFPXKmZ0BEz2dGn66c2B+cO/k9ZNcU2qPWPycQPQ+B+P2MBXbKYq0xV9BZmFIvkUrmFWAQ==} + engines: {bun: '>=1.2', node: '>=20'} peerDependencies: kysely: '>= 0.24.0 < 1' - postgres: '>= 3.4.0 < 4' + postgres: ^3.4.0 + peerDependenciesMeta: + postgres: + optional: true kysely@0.28.2: resolution: {integrity: sha512-4YAVLoF0Sf0UTqlhgQMFU9iQECdah7n+13ANkiuVfRvlK+uI0Etbgd7bVP36dKlG+NXWbhGua8vnGt+sdhvT7A==} @@ -7845,10 +7791,6 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - liquid-json@0.3.1: - resolution: {integrity: sha512-wUayTU8MS827Dam6MxgD72Ui+KOSF+u/eIqpatOtjnvgJ0+mnDq33uC2M7J0tPK+upe/DpUAuK4JUU89iBoNKQ==} - engines: {node: '>=4'} - load-esm@1.0.2: resolution: {integrity: sha512-nVAvWk/jeyrWyXEAs84mpQCYccxRqgKY4OznLuJhJCa0XsPSfdOIr2zvBZEj3IHEHbX97jjscKRRV539bW0Gpw==} engines: {node: '>=13.2.0'} @@ -7857,8 +7799,8 @@ packages: resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - loader-runner@4.3.0: - resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} + loader-runner@4.3.1: + resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} engines: {node: '>=6.11.5'} loader-utils@2.0.4: @@ -7958,10 +7900,6 @@ packages: lunr@2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} - luxon@3.6.1: - resolution: {integrity: sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==} - engines: {node: '>=12'} - luxon@3.7.2: resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} engines: {node: '>=12'} @@ -7995,8 +7933,8 @@ packages: resolution: {integrity: sha512-p8lJFEiqmEQlyv+DQxFAOG/XPWN0Wp7j/Psq93Zywz7qt9CcUKFYDBOoOEKzqe6gudHVJY8/Bhqw6VDpX2lSBg==} engines: {node: '>=6.4.0'} - maplibre-gl@5.7.1: - resolution: {integrity: sha512-iCOQB6W/EGgQx8aU4SyfU5a5/GR2E+ELF92NMsqYfs3x+vnY+8mARmz4gor6XZHCz3tv19mnotVDRlRTMNKyGw==} + maplibre-gl@5.8.0: + resolution: {integrity: sha512-zLblPFK+z5sxeitDF8RL2cnqfRaivNwxbGoQMfwAm9st6d1lRGTxgI7NNNr/U1AEPkp5+X+wjROagiHvJD8aqg==} engines: {node: '>=16.14.0', npm: '>=8.1.0'} mark.js@8.11.1: @@ -8012,11 +7950,6 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} - marked@11.2.0: - resolution: {integrity: sha512-HR0m3bvu0jAPYiIvLUUQtdg1g6D247//lvcekpHO1WMvbwDlwSkZAX9Lw4F4YHE1T0HaaNve0tuAWuV1UJ6vtw==} - engines: {node: '>= 18'} - hasBin: true - marked@7.0.4: resolution: {integrity: sha512-t8eP0dXRJMtMvBojtkcsA7n48BkauktUKzfkPSCq85ZMTJ0v76Rke4DYz01omYpPTUh4p/f7HePgRo3ebG8+QQ==} engines: {node: '>= 16'} @@ -8103,6 +8036,9 @@ packages: resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} engines: {node: '>= 4.0.0'} + memfs@4.49.0: + resolution: {integrity: sha512-L9uC9vGuc4xFybbdOpRLoOAOq1YEBBsocCs5NVW32DfU+CZWWIn3OVF+lB8Gp4ttBVSMazwrTrjv8ussX/e3VQ==} + memoizee@0.4.17: resolution: {integrity: sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==} engines: {node: '>=0.12'} @@ -8264,9 +8200,6 @@ packages: resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} engines: {node: '>= 0.6'} - mime-format@2.0.1: - resolution: {integrity: sha512-XxU3ngPbEnrYnNbIX+lYSaYg0M01v6p2ntd2YaFksTu0vayaw5OJvbdRyWs07EYRlLED5qadUZ+xo+XhOvFhwg==} - mime-types@2.1.18: resolution: {integrity: sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==} engines: {node: '>= 0.6'} @@ -8376,8 +8309,8 @@ packages: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} - minizlib@3.0.2: - resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} engines: {node: '>= 18'} mkdirp-classic@0.5.3: @@ -8396,11 +8329,6 @@ packages: engines: {node: '>=10'} hasBin: true - mkdirp@3.0.1: - resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} - engines: {node: '>=10'} - hasBin: true - mnemonist@0.40.3: resolution: {integrity: sha512-Vjyr90sJ23CKKH/qPAgUKicw/v6pRoamxIEDFOF8uSgFME7DqPRpHgRTejWVjkdGg5dXj0/NyxZHZ9bcjH+2uQ==} @@ -8411,9 +8339,6 @@ packages: module-details-from-path@1.0.4: resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} - monaco-editor@0.31.1: - resolution: {integrity: sha512-FYPwxGZAeP6mRRyrr5XTGHD9gRXVjy7GUzF4IPChnyt3fS5WrNxIkS8DNujWf6EQy0Zlzpxw8oTVE+mWI2/D1Q==} - moo@0.5.2: resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} @@ -8472,9 +8397,6 @@ packages: engines: {node: ^18 || >=20} hasBin: true - native-promise-only@0.8.1: - resolution: {integrity: sha512-zkVhZUA3y8mbz652WrL5x0fB0ehrBkulWT3TomAQ9iDtyXZvzKeEA6GPxAItBYeNYl5yngKRX612qHOhvMkDeg==} - natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -8497,12 +8419,8 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - neotraverse@0.6.15: - resolution: {integrity: sha512-HZpdkco+JeXq0G+WWpMJ4NsX3pqb5O7eR9uGz3FfoFt+LYzU8iRWp49nJtud6hsDoywM8tIrDo3gjgmOqJA8LA==} - engines: {node: '>= 10'} - - nest-commander@3.19.1: - resolution: {integrity: sha512-Pn6xcMeSnidlzZozNLnbe7P4TqXL7g0JuxqTAtJ89KT4S63ntJZKtRU6g/56h/aHUQa+m98j/c9OxBSduK7EPg==} + nest-commander@3.20.1: + resolution: {integrity: sha512-LRI7z6UlWy2vWyQR0PYnAXsaRyJvpfiuvOCmx2jk2kLXJH9+y/omPDl9NE3fq4WMaE0/AhviuUjA12eC/zDqXw==} peerDependencies: '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 @@ -8555,10 +8473,6 @@ packages: resolution: {integrity: sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==} engines: {node: '>=18'} - node-fetch-h2@2.3.0: - resolution: {integrity: sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==} - engines: {node: 4.x || >=6.0.0} - node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -8585,14 +8499,11 @@ packages: engines: {node: ^18.17.0 || >=20.5.0} hasBin: true - node-readfiles@0.2.0: - resolution: {integrity: sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==} + node-releases@2.0.23: + resolution: {integrity: sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==} - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} - - nodemailer@7.0.6: - resolution: {integrity: sha512-F44uVzgwo49xboqbFgBGkRaiMgtoBrBEWCVincJPK9+S9Adkzt/wXCLKbf7dxucmxfTI5gHGB+bEmdyzN6QKjw==} + nodemailer@7.0.9: + resolution: {integrity: sha512-9/Qm0qXIByEP8lEV2qOqcAW7bRpL8CR9jcTwk3NBnHJNmP9fIJ86g2fgmIXqHY+nj55ZEMwWqYAT2QTDpRUYiQ==} engines: {node: '>=6.0.0'} nopt@1.0.10: @@ -8655,28 +8566,8 @@ packages: engines: {node: ^14.16.0 || >=16.10.0} hasBin: true - oas-kit-common@1.0.8: - resolution: {integrity: sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==} - - oas-linter@3.2.2: - resolution: {integrity: sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==} - - oas-resolver-browser@2.5.6: - resolution: {integrity: sha512-Jw5elT/kwUJrnGaVuRWe1D7hmnYWB8rfDDjBnpQ+RYY/dzAewGXeTexXzt4fGEo6PUE4eqKqPWF79MZxxvMppA==} - hasBin: true - - oas-resolver@2.5.6: - resolution: {integrity: sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==} - hasBin: true - - oas-schema-walker@1.1.5: - resolution: {integrity: sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==} - - oas-validator@5.0.8: - resolution: {integrity: sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw==} - - oauth4webapi@3.8.1: - resolution: {integrity: sha512-olkZDELNycOWQf9LrsELFq8n05LwJgV8UkrS0cburk6FOwf8GvLam+YB+Uj5Qvryee+vwWOfQVeI5Vm0MVg7SA==} + oauth4webapi@3.8.2: + resolution: {integrity: sha512-FzZZ+bht5X0FKe7Mwz3DAVAmlH1BV5blSak/lHMBKz0/EBMhX6B10GlQYI51+oRp8ObJaX0g6pXrAxZh5s8rjw==} object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} @@ -8726,21 +8617,20 @@ packages: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} + open@10.2.0: + resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} + engines: {node: '>=18'} + open@8.4.2: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} - openapi-to-postmanv2@4.25.0: - resolution: {integrity: sha512-sIymbkQby0gzxt2Yez8YKB6hoISEel05XwGwNrAhr6+vxJWXNxkmssQc/8UEtVkuJ9ZfUXLkip9PYACIpfPDWg==} - engines: {node: '>=8'} - hasBin: true - opener@1.5.2: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} hasBin: true - openid-client@6.8.0: - resolution: {integrity: sha512-oG1d1nAVhIIE+JSjLS+7E9wY1QOJpZltkzlJdbZ7kEn7Hp3hqur2TEeQ8gLOHoHkhbRAGZJKoOnEQcLOQJuIyg==} + openid-client@6.8.1: + resolution: {integrity: sha512-VoYT6enBo6Vj2j3Q5Ec0AezS+9YGzQo1f5Xc42lreMGlfP4ljiXPKVDvCADh+XHCV/bqPu/wWSiCVXbJKvrODw==} optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} @@ -8798,9 +8688,9 @@ packages: resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} engines: {node: '>=8'} - p-retry@4.6.2: - resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} - engines: {node: '>=8'} + p-retry@6.2.1: + resolution: {integrity: sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==} + engines: {node: '>=16.17'} p-timeout@3.2.0: resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} @@ -8856,9 +8746,6 @@ packages: pascal-case@3.1.2: resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} - path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -8878,9 +8765,6 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-loader@1.0.12: - resolution: {integrity: sha512-n7oDG8B+k/p818uweWrOixY9/Dsr89o2TkCm6tOTex3fpdo2+BFDgR+KpB37mGKBRsBAlR8CIJMFN0OEy/7hIQ==} - path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -8915,9 +8799,6 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - path@0.12.7: - resolution: {integrity: sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==} - pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -9000,13 +8881,13 @@ packages: pkg-types@2.3.0: resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} - playwright-core@1.55.0: - resolution: {integrity: sha512-GvZs4vU3U5ro2nZpeiwyb0zuFaqb9sUiAJuyrWpcGouD8y9/HLgGbNRjIph7zU9D3hnPaisMl9zG9CgFi/biIg==} + playwright-core@1.56.0: + resolution: {integrity: sha512-1SXl7pMfemAMSDn5rkPeZljxOCYAmQnYLBTExuh6E8USHXGSX3dx6lYZN/xPpTz1vimXmPA9CDnILvmJaB8aSQ==} engines: {node: '>=18'} hasBin: true - playwright@1.55.0: - resolution: {integrity: sha512-sdCWStblvV1YU909Xqx0DhOjPZE4/5lJsIS84IfN9dAZfcl/CIZ5O8l3o0j7hPMjDvqoTF8ZUcc+i/GL5erstA==} + playwright@1.56.0: + resolution: {integrity: sha512-X5Q1b8lOdWIE4KAoHpW3SE8HvUB+ZZsUoN64ZhjnN8dOb1UpujxBtENGiZFE+9F/yhzJwYa+ca3u43FeLbboHA==} engines: {node: '>=18'} hasBin: true @@ -9198,16 +9079,22 @@ packages: ts-node: optional: true - postcss-load-config@4.0.2: - resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} - engines: {node: '>= 14'} + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} peerDependencies: + jiti: '>=1.21.0' postcss: '>=8.0.9' - ts-node: '>=9.0.0' + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: + jiti: + optional: true postcss: optional: true - ts-node: + tsx: + optional: true + yaml: optional: true postcss-loader@7.3.4: @@ -9496,18 +9383,6 @@ packages: resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==} engines: {node: '>=12'} - postman-code-generators@1.14.2: - resolution: {integrity: sha512-qZAyyowfQAFE4MSCu2KtMGGQE/+oG1JhMZMJNMdZHYCSfQiVVeKxgk3oI4+KJ3d1y5rrm2D6C6x+Z+7iyqm+fA==} - engines: {node: '>=12'} - - postman-collection@4.5.0: - resolution: {integrity: sha512-152JSW9pdbaoJihwjc7Q8lc3nPg/PC9lPTHdMk7SHnHhu/GBJB7b2yb9zG7Qua578+3PxkQ/HYBuXpDSvsf7GQ==} - engines: {node: '>=10'} - - postman-url-encoder@3.0.5: - resolution: {integrity: sha512-jOrdVvzUXBC7C+9gkIkpDJ3HIxOHTIqjpQ4C1EMt1ZGeMvSEpbFCKq23DEfgsj46vMnDgyQf+1ZLp2Wm+bKSsA==} - engines: {node: '>=10'} - potpack@1.0.2: resolution: {integrity: sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==} @@ -9522,8 +9397,8 @@ packages: resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} engines: {node: '>=6.0.0'} - prettier-plugin-organize-imports@4.2.0: - resolution: {integrity: sha512-Zdy27UhlmyvATZi67BTnLcKTo8fm6Oik59Sz6H64PgZJVs6NJpPD1mT240mmJn62c98/QaL+r3kx9Q3gRpDajg==} + prettier-plugin-organize-imports@4.3.0: + resolution: {integrity: sha512-FxFz0qFhyBsGdIsb697f/EkvHzi5SZOhWAjxcx2dLt+Q532bAlhswcXGYB1yzjZ69kW8UoadFBw7TyNwlq96Iw==} peerDependencies: prettier: '>=2.0' typescript: '>=2.9' @@ -9712,13 +9587,13 @@ packages: peerDependencies: react: ^18.3.1 - react-dom@19.1.1: - resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==} + react-dom@19.2.0: + resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==} peerDependencies: - react: ^19.1.1 + react: ^19.2.0 - react-email@4.2.11: - resolution: {integrity: sha512-/7TXRgsTrXcV1u7kc5ZXDVlPvZqEBaYcflMhE2FgWIJh3OHLjj2FqctFTgYcp0iwzbR59a7gzJLmSKyD0wYJEQ==} + react-email@4.3.0: + resolution: {integrity: sha512-XFHCSfhdlO7k5q2TYGwC0HsVh5Yn13YaOdahuJEUEOfOJKHEpSP4PKg7R/RiKFoK9cDvzunhY+58pXxz0vE2zA==} engines: {node: '>=18.0.0'} hasBin: true @@ -9744,24 +9619,9 @@ packages: react-loadable: '*' webpack: '>=4.41.1 || 5.x' - react-magic-dropzone@1.0.1: - resolution: {integrity: sha512-0BIROPARmXHpk4AS3eWBOsewxoM5ndk2psYP/JmbCq8tz3uR2LIV1XiroZ9PKrmDRMctpW+TvsBCtWasuS8vFA==} - react-promise-suspense@0.3.4: resolution: {integrity: sha512-I42jl7L3Ze6kZaq+7zXWSunBa3b1on5yfvUW6Eo/3fFOj6dZ5Bqmcd264nJbTK/gn1HjjILAjSwnZbV4RpSaNQ==} - react-redux@7.2.9: - resolution: {integrity: sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==} - peerDependencies: - react: ^16.8.3 || ^17 || ^18 - react-dom: '*' - react-native: '*' - peerDependenciesMeta: - react-dom: - optional: true - react-native: - optional: true - react-router-config@5.1.1: resolution: {integrity: sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==} peerDependencies: @@ -9782,8 +9642,8 @@ packages: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} - react@19.1.1: - resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==} + react@19.2.0: + resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} engines: {node: '>=0.10.0'} read-cache@1.0.0: @@ -9811,15 +9671,13 @@ packages: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} - rechoir@0.6.2: - resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} - engines: {node: '>= 0.10'} - recma-build-jsx@1.0.0: resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} - recma-jsx@1.0.0: - resolution: {integrity: sha512-5vwkv65qWwYxg+Atz95acp8DMu1JDSqdGkA2Of1j6rCreyFUE/gp15fC8MnGEuG1W68UKjM6x6+YTWIh7hZM/Q==} + recma-jsx@1.0.1: + resolution: {integrity: sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 recma-parse@1.0.0: resolution: {integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==} @@ -9839,29 +9697,9 @@ packages: resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} engines: {node: '>=4'} - redux-devtools-extension@2.13.9: - resolution: {integrity: sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A==} - deprecated: Package moved to @redux-devtools/extension. - peerDependencies: - redux: ^3.1.0 || ^4.0.0 - - redux-thunk@2.4.2: - resolution: {integrity: sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==} - peerDependencies: - redux: ^4 - - redux@4.2.1: - resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==} - reflect-metadata@0.2.2: resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} - refractor@4.9.0: - resolution: {integrity: sha512-nEG1SPXFoGGx+dcjftjv8cAjEusIh6ED1xhf5DG3C0x/k+rmZ2duKnc3QLpt6qeHv5fPb8uwN3VWN2BT7fr3Og==} - - reftools@1.1.9: - resolution: {integrity: sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==} - regenerate-unicode-properties@10.2.0: resolution: {integrity: sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==} engines: {node: '>=4'} @@ -9918,8 +9756,8 @@ packages: remark-gfm@4.0.1: resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} - remark-mdx@3.1.0: - resolution: {integrity: sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA==} + remark-mdx@3.1.1: + resolution: {integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==} remark-parse@11.0.0: resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} @@ -9945,9 +9783,9 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - require-in-the-middle@7.5.2: - resolution: {integrity: sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==} - engines: {node: '>=8.6.0'} + require-in-the-middle@8.0.0: + resolution: {integrity: sha512-9s0pnM5tH8G4dSI3pms2GboYOs25LwOGnRMxN/Hx3TYT1K0rh6OjaWf4dI0DAQnMyaEXWoGVnSTPQasqwzTTAA==} + engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'} require-like@0.1.2: resolution: {integrity: sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==} @@ -9958,9 +9796,6 @@ packages: requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - reselect@4.1.8: - resolution: {integrity: sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==} - resolve-alpn@1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} @@ -10019,8 +9854,8 @@ packages: robust-predicates@3.0.2: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} - rollup-plugin-visualizer@6.0.3: - resolution: {integrity: sha512-ZU41GwrkDcCpVoffviuM9Clwjy5fcUxlz0oMoTXTYsK+tcIFzbdacnrr2n8TXcHxbGKKXtOdjxM2HUS4HjkwIw==} + rollup-plugin-visualizer@6.0.4: + resolution: {integrity: sha512-q8Q7J/6YofkmaGW1sH/fPRAz37x/+pd7VBuaUU7lwvOS/YikuiiEU9jeb9PH8XHiq50XFrUsBbOxeAMYQ7KZkg==} engines: {node: '>=18'} hasBin: true peerDependencies: @@ -10032,8 +9867,8 @@ packages: rollup: optional: true - rollup@4.50.1: - resolution: {integrity: sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==} + rollup@4.52.4: + resolution: {integrity: sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -10049,6 +9884,10 @@ packages: engines: {node: '>=12.0.0'} hasBin: true + run-applescript@7.1.0: + resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} + engines: {node: '>=18'} + run-async@2.4.1: resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} engines: {node: '>=0.12.0'} @@ -10099,8 +9938,8 @@ packages: scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} - scheduler@0.26.0: - resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} schema-dts@1.1.5: resolution: {integrity: sha512-RJr9EaCmsLzBX2NDiO5Z3ux2BVosNZN5jo0gWgsyKvxKIUL5R3swNvoorulAeL9kLB0iTSX7V6aokhla2m7xbg==} @@ -10109,8 +9948,8 @@ packages: resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} engines: {node: '>= 10.13.0'} - schema-utils@4.3.2: - resolution: {integrity: sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==} + schema-utils@4.3.3: + resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} engines: {node: '>= 10.13.0'} search-insights@2.17.3: @@ -10138,13 +9977,8 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.6.3: - resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} - engines: {node: '>=10'} - hasBin: true - - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} hasBin: true @@ -10201,8 +10035,8 @@ packages: resolution: {integrity: sha512-rLGSWeK2ufzCVx05wYd+xrWnOOdSV7xNUW5/XFgx3Bc02hBkpMlrd2F1dDII7/jhWzv0MSyBFh5uJIy9hLdfuw==} hasBin: true - sharp@0.34.3: - resolution: {integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==} + sharp@0.34.4: + resolution: {integrity: sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} shebang-command@2.0.0: @@ -10217,29 +10051,6 @@ packages: resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} engines: {node: '>= 0.4'} - shelljs@0.8.5: - resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} - engines: {node: '>=4'} - hasBin: true - - should-equal@2.0.0: - resolution: {integrity: sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==} - - should-format@3.0.3: - resolution: {integrity: sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==} - - should-type-adaptors@1.1.0: - resolution: {integrity: sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==} - - should-type@1.4.0: - resolution: {integrity: sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==} - - should-util@1.0.1: - resolution: {integrity: sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==} - - should@13.2.3: - resolution: {integrity: sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==} - side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} @@ -10272,13 +10083,10 @@ packages: simple-get@3.1.1: resolution: {integrity: sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==} - simple-icons@15.15.0: - resolution: {integrity: sha512-ohh1Uo9AjH10WN5wpPmtjnmbSLv6MjiULHS4dYA821uIsPAp0Q3XoluPnjBnQAFsztasmM6z2dezJBrQbtserg==} + simple-icons@15.16.1: + resolution: {integrity: sha512-UpT6dslPDUfl+9xAjxHAh4ralefkrAqBe8o4cN+HtBOa2HjYZnEAiQsUkAdkaMfW05rU/rc/MWaKxF/cpUllCg==} engines: {node: '>=0.12.18'} - simple-swizzle@0.2.2: - resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} - sirv@2.0.4: resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==} engines: {node: '>= 10'} @@ -10362,6 +10170,10 @@ packages: resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} engines: {node: '>= 8'} + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + space-separated-tokens@1.1.5: resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} @@ -10385,8 +10197,8 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - sql-formatter@15.6.9: - resolution: {integrity: sha512-r9VKnkRfKW7jbhTgytwbM+JqmFclQYN9L58Z3UTktuy9V1f1Y+rGK3t70Truh2wIOJzvZkzobAQ2PwGjjXsr6Q==} + sql-formatter@15.6.10: + resolution: {integrity: sha512-0bJOPQrRO/JkjQhiThVayq0hOKnI1tHI+2OTkmT7TGtc6kqS+V7kveeMzRW+RNQGxofmTmet9ILvztyuxv0cJQ==} hasBin: true srcset@4.0.0: @@ -10396,8 +10208,8 @@ packages: ssh-remote-port-forward@1.0.4: resolution: {integrity: sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==} - ssh2@1.16.0: - resolution: {integrity: sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==} + ssh2@1.17.0: + resolution: {integrity: sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==} engines: {node: '>=10.16.0'} ssri@12.0.0: @@ -10410,9 +10222,6 @@ packages: standard-as-callback@2.1.0: resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} - state-local@1.0.7: - resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==} - statuses@1.5.0: resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} engines: {node: '>= 0.6'} @@ -10439,8 +10248,8 @@ packages: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} - streamx@2.22.1: - resolution: {integrity: sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==} + streamx@2.23.0: + resolution: {integrity: sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==} string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} @@ -10506,9 +10315,6 @@ packages: strip-literal@3.0.0: resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} - striptags@3.2.0: - resolution: {integrity: sha512-g45ZOGzHDMe2bdYMdIvdAfCQkCTDMGBazSw1ypMowwGIee7ZQ5dU0rBJ8Jqgl+jAKIv4dbeE1jscZq9wid1Tkw==} - strnum@2.1.1: resolution: {integrity: sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==} @@ -10537,11 +10343,6 @@ packages: resolution: {integrity: sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==} engines: {node: '>=14.18.0'} - superagent@7.1.6: - resolution: {integrity: sha512-gZkVCQR1gy/oUXr+kxJMLDjla434KmSOKbx5iGD30Ql+AkJQ/YlPKECJy2nhqOsHLjGHzoDTXNSjhnvWhzKk7g==} - engines: {node: '>=6.4.0 <13 || >=14'} - deprecated: Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net - supercluster@7.1.5: resolution: {integrity: sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==} @@ -10564,16 +10365,16 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svelte-check@4.3.1: - resolution: {integrity: sha512-lkh8gff5gpHLjxIV+IaApMxQhTGnir2pNUAqcNgeKkvK5bT/30Ey/nzBxNLDlkztCH4dP7PixkMt9SWEKFPBWg==} + svelte-check@4.3.3: + resolution: {integrity: sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg==} engines: {node: '>= 18.0.0'} hasBin: true peerDependencies: svelte: ^4.0.0 || ^5.0.0-next.0 typescript: '>=5.0.0' - svelte-eslint-parser@1.3.2: - resolution: {integrity: sha512-whla4VlUbwJidn/bNyC3Ho3pBrXnR2CBEkuJwtaURW+wfwgKHPaYtZAmwAkp6HWWKCw1ILZL6iKsFdVY11rpDA==} + svelte-eslint-parser@1.3.3: + resolution: {integrity: sha512-oTrDR8Z7Wnguut7QH3YKh7JR19xv1seB/bz4dxU5J/86eJtZOU4eh0/jZq4dy6tAlz/KROxnkRQspv5ZEt7t+Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 @@ -10584,6 +10385,9 @@ packages: svelte-gestures@5.2.2: resolution: {integrity: sha512-Y+chXPaSx8OsPoFppUwPk8PJzgrZ7xoDJKXeiEc7JBqyKKzXer9hlf8F9O34eFuAWB4/WQEvccACvyBplESL7A==} + svelte-highlight@7.8.4: + resolution: {integrity: sha512-aVp+Q0hH9kI7PlSDrklmFTF4Uj7wYj7UGuqkREnkXlqpEffxr2g6esZcMMaTgECdg5rb2mJqM88+nwS10ecoTg==} + svelte-i18n@4.0.1: resolution: {integrity: sha512-jaykGlGT5PUaaq04JWbJREvivlCnALtT+m87Kbm0fxyYHynkQaxQMnIKHLm2WeIuBRoljzwgyvz0Z6/CMwfdmQ==} engines: {node: '>= 16'} @@ -10623,8 +10427,8 @@ packages: peerDependencies: svelte: ^5.30.2 - svelte@5.38.10: - resolution: {integrity: sha512-UY+OhrWK7WI22bCZ00P/M3HtyWgwJPi9IxSRkoAE2MeAy6kl7ZlZWJZ8RaB+X4KD/G+wjis+cGVnVYaoqbzBqg==} + svelte@5.39.11: + resolution: {integrity: sha512-8MxWVm2+3YwrFbPaxOlT1bbMi6OTenrAgks6soZfiaS8Fptk4EVyRIFhJc3RpO264EeSNwgjWAdki0ufg4zkGw==} engines: {node: '>=18'} svg-parser@2.0.4: @@ -10638,10 +10442,6 @@ packages: swagger-ui-dist@5.21.0: resolution: {integrity: sha512-E0K3AB6HvQd8yQNSMR7eE5bk+323AUxjtCz/4ZNKiahOlPhPJxqn3UPIGs00cyY/dhrTDJ61L7C/a8u6zhGrZg==} - swagger2openapi@7.0.8: - resolution: {integrity: sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==} - hasBin: true - symbol-observable@4.0.0: resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} engines: {node: '>=0.10'} @@ -10692,23 +10492,23 @@ packages: peerDependencies: tailwindcss: '>=3.4.17' - tailwindcss@3.4.17: - resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} + tailwindcss@3.4.18: + resolution: {integrity: sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==} engines: {node: '>=14.0.0'} hasBin: true - tailwindcss@4.1.13: - resolution: {integrity: sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==} + tailwindcss@4.1.14: + resolution: {integrity: sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==} - tapable@2.2.2: - resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} - tar-fs@2.1.3: - resolution: {integrity: sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==} + tar-fs@2.1.4: + resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} - tar-fs@3.1.0: - resolution: {integrity: sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w==} + tar-fs@3.1.1: + resolution: {integrity: sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==} tar-stream@2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} @@ -10721,8 +10521,8 @@ packages: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} - tar@7.4.3: - resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + tar@7.5.1: + resolution: {integrity: sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==} engines: {node: '>=18'} terser-webpack-plugin@5.3.14: @@ -10750,8 +10550,8 @@ packages: resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} engines: {node: '>=18'} - testcontainers@11.5.1: - resolution: {integrity: sha512-YSSP4lSJB8498zTeu4HYTZYgSky54ozBmIDdC8PFU5inj+vBo5hPpilhcYTgmsqsYjrXOJGV7jl0MWByS7GwuA==} + testcontainers@11.7.1: + resolution: {integrity: sha512-fjut+07G4Avp6Lly/6hQePpUpQFv9ZyQd+7JC5iCDKg+dWa2Sw7fXD3pBrkzslYFfKqGx9M6kyIaLpg9VeMsjw==} text-decoder@1.2.3: resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==} @@ -10767,6 +10567,12 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + thingies@2.5.0: + resolution: {integrity: sha512-s+2Bwztg6PhWUD7XMfeYm5qliDdSiZm7M7n8KjTkIsm3l/2lgVRc2/Gx/v+ZX8lT4FMA+i8aQvhcWylldc+ZNw==} + engines: {node: '>=10.18'} + peerDependencies: + tslib: ^2 + three@0.179.1: resolution: {integrity: sha512-5y/elSIQbrvKOISxpwXCR4sQqHtGiOI+MKLc3SsBdDXA2hz3Mdp3X59aUp8DyybMa34aeBwbFTpdoLJaUDEWSw==} @@ -10872,6 +10678,12 @@ packages: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} + tree-dump@1.1.0: + resolution: {integrity: sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -10958,8 +10770,8 @@ packages: typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - typescript-eslint@8.43.0: - resolution: {integrity: sha512-FyRGJKUGvcFekRRcBKFBlAhnp4Ng8rhe8tuvvkR9OiU0gfd4vyvTRQHEckO6VDlH57jbeUQem2IpqPq9kLJH+w==} + typescript-eslint@8.46.0: + resolution: {integrity: sha512-6+ZrB6y2bT2DX3K+Qd9vn7OFOJR+xSLDj+Aw/N3zBwUt27uTw2sw2TE2+UcY1RiyBZkaGbTkVg9SSdPNUG6aUw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -10970,8 +10782,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - typescript@5.9.2: - resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} hasBin: true @@ -11005,8 +10817,8 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - undici-types@7.12.0: - resolution: {integrity: sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==} + undici-types@7.14.0: + resolution: {integrity: sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==} undici@7.16.0: resolution: {integrity: sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==} @@ -11140,9 +10952,6 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - util@0.10.4: - resolution: {integrity: sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==} - utila@0.4.0: resolution: {integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==} @@ -11170,25 +10979,6 @@ packages: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true - uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} - hasBin: true - - validate.io-array@1.0.6: - resolution: {integrity: sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==} - - validate.io-function@1.0.2: - resolution: {integrity: sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ==} - - validate.io-integer-array@1.0.0: - resolution: {integrity: sha512-mTrMk/1ytQHtCY0oNO3dztafHYyGU88KL+jRxWuzfOmQb+4qqnWmI+gykvGp8usKZOM0H7keJHEbRaFiYA0VrA==} - - validate.io-integer@1.0.5: - resolution: {integrity: sha512-22izsYSLojN/P6bppBqhgUDjCkr5RY2jd+N2a3DCAUey8ydvrZ/OkGvFPR7qfOpwR2LC5p4Ngzxz36g5Vgr/hQ==} - - validate.io-number@1.0.3: - resolution: {integrity: sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg==} - validator@13.15.15: resolution: {integrity: sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==} engines: {node: '>= 0.10'} @@ -11209,8 +10999,8 @@ packages: vfile-message@2.0.4: resolution: {integrity: sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==} - vfile-message@4.0.2: - resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} vfile@4.2.1: resolution: {integrity: sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==} @@ -11235,8 +11025,8 @@ packages: vite: optional: true - vite@7.1.5: - resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==} + vite@7.1.9: + resolution: {integrity: sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -11356,18 +11146,21 @@ packages: engines: {node: '>= 10.13.0'} hasBin: true - webpack-dev-middleware@5.3.4: - resolution: {integrity: sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==} - engines: {node: '>= 12.13.0'} + webpack-dev-middleware@7.4.5: + resolution: {integrity: sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA==} + engines: {node: '>= 18.12.0'} peerDependencies: - webpack: ^4.0.0 || ^5.0.0 + webpack: ^5.0.0 + peerDependenciesMeta: + webpack: + optional: true - webpack-dev-server@4.15.2: - resolution: {integrity: sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==} - engines: {node: '>= 12.13.0'} + webpack-dev-server@5.2.2: + resolution: {integrity: sha512-QcQ72gh8a+7JO63TAx/6XZf/CWhgMzu5m0QirvPfGvptOusAxG12w2+aua1Jkjr7hzaWDnJ2n6JFeexMHI+Zjg==} + engines: {node: '>= 18.12.0'} hasBin: true peerDependencies: - webpack: ^4.37.0 || ^5.0.0 + webpack: ^5.0.0 webpack-cli: '*' peerDependenciesMeta: webpack: @@ -11404,6 +11197,16 @@ packages: webpack-cli: optional: true + webpack@5.102.1: + resolution: {integrity: sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + webpackbar@6.0.1: resolution: {integrity: sha512-TnErZpmuKdwWBdMoexjio3KKX6ZtoKHRVvLIU0A47R0VVBDtx3ZyOJDktgYixhoJokZTYTt1Z37OkO9pnGJa9Q==} engines: {node: '>=14.21.3'} @@ -11538,6 +11341,10 @@ packages: utf-8-validate: optional: true + wsl-utils@0.1.0: + resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} + engines: {node: '>=18'} + xdg-basedir@5.1.0: resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} engines: {node: '>=12'} @@ -11623,8 +11430,8 @@ packages: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} - zimmerframe@1.1.2: - resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} + zimmerframe@1.1.4: + resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} zip-stream@6.0.1: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} @@ -11640,119 +11447,126 @@ snapshots: '@adobe/css-tools@4.4.4': {} - '@algolia/autocomplete-core@1.17.9(@algolia/client-search@5.29.0)(algoliasearch@5.29.0)(search-insights@2.17.3)': + '@algolia/abtesting@1.6.0': dependencies: - '@algolia/autocomplete-plugin-algolia-insights': 1.17.9(@algolia/client-search@5.29.0)(algoliasearch@5.29.0)(search-insights@2.17.3) - '@algolia/autocomplete-shared': 1.17.9(@algolia/client-search@5.29.0)(algoliasearch@5.29.0) + '@algolia/client-common': 5.40.0 + '@algolia/requester-browser-xhr': 5.40.0 + '@algolia/requester-fetch': 5.40.0 + '@algolia/requester-node-http': 5.40.0 + + '@algolia/autocomplete-core@1.17.9(@algolia/client-search@5.40.0)(algoliasearch@5.40.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-plugin-algolia-insights': 1.17.9(@algolia/client-search@5.40.0)(algoliasearch@5.40.0)(search-insights@2.17.3) + '@algolia/autocomplete-shared': 1.17.9(@algolia/client-search@5.40.0)(algoliasearch@5.40.0) transitivePeerDependencies: - '@algolia/client-search' - algoliasearch - search-insights - '@algolia/autocomplete-plugin-algolia-insights@1.17.9(@algolia/client-search@5.29.0)(algoliasearch@5.29.0)(search-insights@2.17.3)': + '@algolia/autocomplete-plugin-algolia-insights@1.17.9(@algolia/client-search@5.40.0)(algoliasearch@5.40.0)(search-insights@2.17.3)': dependencies: - '@algolia/autocomplete-shared': 1.17.9(@algolia/client-search@5.29.0)(algoliasearch@5.29.0) + '@algolia/autocomplete-shared': 1.17.9(@algolia/client-search@5.40.0)(algoliasearch@5.40.0) search-insights: 2.17.3 transitivePeerDependencies: - '@algolia/client-search' - algoliasearch - '@algolia/autocomplete-preset-algolia@1.17.9(@algolia/client-search@5.29.0)(algoliasearch@5.29.0)': + '@algolia/autocomplete-preset-algolia@1.17.9(@algolia/client-search@5.40.0)(algoliasearch@5.40.0)': dependencies: - '@algolia/autocomplete-shared': 1.17.9(@algolia/client-search@5.29.0)(algoliasearch@5.29.0) - '@algolia/client-search': 5.29.0 - algoliasearch: 5.29.0 + '@algolia/autocomplete-shared': 1.17.9(@algolia/client-search@5.40.0)(algoliasearch@5.40.0) + '@algolia/client-search': 5.40.0 + algoliasearch: 5.40.0 - '@algolia/autocomplete-shared@1.17.9(@algolia/client-search@5.29.0)(algoliasearch@5.29.0)': + '@algolia/autocomplete-shared@1.17.9(@algolia/client-search@5.40.0)(algoliasearch@5.40.0)': dependencies: - '@algolia/client-search': 5.29.0 - algoliasearch: 5.29.0 + '@algolia/client-search': 5.40.0 + algoliasearch: 5.40.0 - '@algolia/client-abtesting@5.29.0': + '@algolia/client-abtesting@5.40.0': dependencies: - '@algolia/client-common': 5.29.0 - '@algolia/requester-browser-xhr': 5.29.0 - '@algolia/requester-fetch': 5.29.0 - '@algolia/requester-node-http': 5.29.0 + '@algolia/client-common': 5.40.0 + '@algolia/requester-browser-xhr': 5.40.0 + '@algolia/requester-fetch': 5.40.0 + '@algolia/requester-node-http': 5.40.0 - '@algolia/client-analytics@5.29.0': + '@algolia/client-analytics@5.40.0': dependencies: - '@algolia/client-common': 5.29.0 - '@algolia/requester-browser-xhr': 5.29.0 - '@algolia/requester-fetch': 5.29.0 - '@algolia/requester-node-http': 5.29.0 + '@algolia/client-common': 5.40.0 + '@algolia/requester-browser-xhr': 5.40.0 + '@algolia/requester-fetch': 5.40.0 + '@algolia/requester-node-http': 5.40.0 - '@algolia/client-common@5.29.0': {} + '@algolia/client-common@5.40.0': {} - '@algolia/client-insights@5.29.0': + '@algolia/client-insights@5.40.0': dependencies: - '@algolia/client-common': 5.29.0 - '@algolia/requester-browser-xhr': 5.29.0 - '@algolia/requester-fetch': 5.29.0 - '@algolia/requester-node-http': 5.29.0 + '@algolia/client-common': 5.40.0 + '@algolia/requester-browser-xhr': 5.40.0 + '@algolia/requester-fetch': 5.40.0 + '@algolia/requester-node-http': 5.40.0 - '@algolia/client-personalization@5.29.0': + '@algolia/client-personalization@5.40.0': dependencies: - '@algolia/client-common': 5.29.0 - '@algolia/requester-browser-xhr': 5.29.0 - '@algolia/requester-fetch': 5.29.0 - '@algolia/requester-node-http': 5.29.0 + '@algolia/client-common': 5.40.0 + '@algolia/requester-browser-xhr': 5.40.0 + '@algolia/requester-fetch': 5.40.0 + '@algolia/requester-node-http': 5.40.0 - '@algolia/client-query-suggestions@5.29.0': + '@algolia/client-query-suggestions@5.40.0': dependencies: - '@algolia/client-common': 5.29.0 - '@algolia/requester-browser-xhr': 5.29.0 - '@algolia/requester-fetch': 5.29.0 - '@algolia/requester-node-http': 5.29.0 + '@algolia/client-common': 5.40.0 + '@algolia/requester-browser-xhr': 5.40.0 + '@algolia/requester-fetch': 5.40.0 + '@algolia/requester-node-http': 5.40.0 - '@algolia/client-search@5.29.0': + '@algolia/client-search@5.40.0': dependencies: - '@algolia/client-common': 5.29.0 - '@algolia/requester-browser-xhr': 5.29.0 - '@algolia/requester-fetch': 5.29.0 - '@algolia/requester-node-http': 5.29.0 + '@algolia/client-common': 5.40.0 + '@algolia/requester-browser-xhr': 5.40.0 + '@algolia/requester-fetch': 5.40.0 + '@algolia/requester-node-http': 5.40.0 '@algolia/events@4.0.1': {} - '@algolia/ingestion@1.29.0': + '@algolia/ingestion@1.40.0': dependencies: - '@algolia/client-common': 5.29.0 - '@algolia/requester-browser-xhr': 5.29.0 - '@algolia/requester-fetch': 5.29.0 - '@algolia/requester-node-http': 5.29.0 + '@algolia/client-common': 5.40.0 + '@algolia/requester-browser-xhr': 5.40.0 + '@algolia/requester-fetch': 5.40.0 + '@algolia/requester-node-http': 5.40.0 - '@algolia/monitoring@1.29.0': + '@algolia/monitoring@1.40.0': dependencies: - '@algolia/client-common': 5.29.0 - '@algolia/requester-browser-xhr': 5.29.0 - '@algolia/requester-fetch': 5.29.0 - '@algolia/requester-node-http': 5.29.0 + '@algolia/client-common': 5.40.0 + '@algolia/requester-browser-xhr': 5.40.0 + '@algolia/requester-fetch': 5.40.0 + '@algolia/requester-node-http': 5.40.0 - '@algolia/recommend@5.29.0': + '@algolia/recommend@5.40.0': dependencies: - '@algolia/client-common': 5.29.0 - '@algolia/requester-browser-xhr': 5.29.0 - '@algolia/requester-fetch': 5.29.0 - '@algolia/requester-node-http': 5.29.0 + '@algolia/client-common': 5.40.0 + '@algolia/requester-browser-xhr': 5.40.0 + '@algolia/requester-fetch': 5.40.0 + '@algolia/requester-node-http': 5.40.0 - '@algolia/requester-browser-xhr@5.29.0': + '@algolia/requester-browser-xhr@5.40.0': dependencies: - '@algolia/client-common': 5.29.0 + '@algolia/client-common': 5.40.0 - '@algolia/requester-fetch@5.29.0': + '@algolia/requester-fetch@5.40.0': dependencies: - '@algolia/client-common': 5.29.0 + '@algolia/client-common': 5.40.0 - '@algolia/requester-node-http@5.29.0': + '@algolia/requester-node-http@5.40.0': dependencies: - '@algolia/client-common': 5.29.0 + '@algolia/client-common': 5.40.0 '@alloc/quick-lru@5.2.0': {} '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 '@angular-devkit/core@19.2.15(chokidar@4.0.3)': dependencies: @@ -11765,11 +11579,22 @@ snapshots: optionalDependencies: chokidar: 4.0.3 - '@angular-devkit/schematics-cli@19.2.15(@types/node@22.18.5)(chokidar@4.0.3)': + '@angular-devkit/core@19.2.17(chokidar@4.0.3)': + dependencies: + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) + jsonc-parser: 3.3.1 + picomatch: 4.0.2 + rxjs: 7.8.1 + source-map: 0.7.4 + optionalDependencies: + chokidar: 4.0.3 + + '@angular-devkit/schematics-cli@19.2.15(@types/node@22.18.10)(chokidar@4.0.3)': dependencies: '@angular-devkit/core': 19.2.15(chokidar@4.0.3) '@angular-devkit/schematics': 19.2.15(chokidar@4.0.3) - '@inquirer/prompts': 7.3.2(@types/node@22.18.5) + '@inquirer/prompts': 7.3.2(@types/node@22.18.10) ansi-colors: 4.1.3 symbol-observable: 4.0.0 yargs-parser: 21.1.1 @@ -11787,6 +11612,16 @@ snapshots: transitivePeerDependencies: - chokidar + '@angular-devkit/schematics@19.2.17(chokidar@4.0.3)': + dependencies: + '@angular-devkit/core': 19.2.17(chokidar@4.0.3) + jsonc-parser: 3.3.1 + magic-string: 0.30.17 + ora: 5.4.1 + rxjs: 7.8.1 + transitivePeerDependencies: + - chokidar + '@asamuzakjp/css-color@3.2.0': dependencies: '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) @@ -11801,15 +11636,15 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.887.0 - '@aws-sdk/util-locate-window': 3.873.0 + '@aws-sdk/types': 3.901.0 + '@aws-sdk/util-locate-window': 3.893.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.887.0 + '@aws-sdk/types': 3.901.0 tslib: 2.8.1 '@aws-crypto/supports-web-crypto@5.2.0': @@ -11818,366 +11653,365 @@ snapshots: '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.887.0 + '@aws-sdk/types': 3.901.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-sesv2@3.890.0': + '@aws-sdk/client-sesv2@3.907.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.890.0 - '@aws-sdk/credential-provider-node': 3.890.0 - '@aws-sdk/middleware-host-header': 3.887.0 - '@aws-sdk/middleware-logger': 3.887.0 - '@aws-sdk/middleware-recursion-detection': 3.887.0 - '@aws-sdk/middleware-user-agent': 3.890.0 - '@aws-sdk/region-config-resolver': 3.890.0 - '@aws-sdk/signature-v4-multi-region': 3.890.0 - '@aws-sdk/types': 3.887.0 - '@aws-sdk/util-endpoints': 3.890.0 - '@aws-sdk/util-user-agent-browser': 3.887.0 - '@aws-sdk/util-user-agent-node': 3.890.0 - '@smithy/config-resolver': 4.2.2 - '@smithy/core': 3.11.0 - '@smithy/fetch-http-handler': 5.2.1 - '@smithy/hash-node': 4.1.1 - '@smithy/invalid-dependency': 4.1.1 - '@smithy/middleware-content-length': 4.1.1 - '@smithy/middleware-endpoint': 4.2.2 - '@smithy/middleware-retry': 4.2.2 - '@smithy/middleware-serde': 4.1.1 - '@smithy/middleware-stack': 4.1.1 - '@smithy/node-config-provider': 4.2.2 - '@smithy/node-http-handler': 4.2.1 - '@smithy/protocol-http': 5.2.1 - '@smithy/smithy-client': 4.6.2 - '@smithy/types': 4.5.0 - '@smithy/url-parser': 4.1.1 - '@smithy/util-base64': 4.1.0 - '@smithy/util-body-length-browser': 4.1.0 - '@smithy/util-body-length-node': 4.1.0 - '@smithy/util-defaults-mode-browser': 4.1.2 - '@smithy/util-defaults-mode-node': 4.1.2 - '@smithy/util-endpoints': 3.1.2 - '@smithy/util-middleware': 4.1.1 - '@smithy/util-retry': 4.1.1 - '@smithy/util-utf8': 4.1.0 + '@aws-sdk/core': 3.907.0 + '@aws-sdk/credential-provider-node': 3.907.0 + '@aws-sdk/middleware-host-header': 3.901.0 + '@aws-sdk/middleware-logger': 3.901.0 + '@aws-sdk/middleware-recursion-detection': 3.901.0 + '@aws-sdk/middleware-user-agent': 3.907.0 + '@aws-sdk/region-config-resolver': 3.901.0 + '@aws-sdk/signature-v4-multi-region': 3.907.0 + '@aws-sdk/types': 3.901.0 + '@aws-sdk/util-endpoints': 3.901.0 + '@aws-sdk/util-user-agent-browser': 3.907.0 + '@aws-sdk/util-user-agent-node': 3.907.0 + '@smithy/config-resolver': 4.3.0 + '@smithy/core': 3.15.0 + '@smithy/fetch-http-handler': 5.3.1 + '@smithy/hash-node': 4.2.0 + '@smithy/invalid-dependency': 4.2.0 + '@smithy/middleware-content-length': 4.2.0 + '@smithy/middleware-endpoint': 4.3.1 + '@smithy/middleware-retry': 4.4.1 + '@smithy/middleware-serde': 4.2.0 + '@smithy/middleware-stack': 4.2.0 + '@smithy/node-config-provider': 4.3.0 + '@smithy/node-http-handler': 4.3.0 + '@smithy/protocol-http': 5.3.0 + '@smithy/smithy-client': 4.7.1 + '@smithy/types': 4.6.0 + '@smithy/url-parser': 4.2.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-body-length-node': 4.2.1 + '@smithy/util-defaults-mode-browser': 4.3.0 + '@smithy/util-defaults-mode-node': 4.2.1 + '@smithy/util-endpoints': 3.2.0 + '@smithy/util-middleware': 4.2.0 + '@smithy/util-retry': 4.2.0 + '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.890.0': + '@aws-sdk/client-sso@3.907.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.890.0 - '@aws-sdk/middleware-host-header': 3.887.0 - '@aws-sdk/middleware-logger': 3.887.0 - '@aws-sdk/middleware-recursion-detection': 3.887.0 - '@aws-sdk/middleware-user-agent': 3.890.0 - '@aws-sdk/region-config-resolver': 3.890.0 - '@aws-sdk/types': 3.887.0 - '@aws-sdk/util-endpoints': 3.890.0 - '@aws-sdk/util-user-agent-browser': 3.887.0 - '@aws-sdk/util-user-agent-node': 3.890.0 - '@smithy/config-resolver': 4.2.2 - '@smithy/core': 3.11.0 - '@smithy/fetch-http-handler': 5.2.1 - '@smithy/hash-node': 4.1.1 - '@smithy/invalid-dependency': 4.1.1 - '@smithy/middleware-content-length': 4.1.1 - '@smithy/middleware-endpoint': 4.2.2 - '@smithy/middleware-retry': 4.2.2 - '@smithy/middleware-serde': 4.1.1 - '@smithy/middleware-stack': 4.1.1 - '@smithy/node-config-provider': 4.2.2 - '@smithy/node-http-handler': 4.2.1 - '@smithy/protocol-http': 5.2.1 - '@smithy/smithy-client': 4.6.2 - '@smithy/types': 4.5.0 - '@smithy/url-parser': 4.1.1 - '@smithy/util-base64': 4.1.0 - '@smithy/util-body-length-browser': 4.1.0 - '@smithy/util-body-length-node': 4.1.0 - '@smithy/util-defaults-mode-browser': 4.1.2 - '@smithy/util-defaults-mode-node': 4.1.2 - '@smithy/util-endpoints': 3.1.2 - '@smithy/util-middleware': 4.1.1 - '@smithy/util-retry': 4.1.1 - '@smithy/util-utf8': 4.1.0 + '@aws-sdk/core': 3.907.0 + '@aws-sdk/middleware-host-header': 3.901.0 + '@aws-sdk/middleware-logger': 3.901.0 + '@aws-sdk/middleware-recursion-detection': 3.901.0 + '@aws-sdk/middleware-user-agent': 3.907.0 + '@aws-sdk/region-config-resolver': 3.901.0 + '@aws-sdk/types': 3.901.0 + '@aws-sdk/util-endpoints': 3.901.0 + '@aws-sdk/util-user-agent-browser': 3.907.0 + '@aws-sdk/util-user-agent-node': 3.907.0 + '@smithy/config-resolver': 4.3.0 + '@smithy/core': 3.15.0 + '@smithy/fetch-http-handler': 5.3.1 + '@smithy/hash-node': 4.2.0 + '@smithy/invalid-dependency': 4.2.0 + '@smithy/middleware-content-length': 4.2.0 + '@smithy/middleware-endpoint': 4.3.1 + '@smithy/middleware-retry': 4.4.1 + '@smithy/middleware-serde': 4.2.0 + '@smithy/middleware-stack': 4.2.0 + '@smithy/node-config-provider': 4.3.0 + '@smithy/node-http-handler': 4.3.0 + '@smithy/protocol-http': 5.3.0 + '@smithy/smithy-client': 4.7.1 + '@smithy/types': 4.6.0 + '@smithy/url-parser': 4.2.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-body-length-node': 4.2.1 + '@smithy/util-defaults-mode-browser': 4.3.0 + '@smithy/util-defaults-mode-node': 4.2.1 + '@smithy/util-endpoints': 3.2.0 + '@smithy/util-middleware': 4.2.0 + '@smithy/util-retry': 4.2.0 + '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/core@3.890.0': + '@aws-sdk/core@3.907.0': dependencies: - '@aws-sdk/types': 3.887.0 - '@aws-sdk/xml-builder': 3.887.0 - '@smithy/core': 3.11.0 - '@smithy/node-config-provider': 4.2.2 - '@smithy/property-provider': 4.1.1 - '@smithy/protocol-http': 5.2.1 - '@smithy/signature-v4': 5.2.1 - '@smithy/smithy-client': 4.6.2 - '@smithy/types': 4.5.0 - '@smithy/util-base64': 4.1.0 - '@smithy/util-body-length-browser': 4.1.0 - '@smithy/util-middleware': 4.1.1 - '@smithy/util-utf8': 4.1.0 - fast-xml-parser: 5.2.5 + '@aws-sdk/types': 3.901.0 + '@aws-sdk/xml-builder': 3.901.0 + '@smithy/core': 3.15.0 + '@smithy/node-config-provider': 4.3.0 + '@smithy/property-provider': 4.2.0 + '@smithy/protocol-http': 5.3.0 + '@smithy/signature-v4': 5.3.0 + '@smithy/smithy-client': 4.7.1 + '@smithy/types': 4.6.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-middleware': 4.2.0 + '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.890.0': + '@aws-sdk/credential-provider-env@3.907.0': dependencies: - '@aws-sdk/core': 3.890.0 - '@aws-sdk/types': 3.887.0 - '@smithy/property-provider': 4.1.1 - '@smithy/types': 4.5.0 + '@aws-sdk/core': 3.907.0 + '@aws-sdk/types': 3.901.0 + '@smithy/property-provider': 4.2.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.890.0': + '@aws-sdk/credential-provider-http@3.907.0': dependencies: - '@aws-sdk/core': 3.890.0 - '@aws-sdk/types': 3.887.0 - '@smithy/fetch-http-handler': 5.2.1 - '@smithy/node-http-handler': 4.2.1 - '@smithy/property-provider': 4.1.1 - '@smithy/protocol-http': 5.2.1 - '@smithy/smithy-client': 4.6.2 - '@smithy/types': 4.5.0 - '@smithy/util-stream': 4.3.1 + '@aws-sdk/core': 3.907.0 + '@aws-sdk/types': 3.901.0 + '@smithy/fetch-http-handler': 5.3.1 + '@smithy/node-http-handler': 4.3.0 + '@smithy/property-provider': 4.2.0 + '@smithy/protocol-http': 5.3.0 + '@smithy/smithy-client': 4.7.1 + '@smithy/types': 4.6.0 + '@smithy/util-stream': 4.5.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.890.0': + '@aws-sdk/credential-provider-ini@3.907.0': dependencies: - '@aws-sdk/core': 3.890.0 - '@aws-sdk/credential-provider-env': 3.890.0 - '@aws-sdk/credential-provider-http': 3.890.0 - '@aws-sdk/credential-provider-process': 3.890.0 - '@aws-sdk/credential-provider-sso': 3.890.0 - '@aws-sdk/credential-provider-web-identity': 3.890.0 - '@aws-sdk/nested-clients': 3.890.0 - '@aws-sdk/types': 3.887.0 - '@smithy/credential-provider-imds': 4.1.2 - '@smithy/property-provider': 4.1.1 - '@smithy/shared-ini-file-loader': 4.2.0 - '@smithy/types': 4.5.0 + '@aws-sdk/core': 3.907.0 + '@aws-sdk/credential-provider-env': 3.907.0 + '@aws-sdk/credential-provider-http': 3.907.0 + '@aws-sdk/credential-provider-process': 3.907.0 + '@aws-sdk/credential-provider-sso': 3.907.0 + '@aws-sdk/credential-provider-web-identity': 3.907.0 + '@aws-sdk/nested-clients': 3.907.0 + '@aws-sdk/types': 3.901.0 + '@smithy/credential-provider-imds': 4.2.0 + '@smithy/property-provider': 4.2.0 + '@smithy/shared-ini-file-loader': 4.3.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.890.0': + '@aws-sdk/credential-provider-node@3.907.0': dependencies: - '@aws-sdk/credential-provider-env': 3.890.0 - '@aws-sdk/credential-provider-http': 3.890.0 - '@aws-sdk/credential-provider-ini': 3.890.0 - '@aws-sdk/credential-provider-process': 3.890.0 - '@aws-sdk/credential-provider-sso': 3.890.0 - '@aws-sdk/credential-provider-web-identity': 3.890.0 - '@aws-sdk/types': 3.887.0 - '@smithy/credential-provider-imds': 4.1.2 - '@smithy/property-provider': 4.1.1 - '@smithy/shared-ini-file-loader': 4.2.0 - '@smithy/types': 4.5.0 + '@aws-sdk/credential-provider-env': 3.907.0 + '@aws-sdk/credential-provider-http': 3.907.0 + '@aws-sdk/credential-provider-ini': 3.907.0 + '@aws-sdk/credential-provider-process': 3.907.0 + '@aws-sdk/credential-provider-sso': 3.907.0 + '@aws-sdk/credential-provider-web-identity': 3.907.0 + '@aws-sdk/types': 3.901.0 + '@smithy/credential-provider-imds': 4.2.0 + '@smithy/property-provider': 4.2.0 + '@smithy/shared-ini-file-loader': 4.3.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.890.0': + '@aws-sdk/credential-provider-process@3.907.0': dependencies: - '@aws-sdk/core': 3.890.0 - '@aws-sdk/types': 3.887.0 - '@smithy/property-provider': 4.1.1 - '@smithy/shared-ini-file-loader': 4.2.0 - '@smithy/types': 4.5.0 + '@aws-sdk/core': 3.907.0 + '@aws-sdk/types': 3.901.0 + '@smithy/property-provider': 4.2.0 + '@smithy/shared-ini-file-loader': 4.3.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.890.0': + '@aws-sdk/credential-provider-sso@3.907.0': dependencies: - '@aws-sdk/client-sso': 3.890.0 - '@aws-sdk/core': 3.890.0 - '@aws-sdk/token-providers': 3.890.0 - '@aws-sdk/types': 3.887.0 - '@smithy/property-provider': 4.1.1 - '@smithy/shared-ini-file-loader': 4.2.0 - '@smithy/types': 4.5.0 + '@aws-sdk/client-sso': 3.907.0 + '@aws-sdk/core': 3.907.0 + '@aws-sdk/token-providers': 3.907.0 + '@aws-sdk/types': 3.901.0 + '@smithy/property-provider': 4.2.0 + '@smithy/shared-ini-file-loader': 4.3.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-web-identity@3.890.0': + '@aws-sdk/credential-provider-web-identity@3.907.0': dependencies: - '@aws-sdk/core': 3.890.0 - '@aws-sdk/nested-clients': 3.890.0 - '@aws-sdk/types': 3.887.0 - '@smithy/property-provider': 4.1.1 - '@smithy/shared-ini-file-loader': 4.2.0 - '@smithy/types': 4.5.0 + '@aws-sdk/core': 3.907.0 + '@aws-sdk/nested-clients': 3.907.0 + '@aws-sdk/types': 3.901.0 + '@smithy/property-provider': 4.2.0 + '@smithy/shared-ini-file-loader': 4.3.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/middleware-host-header@3.887.0': + '@aws-sdk/middleware-host-header@3.901.0': dependencies: - '@aws-sdk/types': 3.887.0 - '@smithy/protocol-http': 5.2.1 - '@smithy/types': 4.5.0 + '@aws-sdk/types': 3.901.0 + '@smithy/protocol-http': 5.3.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@aws-sdk/middleware-logger@3.887.0': + '@aws-sdk/middleware-logger@3.901.0': dependencies: - '@aws-sdk/types': 3.887.0 - '@smithy/types': 4.5.0 + '@aws-sdk/types': 3.901.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.887.0': + '@aws-sdk/middleware-recursion-detection@3.901.0': dependencies: - '@aws-sdk/types': 3.887.0 + '@aws-sdk/types': 3.901.0 '@aws/lambda-invoke-store': 0.0.1 - '@smithy/protocol-http': 5.2.1 - '@smithy/types': 4.5.0 + '@smithy/protocol-http': 5.3.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@aws-sdk/middleware-sdk-s3@3.890.0': + '@aws-sdk/middleware-sdk-s3@3.907.0': dependencies: - '@aws-sdk/core': 3.890.0 - '@aws-sdk/types': 3.887.0 - '@aws-sdk/util-arn-parser': 3.873.0 - '@smithy/core': 3.11.0 - '@smithy/node-config-provider': 4.2.2 - '@smithy/protocol-http': 5.2.1 - '@smithy/signature-v4': 5.2.1 - '@smithy/smithy-client': 4.6.2 - '@smithy/types': 4.5.0 - '@smithy/util-config-provider': 4.1.0 - '@smithy/util-middleware': 4.1.1 - '@smithy/util-stream': 4.3.1 - '@smithy/util-utf8': 4.1.0 + '@aws-sdk/core': 3.907.0 + '@aws-sdk/types': 3.901.0 + '@aws-sdk/util-arn-parser': 3.893.0 + '@smithy/core': 3.15.0 + '@smithy/node-config-provider': 4.3.0 + '@smithy/protocol-http': 5.3.0 + '@smithy/signature-v4': 5.3.0 + '@smithy/smithy-client': 4.7.1 + '@smithy/types': 4.6.0 + '@smithy/util-config-provider': 4.2.0 + '@smithy/util-middleware': 4.2.0 + '@smithy/util-stream': 4.5.0 + '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.890.0': + '@aws-sdk/middleware-user-agent@3.907.0': dependencies: - '@aws-sdk/core': 3.890.0 - '@aws-sdk/types': 3.887.0 - '@aws-sdk/util-endpoints': 3.890.0 - '@smithy/core': 3.11.0 - '@smithy/protocol-http': 5.2.1 - '@smithy/types': 4.5.0 + '@aws-sdk/core': 3.907.0 + '@aws-sdk/types': 3.901.0 + '@aws-sdk/util-endpoints': 3.901.0 + '@smithy/core': 3.15.0 + '@smithy/protocol-http': 5.3.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.890.0': + '@aws-sdk/nested-clients@3.907.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.890.0 - '@aws-sdk/middleware-host-header': 3.887.0 - '@aws-sdk/middleware-logger': 3.887.0 - '@aws-sdk/middleware-recursion-detection': 3.887.0 - '@aws-sdk/middleware-user-agent': 3.890.0 - '@aws-sdk/region-config-resolver': 3.890.0 - '@aws-sdk/types': 3.887.0 - '@aws-sdk/util-endpoints': 3.890.0 - '@aws-sdk/util-user-agent-browser': 3.887.0 - '@aws-sdk/util-user-agent-node': 3.890.0 - '@smithy/config-resolver': 4.2.2 - '@smithy/core': 3.11.0 - '@smithy/fetch-http-handler': 5.2.1 - '@smithy/hash-node': 4.1.1 - '@smithy/invalid-dependency': 4.1.1 - '@smithy/middleware-content-length': 4.1.1 - '@smithy/middleware-endpoint': 4.2.2 - '@smithy/middleware-retry': 4.2.2 - '@smithy/middleware-serde': 4.1.1 - '@smithy/middleware-stack': 4.1.1 - '@smithy/node-config-provider': 4.2.2 - '@smithy/node-http-handler': 4.2.1 - '@smithy/protocol-http': 5.2.1 - '@smithy/smithy-client': 4.6.2 - '@smithy/types': 4.5.0 - '@smithy/url-parser': 4.1.1 - '@smithy/util-base64': 4.1.0 - '@smithy/util-body-length-browser': 4.1.0 - '@smithy/util-body-length-node': 4.1.0 - '@smithy/util-defaults-mode-browser': 4.1.2 - '@smithy/util-defaults-mode-node': 4.1.2 - '@smithy/util-endpoints': 3.1.2 - '@smithy/util-middleware': 4.1.1 - '@smithy/util-retry': 4.1.1 - '@smithy/util-utf8': 4.1.0 + '@aws-sdk/core': 3.907.0 + '@aws-sdk/middleware-host-header': 3.901.0 + '@aws-sdk/middleware-logger': 3.901.0 + '@aws-sdk/middleware-recursion-detection': 3.901.0 + '@aws-sdk/middleware-user-agent': 3.907.0 + '@aws-sdk/region-config-resolver': 3.901.0 + '@aws-sdk/types': 3.901.0 + '@aws-sdk/util-endpoints': 3.901.0 + '@aws-sdk/util-user-agent-browser': 3.907.0 + '@aws-sdk/util-user-agent-node': 3.907.0 + '@smithy/config-resolver': 4.3.0 + '@smithy/core': 3.15.0 + '@smithy/fetch-http-handler': 5.3.1 + '@smithy/hash-node': 4.2.0 + '@smithy/invalid-dependency': 4.2.0 + '@smithy/middleware-content-length': 4.2.0 + '@smithy/middleware-endpoint': 4.3.1 + '@smithy/middleware-retry': 4.4.1 + '@smithy/middleware-serde': 4.2.0 + '@smithy/middleware-stack': 4.2.0 + '@smithy/node-config-provider': 4.3.0 + '@smithy/node-http-handler': 4.3.0 + '@smithy/protocol-http': 5.3.0 + '@smithy/smithy-client': 4.7.1 + '@smithy/types': 4.6.0 + '@smithy/url-parser': 4.2.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-body-length-node': 4.2.1 + '@smithy/util-defaults-mode-browser': 4.3.0 + '@smithy/util-defaults-mode-node': 4.2.1 + '@smithy/util-endpoints': 3.2.0 + '@smithy/util-middleware': 4.2.0 + '@smithy/util-retry': 4.2.0 + '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/region-config-resolver@3.890.0': + '@aws-sdk/region-config-resolver@3.901.0': dependencies: - '@aws-sdk/types': 3.887.0 - '@smithy/node-config-provider': 4.2.2 - '@smithy/types': 4.5.0 - '@smithy/util-config-provider': 4.1.0 - '@smithy/util-middleware': 4.1.1 + '@aws-sdk/types': 3.901.0 + '@smithy/node-config-provider': 4.3.0 + '@smithy/types': 4.6.0 + '@smithy/util-config-provider': 4.2.0 + '@smithy/util-middleware': 4.2.0 tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.890.0': + '@aws-sdk/signature-v4-multi-region@3.907.0': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.890.0 - '@aws-sdk/types': 3.887.0 - '@smithy/protocol-http': 5.2.1 - '@smithy/signature-v4': 5.2.1 - '@smithy/types': 4.5.0 + '@aws-sdk/middleware-sdk-s3': 3.907.0 + '@aws-sdk/types': 3.901.0 + '@smithy/protocol-http': 5.3.0 + '@smithy/signature-v4': 5.3.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@aws-sdk/token-providers@3.890.0': + '@aws-sdk/token-providers@3.907.0': dependencies: - '@aws-sdk/core': 3.890.0 - '@aws-sdk/nested-clients': 3.890.0 - '@aws-sdk/types': 3.887.0 - '@smithy/property-provider': 4.1.1 - '@smithy/shared-ini-file-loader': 4.2.0 - '@smithy/types': 4.5.0 + '@aws-sdk/core': 3.907.0 + '@aws-sdk/nested-clients': 3.907.0 + '@aws-sdk/types': 3.901.0 + '@smithy/property-provider': 4.2.0 + '@smithy/shared-ini-file-loader': 4.3.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/types@3.887.0': + '@aws-sdk/types@3.901.0': dependencies: - '@smithy/types': 4.5.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@aws-sdk/util-arn-parser@3.873.0': + '@aws-sdk/util-arn-parser@3.893.0': dependencies: tslib: 2.8.1 - '@aws-sdk/util-endpoints@3.890.0': + '@aws-sdk/util-endpoints@3.901.0': dependencies: - '@aws-sdk/types': 3.887.0 - '@smithy/types': 4.5.0 - '@smithy/url-parser': 4.1.1 - '@smithy/util-endpoints': 3.1.2 + '@aws-sdk/types': 3.901.0 + '@smithy/types': 4.6.0 + '@smithy/url-parser': 4.2.0 + '@smithy/util-endpoints': 3.2.0 tslib: 2.8.1 - '@aws-sdk/util-locate-window@3.873.0': + '@aws-sdk/util-locate-window@3.893.0': dependencies: tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.887.0': + '@aws-sdk/util-user-agent-browser@3.907.0': dependencies: - '@aws-sdk/types': 3.887.0 - '@smithy/types': 4.5.0 + '@aws-sdk/types': 3.901.0 + '@smithy/types': 4.6.0 bowser: 2.12.1 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.890.0': + '@aws-sdk/util-user-agent-node@3.907.0': dependencies: - '@aws-sdk/middleware-user-agent': 3.890.0 - '@aws-sdk/types': 3.887.0 - '@smithy/node-config-provider': 4.2.2 - '@smithy/types': 4.5.0 + '@aws-sdk/middleware-user-agent': 3.907.0 + '@aws-sdk/types': 3.901.0 + '@smithy/node-config-provider': 4.3.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@aws-sdk/xml-builder@3.887.0': + '@aws-sdk/xml-builder@3.901.0': dependencies: - '@smithy/types': 4.5.0 + '@smithy/types': 4.6.0 + fast-xml-parser: 5.2.5 tslib: 2.8.1 '@aws/lambda-invoke-store@0.0.1': {} @@ -12215,7 +12049,7 @@ snapshots: '@babel/parser': 7.28.4 '@babel/types': 7.28.4 '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 '@babel/helper-annotate-as-pure@7.27.3': @@ -12226,7 +12060,7 @@ snapshots: dependencies: '@babel/compat-data': 7.27.7 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.25.3 + browserslist: 4.26.3 lru-cache: 5.1.1 semver: 6.3.1 @@ -13200,21 +13034,21 @@ snapshots: '@docsearch/css@3.9.0': {} - '@docsearch/react@3.9.0(@algolia/client-search@5.29.0)(@types/react@19.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3)': + '@docsearch/react@3.9.0(@algolia/client-search@5.40.0)(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3)': dependencies: - '@algolia/autocomplete-core': 1.17.9(@algolia/client-search@5.29.0)(algoliasearch@5.29.0)(search-insights@2.17.3) - '@algolia/autocomplete-preset-algolia': 1.17.9(@algolia/client-search@5.29.0)(algoliasearch@5.29.0) + '@algolia/autocomplete-core': 1.17.9(@algolia/client-search@5.40.0)(algoliasearch@5.40.0)(search-insights@2.17.3) + '@algolia/autocomplete-preset-algolia': 1.17.9(@algolia/client-search@5.40.0)(algoliasearch@5.40.0) '@docsearch/css': 3.9.0 - algoliasearch: 5.29.0 + algoliasearch: 5.40.0 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.2.2 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) search-insights: 2.17.3 transitivePeerDependencies: - '@algolia/client-search' - '@docusaurus/babel@3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@docusaurus/babel@3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/core': 7.27.7 '@babel/generator': 7.28.3 @@ -13226,14 +13060,13 @@ snapshots: '@babel/runtime': 7.28.4 '@babel/runtime-corejs3': 7.27.6 '@babel/traverse': 7.28.4 - '@docusaurus/logger': 3.8.1 - '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/logger': 3.9.1 + '@docusaurus/utils': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) babel-plugin-dynamic-import-node: 2.3.3 fs-extra: 11.3.0 tslib: 2.8.1 transitivePeerDependencies: - '@swc/core' - - acorn - esbuild - react - react-dom @@ -13241,38 +13074,37 @@ snapshots: - uglify-js - webpack-cli - '@docusaurus/bundler@3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + '@docusaurus/bundler@3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3)': dependencies: '@babel/core': 7.27.7 - '@docusaurus/babel': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/cssnano-preset': 3.8.1 - '@docusaurus/logger': 3.8.1 - '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - babel-loader: 9.2.1(@babel/core@7.27.7)(webpack@5.100.2) + '@docusaurus/babel': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/cssnano-preset': 3.9.1 + '@docusaurus/logger': 3.9.1 + '@docusaurus/types': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + babel-loader: 9.2.1(@babel/core@7.27.7)(webpack@5.102.1) clean-css: 5.3.3 - copy-webpack-plugin: 11.0.0(webpack@5.100.2) - css-loader: 6.11.0(webpack@5.100.2) - css-minimizer-webpack-plugin: 5.0.1(clean-css@5.3.3)(webpack@5.100.2) + copy-webpack-plugin: 11.0.0(webpack@5.102.1) + css-loader: 6.11.0(webpack@5.102.1) + css-minimizer-webpack-plugin: 5.0.1(clean-css@5.3.3)(webpack@5.102.1) cssnano: 6.1.2(postcss@8.5.6) - file-loader: 6.2.0(webpack@5.100.2) + file-loader: 6.2.0(webpack@5.102.1) html-minifier-terser: 7.2.0 - mini-css-extract-plugin: 2.9.2(webpack@5.100.2) - null-loader: 4.0.1(webpack@5.100.2) + mini-css-extract-plugin: 2.9.2(webpack@5.102.1) + null-loader: 4.0.1(webpack@5.102.1) postcss: 8.5.6 - postcss-loader: 7.3.4(postcss@8.5.6)(typescript@5.9.2)(webpack@5.100.2) + postcss-loader: 7.3.4(postcss@8.5.6)(typescript@5.9.3)(webpack@5.102.1) postcss-preset-env: 10.2.4(postcss@8.5.6) - terser-webpack-plugin: 5.3.14(webpack@5.100.2) + terser-webpack-plugin: 5.3.14(webpack@5.102.1) tslib: 2.8.1 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.100.2))(webpack@5.100.2) - webpack: 5.100.2 - webpackbar: 6.0.1(webpack@5.100.2) + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.102.1))(webpack@5.102.1) + webpack: 5.102.1 + webpackbar: 6.0.1(webpack@5.102.1) transitivePeerDependencies: - '@parcel/css' - '@rspack/core' - '@swc/core' - '@swc/css' - - acorn - csso - esbuild - lightningcss @@ -13283,16 +13115,16 @@ snapshots: - uglify-js - webpack-cli - '@docusaurus/core@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + '@docusaurus/core@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3)': dependencies: - '@docusaurus/babel': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/bundler': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/logger': 3.8.1 - '@docusaurus/mdx-loader': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-common': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@mdx-js/react': 3.1.1(@types/react@19.1.13)(react@18.3.1) + '@docusaurus/babel': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/bundler': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/logger': 3.9.1 + '@docusaurus/mdx-loader': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mdx-js/react': 3.1.1(@types/react@19.2.2)(react@18.3.1) boxen: 6.2.1 chalk: 4.1.2 chokidar: 3.6.0 @@ -13307,7 +13139,7 @@ snapshots: execa: 5.1.1 fs-extra: 11.3.0 html-tags: 3.3.1 - html-webpack-plugin: 5.6.3(webpack@5.100.2) + html-webpack-plugin: 5.6.3(webpack@5.102.1) leven: 3.1.0 lodash: 4.17.21 open: 8.4.2 @@ -13317,18 +13149,18 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-helmet-async: '@slorber/react-helmet-async@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)' react-loadable: '@docusaurus/react-loadable@6.0.0(react@18.3.1)' - react-loadable-ssr-addon-v5-slorber: 1.0.1(@docusaurus/react-loadable@6.0.0(react@18.3.1))(webpack@5.100.2) + react-loadable-ssr-addon-v5-slorber: 1.0.1(@docusaurus/react-loadable@6.0.0(react@18.3.1))(webpack@5.102.1) react-router: 5.3.4(react@18.3.1) react-router-config: 5.1.1(react-router@5.3.4(react@18.3.1))(react@18.3.1) react-router-dom: 5.3.4(react@18.3.1) - semver: 7.7.2 + semver: 7.7.3 serve-handler: 6.1.6 tinypool: 1.1.1 tslib: 2.8.1 update-notifier: 6.0.2 - webpack: 5.100.2 + webpack: 5.102.1 webpack-bundle-analyzer: 4.10.2 - webpack-dev-server: 4.15.2(webpack@5.100.2) + webpack-dev-server: 5.2.2(webpack@5.102.1) webpack-merge: 6.0.1 transitivePeerDependencies: - '@docusaurus/faster' @@ -13336,7 +13168,6 @@ snapshots: - '@rspack/core' - '@swc/core' - '@swc/css' - - acorn - bufferutil - csso - debug @@ -13348,28 +13179,28 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/cssnano-preset@3.8.1': + '@docusaurus/cssnano-preset@3.9.1': dependencies: cssnano-preset-advanced: 6.1.2(postcss@8.5.6) postcss: 8.5.6 postcss-sort-media-queries: 5.2.0(postcss@8.5.6) tslib: 2.8.1 - '@docusaurus/logger@3.8.1': + '@docusaurus/logger@3.9.1': dependencies: chalk: 4.1.2 tslib: 2.8.1 - '@docusaurus/mdx-loader@3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@docusaurus/mdx-loader@3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@docusaurus/logger': 3.8.1 - '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@mdx-js/mdx': 3.1.0(acorn@8.15.0) + '@docusaurus/logger': 3.9.1 + '@docusaurus/utils': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mdx-js/mdx': 3.1.1 '@slorber/remark-comment': 1.0.0 escape-html: 1.0.3 estree-util-value-to-estree: 3.4.0 - file-loader: 6.2.0(webpack@5.100.2) + file-loader: 6.2.0(webpack@5.102.1) fs-extra: 11.3.0 image-size: 2.0.2 mdast-util-mdx: 3.0.0 @@ -13385,22 +13216,21 @@ snapshots: tslib: 2.8.1 unified: 11.0.5 unist-util-visit: 5.0.0 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.100.2))(webpack@5.100.2) + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.102.1))(webpack@5.102.1) vfile: 6.0.3 - webpack: 5.100.2 + webpack: 5.102.1 transitivePeerDependencies: - '@swc/core' - - acorn - esbuild - supports-color - uglify-js - webpack-cli - '@docusaurus/module-type-aliases@3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@docusaurus/module-type-aliases@3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/types': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/history': 4.7.11 - '@types/react': 19.1.13 + '@types/react': 19.2.2 '@types/react-router-config': 5.0.11 '@types/react-router-dom': 5.3.3 react: 18.3.1 @@ -13409,23 +13239,22 @@ snapshots: react-loadable: '@docusaurus/react-loadable@6.0.0(react@18.3.1)' transitivePeerDependencies: - '@swc/core' - - acorn - esbuild - supports-color - uglify-js - webpack-cli - '@docusaurus/plugin-content-blog@3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + '@docusaurus/plugin-content-blog@3.9.1(@docusaurus/plugin-content-docs@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3))(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/logger': 3.8.1 - '@docusaurus/mdx-loader': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/plugin-content-docs': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/theme-common': 3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-common': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/core': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/logger': 3.9.1 + '@docusaurus/mdx-loader': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/plugin-content-docs': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/theme-common': 3.9.1(@docusaurus/plugin-content-docs@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/types': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) cheerio: 1.0.0-rc.12 feed: 4.2.2 fs-extra: 11.3.0 @@ -13437,7 +13266,7 @@ snapshots: tslib: 2.8.1 unist-util-visit: 5.0.0 utility-types: 3.11.0 - webpack: 5.100.2 + webpack: 5.102.1 transitivePeerDependencies: - '@docusaurus/faster' - '@mdx-js/react' @@ -13445,7 +13274,6 @@ snapshots: - '@rspack/core' - '@swc/core' - '@swc/css' - - acorn - bufferutil - csso - debug @@ -13457,17 +13285,17 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + '@docusaurus/plugin-content-docs@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/logger': 3.8.1 - '@docusaurus/mdx-loader': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/module-type-aliases': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/theme-common': 3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-common': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/core': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/logger': 3.9.1 + '@docusaurus/mdx-loader': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/module-type-aliases': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/theme-common': 3.9.1(@docusaurus/plugin-content-docs@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/types': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/react-router-config': 5.0.11 combine-promises: 1.2.0 fs-extra: 11.3.0 @@ -13478,7 +13306,7 @@ snapshots: schema-dts: 1.1.5 tslib: 2.8.1 utility-types: 3.11.0 - webpack: 5.100.2 + webpack: 5.102.1 transitivePeerDependencies: - '@docusaurus/faster' - '@mdx-js/react' @@ -13486,7 +13314,6 @@ snapshots: - '@rspack/core' - '@swc/core' - '@swc/css' - - acorn - bufferutil - csso - debug @@ -13498,18 +13325,18 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-content-pages@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + '@docusaurus/plugin-content-pages@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/mdx-loader': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/core': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/mdx-loader': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/types': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) fs-extra: 11.3.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) tslib: 2.8.1 - webpack: 5.100.2 + webpack: 5.102.1 transitivePeerDependencies: - '@docusaurus/faster' - '@mdx-js/react' @@ -13517,7 +13344,6 @@ snapshots: - '@rspack/core' - '@swc/core' - '@swc/css' - - acorn - bufferutil - csso - debug @@ -13529,12 +13355,12 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-css-cascade-layers@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + '@docusaurus/plugin-css-cascade-layers@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/core': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/types': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) tslib: 2.8.1 transitivePeerDependencies: - '@docusaurus/faster' @@ -13543,7 +13369,6 @@ snapshots: - '@rspack/core' - '@swc/core' - '@swc/css' - - acorn - bufferutil - csso - debug @@ -13557,11 +13382,11 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-debug@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + '@docusaurus/plugin-debug@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/core': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/types': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) fs-extra: 11.3.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -13574,7 +13399,6 @@ snapshots: - '@rspack/core' - '@swc/core' - '@swc/css' - - acorn - bufferutil - csso - debug @@ -13586,11 +13410,11 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-google-analytics@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + '@docusaurus/plugin-google-analytics@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/core': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/types': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) tslib: 2.8.1 @@ -13601,7 +13425,6 @@ snapshots: - '@rspack/core' - '@swc/core' - '@swc/css' - - acorn - bufferutil - csso - debug @@ -13613,11 +13436,11 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-google-gtag@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + '@docusaurus/plugin-google-gtag@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/core': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/types': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/gtag.js': 0.0.12 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -13629,7 +13452,6 @@ snapshots: - '@rspack/core' - '@swc/core' - '@swc/css' - - acorn - bufferutil - csso - debug @@ -13641,11 +13463,11 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-google-tag-manager@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + '@docusaurus/plugin-google-tag-manager@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/core': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/types': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) tslib: 2.8.1 @@ -13656,7 +13478,6 @@ snapshots: - '@rspack/core' - '@swc/core' - '@swc/css' - - acorn - bufferutil - csso - debug @@ -13668,14 +13489,14 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-sitemap@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + '@docusaurus/plugin-sitemap@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/logger': 3.8.1 - '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-common': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/core': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/logger': 3.9.1 + '@docusaurus/types': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) fs-extra: 11.3.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -13688,7 +13509,6 @@ snapshots: - '@rspack/core' - '@swc/core' - '@swc/css' - - acorn - bufferutil - csso - debug @@ -13700,18 +13520,18 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-svgr@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + '@docusaurus/plugin-svgr@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@svgr/core': 8.1.0(typescript@5.9.2) - '@svgr/webpack': 8.1.0(typescript@5.9.2) + '@docusaurus/core': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/types': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@svgr/core': 8.1.0(typescript@5.9.3) + '@svgr/webpack': 8.1.0(typescript@5.9.3) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) tslib: 2.8.1 - webpack: 5.100.2 + webpack: 5.102.1 transitivePeerDependencies: - '@docusaurus/faster' - '@mdx-js/react' @@ -13719,7 +13539,6 @@ snapshots: - '@rspack/core' - '@swc/core' - '@swc/css' - - acorn - bufferutil - csso - debug @@ -13731,23 +13550,23 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/preset-classic@3.8.1(@algolia/client-search@5.29.0)(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(@types/react@19.1.13)(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3)(typescript@5.9.2)': + '@docusaurus/preset-classic@3.9.1(@algolia/client-search@5.40.0)(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/plugin-content-blog': 3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/plugin-content-docs': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/plugin-content-pages': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/plugin-css-cascade-layers': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/plugin-debug': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/plugin-google-analytics': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/plugin-google-gtag': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/plugin-google-tag-manager': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/plugin-sitemap': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/plugin-svgr': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/theme-classic': 3.8.1(@types/react@19.1.13)(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/theme-common': 3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/theme-search-algolia': 3.8.1(@algolia/client-search@5.29.0)(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(@types/react@19.1.13)(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3)(typescript@5.9.2) - '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/core': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/plugin-content-blog': 3.9.1(@docusaurus/plugin-content-docs@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3))(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/plugin-content-docs': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/plugin-content-pages': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/plugin-css-cascade-layers': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/plugin-debug': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/plugin-google-analytics': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/plugin-google-gtag': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/plugin-google-tag-manager': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/plugin-sitemap': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/plugin-svgr': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/theme-classic': 3.9.1(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/theme-common': 3.9.1(@docusaurus/plugin-content-docs@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/theme-search-algolia': 3.9.1(@algolia/client-search@5.40.0)(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3)(typescript@5.9.3) + '@docusaurus/types': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) transitivePeerDependencies: @@ -13759,7 +13578,6 @@ snapshots: - '@swc/core' - '@swc/css' - '@types/react' - - acorn - bufferutil - csso - debug @@ -13774,27 +13592,26 @@ snapshots: '@docusaurus/react-loadable@6.0.0(react@18.3.1)': dependencies: - '@types/react': 19.1.13 + '@types/react': 19.2.2 react: 18.3.1 - '@docusaurus/theme-classic@3.8.1(@types/react@19.1.13)(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + '@docusaurus/theme-classic@3.9.1(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/logger': 3.8.1 - '@docusaurus/mdx-loader': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/module-type-aliases': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/plugin-content-blog': 3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/plugin-content-docs': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/plugin-content-pages': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/theme-common': 3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/theme-translations': 3.8.1 - '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-common': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@mdx-js/react': 3.1.1(@types/react@19.1.13)(react@18.3.1) + '@docusaurus/core': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/logger': 3.9.1 + '@docusaurus/mdx-loader': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/module-type-aliases': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/plugin-content-blog': 3.9.1(@docusaurus/plugin-content-docs@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3))(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/plugin-content-docs': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/plugin-content-pages': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/theme-common': 3.9.1(@docusaurus/plugin-content-docs@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/theme-translations': 3.9.1 + '@docusaurus/types': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mdx-js/react': 3.1.1(@types/react@19.2.2)(react@18.3.1) clsx: 2.1.1 - copy-text-to-clipboard: 3.2.0 infima: 0.2.0-alpha.45 lodash: 4.17.21 nprogress: 0.2.0 @@ -13814,7 +13631,6 @@ snapshots: - '@swc/core' - '@swc/css' - '@types/react' - - acorn - bufferutil - csso - debug @@ -13826,15 +13642,15 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/theme-common@3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@docusaurus/theme-common@3.9.1(@docusaurus/plugin-content-docs@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@docusaurus/mdx-loader': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/module-type-aliases': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/plugin-content-docs': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-common': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/mdx-loader': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/module-type-aliases': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/plugin-content-docs': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/utils': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/history': 4.7.11 - '@types/react': 19.1.13 + '@types/react': 19.2.2 '@types/react-router-config': 5.0.11 clsx: 2.1.1 parse-numeric-range: 1.3.0 @@ -13845,24 +13661,23 @@ snapshots: utility-types: 3.11.0 transitivePeerDependencies: - '@swc/core' - - acorn - esbuild - supports-color - uglify-js - webpack-cli - '@docusaurus/theme-search-algolia@3.8.1(@algolia/client-search@5.29.0)(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(@types/react@19.1.13)(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3)(typescript@5.9.2)': + '@docusaurus/theme-search-algolia@3.9.1(@algolia/client-search@5.40.0)(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3)(typescript@5.9.3)': dependencies: - '@docsearch/react': 3.9.0(@algolia/client-search@5.29.0)(@types/react@19.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3) - '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/logger': 3.8.1 - '@docusaurus/plugin-content-docs': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/theme-common': 3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/theme-translations': 3.8.1 - '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - algoliasearch: 5.29.0 - algoliasearch-helper: 3.26.0(algoliasearch@5.29.0) + '@docsearch/react': 3.9.0(@algolia/client-search@5.40.0)(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3) + '@docusaurus/core': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/logger': 3.9.1 + '@docusaurus/plugin-content-docs': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@docusaurus/theme-common': 3.9.1(@docusaurus/plugin-content-docs@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/theme-translations': 3.9.1 + '@docusaurus/utils': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + algoliasearch: 5.40.0 + algoliasearch-helper: 3.26.0(algoliasearch@5.40.0) clsx: 2.1.1 eta: 2.2.0 fs-extra: 11.3.0 @@ -13880,7 +13695,6 @@ snapshots: - '@swc/core' - '@swc/css' - '@types/react' - - acorn - bufferutil - csso - debug @@ -13893,41 +13707,40 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/theme-translations@3.8.1': + '@docusaurus/theme-translations@3.9.1': dependencies: fs-extra: 11.3.0 tslib: 2.8.1 - '@docusaurus/tsconfig@3.8.1': {} + '@docusaurus/tsconfig@3.9.1': {} - '@docusaurus/types@3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@docusaurus/types@3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@mdx-js/mdx': 3.1.0(acorn@8.15.0) + '@mdx-js/mdx': 3.1.1 '@types/history': 4.7.11 - '@types/react': 19.1.13 + '@types/mdast': 4.0.4 + '@types/react': 19.2.2 commander: 5.1.0 joi: 17.13.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-helmet-async: '@slorber/react-helmet-async@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)' utility-types: 3.11.0 - webpack: 5.100.2 + webpack: 5.102.1 webpack-merge: 5.10.0 transitivePeerDependencies: - '@swc/core' - - acorn - esbuild - supports-color - uglify-js - webpack-cli - '@docusaurus/utils-common@3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@docusaurus/utils-common@3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/types': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) tslib: 2.8.1 transitivePeerDependencies: - '@swc/core' - - acorn - esbuild - react - react-dom @@ -13935,11 +13748,11 @@ snapshots: - uglify-js - webpack-cli - '@docusaurus/utils-validation@3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@docusaurus/utils-validation@3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@docusaurus/logger': 3.8.1 - '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-common': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/logger': 3.9.1 + '@docusaurus/utils': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) fs-extra: 11.3.0 joi: 17.13.3 js-yaml: 4.1.0 @@ -13947,7 +13760,6 @@ snapshots: tslib: 2.8.1 transitivePeerDependencies: - '@swc/core' - - acorn - esbuild - react - react-dom @@ -13955,14 +13767,14 @@ snapshots: - uglify-js - webpack-cli - '@docusaurus/utils@3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@docusaurus/utils@3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@docusaurus/logger': 3.8.1 - '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-common': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/logger': 3.9.1 + '@docusaurus/types': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) escape-string-regexp: 4.0.0 execa: 5.1.1 - file-loader: 6.2.0(webpack@5.100.2) + file-loader: 6.2.0(webpack@5.102.1) fs-extra: 11.3.0 github-slugger: 1.5.0 globby: 11.1.0 @@ -13975,12 +13787,11 @@ snapshots: prompts: 2.4.2 resolve-pathname: 3.0.0 tslib: 2.8.1 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.100.2))(webpack@5.100.2) + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.102.1))(webpack@5.102.1) utility-types: 3.11.0 - webpack: 5.100.2 + webpack: 5.102.1 transitivePeerDependencies: - '@swc/core' - - acorn - esbuild - react - react-dom @@ -13996,153 +13807,153 @@ snapshots: '@esbuild/aix-ppc64@0.19.12': optional: true - '@esbuild/aix-ppc64@0.25.9': + '@esbuild/aix-ppc64@0.25.10': optional: true '@esbuild/android-arm64@0.19.12': optional: true - '@esbuild/android-arm64@0.25.9': + '@esbuild/android-arm64@0.25.10': optional: true '@esbuild/android-arm@0.19.12': optional: true - '@esbuild/android-arm@0.25.9': + '@esbuild/android-arm@0.25.10': optional: true '@esbuild/android-x64@0.19.12': optional: true - '@esbuild/android-x64@0.25.9': + '@esbuild/android-x64@0.25.10': optional: true '@esbuild/darwin-arm64@0.19.12': optional: true - '@esbuild/darwin-arm64@0.25.9': + '@esbuild/darwin-arm64@0.25.10': optional: true '@esbuild/darwin-x64@0.19.12': optional: true - '@esbuild/darwin-x64@0.25.9': + '@esbuild/darwin-x64@0.25.10': optional: true '@esbuild/freebsd-arm64@0.19.12': optional: true - '@esbuild/freebsd-arm64@0.25.9': + '@esbuild/freebsd-arm64@0.25.10': optional: true '@esbuild/freebsd-x64@0.19.12': optional: true - '@esbuild/freebsd-x64@0.25.9': + '@esbuild/freebsd-x64@0.25.10': optional: true '@esbuild/linux-arm64@0.19.12': optional: true - '@esbuild/linux-arm64@0.25.9': + '@esbuild/linux-arm64@0.25.10': optional: true '@esbuild/linux-arm@0.19.12': optional: true - '@esbuild/linux-arm@0.25.9': + '@esbuild/linux-arm@0.25.10': optional: true '@esbuild/linux-ia32@0.19.12': optional: true - '@esbuild/linux-ia32@0.25.9': + '@esbuild/linux-ia32@0.25.10': optional: true '@esbuild/linux-loong64@0.19.12': optional: true - '@esbuild/linux-loong64@0.25.9': + '@esbuild/linux-loong64@0.25.10': optional: true '@esbuild/linux-mips64el@0.19.12': optional: true - '@esbuild/linux-mips64el@0.25.9': + '@esbuild/linux-mips64el@0.25.10': optional: true '@esbuild/linux-ppc64@0.19.12': optional: true - '@esbuild/linux-ppc64@0.25.9': + '@esbuild/linux-ppc64@0.25.10': optional: true '@esbuild/linux-riscv64@0.19.12': optional: true - '@esbuild/linux-riscv64@0.25.9': + '@esbuild/linux-riscv64@0.25.10': optional: true '@esbuild/linux-s390x@0.19.12': optional: true - '@esbuild/linux-s390x@0.25.9': + '@esbuild/linux-s390x@0.25.10': optional: true '@esbuild/linux-x64@0.19.12': optional: true - '@esbuild/linux-x64@0.25.9': + '@esbuild/linux-x64@0.25.10': optional: true - '@esbuild/netbsd-arm64@0.25.9': + '@esbuild/netbsd-arm64@0.25.10': optional: true '@esbuild/netbsd-x64@0.19.12': optional: true - '@esbuild/netbsd-x64@0.25.9': + '@esbuild/netbsd-x64@0.25.10': optional: true - '@esbuild/openbsd-arm64@0.25.9': + '@esbuild/openbsd-arm64@0.25.10': optional: true '@esbuild/openbsd-x64@0.19.12': optional: true - '@esbuild/openbsd-x64@0.25.9': + '@esbuild/openbsd-x64@0.25.10': optional: true - '@esbuild/openharmony-arm64@0.25.9': + '@esbuild/openharmony-arm64@0.25.10': optional: true '@esbuild/sunos-x64@0.19.12': optional: true - '@esbuild/sunos-x64@0.25.9': + '@esbuild/sunos-x64@0.25.10': optional: true '@esbuild/win32-arm64@0.19.12': optional: true - '@esbuild/win32-arm64@0.25.9': + '@esbuild/win32-arm64@0.25.10': optional: true '@esbuild/win32-ia32@0.19.12': optional: true - '@esbuild/win32-ia32@0.25.9': + '@esbuild/win32-ia32@0.25.10': optional: true '@esbuild/win32-x64@0.19.12': optional: true - '@esbuild/win32-x64@0.25.9': + '@esbuild/win32-x64@0.25.10': optional: true - '@eslint-community/eslint-utils@4.9.0(eslint@9.35.0(jiti@2.5.1))': + '@eslint-community/eslint-utils@4.9.0(eslint@9.37.0(jiti@2.6.1))': dependencies: - eslint: 9.35.0(jiti@2.5.1) + eslint: 9.37.0(jiti@2.6.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} @@ -14155,12 +13966,18 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.3.1': {} + '@eslint/config-helpers@0.4.0': + dependencies: + '@eslint/core': 0.16.0 '@eslint/core@0.15.2': dependencies: '@types/json-schema': 7.0.15 + '@eslint/core@0.16.0': + dependencies: + '@types/json-schema': 7.0.15 + '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 @@ -14175,7 +13992,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.35.0': {} + '@eslint/js@9.37.0': {} '@eslint/object-schema@2.1.6': {} @@ -14184,12 +14001,13 @@ snapshots: '@eslint/core': 0.15.2 levn: 0.4.1 - '@exodus/schemasafe@1.3.0': {} + '@eslint/plugin-kit@0.4.0': + dependencies: + '@eslint/core': 0.16.0 + levn: 0.4.1 '@faker-js/faker@10.0.0': {} - '@faker-js/faker@5.5.3': {} - '@fig/complete-commander@3.2.0(commander@11.1.0)': dependencies: commander: 11.1.0 @@ -14206,10 +14024,10 @@ snapshots: '@floating-ui/utils@0.2.10': {} - '@formatjs/ecma402-abstract@2.3.4': + '@formatjs/ecma402-abstract@2.3.5': dependencies: '@formatjs/fast-memoize': 2.2.7 - '@formatjs/intl-localematcher': 0.6.1 + '@formatjs/intl-localematcher': 0.6.2 decimal.js: 10.6.0 tslib: 2.8.1 @@ -14217,30 +14035,30 @@ snapshots: dependencies: tslib: 2.8.1 - '@formatjs/icu-messageformat-parser@2.11.2': + '@formatjs/icu-messageformat-parser@2.11.3': dependencies: - '@formatjs/ecma402-abstract': 2.3.4 - '@formatjs/icu-skeleton-parser': 1.8.14 + '@formatjs/ecma402-abstract': 2.3.5 + '@formatjs/icu-skeleton-parser': 1.8.15 tslib: 2.8.1 - '@formatjs/icu-skeleton-parser@1.8.14': + '@formatjs/icu-skeleton-parser@1.8.15': dependencies: - '@formatjs/ecma402-abstract': 2.3.4 + '@formatjs/ecma402-abstract': 2.3.5 tslib: 2.8.1 - '@formatjs/intl-localematcher@0.6.1': + '@formatjs/intl-localematcher@0.6.2': dependencies: tslib: 2.8.1 - '@golevelup/nestjs-discovery@4.0.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)': + '@golevelup/nestjs-discovery@5.0.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)': dependencies: '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) lodash: 4.17.21 - '@grpc/grpc-js@1.13.4': + '@grpc/grpc-js@1.14.0': dependencies: - '@grpc/proto-loader': 0.7.15 + '@grpc/proto-loader': 0.8.0 '@js-sdsl/ordered-map': 4.4.2 '@grpc/proto-loader@0.7.15': @@ -14250,6 +14068,13 @@ snapshots: protobufjs: 7.5.4 yargs: 17.7.2 + '@grpc/proto-loader@0.8.0': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.5.4 + yargs: 17.7.2 + '@hapi/hoek@9.3.0': {} '@hapi/topo@5.1.0': @@ -14267,125 +14092,129 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@img/sharp-darwin-arm64@0.34.3': + '@img/colour@1.0.0': {} + + '@img/sharp-darwin-arm64@0.34.4': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.0 + '@img/sharp-libvips-darwin-arm64': 1.2.3 optional: true - '@img/sharp-darwin-x64@0.34.3': + '@img/sharp-darwin-x64@0.34.4': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.0 + '@img/sharp-libvips-darwin-x64': 1.2.3 optional: true - '@img/sharp-libvips-darwin-arm64@1.2.0': + '@img/sharp-libvips-darwin-arm64@1.2.3': optional: true - '@img/sharp-libvips-darwin-x64@1.2.0': + '@img/sharp-libvips-darwin-x64@1.2.3': optional: true - '@img/sharp-libvips-linux-arm64@1.2.0': + '@img/sharp-libvips-linux-arm64@1.2.3': optional: true - '@img/sharp-libvips-linux-arm@1.2.0': + '@img/sharp-libvips-linux-arm@1.2.3': optional: true - '@img/sharp-libvips-linux-ppc64@1.2.0': + '@img/sharp-libvips-linux-ppc64@1.2.3': optional: true - '@img/sharp-libvips-linux-s390x@1.2.0': + '@img/sharp-libvips-linux-s390x@1.2.3': optional: true - '@img/sharp-libvips-linux-x64@1.2.0': + '@img/sharp-libvips-linux-x64@1.2.3': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.2.0': + '@img/sharp-libvips-linuxmusl-arm64@1.2.3': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.2.0': + '@img/sharp-libvips-linuxmusl-x64@1.2.3': optional: true - '@img/sharp-linux-arm64@0.34.3': + '@img/sharp-linux-arm64@0.34.4': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.0 + '@img/sharp-libvips-linux-arm64': 1.2.3 optional: true - '@img/sharp-linux-arm@0.34.3': + '@img/sharp-linux-arm@0.34.4': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.0 + '@img/sharp-libvips-linux-arm': 1.2.3 optional: true - '@img/sharp-linux-ppc64@0.34.3': + '@img/sharp-linux-ppc64@0.34.4': optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.0 + '@img/sharp-libvips-linux-ppc64': 1.2.3 optional: true - '@img/sharp-linux-s390x@0.34.3': + '@img/sharp-linux-s390x@0.34.4': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.0 + '@img/sharp-libvips-linux-s390x': 1.2.3 optional: true - '@img/sharp-linux-x64@0.34.3': + '@img/sharp-linux-x64@0.34.4': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.0 + '@img/sharp-libvips-linux-x64': 1.2.3 optional: true - '@img/sharp-linuxmusl-arm64@0.34.3': + '@img/sharp-linuxmusl-arm64@0.34.4': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 optional: true - '@img/sharp-linuxmusl-x64@0.34.3': + '@img/sharp-linuxmusl-x64@0.34.4': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.0 + '@img/sharp-libvips-linuxmusl-x64': 1.2.3 optional: true - '@img/sharp-wasm32@0.34.3': + '@img/sharp-wasm32@0.34.4': dependencies: '@emnapi/runtime': 1.5.0 optional: true - '@img/sharp-win32-arm64@0.34.3': + '@img/sharp-win32-arm64@0.34.4': optional: true - '@img/sharp-win32-ia32@0.34.3': + '@img/sharp-win32-ia32@0.34.4': optional: true - '@img/sharp-win32-x64@0.34.3': + '@img/sharp-win32-x64@0.34.4': optional: true - '@immich/ui@0.29.0(@internationalized/date@3.8.2)(svelte@5.38.10)': + '@immich/ui@0.34.2(@internationalized/date@3.8.2)(svelte@5.39.11)': dependencies: '@mdi/js': 7.4.47 - bits-ui: 2.9.8(@internationalized/date@3.8.2)(svelte@5.38.10) - simple-icons: 15.15.0 - svelte: 5.38.10 + bits-ui: 2.9.8(@internationalized/date@3.8.2)(svelte@5.39.11) + luxon: 3.7.2 + simple-icons: 15.16.1 + svelte: 5.39.11 + svelte-highlight: 7.8.4 tailwind-merge: 3.3.1 - tailwind-variants: 3.1.1(tailwind-merge@3.3.1)(tailwindcss@4.1.13) - tailwindcss: 4.1.13 + tailwind-variants: 3.1.1(tailwind-merge@3.3.1)(tailwindcss@4.1.14) + tailwindcss: 4.1.14 transitivePeerDependencies: - '@internationalized/date' - '@inquirer/checkbox@4.2.1(@types/node@22.18.5)': + '@inquirer/checkbox@4.2.1(@types/node@22.18.10)': dependencies: - '@inquirer/core': 10.1.15(@types/node@22.18.5) + '@inquirer/core': 10.1.15(@types/node@22.18.10) '@inquirer/figures': 1.0.13 - '@inquirer/type': 3.0.8(@types/node@22.18.5) + '@inquirer/type': 3.0.8(@types/node@22.18.10) ansi-escapes: 4.3.2 yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 - '@inquirer/confirm@5.1.15(@types/node@22.18.5)': + '@inquirer/confirm@5.1.15(@types/node@22.18.10)': dependencies: - '@inquirer/core': 10.1.15(@types/node@22.18.5) - '@inquirer/type': 3.0.8(@types/node@22.18.5) + '@inquirer/core': 10.1.15(@types/node@22.18.10) + '@inquirer/type': 3.0.8(@types/node@22.18.10) optionalDependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 - '@inquirer/core@10.1.15(@types/node@22.18.5)': + '@inquirer/core@10.1.15(@types/node@22.18.10)': dependencies: '@inquirer/figures': 1.0.13 - '@inquirer/type': 3.0.8(@types/node@22.18.5) + '@inquirer/type': 3.0.8(@types/node@22.18.10) ansi-escapes: 4.3.2 cli-width: 4.1.0 mute-stream: 2.0.0 @@ -14393,121 +14222,121 @@ snapshots: wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 - '@inquirer/editor@4.2.17(@types/node@22.18.5)': + '@inquirer/editor@4.2.17(@types/node@22.18.10)': dependencies: - '@inquirer/core': 10.1.15(@types/node@22.18.5) - '@inquirer/external-editor': 1.0.2(@types/node@22.18.5) - '@inquirer/type': 3.0.8(@types/node@22.18.5) + '@inquirer/core': 10.1.15(@types/node@22.18.10) + '@inquirer/external-editor': 1.0.2(@types/node@22.18.10) + '@inquirer/type': 3.0.8(@types/node@22.18.10) optionalDependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 - '@inquirer/expand@4.0.17(@types/node@22.18.5)': + '@inquirer/expand@4.0.17(@types/node@22.18.10)': dependencies: - '@inquirer/core': 10.1.15(@types/node@22.18.5) - '@inquirer/type': 3.0.8(@types/node@22.18.5) + '@inquirer/core': 10.1.15(@types/node@22.18.10) + '@inquirer/type': 3.0.8(@types/node@22.18.10) yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 - '@inquirer/external-editor@1.0.2(@types/node@22.18.5)': + '@inquirer/external-editor@1.0.2(@types/node@22.18.10)': dependencies: chardet: 2.1.0 iconv-lite: 0.7.0 optionalDependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@inquirer/figures@1.0.13': {} - '@inquirer/input@4.2.1(@types/node@22.18.5)': + '@inquirer/input@4.2.1(@types/node@22.18.10)': dependencies: - '@inquirer/core': 10.1.15(@types/node@22.18.5) - '@inquirer/type': 3.0.8(@types/node@22.18.5) + '@inquirer/core': 10.1.15(@types/node@22.18.10) + '@inquirer/type': 3.0.8(@types/node@22.18.10) optionalDependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 - '@inquirer/number@3.0.17(@types/node@22.18.5)': + '@inquirer/number@3.0.17(@types/node@22.18.10)': dependencies: - '@inquirer/core': 10.1.15(@types/node@22.18.5) - '@inquirer/type': 3.0.8(@types/node@22.18.5) + '@inquirer/core': 10.1.15(@types/node@22.18.10) + '@inquirer/type': 3.0.8(@types/node@22.18.10) optionalDependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 - '@inquirer/password@4.0.17(@types/node@22.18.5)': + '@inquirer/password@4.0.17(@types/node@22.18.10)': dependencies: - '@inquirer/core': 10.1.15(@types/node@22.18.5) - '@inquirer/type': 3.0.8(@types/node@22.18.5) + '@inquirer/core': 10.1.15(@types/node@22.18.10) + '@inquirer/type': 3.0.8(@types/node@22.18.10) ansi-escapes: 4.3.2 optionalDependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 - '@inquirer/prompts@7.3.2(@types/node@22.18.5)': + '@inquirer/prompts@7.3.2(@types/node@22.18.10)': dependencies: - '@inquirer/checkbox': 4.2.1(@types/node@22.18.5) - '@inquirer/confirm': 5.1.15(@types/node@22.18.5) - '@inquirer/editor': 4.2.17(@types/node@22.18.5) - '@inquirer/expand': 4.0.17(@types/node@22.18.5) - '@inquirer/input': 4.2.1(@types/node@22.18.5) - '@inquirer/number': 3.0.17(@types/node@22.18.5) - '@inquirer/password': 4.0.17(@types/node@22.18.5) - '@inquirer/rawlist': 4.1.5(@types/node@22.18.5) - '@inquirer/search': 3.1.0(@types/node@22.18.5) - '@inquirer/select': 4.3.1(@types/node@22.18.5) + '@inquirer/checkbox': 4.2.1(@types/node@22.18.10) + '@inquirer/confirm': 5.1.15(@types/node@22.18.10) + '@inquirer/editor': 4.2.17(@types/node@22.18.10) + '@inquirer/expand': 4.0.17(@types/node@22.18.10) + '@inquirer/input': 4.2.1(@types/node@22.18.10) + '@inquirer/number': 3.0.17(@types/node@22.18.10) + '@inquirer/password': 4.0.17(@types/node@22.18.10) + '@inquirer/rawlist': 4.1.5(@types/node@22.18.10) + '@inquirer/search': 3.1.0(@types/node@22.18.10) + '@inquirer/select': 4.3.1(@types/node@22.18.10) optionalDependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 - '@inquirer/prompts@7.8.0(@types/node@22.18.5)': + '@inquirer/prompts@7.8.0(@types/node@22.18.10)': dependencies: - '@inquirer/checkbox': 4.2.1(@types/node@22.18.5) - '@inquirer/confirm': 5.1.15(@types/node@22.18.5) - '@inquirer/editor': 4.2.17(@types/node@22.18.5) - '@inquirer/expand': 4.0.17(@types/node@22.18.5) - '@inquirer/input': 4.2.1(@types/node@22.18.5) - '@inquirer/number': 3.0.17(@types/node@22.18.5) - '@inquirer/password': 4.0.17(@types/node@22.18.5) - '@inquirer/rawlist': 4.1.5(@types/node@22.18.5) - '@inquirer/search': 3.1.0(@types/node@22.18.5) - '@inquirer/select': 4.3.1(@types/node@22.18.5) + '@inquirer/checkbox': 4.2.1(@types/node@22.18.10) + '@inquirer/confirm': 5.1.15(@types/node@22.18.10) + '@inquirer/editor': 4.2.17(@types/node@22.18.10) + '@inquirer/expand': 4.0.17(@types/node@22.18.10) + '@inquirer/input': 4.2.1(@types/node@22.18.10) + '@inquirer/number': 3.0.17(@types/node@22.18.10) + '@inquirer/password': 4.0.17(@types/node@22.18.10) + '@inquirer/rawlist': 4.1.5(@types/node@22.18.10) + '@inquirer/search': 3.1.0(@types/node@22.18.10) + '@inquirer/select': 4.3.1(@types/node@22.18.10) optionalDependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 - '@inquirer/rawlist@4.1.5(@types/node@22.18.5)': + '@inquirer/rawlist@4.1.5(@types/node@22.18.10)': dependencies: - '@inquirer/core': 10.1.15(@types/node@22.18.5) - '@inquirer/type': 3.0.8(@types/node@22.18.5) + '@inquirer/core': 10.1.15(@types/node@22.18.10) + '@inquirer/type': 3.0.8(@types/node@22.18.10) yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 - '@inquirer/search@3.1.0(@types/node@22.18.5)': + '@inquirer/search@3.1.0(@types/node@22.18.10)': dependencies: - '@inquirer/core': 10.1.15(@types/node@22.18.5) + '@inquirer/core': 10.1.15(@types/node@22.18.10) '@inquirer/figures': 1.0.13 - '@inquirer/type': 3.0.8(@types/node@22.18.5) + '@inquirer/type': 3.0.8(@types/node@22.18.10) yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 - '@inquirer/select@4.3.1(@types/node@22.18.5)': + '@inquirer/select@4.3.1(@types/node@22.18.10)': dependencies: - '@inquirer/core': 10.1.15(@types/node@22.18.5) + '@inquirer/core': 10.1.15(@types/node@22.18.10) '@inquirer/figures': 1.0.13 - '@inquirer/type': 3.0.8(@types/node@22.18.5) + '@inquirer/type': 3.0.8(@types/node@22.18.10) ansi-escapes: 4.3.2 yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 - '@inquirer/type@3.0.8(@types/node@22.18.5)': + '@inquirer/type@3.0.8(@types/node@22.18.10)': optionalDependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@internationalized/date@3.8.2': dependencies: '@swc/helpers': 0.5.17 - '@ioredis/commands@1.3.0': {} + '@ioredis/commands@1.4.0': {} '@isaacs/balanced-match@4.0.1': {} @@ -14539,36 +14368,71 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/yargs': 17.0.33 chalk: 4.1.2 '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/remapping@2.3.5': dependencies: '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/source-map@0.3.6': dependencies: '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/sourcemap-codec@1.5.5': {} - '@jridgewell/trace-mapping@0.3.30': + '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 '@js-sdsl/ordered-map@4.4.2': {} + '@jsonjoy.com/base64@1.1.2(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/buffers@1.2.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/codegen@1.0.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/json-pack@1.20.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/base64': 1.1.2(tslib@2.8.1) + '@jsonjoy.com/buffers': 1.2.0(tslib@2.8.1) + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + '@jsonjoy.com/json-pointer': 1.0.2(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + hyperdyperid: 1.2.0 + thingies: 2.5.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pointer@1.0.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/util@1.9.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 1.2.0(tslib@2.8.1) + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + tslib: 2.8.1 + '@koa/cors@5.0.0': dependencies: vary: 1.1.2 @@ -14582,12 +14446,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@koddsson/eslint-plugin-tscompat@0.2.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)': + '@koddsson/eslint-plugin-tscompat@0.2.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@mdn/browser-compat-data': 6.0.27 - '@typescript-eslint/type-utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) - browserslist: 4.25.3 + '@typescript-eslint/type-utils': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + browserslist: 4.26.3 transitivePeerDependencies: - eslint - supports-color @@ -14616,14 +14480,14 @@ snapshots: '@mapbox/node-pre-gyp@1.0.11': dependencies: - detect-libc: 2.1.0 + detect-libc: 2.1.2 https-proxy-agent: 5.0.1 make-dir: 3.1.0 node-fetch: 2.7.0 nopt: 5.0.0 npmlog: 5.0.1 rimraf: 3.0.2 - semver: 7.7.2 + semver: 7.7.3 tar: 6.2.1 transitivePeerDependencies: - encoding @@ -14632,14 +14496,14 @@ snapshots: '@mapbox/node-pre-gyp@1.0.11(encoding@0.1.13)': dependencies: - detect-libc: 2.1.0 + detect-libc: 2.1.2 https-proxy-agent: 5.0.1 make-dir: 3.1.0 node-fetch: 2.7.0(encoding@0.1.13) nopt: 5.0.0 npmlog: 5.0.1 rimraf: 3.0.2 - semver: 7.7.2 + semver: 7.7.3 tar: 6.2.1 transitivePeerDependencies: - encoding @@ -14669,7 +14533,7 @@ snapshots: '@mapbox/whoots-js@3.1.0': {} - '@maplibre/maplibre-gl-style-spec@23.3.0': + '@maplibre/maplibre-gl-style-spec@24.2.0': dependencies: '@mapbox/jsonlint-lines-primitives': 2.0.2 '@mapbox/unitbezier': 0.0.1 @@ -14699,12 +14563,13 @@ snapshots: '@mdn/browser-compat-data@6.0.27': {} - '@mdx-js/mdx@3.1.0(acorn@8.15.0)': + '@mdx-js/mdx@3.1.1': dependencies: '@types/estree': 1.0.8 '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.4 '@types/mdx': 2.0.13 + acorn: 8.15.0 collapse-white-space: 2.1.0 devlop: 1.1.0 estree-util-is-identifier-name: 3.0.0 @@ -14713,41 +14578,29 @@ snapshots: hast-util-to-jsx-runtime: 2.3.6 markdown-extensions: 2.0.0 recma-build-jsx: 1.0.0 - recma-jsx: 1.0.0(acorn@8.15.0) + recma-jsx: 1.0.1(acorn@8.15.0) recma-stringify: 1.0.0 rehype-recma: 1.0.0 - remark-mdx: 3.1.0 + remark-mdx: 3.1.1 remark-parse: 11.0.0 remark-rehype: 11.1.2 - source-map: 0.7.4 + source-map: 0.7.6 unified: 11.0.5 unist-util-position-from-estree: 2.0.0 unist-util-stringify-position: 4.0.0 unist-util-visit: 5.0.0 vfile: 6.0.3 transitivePeerDependencies: - - acorn - supports-color - '@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1)': + '@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1)': dependencies: '@types/mdx': 2.0.13 - '@types/react': 19.1.13 + '@types/react': 19.2.2 react: 18.3.1 '@microsoft/tsdoc@0.15.1': {} - '@monaco-editor/loader@1.5.0': - dependencies: - state-local: 1.0.7 - - '@monaco-editor/react@4.7.0(monaco-editor@0.31.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@monaco-editor/loader': 1.5.0 - monaco-editor: 0.31.1 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': optional: true @@ -14774,21 +14627,21 @@ snapshots: '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) tslib: 2.8.1 - '@nestjs/bullmq@11.0.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(bullmq@5.58.5)': + '@nestjs/bullmq@11.0.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(bullmq@5.61.0)': dependencies: '@nestjs/bull-shared': 11.0.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6) '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) - bullmq: 5.58.5 + bullmq: 5.61.0 tslib: 2.8.1 - '@nestjs/cli@11.0.10(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.18.5)': + '@nestjs/cli@11.0.10(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.18.10)': dependencies: '@angular-devkit/core': 19.2.15(chokidar@4.0.3) '@angular-devkit/schematics': 19.2.15(chokidar@4.0.3) - '@angular-devkit/schematics-cli': 19.2.15(@types/node@22.18.5)(chokidar@4.0.3) - '@inquirer/prompts': 7.8.0(@types/node@22.18.5) - '@nestjs/schematics': 11.0.7(chokidar@4.0.3)(typescript@5.8.3) + '@angular-devkit/schematics-cli': 19.2.15(@types/node@22.18.10)(chokidar@4.0.3) + '@inquirer/prompts': 7.8.0(@types/node@22.18.10) + '@nestjs/schematics': 11.0.8(chokidar@4.0.3)(typescript@5.8.3) ansis: 4.1.0 chokidar: 4.0.3 cli-table3: 0.6.5 @@ -14873,31 +14726,31 @@ snapshots: - supports-color - utf-8-validate - '@nestjs/schedule@6.0.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)': + '@nestjs/schedule@6.0.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)': dependencies: '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) - cron: 4.3.0 + cron: 4.3.3 - '@nestjs/schematics@11.0.7(chokidar@4.0.3)(typescript@5.8.3)': + '@nestjs/schematics@11.0.8(chokidar@4.0.3)(typescript@5.8.3)': dependencies: - '@angular-devkit/core': 19.2.15(chokidar@4.0.3) - '@angular-devkit/schematics': 19.2.15(chokidar@4.0.3) - comment-json: 4.2.5 + '@angular-devkit/core': 19.2.17(chokidar@4.0.3) + '@angular-devkit/schematics': 19.2.17(chokidar@4.0.3) + comment-json: 4.4.1 jsonc-parser: 3.3.1 pluralize: 8.0.0 typescript: 5.8.3 transitivePeerDependencies: - chokidar - '@nestjs/schematics@11.0.7(chokidar@4.0.3)(typescript@5.9.2)': + '@nestjs/schematics@11.0.8(chokidar@4.0.3)(typescript@5.9.3)': dependencies: - '@angular-devkit/core': 19.2.15(chokidar@4.0.3) - '@angular-devkit/schematics': 19.2.15(chokidar@4.0.3) - comment-json: 4.2.5 + '@angular-devkit/core': 19.2.17(chokidar@4.0.3) + '@angular-devkit/schematics': 19.2.17(chokidar@4.0.3) + comment-json: 4.4.1 jsonc-parser: 3.3.1 pluralize: 8.0.0 - typescript: 5.9.2 + typescript: 5.9.3 transitivePeerDependencies: - chokidar @@ -14962,7 +14815,7 @@ snapshots: '@npmcli/fs@4.0.0': dependencies: - semver: 7.7.2 + semver: 7.7.3 '@nuxt/opencollective@0.4.1': dependencies: @@ -14970,7 +14823,7 @@ snapshots: '@oazapfts/runtime@1.0.4': {} - '@opentelemetry/api-logs@0.205.0': + '@opentelemetry/api-logs@0.206.0': dependencies: '@opentelemetry/api': 1.9.0 @@ -14985,100 +14838,100 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.37.0 - '@opentelemetry/exporter-logs-otlp-grpc@0.205.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/exporter-logs-otlp-grpc@0.206.0(@opentelemetry/api@1.9.0)': dependencies: - '@grpc/grpc-js': 1.13.4 + '@grpc/grpc-js': 1.14.0 '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-grpc-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-grpc-exporter-base': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.206.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-logs-otlp-http@0.205.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/exporter-logs-otlp-http@0.206.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.205.0 + '@opentelemetry/api-logs': 0.206.0 '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.206.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-logs-otlp-proto@0.205.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/exporter-logs-otlp-proto@0.206.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.205.0 + '@opentelemetry/api-logs': 0.206.0 '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.206.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.206.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-metrics-otlp-grpc@0.205.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/exporter-metrics-otlp-grpc@0.206.0(@opentelemetry/api@1.9.0)': dependencies: - '@grpc/grpc-js': 1.13.4 + '@grpc/grpc-js': 1.14.0 '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-metrics-otlp-http': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-grpc-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-http': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-grpc-exporter-base': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.206.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-metrics': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-metrics-otlp-http@0.205.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/exporter-metrics-otlp-http@0.206.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.206.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-metrics': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-metrics-otlp-proto@0.205.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/exporter-metrics-otlp-proto@0.206.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-metrics-otlp-http': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-http': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.206.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-metrics': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-prometheus@0.205.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/exporter-prometheus@0.206.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-metrics': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-trace-otlp-grpc@0.205.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/exporter-trace-otlp-grpc@0.206.0(@opentelemetry/api@1.9.0)': dependencies: - '@grpc/grpc-js': 1.13.4 + '@grpc/grpc-js': 1.14.0 '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-grpc-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-grpc-exporter-base': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.206.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-trace-otlp-http@0.205.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/exporter-trace-otlp-http@0.206.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.206.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-trace-otlp-proto@0.205.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/exporter-trace-otlp-proto@0.206.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.206.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.1.0(@opentelemetry/api@1.9.0) @@ -15095,75 +14948,74 @@ snapshots: '@opentelemetry/api': 1.9.0 systeminformation: 5.23.8 - '@opentelemetry/instrumentation-http@0.205.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-http@0.206.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.206.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.37.0 forwarded-parse: 2.1.2 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-ioredis@0.53.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-ioredis@0.54.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/redis-common': 0.38.0 + '@opentelemetry/instrumentation': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/redis-common': 0.38.2 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-nestjs-core@0.53.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.206.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.37.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-nestjs-core@0.51.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-pg@0.58.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-pg@0.59.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.206.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.37.0 - '@opentelemetry/sql-common': 0.41.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0) '@types/pg': 8.15.5 '@types/pg-pool': 2.0.6 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation@0.205.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation@0.206.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.205.0 - import-in-the-middle: 1.14.2 - require-in-the-middle: 7.5.2 + '@opentelemetry/api-logs': 0.206.0 + import-in-the-middle: 1.14.4 + require-in-the-middle: 8.0.0 transitivePeerDependencies: - supports-color - '@opentelemetry/otlp-exporter-base@0.205.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/otlp-exporter-base@0.206.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.206.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-grpc-exporter-base@0.205.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/otlp-grpc-exporter-base@0.206.0(@opentelemetry/api@1.9.0)': dependencies: - '@grpc/grpc-js': 1.13.4 + '@grpc/grpc-js': 1.14.0 '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.206.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer@0.205.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/otlp-transformer@0.206.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.205.0 + '@opentelemetry/api-logs': 0.206.0 '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.206.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-metrics': 2.1.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.1.0(@opentelemetry/api@1.9.0) protobufjs: 7.5.4 @@ -15178,7 +15030,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/redis-common@0.38.0': {} + '@opentelemetry/redis-common@0.38.2': {} '@opentelemetry/resources@2.1.0(@opentelemetry/api@1.9.0)': dependencies: @@ -15186,10 +15038,10 @@ snapshots: '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.37.0 - '@opentelemetry/sdk-logs@0.205.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/sdk-logs@0.206.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.205.0 + '@opentelemetry/api-logs': 0.206.0 '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) @@ -15199,27 +15051,27 @@ snapshots: '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-node@0.205.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/sdk-node@0.206.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.205.0 + '@opentelemetry/api-logs': 0.206.0 '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-logs-otlp-grpc': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-logs-otlp-http': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-logs-otlp-proto': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-metrics-otlp-grpc': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-metrics-otlp-http': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-metrics-otlp-proto': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-prometheus': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-trace-otlp-grpc': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-trace-otlp-http': 0.205.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-trace-otlp-proto': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-logs-otlp-grpc': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-logs-otlp-http': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-logs-otlp-proto': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-grpc': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-http': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-proto': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-prometheus': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-trace-otlp-grpc': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-trace-otlp-http': 0.206.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-trace-otlp-proto': 0.206.0(@opentelemetry/api@1.9.0) '@opentelemetry/exporter-zipkin': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.206.0(@opentelemetry/api@1.9.0) '@opentelemetry/propagator-b3': 2.1.0(@opentelemetry/api@1.9.0) '@opentelemetry/propagator-jaeger': 2.1.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.206.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-metrics': 2.1.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.1.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-node': 2.1.0(@opentelemetry/api@1.9.0) @@ -15243,7 +15095,7 @@ snapshots: '@opentelemetry/semantic-conventions@1.37.0': {} - '@opentelemetry/sql-common@0.41.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/sql-common@0.41.2(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) @@ -15283,9 +15135,9 @@ snapshots: '@pkgr/core@0.2.9': {} - '@playwright/test@1.55.0': + '@playwright/test@1.56.0': dependencies: - playwright: 1.55.0 + playwright: 1.56.0 '@pnpm/config.env-replace@1.1.0': {} @@ -15324,197 +15176,190 @@ snapshots: '@protobufjs/utf8@1.1.0': {} - '@react-email/body@0.1.0(react@19.1.1)': + '@react-email/body@0.1.0(react@19.2.0)': dependencies: - react: 19.1.1 + react: 19.2.0 - '@react-email/button@0.2.0(react@19.1.1)': + '@react-email/button@0.2.0(react@19.2.0)': dependencies: - react: 19.1.1 + react: 19.2.0 - '@react-email/code-block@0.1.0(react@19.1.1)': + '@react-email/code-block@0.1.0(react@19.2.0)': dependencies: prismjs: 1.30.0 - react: 19.1.1 + react: 19.2.0 - '@react-email/code-inline@0.0.5(react@19.1.1)': + '@react-email/code-inline@0.0.5(react@19.2.0)': dependencies: - react: 19.1.1 + react: 19.2.0 - '@react-email/column@0.0.13(react@19.1.1)': + '@react-email/column@0.0.13(react@19.2.0)': dependencies: - react: 19.1.1 + react: 19.2.0 - '@react-email/components@0.5.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@react-email/components@0.5.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@react-email/body': 0.1.0(react@19.1.1) - '@react-email/button': 0.2.0(react@19.1.1) - '@react-email/code-block': 0.1.0(react@19.1.1) - '@react-email/code-inline': 0.0.5(react@19.1.1) - '@react-email/column': 0.0.13(react@19.1.1) - '@react-email/container': 0.0.15(react@19.1.1) - '@react-email/font': 0.0.9(react@19.1.1) - '@react-email/head': 0.0.12(react@19.1.1) - '@react-email/heading': 0.0.15(react@19.1.1) - '@react-email/hr': 0.0.11(react@19.1.1) - '@react-email/html': 0.0.11(react@19.1.1) - '@react-email/img': 0.0.11(react@19.1.1) - '@react-email/link': 0.0.12(react@19.1.1) - '@react-email/markdown': 0.0.15(react@19.1.1) - '@react-email/preview': 0.0.13(react@19.1.1) - '@react-email/render': 1.2.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@react-email/row': 0.0.12(react@19.1.1) - '@react-email/section': 0.0.16(react@19.1.1) - '@react-email/tailwind': 1.2.2(react@19.1.1) - '@react-email/text': 0.1.5(react@19.1.1) - react: 19.1.1 + '@react-email/body': 0.1.0(react@19.2.0) + '@react-email/button': 0.2.0(react@19.2.0) + '@react-email/code-block': 0.1.0(react@19.2.0) + '@react-email/code-inline': 0.0.5(react@19.2.0) + '@react-email/column': 0.0.13(react@19.2.0) + '@react-email/container': 0.0.15(react@19.2.0) + '@react-email/font': 0.0.9(react@19.2.0) + '@react-email/head': 0.0.12(react@19.2.0) + '@react-email/heading': 0.0.15(react@19.2.0) + '@react-email/hr': 0.0.11(react@19.2.0) + '@react-email/html': 0.0.11(react@19.2.0) + '@react-email/img': 0.0.11(react@19.2.0) + '@react-email/link': 0.0.12(react@19.2.0) + '@react-email/markdown': 0.0.15(react@19.2.0) + '@react-email/preview': 0.0.13(react@19.2.0) + '@react-email/render': 1.3.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@react-email/row': 0.0.12(react@19.2.0) + '@react-email/section': 0.0.16(react@19.2.0) + '@react-email/tailwind': 1.2.2(react@19.2.0) + '@react-email/text': 0.1.5(react@19.2.0) + react: 19.2.0 transitivePeerDependencies: - react-dom - '@react-email/container@0.0.15(react@19.1.1)': + '@react-email/container@0.0.15(react@19.2.0)': dependencies: - react: 19.1.1 + react: 19.2.0 - '@react-email/font@0.0.9(react@19.1.1)': + '@react-email/font@0.0.9(react@19.2.0)': dependencies: - react: 19.1.1 + react: 19.2.0 - '@react-email/head@0.0.12(react@19.1.1)': + '@react-email/head@0.0.12(react@19.2.0)': dependencies: - react: 19.1.1 + react: 19.2.0 - '@react-email/heading@0.0.15(react@19.1.1)': + '@react-email/heading@0.0.15(react@19.2.0)': dependencies: - react: 19.1.1 + react: 19.2.0 - '@react-email/hr@0.0.11(react@19.1.1)': + '@react-email/hr@0.0.11(react@19.2.0)': dependencies: - react: 19.1.1 + react: 19.2.0 - '@react-email/html@0.0.11(react@19.1.1)': + '@react-email/html@0.0.11(react@19.2.0)': dependencies: - react: 19.1.1 + react: 19.2.0 - '@react-email/img@0.0.11(react@19.1.1)': + '@react-email/img@0.0.11(react@19.2.0)': dependencies: - react: 19.1.1 + react: 19.2.0 - '@react-email/link@0.0.12(react@19.1.1)': + '@react-email/link@0.0.12(react@19.2.0)': dependencies: - react: 19.1.1 + react: 19.2.0 - '@react-email/markdown@0.0.15(react@19.1.1)': + '@react-email/markdown@0.0.15(react@19.2.0)': dependencies: - md-to-react-email: 5.0.5(react@19.1.1) - react: 19.1.1 + md-to-react-email: 5.0.5(react@19.2.0) + react: 19.2.0 - '@react-email/preview@0.0.13(react@19.1.1)': + '@react-email/preview@0.0.13(react@19.2.0)': dependencies: - react: 19.1.1 + react: 19.2.0 - '@react-email/render@1.2.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@react-email/render@1.3.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: html-to-text: 9.0.5 prettier: 3.6.2 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) react-promise-suspense: 0.3.4 - '@react-email/row@0.0.12(react@19.1.1)': + '@react-email/row@0.0.12(react@19.2.0)': dependencies: - react: 19.1.1 + react: 19.2.0 - '@react-email/section@0.0.16(react@19.1.1)': + '@react-email/section@0.0.16(react@19.2.0)': dependencies: - react: 19.1.1 + react: 19.2.0 - '@react-email/tailwind@1.2.2(react@19.1.1)': + '@react-email/tailwind@1.2.2(react@19.2.0)': dependencies: - react: 19.1.1 + react: 19.2.0 - '@react-email/text@0.1.5(react@19.1.1)': + '@react-email/text@0.1.5(react@19.2.0)': dependencies: - react: 19.1.1 + react: 19.2.0 - '@reduxjs/toolkit@1.9.7(react-redux@7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': - dependencies: - immer: 9.0.21 - redux: 4.2.1 - redux-thunk: 2.4.2(redux@4.2.1) - reselect: 4.1.8 - optionalDependencies: - react: 18.3.1 - react-redux: 7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - - '@rollup/pluginutils@5.3.0(rollup@4.50.1)': + '@rollup/pluginutils@5.3.0(rollup@4.52.4)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.50.1 + rollup: 4.52.4 - '@rollup/rollup-android-arm-eabi@4.50.1': + '@rollup/rollup-android-arm-eabi@4.52.4': optional: true - '@rollup/rollup-android-arm64@4.50.1': + '@rollup/rollup-android-arm64@4.52.4': optional: true - '@rollup/rollup-darwin-arm64@4.50.1': + '@rollup/rollup-darwin-arm64@4.52.4': optional: true - '@rollup/rollup-darwin-x64@4.50.1': + '@rollup/rollup-darwin-x64@4.52.4': optional: true - '@rollup/rollup-freebsd-arm64@4.50.1': + '@rollup/rollup-freebsd-arm64@4.52.4': optional: true - '@rollup/rollup-freebsd-x64@4.50.1': + '@rollup/rollup-freebsd-x64@4.52.4': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.50.1': + '@rollup/rollup-linux-arm-gnueabihf@4.52.4': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.50.1': + '@rollup/rollup-linux-arm-musleabihf@4.52.4': optional: true - '@rollup/rollup-linux-arm64-gnu@4.50.1': + '@rollup/rollup-linux-arm64-gnu@4.52.4': optional: true - '@rollup/rollup-linux-arm64-musl@4.50.1': + '@rollup/rollup-linux-arm64-musl@4.52.4': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.50.1': + '@rollup/rollup-linux-loong64-gnu@4.52.4': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.50.1': + '@rollup/rollup-linux-ppc64-gnu@4.52.4': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.50.1': + '@rollup/rollup-linux-riscv64-gnu@4.52.4': optional: true - '@rollup/rollup-linux-riscv64-musl@4.50.1': + '@rollup/rollup-linux-riscv64-musl@4.52.4': optional: true - '@rollup/rollup-linux-s390x-gnu@4.50.1': + '@rollup/rollup-linux-s390x-gnu@4.52.4': optional: true - '@rollup/rollup-linux-x64-gnu@4.50.1': + '@rollup/rollup-linux-x64-gnu@4.52.4': optional: true - '@rollup/rollup-linux-x64-musl@4.50.1': + '@rollup/rollup-linux-x64-musl@4.52.4': optional: true - '@rollup/rollup-openharmony-arm64@4.50.1': + '@rollup/rollup-openharmony-arm64@4.52.4': optional: true - '@rollup/rollup-win32-arm64-msvc@4.50.1': + '@rollup/rollup-win32-arm64-msvc@4.52.4': optional: true - '@rollup/rollup-win32-ia32-msvc@4.50.1': + '@rollup/rollup-win32-ia32-msvc@4.52.4': optional: true - '@rollup/rollup-win32-x64-msvc@4.50.1': + '@rollup/rollup-win32-x64-gnu@4.52.4': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.52.4': optional: true '@scarf/scarf@1.4.0': {} @@ -15554,197 +15399,195 @@ snapshots: micromark-util-character: 1.2.0 micromark-util-symbol: 1.1.0 - '@smithy/abort-controller@4.1.1': + '@smithy/abort-controller@4.2.0': dependencies: - '@smithy/types': 4.5.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@smithy/config-resolver@4.2.2': + '@smithy/config-resolver@4.3.0': dependencies: - '@smithy/node-config-provider': 4.2.2 - '@smithy/types': 4.5.0 - '@smithy/util-config-provider': 4.1.0 - '@smithy/util-middleware': 4.1.1 + '@smithy/node-config-provider': 4.3.0 + '@smithy/types': 4.6.0 + '@smithy/util-config-provider': 4.2.0 + '@smithy/util-middleware': 4.2.0 tslib: 2.8.1 - '@smithy/core@3.11.0': + '@smithy/core@3.15.0': dependencies: - '@smithy/middleware-serde': 4.1.1 - '@smithy/protocol-http': 5.2.1 - '@smithy/types': 4.5.0 - '@smithy/util-base64': 4.1.0 - '@smithy/util-body-length-browser': 4.1.0 - '@smithy/util-middleware': 4.1.1 - '@smithy/util-stream': 4.3.1 - '@smithy/util-utf8': 4.1.0 - '@types/uuid': 9.0.8 - tslib: 2.8.1 - uuid: 9.0.1 - - '@smithy/credential-provider-imds@4.1.2': - dependencies: - '@smithy/node-config-provider': 4.2.2 - '@smithy/property-provider': 4.1.1 - '@smithy/types': 4.5.0 - '@smithy/url-parser': 4.1.1 + '@smithy/middleware-serde': 4.2.0 + '@smithy/protocol-http': 5.3.0 + '@smithy/types': 4.6.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-middleware': 4.2.0 + '@smithy/util-stream': 4.5.0 + '@smithy/util-utf8': 4.2.0 + '@smithy/uuid': 1.1.0 tslib: 2.8.1 - '@smithy/fetch-http-handler@5.2.1': + '@smithy/credential-provider-imds@4.2.0': dependencies: - '@smithy/protocol-http': 5.2.1 - '@smithy/querystring-builder': 4.1.1 - '@smithy/types': 4.5.0 - '@smithy/util-base64': 4.1.0 + '@smithy/node-config-provider': 4.3.0 + '@smithy/property-provider': 4.2.0 + '@smithy/types': 4.6.0 + '@smithy/url-parser': 4.2.0 tslib: 2.8.1 - '@smithy/hash-node@4.1.1': + '@smithy/fetch-http-handler@5.3.1': dependencies: - '@smithy/types': 4.5.0 - '@smithy/util-buffer-from': 4.1.0 - '@smithy/util-utf8': 4.1.0 + '@smithy/protocol-http': 5.3.0 + '@smithy/querystring-builder': 4.2.0 + '@smithy/types': 4.6.0 + '@smithy/util-base64': 4.3.0 tslib: 2.8.1 - '@smithy/invalid-dependency@4.1.1': + '@smithy/hash-node@4.2.0': dependencies: - '@smithy/types': 4.5.0 + '@smithy/types': 4.6.0 + '@smithy/util-buffer-from': 4.2.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@smithy/invalid-dependency@4.2.0': + dependencies: + '@smithy/types': 4.6.0 tslib: 2.8.1 '@smithy/is-array-buffer@2.2.0': dependencies: tslib: 2.8.1 - '@smithy/is-array-buffer@4.1.0': + '@smithy/is-array-buffer@4.2.0': dependencies: tslib: 2.8.1 - '@smithy/middleware-content-length@4.1.1': + '@smithy/middleware-content-length@4.2.0': dependencies: - '@smithy/protocol-http': 5.2.1 - '@smithy/types': 4.5.0 + '@smithy/protocol-http': 5.3.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@smithy/middleware-endpoint@4.2.2': + '@smithy/middleware-endpoint@4.3.1': dependencies: - '@smithy/core': 3.11.0 - '@smithy/middleware-serde': 4.1.1 - '@smithy/node-config-provider': 4.2.2 - '@smithy/shared-ini-file-loader': 4.2.0 - '@smithy/types': 4.5.0 - '@smithy/url-parser': 4.1.1 - '@smithy/util-middleware': 4.1.1 + '@smithy/core': 3.15.0 + '@smithy/middleware-serde': 4.2.0 + '@smithy/node-config-provider': 4.3.0 + '@smithy/shared-ini-file-loader': 4.3.0 + '@smithy/types': 4.6.0 + '@smithy/url-parser': 4.2.0 + '@smithy/util-middleware': 4.2.0 tslib: 2.8.1 - '@smithy/middleware-retry@4.2.2': + '@smithy/middleware-retry@4.4.1': dependencies: - '@smithy/node-config-provider': 4.2.2 - '@smithy/protocol-http': 5.2.1 - '@smithy/service-error-classification': 4.1.1 - '@smithy/smithy-client': 4.6.2 - '@smithy/types': 4.5.0 - '@smithy/util-middleware': 4.1.1 - '@smithy/util-retry': 4.1.1 - '@types/uuid': 9.0.8 - tslib: 2.8.1 - uuid: 9.0.1 - - '@smithy/middleware-serde@4.1.1': - dependencies: - '@smithy/protocol-http': 5.2.1 - '@smithy/types': 4.5.0 + '@smithy/node-config-provider': 4.3.0 + '@smithy/protocol-http': 5.3.0 + '@smithy/service-error-classification': 4.2.0 + '@smithy/smithy-client': 4.7.1 + '@smithy/types': 4.6.0 + '@smithy/util-middleware': 4.2.0 + '@smithy/util-retry': 4.2.0 + '@smithy/uuid': 1.1.0 tslib: 2.8.1 - '@smithy/middleware-stack@4.1.1': + '@smithy/middleware-serde@4.2.0': dependencies: - '@smithy/types': 4.5.0 + '@smithy/protocol-http': 5.3.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@smithy/node-config-provider@4.2.2': + '@smithy/middleware-stack@4.2.0': dependencies: - '@smithy/property-provider': 4.1.1 - '@smithy/shared-ini-file-loader': 4.2.0 - '@smithy/types': 4.5.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@smithy/node-http-handler@4.2.1': + '@smithy/node-config-provider@4.3.0': dependencies: - '@smithy/abort-controller': 4.1.1 - '@smithy/protocol-http': 5.2.1 - '@smithy/querystring-builder': 4.1.1 - '@smithy/types': 4.5.0 + '@smithy/property-provider': 4.2.0 + '@smithy/shared-ini-file-loader': 4.3.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@smithy/property-provider@4.1.1': + '@smithy/node-http-handler@4.3.0': dependencies: - '@smithy/types': 4.5.0 + '@smithy/abort-controller': 4.2.0 + '@smithy/protocol-http': 5.3.0 + '@smithy/querystring-builder': 4.2.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@smithy/protocol-http@5.2.1': + '@smithy/property-provider@4.2.0': dependencies: - '@smithy/types': 4.5.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@smithy/querystring-builder@4.1.1': + '@smithy/protocol-http@5.3.0': dependencies: - '@smithy/types': 4.5.0 - '@smithy/util-uri-escape': 4.1.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@smithy/querystring-parser@4.1.1': + '@smithy/querystring-builder@4.2.0': dependencies: - '@smithy/types': 4.5.0 + '@smithy/types': 4.6.0 + '@smithy/util-uri-escape': 4.2.0 tslib: 2.8.1 - '@smithy/service-error-classification@4.1.1': + '@smithy/querystring-parser@4.2.0': dependencies: - '@smithy/types': 4.5.0 - - '@smithy/shared-ini-file-loader@4.2.0': - dependencies: - '@smithy/types': 4.5.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@smithy/signature-v4@5.2.1': + '@smithy/service-error-classification@4.2.0': dependencies: - '@smithy/is-array-buffer': 4.1.0 - '@smithy/protocol-http': 5.2.1 - '@smithy/types': 4.5.0 - '@smithy/util-hex-encoding': 4.1.0 - '@smithy/util-middleware': 4.1.1 - '@smithy/util-uri-escape': 4.1.0 - '@smithy/util-utf8': 4.1.0 + '@smithy/types': 4.6.0 + + '@smithy/shared-ini-file-loader@4.3.0': + dependencies: + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@smithy/smithy-client@4.6.2': + '@smithy/signature-v4@5.3.0': dependencies: - '@smithy/core': 3.11.0 - '@smithy/middleware-endpoint': 4.2.2 - '@smithy/middleware-stack': 4.1.1 - '@smithy/protocol-http': 5.2.1 - '@smithy/types': 4.5.0 - '@smithy/util-stream': 4.3.1 + '@smithy/is-array-buffer': 4.2.0 + '@smithy/protocol-http': 5.3.0 + '@smithy/types': 4.6.0 + '@smithy/util-hex-encoding': 4.2.0 + '@smithy/util-middleware': 4.2.0 + '@smithy/util-uri-escape': 4.2.0 + '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 - '@smithy/types@4.5.0': + '@smithy/smithy-client@4.7.1': + dependencies: + '@smithy/core': 3.15.0 + '@smithy/middleware-endpoint': 4.3.1 + '@smithy/middleware-stack': 4.2.0 + '@smithy/protocol-http': 5.3.0 + '@smithy/types': 4.6.0 + '@smithy/util-stream': 4.5.0 + tslib: 2.8.1 + + '@smithy/types@4.6.0': dependencies: tslib: 2.8.1 - '@smithy/url-parser@4.1.1': + '@smithy/url-parser@4.2.0': dependencies: - '@smithy/querystring-parser': 4.1.1 - '@smithy/types': 4.5.0 + '@smithy/querystring-parser': 4.2.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@smithy/util-base64@4.1.0': + '@smithy/util-base64@4.3.0': dependencies: - '@smithy/util-buffer-from': 4.1.0 - '@smithy/util-utf8': 4.1.0 + '@smithy/util-buffer-from': 4.2.0 + '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 - '@smithy/util-body-length-browser@4.1.0': + '@smithy/util-body-length-browser@4.2.0': dependencies: tslib: 2.8.1 - '@smithy/util-body-length-node@4.1.0': + '@smithy/util-body-length-node@4.2.1': dependencies: tslib: 2.8.1 @@ -15753,66 +15596,65 @@ snapshots: '@smithy/is-array-buffer': 2.2.0 tslib: 2.8.1 - '@smithy/util-buffer-from@4.1.0': + '@smithy/util-buffer-from@4.2.0': dependencies: - '@smithy/is-array-buffer': 4.1.0 + '@smithy/is-array-buffer': 4.2.0 tslib: 2.8.1 - '@smithy/util-config-provider@4.1.0': + '@smithy/util-config-provider@4.2.0': dependencies: tslib: 2.8.1 - '@smithy/util-defaults-mode-browser@4.1.2': + '@smithy/util-defaults-mode-browser@4.3.0': dependencies: - '@smithy/property-provider': 4.1.1 - '@smithy/smithy-client': 4.6.2 - '@smithy/types': 4.5.0 - bowser: 2.12.1 + '@smithy/property-provider': 4.2.0 + '@smithy/smithy-client': 4.7.1 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@smithy/util-defaults-mode-node@4.1.2': + '@smithy/util-defaults-mode-node@4.2.1': dependencies: - '@smithy/config-resolver': 4.2.2 - '@smithy/credential-provider-imds': 4.1.2 - '@smithy/node-config-provider': 4.2.2 - '@smithy/property-provider': 4.1.1 - '@smithy/smithy-client': 4.6.2 - '@smithy/types': 4.5.0 + '@smithy/config-resolver': 4.3.0 + '@smithy/credential-provider-imds': 4.2.0 + '@smithy/node-config-provider': 4.3.0 + '@smithy/property-provider': 4.2.0 + '@smithy/smithy-client': 4.7.1 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@smithy/util-endpoints@3.1.2': + '@smithy/util-endpoints@3.2.0': dependencies: - '@smithy/node-config-provider': 4.2.2 - '@smithy/types': 4.5.0 + '@smithy/node-config-provider': 4.3.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@smithy/util-hex-encoding@4.1.0': + '@smithy/util-hex-encoding@4.2.0': dependencies: tslib: 2.8.1 - '@smithy/util-middleware@4.1.1': + '@smithy/util-middleware@4.2.0': dependencies: - '@smithy/types': 4.5.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@smithy/util-retry@4.1.1': + '@smithy/util-retry@4.2.0': dependencies: - '@smithy/service-error-classification': 4.1.1 - '@smithy/types': 4.5.0 + '@smithy/service-error-classification': 4.2.0 + '@smithy/types': 4.6.0 tslib: 2.8.1 - '@smithy/util-stream@4.3.1': + '@smithy/util-stream@4.5.0': dependencies: - '@smithy/fetch-http-handler': 5.2.1 - '@smithy/node-http-handler': 4.2.1 - '@smithy/types': 4.5.0 - '@smithy/util-base64': 4.1.0 - '@smithy/util-buffer-from': 4.1.0 - '@smithy/util-hex-encoding': 4.1.0 - '@smithy/util-utf8': 4.1.0 + '@smithy/fetch-http-handler': 5.3.1 + '@smithy/node-http-handler': 4.3.0 + '@smithy/types': 4.6.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-buffer-from': 4.2.0 + '@smithy/util-hex-encoding': 4.2.0 + '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 - '@smithy/util-uri-escape@4.1.0': + '@smithy/util-uri-escape@4.2.0': dependencies: tslib: 2.8.1 @@ -15821,9 +15663,13 @@ snapshots: '@smithy/util-buffer-from': 2.2.0 tslib: 2.8.1 - '@smithy/util-utf8@4.1.0': + '@smithy/util-utf8@4.2.0': + dependencies: + '@smithy/util-buffer-from': 4.2.0 + tslib: 2.8.1 + + '@smithy/uuid@1.1.0': dependencies: - '@smithy/util-buffer-from': 4.1.0 tslib: 2.8.1 '@socket.io/component-emitter@3.1.2': {} @@ -15839,33 +15685,33 @@ snapshots: '@standard-schema/spec@1.0.0': {} - '@sveltejs/acorn-typescript@1.0.5(acorn@8.15.0)': + '@sveltejs/acorn-typescript@1.0.6(acorn@8.15.0)': dependencies: acorn: 8.15.0 - '@sveltejs/adapter-static@3.0.9(@sveltejs/kit@2.38.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))': + '@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.46.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))': dependencies: - '@sveltejs/kit': 2.38.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/kit': 2.46.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) - '@sveltejs/enhanced-img@0.8.1(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.50.1)(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@sveltejs/enhanced-img@0.8.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.52.4)(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) magic-string: 0.30.19 - sharp: 0.34.3 - svelte: 5.38.10 - svelte-parse-markup: 0.1.5(svelte@5.38.10) - vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - vite-imagetools: 8.0.0(rollup@4.50.1) - zimmerframe: 1.1.2 + sharp: 0.34.4 + svelte: 5.39.11 + svelte-parse-markup: 0.1.5(svelte@5.39.11) + vite: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite-imagetools: 8.0.0(rollup@4.52.4) + zimmerframe: 1.1.4 transitivePeerDependencies: - rollup - supports-color - '@sveltejs/kit@2.38.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@sveltejs/kit@2.46.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@standard-schema/spec': 1.0.0 - '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) - '@sveltejs/vite-plugin-svelte': 6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/acorn-typescript': 1.0.6(acorn@8.15.0) + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@types/cookie': 0.6.0 acorn: 8.15.0 cookie: 0.6.0 @@ -15877,29 +15723,29 @@ snapshots: sade: 1.8.1 set-cookie-parser: 2.7.1 sirv: 3.0.2 - svelte: 5.38.10 - vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + svelte: 5.39.11 + vite: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) optionalDependencies: '@opentelemetry/api': 1.9.0 - '@sveltejs/vite-plugin-svelte-inspector@5.0.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@sveltejs/vite-plugin-svelte-inspector@5.0.0(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) debug: 4.4.3 - svelte: 5.38.10 - vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + svelte: 5.39.11 + vite: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 5.0.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/vite-plugin-svelte-inspector': 5.0.0(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) debug: 4.4.3 deepmerge: 4.3.1 magic-string: 0.30.19 - svelte: 5.38.10 - vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - vitefu: 1.1.1(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + svelte: 5.39.11 + vite: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vitefu: 1.1.1(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) transitivePeerDependencies: - supports-color @@ -15947,12 +15793,12 @@ snapshots: '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.27.7) '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.27.7) - '@svgr/core@8.1.0(typescript@5.9.2)': + '@svgr/core@8.1.0(typescript@5.9.3)': dependencies: '@babel/core': 7.27.7 '@svgr/babel-preset': 8.1.0(@babel/core@7.27.7) camelcase: 6.3.0 - cosmiconfig: 8.3.6(typescript@5.9.2) + cosmiconfig: 8.3.6(typescript@5.9.3) snake-case: 3.0.4 transitivePeerDependencies: - supports-color @@ -15963,35 +15809,35 @@ snapshots: '@babel/types': 7.28.4 entities: 4.5.0 - '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.9.2))': + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.9.3))': dependencies: '@babel/core': 7.27.7 '@svgr/babel-preset': 8.1.0(@babel/core@7.27.7) - '@svgr/core': 8.1.0(typescript@5.9.2) + '@svgr/core': 8.1.0(typescript@5.9.3) '@svgr/hast-util-to-babel-ast': 8.0.0 svg-parser: 2.0.4 transitivePeerDependencies: - supports-color - '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.9.2))(typescript@5.9.2)': + '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@5.9.3)': dependencies: - '@svgr/core': 8.1.0(typescript@5.9.2) - cosmiconfig: 8.3.6(typescript@5.9.2) + '@svgr/core': 8.1.0(typescript@5.9.3) + cosmiconfig: 8.3.6(typescript@5.9.3) deepmerge: 4.3.1 svgo: 3.3.2 transitivePeerDependencies: - typescript - '@svgr/webpack@8.1.0(typescript@5.9.2)': + '@svgr/webpack@8.1.0(typescript@5.9.3)': dependencies: '@babel/core': 7.27.7 '@babel/plugin-transform-react-constant-elements': 7.27.1(@babel/core@7.27.7) '@babel/preset-env': 7.27.2(@babel/core@7.27.7) '@babel/preset-react': 7.27.1(@babel/core@7.27.7) '@babel/preset-typescript': 7.27.1(@babel/core@7.27.7) - '@svgr/core': 8.1.0(typescript@5.9.2) - '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.2)) - '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.2))(typescript@5.9.2) + '@svgr/core': 8.1.0(typescript@5.9.3) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3)) + '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@5.9.3) transitivePeerDependencies: - supports-color - typescript @@ -16057,76 +15903,76 @@ snapshots: dependencies: defer-to-connect: 2.0.1 - '@tailwindcss/node@4.1.13': + '@tailwindcss/node@4.1.14': dependencies: '@jridgewell/remapping': 2.3.5 enhanced-resolve: 5.18.3 - jiti: 2.5.1 + jiti: 2.6.1 lightningcss: 1.30.1 magic-string: 0.30.19 source-map-js: 1.2.1 - tailwindcss: 4.1.13 + tailwindcss: 4.1.14 - '@tailwindcss/oxide-android-arm64@4.1.13': + '@tailwindcss/oxide-android-arm64@4.1.14': optional: true - '@tailwindcss/oxide-darwin-arm64@4.1.13': + '@tailwindcss/oxide-darwin-arm64@4.1.14': optional: true - '@tailwindcss/oxide-darwin-x64@4.1.13': + '@tailwindcss/oxide-darwin-x64@4.1.14': optional: true - '@tailwindcss/oxide-freebsd-x64@4.1.13': + '@tailwindcss/oxide-freebsd-x64@4.1.14': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.1.13': + '@tailwindcss/oxide-linux-arm64-gnu@4.1.14': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.1.13': + '@tailwindcss/oxide-linux-arm64-musl@4.1.14': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.1.13': + '@tailwindcss/oxide-linux-x64-gnu@4.1.14': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.1.13': + '@tailwindcss/oxide-linux-x64-musl@4.1.14': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.1.13': + '@tailwindcss/oxide-wasm32-wasi@4.1.14': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.1.13': + '@tailwindcss/oxide-win32-arm64-msvc@4.1.14': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.1.13': + '@tailwindcss/oxide-win32-x64-msvc@4.1.14': optional: true - '@tailwindcss/oxide@4.1.13': + '@tailwindcss/oxide@4.1.14': dependencies: - detect-libc: 2.1.0 - tar: 7.4.3 + detect-libc: 2.1.2 + tar: 7.5.1 optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.1.13 - '@tailwindcss/oxide-darwin-arm64': 4.1.13 - '@tailwindcss/oxide-darwin-x64': 4.1.13 - '@tailwindcss/oxide-freebsd-x64': 4.1.13 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.13 - '@tailwindcss/oxide-linux-arm64-gnu': 4.1.13 - '@tailwindcss/oxide-linux-arm64-musl': 4.1.13 - '@tailwindcss/oxide-linux-x64-gnu': 4.1.13 - '@tailwindcss/oxide-linux-x64-musl': 4.1.13 - '@tailwindcss/oxide-wasm32-wasi': 4.1.13 - '@tailwindcss/oxide-win32-arm64-msvc': 4.1.13 - '@tailwindcss/oxide-win32-x64-msvc': 4.1.13 + '@tailwindcss/oxide-android-arm64': 4.1.14 + '@tailwindcss/oxide-darwin-arm64': 4.1.14 + '@tailwindcss/oxide-darwin-x64': 4.1.14 + '@tailwindcss/oxide-freebsd-x64': 4.1.14 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.14 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.14 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.14 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.14 + '@tailwindcss/oxide-linux-x64-musl': 4.1.14 + '@tailwindcss/oxide-wasm32-wasi': 4.1.14 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.14 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.14 - '@tailwindcss/vite@4.1.13(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@tailwindcss/vite@4.1.14(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: - '@tailwindcss/node': 4.1.13 - '@tailwindcss/oxide': 4.1.13 - tailwindcss: 4.1.13 - vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + '@tailwindcss/node': 4.1.14 + '@tailwindcss/oxide': 4.1.14 + tailwindcss: 4.1.14 + vite: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) '@testing-library/dom@10.4.0': dependencies: @@ -16139,7 +15985,7 @@ snapshots: lz-string: 1.5.0 pretty-format: 27.5.1 - '@testing-library/jest-dom@6.8.0': + '@testing-library/jest-dom@6.9.1': dependencies: '@adobe/css-tools': 4.4.4 aria-query: 5.3.2 @@ -16148,13 +15994,13 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/svelte@5.2.8(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.5.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@testing-library/svelte@5.2.8(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.7.2)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@testing-library/dom': 10.4.0 - svelte: 5.38.10 + svelte: 5.39.11 optionalDependencies: - vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.5.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.7.2)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.0)': dependencies: @@ -16196,7 +16042,7 @@ snapshots: '@types/accepts@1.3.7': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/archiver@6.0.3': dependencies: @@ -16208,16 +16054,16 @@ snapshots: '@types/bcrypt@6.0.0': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/bonjour@3.5.13': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/braces@3.0.5': {} @@ -16238,21 +16084,21 @@ snapshots: '@types/cli-progress@3.11.6': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/compression@1.8.1': dependencies: '@types/express': 5.0.3 - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/connect-history-api-fallback@1.5.4': dependencies: '@types/express-serve-static-core': 5.0.6 - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/connect@3.4.38': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/content-disposition@0.5.9': {} @@ -16269,11 +16115,11 @@ snapshots: '@types/connect': 3.4.38 '@types/express': 5.0.3 '@types/keygrip': 1.0.6 - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/cors@2.8.19': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/debug@4.1.12': dependencies: @@ -16283,13 +16129,13 @@ snapshots: '@types/docker-modem@3.0.6': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/ssh2': 1.15.5 - '@types/dockerode@3.3.42': + '@types/dockerode@3.3.44': dependencies: '@types/docker-modem': 3.0.6 - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/ssh2': 1.15.5 '@types/dom-to-image@2.6.7': {} @@ -16312,14 +16158,14 @@ snapshots: '@types/express-serve-static-core@4.19.6': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 0.17.5 '@types/express-serve-static-core@5.0.6': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 0.17.5 @@ -16345,7 +16191,7 @@ snapshots: '@types/fluent-ffmpeg@2.1.27': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/geojson-vt@3.2.5': dependencies: @@ -16367,11 +16213,6 @@ snapshots: '@types/history@4.7.11': {} - '@types/hoist-non-react-statics@3.3.6': - dependencies: - '@types/react': 19.1.13 - hoist-non-react-statics: 3.3.2 - '@types/html-minifier-terser@6.1.0': {} '@types/http-assert@1.5.6': {} @@ -16382,7 +16223,7 @@ snapshots: '@types/http-proxy@1.17.16': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/inquirer@8.2.11': dependencies: @@ -16420,7 +16261,7 @@ snapshots: '@types/http-errors': 2.0.5 '@types/keygrip': 1.0.6 '@types/koa-compose': 3.2.8 - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/leaflet@1.9.20': dependencies: @@ -16432,8 +16273,6 @@ snapshots: '@types/lodash@4.17.20': {} - '@types/luxon@3.6.2': {} - '@types/luxon@3.7.1': {} '@types/mdast@4.0.4': @@ -16452,7 +16291,7 @@ snapshots: '@types/mock-fs@4.13.4': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/ms@2.1.0': {} @@ -16462,31 +16301,31 @@ snapshots: '@types/node-forge@1.3.11': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/node@17.0.45': {} - '@types/node@18.19.126': + '@types/node@18.19.130': dependencies: undici-types: 5.26.5 - '@types/node@20.19.2': + '@types/node@20.19.21': dependencies: undici-types: 6.21.0 - '@types/node@22.18.5': + '@types/node@22.18.10': dependencies: undici-types: 6.21.0 - '@types/node@24.5.1': + '@types/node@24.7.2': dependencies: - undici-types: 7.12.0 + undici-types: 7.14.0 optional: true - '@types/nodemailer@7.0.1': + '@types/nodemailer@7.0.2': dependencies: - '@aws-sdk/client-sesv2': 3.890.0 - '@types/node': 22.18.5 + '@aws-sdk/client-sesv2': 3.907.0 + '@types/node': 22.18.10 transitivePeerDependencies: - aws-crt @@ -16494,7 +16333,7 @@ snapshots: dependencies: '@types/keygrip': 1.0.6 '@types/koa': 3.0.0 - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/parse5@5.0.3': {} @@ -16504,7 +16343,7 @@ snapshots: '@types/pg@8.15.5': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 pg-protocol: 1.10.3 pg-types: 2.2.0 @@ -16512,51 +16351,44 @@ snapshots: '@types/pngjs@6.0.5': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/prismjs@1.26.5': {} '@types/qrcode@1.5.5': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/qs@6.14.0': {} '@types/range-parser@1.2.7': {} - '@types/react-redux@7.1.34': - dependencies: - '@types/hoist-non-react-statics': 3.3.6 - '@types/react': 19.1.13 - hoist-non-react-statics: 3.3.2 - redux: 4.2.1 - '@types/react-router-config@5.0.11': dependencies: '@types/history': 4.7.11 - '@types/react': 19.1.13 + '@types/react': 19.2.2 '@types/react-router': 5.1.20 '@types/react-router-dom@5.3.3': dependencies: '@types/history': 4.7.11 - '@types/react': 19.1.13 + '@types/react': 19.2.2 '@types/react-router': 5.1.20 '@types/react-router@5.1.20': dependencies: '@types/history': 4.7.11 - '@types/react': 19.1.13 + '@types/react': 19.2.2 - '@types/react@19.1.13': + '@types/react@19.2.2': dependencies: csstype: 3.1.3 '@types/readdir-glob@1.1.5': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 - '@types/retry@0.12.0': {} + '@types/retry@0.12.2': {} '@types/sanitize-html@2.16.0': dependencies: @@ -16564,14 +16396,14 @@ snapshots: '@types/sax@1.2.7': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/semver@7.7.1': {} '@types/send@0.17.5': dependencies: '@types/mime': 1.3.5 - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/serve-index@1.9.4': dependencies: @@ -16580,31 +16412,31 @@ snapshots: '@types/serve-static@1.15.8': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/send': 0.17.5 '@types/sockjs@0.3.36': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/ssh2-streams@0.1.12': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/ssh2@0.5.52': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/ssh2-streams': 0.1.12 '@types/ssh2@1.15.5': dependencies: - '@types/node': 18.19.126 + '@types/node': 18.19.130 '@types/superagent@8.1.9': dependencies: '@types/cookiejar': 2.1.5 '@types/methods': 1.1.4 - '@types/node': 22.18.5 + '@types/node': 22.18.10 form-data: 4.0.4 '@types/supercluster@7.1.3': @@ -16618,7 +16450,7 @@ snapshots: '@types/through@0.0.33': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/ua-parser-js@0.7.39': {} @@ -16626,15 +16458,13 @@ snapshots: '@types/unist@3.0.3': {} - '@types/uuid@9.0.8': {} - '@types/validator@13.15.3': {} '@types/whatwg-mimetype@3.0.2': {} '@types/ws@8.18.1': dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 '@types/yargs-parser@21.0.3': {} @@ -16642,102 +16472,102 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)': + '@typescript-eslint/eslint-plugin@8.46.0(@typescript-eslint/parser@8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/scope-manager': 8.43.0 - '@typescript-eslint/type-utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/visitor-keys': 8.43.0 - eslint: 9.35.0(jiti@2.5.1) + '@typescript-eslint/parser': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.46.0 + '@typescript-eslint/type-utils': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.46.0 + eslint: 9.37.0(jiti@2.6.1) graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.9.2) - typescript: 5.9.2 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)': + '@typescript-eslint/parser@8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.43.0 - '@typescript-eslint/types': 8.43.0 - '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.9.2) - '@typescript-eslint/visitor-keys': 8.43.0 + '@typescript-eslint/scope-manager': 8.46.0 + '@typescript-eslint/types': 8.46.0 + '@typescript-eslint/typescript-estree': 8.46.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.46.0 debug: 4.4.3 - eslint: 9.35.0(jiti@2.5.1) - typescript: 5.9.2 + eslint: 9.37.0(jiti@2.6.1) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.43.0(typescript@5.9.2)': + '@typescript-eslint/project-service@8.46.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.43.0(typescript@5.9.2) - '@typescript-eslint/types': 8.43.0 + '@typescript-eslint/tsconfig-utils': 8.46.0(typescript@5.9.3) + '@typescript-eslint/types': 8.46.0 debug: 4.4.3 - typescript: 5.9.2 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.43.0': + '@typescript-eslint/scope-manager@8.46.0': dependencies: - '@typescript-eslint/types': 8.43.0 - '@typescript-eslint/visitor-keys': 8.43.0 + '@typescript-eslint/types': 8.46.0 + '@typescript-eslint/visitor-keys': 8.46.0 - '@typescript-eslint/tsconfig-utils@8.43.0(typescript@5.9.2)': + '@typescript-eslint/tsconfig-utils@8.46.0(typescript@5.9.3)': dependencies: - typescript: 5.9.2 + typescript: 5.9.3 - '@typescript-eslint/type-utils@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)': + '@typescript-eslint/type-utils@8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.43.0 - '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.9.2) - '@typescript-eslint/utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/types': 8.46.0 + '@typescript-eslint/typescript-estree': 8.46.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) debug: 4.4.3 - eslint: 9.35.0(jiti@2.5.1) - ts-api-utils: 2.1.0(typescript@5.9.2) - typescript: 5.9.2 + eslint: 9.37.0(jiti@2.6.1) + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.43.0': {} + '@typescript-eslint/types@8.46.0': {} - '@typescript-eslint/typescript-estree@8.43.0(typescript@5.9.2)': + '@typescript-eslint/typescript-estree@8.46.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.43.0(typescript@5.9.2) - '@typescript-eslint/tsconfig-utils': 8.43.0(typescript@5.9.2) - '@typescript-eslint/types': 8.43.0 - '@typescript-eslint/visitor-keys': 8.43.0 + '@typescript-eslint/project-service': 8.46.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.46.0(typescript@5.9.3) + '@typescript-eslint/types': 8.46.0 + '@typescript-eslint/visitor-keys': 8.46.0 debug: 4.4.3 fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 - semver: 7.7.2 - ts-api-utils: 2.1.0(typescript@5.9.2) - typescript: 5.9.2 + semver: 7.7.3 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)': + '@typescript-eslint/utils@8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0(jiti@2.5.1)) - '@typescript-eslint/scope-manager': 8.43.0 - '@typescript-eslint/types': 8.43.0 - '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.9.2) - eslint: 9.35.0(jiti@2.5.1) - typescript: 5.9.2 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.46.0 + '@typescript-eslint/types': 8.46.0 + '@typescript-eslint/typescript-estree': 8.46.0(typescript@5.9.3) + eslint: 9.37.0(jiti@2.6.1) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.43.0': + '@typescript-eslint/visitor-keys@8.46.0': dependencies: - '@typescript-eslint/types': 8.43.0 + '@typescript-eslint/types': 8.46.0 eslint-visitor-keys: 4.2.1 '@ungap/structured-clone@1.3.0': {} - '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 @@ -16752,11 +16582,11 @@ snapshots: std-env: 3.9.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - supports-color - '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.5.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.7.2)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 @@ -16771,7 +16601,7 @@ snapshots: std-env: 3.9.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.5.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.7.2)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - supports-color @@ -16783,21 +16613,21 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@vitest/mocker@3.2.4(vite@7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.19 optionalDependencies: - vite: 7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - '@vitest/mocker@3.2.4(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@vitest/mocker@3.2.4(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.19 optionalDependencies: - vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) '@vitest/pretty-format@3.2.4': dependencies: @@ -16905,14 +16735,14 @@ snapshots: '@xtuc/long@4.2.2': {} - '@zoom-image/core@0.41.0': + '@zoom-image/core@0.41.3': dependencies: '@namnode/store': 0.1.0 - '@zoom-image/svelte@0.3.4(svelte@5.38.10)': + '@zoom-image/svelte@0.3.7(svelte@5.39.11)': dependencies: - '@zoom-image/core': 0.41.0 - svelte: 5.38.10 + '@zoom-image/core': 0.41.3 + svelte: 5.39.11 abab@2.0.6: optional: true @@ -16974,14 +16804,6 @@ snapshots: clean-stack: 2.2.0 indent-string: 4.0.0 - ajv-draft-04@1.0.0(ajv@8.11.0): - optionalDependencies: - ajv: 8.11.0 - - ajv-formats@2.1.1(ajv@8.11.0): - optionalDependencies: - ajv: 8.11.0 - ajv-formats@2.1.1(ajv@8.17.1): optionalDependencies: ajv: 8.17.1 @@ -17006,40 +16828,34 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ajv@8.11.0: - dependencies: - fast-deep-equal: 3.1.3 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - uri-js: 4.4.1 - ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.0.6 + fast-uri: 3.1.0 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - algoliasearch-helper@3.26.0(algoliasearch@5.29.0): + algoliasearch-helper@3.26.0(algoliasearch@5.40.0): dependencies: '@algolia/events': 4.0.1 - algoliasearch: 5.29.0 + algoliasearch: 5.40.0 - algoliasearch@5.29.0: + algoliasearch@5.40.0: dependencies: - '@algolia/client-abtesting': 5.29.0 - '@algolia/client-analytics': 5.29.0 - '@algolia/client-common': 5.29.0 - '@algolia/client-insights': 5.29.0 - '@algolia/client-personalization': 5.29.0 - '@algolia/client-query-suggestions': 5.29.0 - '@algolia/client-search': 5.29.0 - '@algolia/ingestion': 1.29.0 - '@algolia/monitoring': 1.29.0 - '@algolia/recommend': 5.29.0 - '@algolia/requester-browser-xhr': 5.29.0 - '@algolia/requester-fetch': 5.29.0 - '@algolia/requester-node-http': 5.29.0 + '@algolia/abtesting': 1.6.0 + '@algolia/client-abtesting': 5.40.0 + '@algolia/client-analytics': 5.40.0 + '@algolia/client-common': 5.40.0 + '@algolia/client-insights': 5.40.0 + '@algolia/client-personalization': 5.40.0 + '@algolia/client-query-suggestions': 5.40.0 + '@algolia/client-search': 5.40.0 + '@algolia/ingestion': 1.40.0 + '@algolia/monitoring': 1.40.0 + '@algolia/recommend': 5.40.0 + '@algolia/requester-browser-xhr': 5.40.0 + '@algolia/requester-fetch': 5.40.0 + '@algolia/requester-node-http': 5.40.0 ansi-align@3.0.1: dependencies: @@ -17139,7 +16955,7 @@ snapshots: ast-v8-to-istanbul@0.3.3: dependencies: - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 estree-walker: 3.0.3 js-tokens: 9.0.1 @@ -17153,10 +16969,6 @@ snapshots: async@0.2.10: {} - async@3.2.2: {} - - async@3.2.4: {} - async@3.2.6: {} asynckit@0.4.0: {} @@ -17167,8 +16979,8 @@ snapshots: autoprefixer@10.4.21(postcss@8.5.6): dependencies: - browserslist: 4.25.3 - caniuse-lite: 1.0.30001735 + browserslist: 4.26.3 + caniuse-lite: 1.0.30001749 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -17179,12 +16991,12 @@ snapshots: b4a@1.6.7: {} - babel-loader@9.2.1(@babel/core@7.27.7)(webpack@5.100.2): + babel-loader@9.2.1(@babel/core@7.27.7)(webpack@5.102.1): dependencies: '@babel/core': 7.27.7 find-cache-dir: 4.0.0 - schema-utils: 4.3.2 - webpack: 5.100.2 + schema-utils: 4.3.3 + webpack: 5.102.1 babel-plugin-dynamic-import-node@2.3.3: dependencies: @@ -17220,35 +17032,43 @@ snapshots: balanced-match@1.0.2: {} - bare-events@2.6.1: - optional: true + bare-events@2.7.0: {} - bare-fs@4.2.0: + bare-fs@4.4.7: dependencies: - bare-events: 2.6.1 + bare-events: 2.7.0 bare-path: 3.0.0 - bare-stream: 2.7.0(bare-events@2.6.1) + bare-stream: 2.7.0(bare-events@2.7.0) + bare-url: 2.2.2 + fast-fifo: 1.3.2 optional: true - bare-os@3.6.1: + bare-os@3.6.2: optional: true bare-path@3.0.0: dependencies: - bare-os: 3.6.1 + bare-os: 3.6.2 optional: true - bare-stream@2.7.0(bare-events@2.6.1): + bare-stream@2.7.0(bare-events@2.7.0): dependencies: - streamx: 2.22.1 + streamx: 2.23.0 optionalDependencies: - bare-events: 2.6.1 + bare-events: 2.7.0 + optional: true + + bare-url@2.2.2: + dependencies: + bare-path: 3.0.0 optional: true base64-js@1.5.1: {} base64id@2.0.0: {} + baseline-browser-mapping@2.8.15: {} + batch-cluster@13.0.0: {} batch@0.6.1: {} @@ -17268,15 +17088,15 @@ snapshots: binary-extensions@2.3.0: {} - bits-ui@2.9.8(@internationalized/date@3.8.2)(svelte@5.38.10): + bits-ui@2.9.8(@internationalized/date@3.8.2)(svelte@5.39.11): dependencies: '@floating-ui/core': 1.7.3 '@floating-ui/dom': 1.7.4 '@internationalized/date': 3.8.2 esm-env: 1.2.2 - runed: 0.29.2(svelte@5.38.10) - svelte: 5.38.10 - svelte-toolbelt: 0.9.3(svelte@5.38.10) + runed: 0.29.2(svelte@5.39.11) + svelte: 5.39.11 + svelte-toolbelt: 0.9.3(svelte@5.39.11) tabbable: 6.2.0 bl@4.1.0: @@ -17360,12 +17180,13 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.25.3: + browserslist@4.26.3: dependencies: - caniuse-lite: 1.0.30001735 - electron-to-chromium: 1.5.207 - node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.25.3) + baseline-browser-mapping: 2.8.15 + caniuse-lite: 1.0.30001749 + electron-to-chromium: 1.5.233 + node-releases: 2.0.23 + update-browserslist-db: 1.1.3(browserslist@4.26.3) buffer-crc32@1.0.0: {} @@ -17386,18 +17207,22 @@ snapshots: builtin-modules@5.0.0: {} - bullmq@5.58.5: + bullmq@5.61.0: dependencies: cron-parser: 4.9.0 - ioredis: 5.7.0 + ioredis: 5.8.1 msgpackr: 1.11.5 node-abort-controller: 3.1.1 - semver: 7.7.2 + semver: 7.7.3 tslib: 2.8.1 - uuid: 9.0.1 + uuid: 11.1.0 transitivePeerDependencies: - supports-color + bundle-name@4.1.0: + dependencies: + run-applescript: 7.1.0 + busboy@1.6.0: dependencies: streamsearch: 1.1.0 @@ -17424,7 +17249,7 @@ snapshots: minipass-pipeline: 1.2.4 p-map: 7.0.3 ssri: 12.0.0 - tar: 7.4.3 + tar: 7.5.1 unique-filename: 4.0.0 cacheable-lookup@7.0.0: {} @@ -17456,8 +17281,6 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 - call-me-maybe@1.0.2: {} - callsites@3.1.0: {} camel-case@4.1.2: @@ -17475,12 +17298,12 @@ snapshots: caniuse-api@3.0.0: dependencies: - browserslist: 4.25.3 - caniuse-lite: 1.0.30001735 + browserslist: 4.26.3 + caniuse-lite: 1.0.30001749 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 - caniuse-lite@1.0.30001735: {} + caniuse-lite@1.0.30001749: {} canvas@2.11.2: dependencies: @@ -17533,8 +17356,6 @@ snapshots: chardet@2.1.0: {} - charset@1.0.1: {} - check-error@2.1.1: {} cheerio-select@2.1.0: @@ -17662,8 +17483,6 @@ snapshots: clone@1.0.4: {} - clsx@1.2.1: {} - clsx@2.1.1: {} cluster-key-slot@1.1.2: {} @@ -17676,18 +17495,8 @@ snapshots: color-name@1.1.4: {} - color-string@1.9.1: - dependencies: - color-name: 1.1.4 - simple-swizzle: 0.2.2 - color-support@1.1.3: {} - color@4.2.3: - dependencies: - color-convert: 2.0.1 - color-string: 1.9.1 - colord@2.9.3: {} colorette@2.0.20: {} @@ -17720,13 +17529,11 @@ snapshots: commander@8.3.0: {} - comment-json@4.2.5: + comment-json@4.4.1: dependencies: array-timsort: 1.0.3 core-util-is: 1.0.3 esprima: 4.0.1 - has-own-prop: 2.0.0 - repeat-string: 1.6.1 common-path-prefix@3.0.0: {} @@ -17756,19 +17563,6 @@ snapshots: transitivePeerDependencies: - supports-color - compute-gcd@1.2.1: - dependencies: - validate.io-array: 1.0.6 - validate.io-function: 1.0.2 - validate.io-integer-array: 1.0.0 - - compute-lcm@1.1.2: - dependencies: - compute-gcd: 1.2.1 - validate.io-array: 1.0.6 - validate.io-function: 1.0.2 - validate.io-integer-array: 1.0.0 - concat-map@0.0.1: {} concat-stream@2.0.0: @@ -17837,21 +17631,19 @@ snapshots: depd: 2.0.0 keygrip: 1.1.0 - copy-text-to-clipboard@3.2.0: {} - - copy-webpack-plugin@11.0.0(webpack@5.100.2): + copy-webpack-plugin@11.0.0(webpack@5.102.1): dependencies: fast-glob: 3.3.3 glob-parent: 6.0.2 globby: 13.2.2 normalize-path: 3.0.0 - schema-utils: 4.3.2 + schema-utils: 4.3.3 serialize-javascript: 6.0.2 - webpack: 5.100.2 + webpack: 5.102.1 core-js-compat@3.45.0: dependencies: - browserslist: 4.25.3 + browserslist: 4.26.3 core-js-pure@3.43.0: {} @@ -17873,14 +17665,14 @@ snapshots: optionalDependencies: typescript: 5.8.3 - cosmiconfig@8.3.6(typescript@5.9.2): + cosmiconfig@8.3.6(typescript@5.9.3): dependencies: import-fresh: 3.3.1 js-yaml: 4.1.0 parse-json: 5.2.0 path-type: 4.0.0 optionalDependencies: - typescript: 5.9.2 + typescript: 5.9.3 cpu-features@0.0.10: dependencies: @@ -17899,10 +17691,10 @@ snapshots: dependencies: luxon: 3.7.2 - cron@4.3.0: + cron@4.3.3: dependencies: - '@types/luxon': 3.6.2 - luxon: 3.6.1 + '@types/luxon': 3.7.1 + luxon: 3.7.2 cross-spawn@7.0.6: dependencies: @@ -17910,8 +17702,6 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - crypto-js@4.2.0: {} - crypto-random-string@4.0.0: dependencies: type-fest: 1.4.0 @@ -17932,7 +17722,7 @@ snapshots: postcss-selector-parser: 7.1.0 postcss-value-parser: 4.2.0 - css-loader@6.11.0(webpack@5.100.2): + css-loader@6.11.0(webpack@5.102.1): dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 @@ -17941,19 +17731,19 @@ snapshots: postcss-modules-scope: 3.2.1(postcss@8.5.6) postcss-modules-values: 4.0.0(postcss@8.5.6) postcss-value-parser: 4.2.0 - semver: 7.7.2 + semver: 7.7.3 optionalDependencies: - webpack: 5.100.2 + webpack: 5.102.1 - css-minimizer-webpack-plugin@5.0.1(clean-css@5.3.3)(webpack@5.100.2): + css-minimizer-webpack-plugin@5.0.1(clean-css@5.3.3)(webpack@5.102.1): dependencies: - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 cssnano: 6.1.2(postcss@8.5.6) jest-worker: 29.7.0 postcss: 8.5.6 - schema-utils: 4.3.2 + schema-utils: 4.3.3 serialize-javascript: 6.0.2 - webpack: 5.100.2 + webpack: 5.102.1 optionalDependencies: clean-css: 5.3.3 @@ -18002,7 +17792,7 @@ snapshots: cssnano-preset-advanced@6.1.2(postcss@8.5.6): dependencies: autoprefixer: 10.4.21(postcss@8.5.6) - browserslist: 4.25.3 + browserslist: 4.26.3 cssnano-preset-default: 6.1.2(postcss@8.5.6) postcss: 8.5.6 postcss-discard-unused: 6.0.5(postcss@8.5.6) @@ -18012,7 +17802,7 @@ snapshots: cssnano-preset-default@6.1.2(postcss@8.5.6): dependencies: - browserslist: 4.25.3 + browserslist: 4.26.3 css-declaration-sorter: 7.2.0(postcss@8.5.6) cssnano-utils: 4.0.2(postcss@8.5.6) postcss: 8.5.6 @@ -18146,9 +17936,12 @@ snapshots: deepmerge@4.3.1: {} - default-gateway@6.0.3: + default-browser-id@5.0.0: {} + + default-browser@5.2.1: dependencies: - execa: 5.1.1 + bundle-name: 4.1.0 + default-browser-id: 5.0.0 defaults@1.0.4: dependencies: @@ -18164,6 +17957,8 @@ snapshots: define-lazy-prop@2.0.0: {} + define-lazy-prop@3.0.0: {} + define-properties@1.2.1: dependencies: define-data-property: 1.1.4 @@ -18186,14 +17981,10 @@ snapshots: detect-europe-js@0.1.2: {} - detect-libc@2.1.0: {} + detect-libc@2.1.2: {} detect-node@2.1.0: {} - detect-package-manager@3.0.2: - dependencies: - execa: 5.1.1 - detect-port@1.6.1: dependencies: address: 1.2.2 @@ -18232,7 +18023,7 @@ snapshots: dependencies: '@leichtgewicht/ip-codec': 2.0.5 - docker-compose@1.2.0: + docker-compose@1.3.0: dependencies: yaml: 2.8.1 @@ -18241,25 +18032,25 @@ snapshots: debug: 4.4.3 readable-stream: 3.6.2 split-ca: 1.0.1 - ssh2: 1.16.0 + ssh2: 1.17.0 transitivePeerDependencies: - supports-color - dockerode@4.0.7: + dockerode@4.0.9: dependencies: '@balena/dockerignore': 1.0.2 - '@grpc/grpc-js': 1.13.4 + '@grpc/grpc-js': 1.14.0 '@grpc/proto-loader': 0.7.15 docker-modem: 5.0.6 protobufjs: 7.5.4 - tar-fs: 2.1.3 + tar-fs: 2.1.4 uuid: 10.0.0 transitivePeerDependencies: - supports-color - docusaurus-lunr-search@3.6.0(@docusaurus/core@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + docusaurus-lunr-search@3.6.0(@docusaurus/core@3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/core': 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) autocomplete.js: 0.37.1 clsx: 2.1.1 gauge: 3.0.2 @@ -18277,129 +18068,6 @@ snapshots: unified: 9.2.2 unist-util-is: 4.1.0 - docusaurus-plugin-openapi@0.7.6(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2): - dependencies: - '@docusaurus/mdx-loader': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/plugin-content-docs': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-common': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - chalk: 4.1.2 - clsx: 1.2.1 - js-yaml: 4.1.0 - json-refs: 3.0.15 - json-schema-resolve-allof: 1.5.0 - lodash: 4.17.21 - openapi-to-postmanv2: 4.25.0(encoding@0.1.13) - postman-collection: 4.5.0 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - webpack: 5.100.2 - transitivePeerDependencies: - - '@docusaurus/faster' - - '@mdx-js/react' - - '@parcel/css' - - '@rspack/core' - - '@swc/core' - - '@swc/css' - - acorn - - bufferutil - - csso - - debug - - encoding - - esbuild - - lightningcss - - supports-color - - typescript - - uglify-js - - utf-8-validate - - webpack-cli - - docusaurus-plugin-proxy@0.7.6: {} - - docusaurus-preset-openapi@0.7.6(@algolia/client-search@5.29.0)(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(@types/react@19.1.13)(acorn@8.15.0)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(redux@4.2.1)(search-insights@2.17.3)(typescript@5.9.2): - dependencies: - '@docusaurus/preset-classic': 3.8.1(@algolia/client-search@5.29.0)(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(@types/react@19.1.13)(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3)(typescript@5.9.2) - docusaurus-plugin-openapi: 0.7.6(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - docusaurus-plugin-proxy: 0.7.6 - docusaurus-theme-openapi: 0.7.6(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(@types/react@19.1.13)(acorn@8.15.0)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(redux@4.2.1)(typescript@5.9.2) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - transitivePeerDependencies: - - '@algolia/client-search' - - '@docusaurus/faster' - - '@docusaurus/plugin-content-docs' - - '@mdx-js/react' - - '@parcel/css' - - '@rspack/core' - - '@swc/core' - - '@swc/css' - - '@types/react' - - acorn - - bufferutil - - csso - - debug - - encoding - - esbuild - - lightningcss - - react-native - - redux - - search-insights - - supports-color - - typescript - - uglify-js - - utf-8-validate - - webpack-cli - - docusaurus-theme-openapi@0.7.6(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(@types/react@19.1.13)(acorn@8.15.0)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(redux@4.2.1)(typescript@5.9.2): - dependencies: - '@docusaurus/theme-common': 3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@mdx-js/react': 3.1.1(@types/react@19.1.13)(react@18.3.1) - '@monaco-editor/react': 4.7.0(monaco-editor@0.31.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@reduxjs/toolkit': 1.9.7(react-redux@7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) - buffer: 6.0.3 - clsx: 1.2.1 - crypto-js: 4.2.0 - docusaurus-plugin-openapi: 0.7.6(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) - immer: 9.0.21 - lodash: 4.17.21 - marked: 11.2.0 - monaco-editor: 0.31.1 - postman-code-generators: 1.14.2 - postman-collection: 4.5.0 - prism-react-renderer: 2.4.1(react@18.3.1) - process: 0.11.10 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-magic-dropzone: 1.0.1 - react-redux: 7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - redux-devtools-extension: 2.13.9(redux@4.2.1) - refractor: 4.9.0 - striptags: 3.2.0 - webpack: 5.100.2 - transitivePeerDependencies: - - '@docusaurus/faster' - - '@docusaurus/plugin-content-docs' - - '@parcel/css' - - '@rspack/core' - - '@swc/core' - - '@swc/css' - - '@types/react' - - acorn - - bufferutil - - csso - - debug - - encoding - - esbuild - - lightningcss - - react-native - - redux - - supports-color - - typescript - - uglify-js - - utf-8-validate - - webpack-cli - dom-accessibility-api@0.5.16: {} dom-accessibility-api@0.6.3: {} @@ -18458,7 +18126,7 @@ snapshots: dependencies: is-obj: 2.0.0 - dotenv@17.2.2: {} + dotenv@17.2.3: {} dunder-proto@1.0.1: dependencies: @@ -18476,9 +18144,9 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.207: {} + electron-to-chromium@1.5.233: {} - emoji-regex@10.5.0: {} + emoji-regex@10.6.0: {} emoji-regex@8.0.0: {} @@ -18520,7 +18188,7 @@ snapshots: engine.io@6.6.4: dependencies: '@types/cors': 2.8.19 - '@types/node': 22.18.5 + '@types/node': 22.18.10 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.7.2 @@ -18536,7 +18204,7 @@ snapshots: enhanced-resolve@5.18.3: dependencies: graceful-fs: 4.2.11 - tapable: 2.2.2 + tapable: 2.3.0 entities@2.2.0: {} @@ -18582,8 +18250,6 @@ snapshots: es5-ext: 0.10.64 es6-symbol: 3.1.4 - es6-promise@3.3.1: {} - es6-symbol@3.1.4: dependencies: d: 1.0.2 @@ -18608,7 +18274,7 @@ snapshots: '@types/estree-jsx': 1.0.5 acorn: 8.15.0 esast-util-from-estree: 2.0.0 - vfile-message: 4.0.2 + vfile-message: 4.0.3 esbuild@0.19.12: optionalDependencies: @@ -18636,34 +18302,34 @@ snapshots: '@esbuild/win32-ia32': 0.19.12 '@esbuild/win32-x64': 0.19.12 - esbuild@0.25.9: + esbuild@0.25.10: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.9 - '@esbuild/android-arm': 0.25.9 - '@esbuild/android-arm64': 0.25.9 - '@esbuild/android-x64': 0.25.9 - '@esbuild/darwin-arm64': 0.25.9 - '@esbuild/darwin-x64': 0.25.9 - '@esbuild/freebsd-arm64': 0.25.9 - '@esbuild/freebsd-x64': 0.25.9 - '@esbuild/linux-arm': 0.25.9 - '@esbuild/linux-arm64': 0.25.9 - '@esbuild/linux-ia32': 0.25.9 - '@esbuild/linux-loong64': 0.25.9 - '@esbuild/linux-mips64el': 0.25.9 - '@esbuild/linux-ppc64': 0.25.9 - '@esbuild/linux-riscv64': 0.25.9 - '@esbuild/linux-s390x': 0.25.9 - '@esbuild/linux-x64': 0.25.9 - '@esbuild/netbsd-arm64': 0.25.9 - '@esbuild/netbsd-x64': 0.25.9 - '@esbuild/openbsd-arm64': 0.25.9 - '@esbuild/openbsd-x64': 0.25.9 - '@esbuild/openharmony-arm64': 0.25.9 - '@esbuild/sunos-x64': 0.25.9 - '@esbuild/win32-arm64': 0.25.9 - '@esbuild/win32-ia32': 0.25.9 - '@esbuild/win32-x64': 0.25.9 + '@esbuild/aix-ppc64': 0.25.10 + '@esbuild/android-arm': 0.25.10 + '@esbuild/android-arm64': 0.25.10 + '@esbuild/android-x64': 0.25.10 + '@esbuild/darwin-arm64': 0.25.10 + '@esbuild/darwin-x64': 0.25.10 + '@esbuild/freebsd-arm64': 0.25.10 + '@esbuild/freebsd-x64': 0.25.10 + '@esbuild/linux-arm': 0.25.10 + '@esbuild/linux-arm64': 0.25.10 + '@esbuild/linux-ia32': 0.25.10 + '@esbuild/linux-loong64': 0.25.10 + '@esbuild/linux-mips64el': 0.25.10 + '@esbuild/linux-ppc64': 0.25.10 + '@esbuild/linux-riscv64': 0.25.10 + '@esbuild/linux-s390x': 0.25.10 + '@esbuild/linux-x64': 0.25.10 + '@esbuild/netbsd-arm64': 0.25.10 + '@esbuild/netbsd-x64': 0.25.10 + '@esbuild/openbsd-arm64': 0.25.10 + '@esbuild/openbsd-x64': 0.25.10 + '@esbuild/openharmony-arm64': 0.25.10 + '@esbuild/sunos-x64': 0.25.10 + '@esbuild/win32-arm64': 0.25.10 + '@esbuild/win32-ia32': 0.25.10 + '@esbuild/win32-x64': 0.25.10 escalade@3.2.0: {} @@ -18686,67 +18352,60 @@ snapshots: source-map: 0.6.1 optional: true - eslint-config-prettier@10.1.8(eslint@9.35.0(jiti@2.5.1)): + eslint-config-prettier@10.1.8(eslint@9.37.0(jiti@2.6.1)): dependencies: - eslint: 9.35.0(jiti@2.5.1) + eslint: 9.37.0(jiti@2.6.1) - eslint-p@0.26.0(jiti@2.5.1): - dependencies: - eslint: 9.35.0(jiti@2.5.1) - transitivePeerDependencies: - - jiti - - supports-color - - eslint-plugin-compat@6.0.2(eslint@9.35.0(jiti@2.5.1)): + eslint-plugin-compat@6.0.2(eslint@9.37.0(jiti@2.6.1)): dependencies: '@mdn/browser-compat-data': 5.7.6 ast-metadata-inferer: 0.8.1 - browserslist: 4.25.3 - caniuse-lite: 1.0.30001735 - eslint: 9.35.0(jiti@2.5.1) + browserslist: 4.26.3 + caniuse-lite: 1.0.30001749 + eslint: 9.37.0(jiti@2.6.1) find-up: 5.0.0 globals: 15.15.0 lodash.memoize: 4.1.2 - semver: 7.7.2 + semver: 7.7.3 - eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.35.0(jiti@2.5.1)))(eslint@9.35.0(jiti@2.5.1))(prettier@3.6.2): + eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1))(prettier@3.6.2): dependencies: - eslint: 9.35.0(jiti@2.5.1) + eslint: 9.37.0(jiti@2.6.1) prettier: 3.6.2 prettier-linter-helpers: 1.0.0 synckit: 0.11.11 optionalDependencies: '@types/eslint': 9.6.1 - eslint-config-prettier: 10.1.8(eslint@9.35.0(jiti@2.5.1)) + eslint-config-prettier: 10.1.8(eslint@9.37.0(jiti@2.6.1)) - eslint-plugin-svelte@3.12.3(eslint@9.35.0(jiti@2.5.1))(svelte@5.38.10): + eslint-plugin-svelte@3.12.4(eslint@9.37.0(jiti@2.6.1))(svelte@5.39.11): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0(jiti@2.5.1)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0(jiti@2.6.1)) '@jridgewell/sourcemap-codec': 1.5.5 - eslint: 9.35.0(jiti@2.5.1) + eslint: 9.37.0(jiti@2.6.1) esutils: 2.0.3 globals: 16.4.0 known-css-properties: 0.37.0 postcss: 8.5.6 postcss-load-config: 3.1.4(postcss@8.5.6) postcss-safe-parser: 7.0.1(postcss@8.5.6) - semver: 7.7.2 - svelte-eslint-parser: 1.3.2(svelte@5.38.10) + semver: 7.7.3 + svelte-eslint-parser: 1.3.3(svelte@5.39.11) optionalDependencies: - svelte: 5.38.10 + svelte: 5.39.11 transitivePeerDependencies: - ts-node - eslint-plugin-unicorn@60.0.0(eslint@9.35.0(jiti@2.5.1)): + eslint-plugin-unicorn@60.0.0(eslint@9.37.0(jiti@2.6.1)): dependencies: '@babel/helper-validator-identifier': 7.27.1 - '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0(jiti@2.5.1)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0(jiti@2.6.1)) '@eslint/plugin-kit': 0.3.5 change-case: 5.4.4 ci-info: 4.3.0 clean-regexp: 1.0.0 core-js-compat: 3.45.0 - eslint: 9.35.0(jiti@2.5.1) + eslint: 9.37.0(jiti@2.6.1) esquery: 1.6.0 find-up-simple: 1.0.1 globals: 16.4.0 @@ -18756,7 +18415,29 @@ snapshots: pluralize: 8.0.0 regexp-tree: 0.1.27 regjsparser: 0.12.0 - semver: 7.7.2 + semver: 7.7.3 + strip-indent: 4.0.0 + + eslint-plugin-unicorn@61.0.2(eslint@9.37.0(jiti@2.6.1)): + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0(jiti@2.6.1)) + '@eslint/plugin-kit': 0.3.5 + change-case: 5.4.4 + ci-info: 4.3.0 + clean-regexp: 1.0.0 + core-js-compat: 3.45.0 + eslint: 9.37.0(jiti@2.6.1) + esquery: 1.6.0 + find-up-simple: 1.0.1 + globals: 16.4.0 + indent-string: 5.0.0 + is-builtin-module: 5.0.0 + jsesc: 3.1.0 + pluralize: 8.0.0 + regexp-tree: 0.1.27 + regjsparser: 0.12.0 + semver: 7.7.3 strip-indent: 4.0.0 eslint-scope@5.1.1: @@ -18773,16 +18454,16 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.35.0(jiti@2.5.1): + eslint@9.37.0(jiti@2.6.1): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0(jiti@2.5.1)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0(jiti@2.6.1)) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.21.0 - '@eslint/config-helpers': 0.3.1 - '@eslint/core': 0.15.2 + '@eslint/config-helpers': 0.4.0 + '@eslint/core': 0.16.0 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.35.0 - '@eslint/plugin-kit': 0.3.5 + '@eslint/js': 9.37.0 + '@eslint/plugin-kit': 0.4.0 '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 @@ -18811,7 +18492,7 @@ snapshots: natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: - jiti: 2.5.1 + jiti: 2.6.1 transitivePeerDependencies: - supports-color @@ -18870,7 +18551,7 @@ snapshots: dependencies: '@types/estree-jsx': 1.0.5 astring: 1.9.0 - source-map: 0.7.4 + source-map: 0.7.6 estree-util-value-to-estree@3.4.0: dependencies: @@ -18897,7 +18578,7 @@ snapshots: eval@0.1.8: dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 require-like: 0.1.2 event-emitter@0.3.5: @@ -18909,6 +18590,10 @@ snapshots: eventemitter3@4.0.7: {} + events-universal@1.0.1: + dependencies: + bare-events: 2.7.0 + events@3.3.0: {} execa@5.1.1: @@ -19060,7 +18745,7 @@ snapshots: fast-safe-stringify@2.1.1: {} - fast-uri@3.0.6: {} + fast-uri@3.1.0: {} fast-xml-parser@5.2.5: dependencies: @@ -19096,11 +18781,11 @@ snapshots: dependencies: flat-cache: 4.0.1 - file-loader@6.2.0(webpack@5.100.2): + file-loader@6.2.0(webpack@5.102.1): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.100.2 + webpack: 5.102.1 file-source@0.6.1: dependencies: @@ -19115,8 +18800,6 @@ snapshots: transitivePeerDependencies: - supports-color - file-type@3.9.0: {} - fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -19182,8 +18865,6 @@ snapshots: follow-redirects@1.15.9: {} - foreach@2.0.6: {} - foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -19201,8 +18882,8 @@ snapshots: minimatch: 3.1.2 node-abort-controller: 3.1.1 schema-utils: 3.3.0 - semver: 7.7.2 - tapable: 2.2.2 + semver: 7.7.3 + tapable: 2.3.0 typescript: 5.8.3 webpack: 5.100.2(@swc/core@1.13.5(@swc/helpers@0.5.17)) @@ -19218,13 +18899,6 @@ snapshots: format@0.2.2: {} - formidable@2.1.5: - dependencies: - '@paralleldrive/cuid2': 2.2.2 - dezalgo: 1.0.4 - once: 1.4.0 - qs: 6.14.0 - formidable@3.5.4: dependencies: '@paralleldrive/cuid2': 2.2.2 @@ -19336,8 +19010,6 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - get-stdin@5.0.1: {} - get-stream@6.0.1: {} github-slugger@1.5.0: {} @@ -19352,6 +19024,10 @@ snapshots: dependencies: is-glob: 4.0.3 + glob-to-regex.js@1.2.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + glob-to-regexp@0.4.1: {} glob@10.4.5: @@ -19436,10 +19112,6 @@ snapshots: graphemer@1.4.0: {} - graphlib@2.1.8: - dependencies: - lodash: 4.17.21 - gray-matter@4.0.3: dependencies: js-yaml: 3.14.1 @@ -19464,16 +19136,14 @@ snapshots: optionalDependencies: uglify-js: 3.19.3 - happy-dom@18.0.1: + happy-dom@20.0.2: dependencies: - '@types/node': 20.19.2 + '@types/node': 20.19.21 '@types/whatwg-mimetype': 3.0.2 whatwg-mimetype: 3.0.0 has-flag@4.0.0: {} - has-own-prop@2.0.0: {} - has-property-descriptors@1.0.2: dependencies: es-define-property: 1.0.1 @@ -19518,10 +19188,6 @@ snapshots: hast-util-parse-selector@2.2.5: {} - hast-util-parse-selector@3.1.1: - dependencies: - '@types/hast': 2.3.10 - hast-util-parse-selector@4.0.0: dependencies: '@types/hast': 3.0.4 @@ -19596,7 +19262,7 @@ snapshots: space-separated-tokens: 2.0.2 style-to-js: 1.1.17 unist-util-position: 5.0.0 - vfile-message: 4.0.2 + vfile-message: 4.0.3 transitivePeerDependencies: - supports-color @@ -19632,14 +19298,6 @@ snapshots: property-information: 5.6.0 space-separated-tokens: 1.1.5 - hastscript@7.2.0: - dependencies: - '@types/hast': 2.3.10 - comma-separated-tokens: 2.0.3 - hast-util-parse-selector: 3.1.1 - property-information: 6.5.0 - space-separated-tokens: 2.0.2 - hastscript@9.0.1: dependencies: '@types/hast': 3.0.4 @@ -19650,6 +19308,8 @@ snapshots: he@1.2.0: {} + highlight.js@11.11.1: {} + history@4.10.1: dependencies: '@babel/runtime': 7.28.4 @@ -19685,8 +19345,6 @@ snapshots: whatwg-encoding: 3.1.1 optional: true - html-entities@2.6.0: {} - html-escaper@2.0.2: {} html-minifier-terser@6.1.0: @@ -19721,15 +19379,15 @@ snapshots: html-void-elements@3.0.0: {} - html-webpack-plugin@5.6.3(webpack@5.100.2): + html-webpack-plugin@5.6.3(webpack@5.102.1): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 lodash: 4.17.21 pretty-error: 4.0.0 - tapable: 2.2.2 + tapable: 2.3.0 optionalDependencies: - webpack: 5.100.2 + webpack: 5.102.1 htmlparser2@6.1.0: dependencies: @@ -19815,10 +19473,6 @@ snapshots: transitivePeerDependencies: - debug - http-reasons@0.1.0: {} - - http2-client@1.3.5: {} - http2-wrapper@2.2.1: dependencies: quick-lru: 5.1.1 @@ -19840,6 +19494,8 @@ snapshots: human-signals@2.1.0: {} + hyperdyperid@1.2.0: {} + i18n-iso-countries@7.14.0: dependencies: diacritics: 1.3.0 @@ -19872,14 +19528,12 @@ snapshots: immediate@3.3.0: {} - immer@9.0.21: {} - import-fresh@3.3.1: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 - import-in-the-middle@1.14.2: + import-in-the-middle@1.14.4: dependencies: acorn: 8.15.0 acorn-import-attributes: 1.9.5(acorn@8.15.0) @@ -19911,9 +19565,9 @@ snapshots: inline-style-parser@0.2.4: {} - inquirer@8.2.7(@types/node@22.18.5): + inquirer@8.2.7(@types/node@22.18.10): dependencies: - '@inquirer/external-editor': 1.0.2(@types/node@22.18.5) + '@inquirer/external-editor': 1.0.2(@types/node@22.18.10) ansi-escapes: 4.3.2 chalk: 4.1.2 cli-cursor: 3.1.0 @@ -19933,22 +19587,20 @@ snapshots: internmap@2.0.3: {} - interpret@1.4.0: {} - - intl-messageformat@10.7.16: + intl-messageformat@10.7.17: dependencies: - '@formatjs/ecma402-abstract': 2.3.4 + '@formatjs/ecma402-abstract': 2.3.5 '@formatjs/fast-memoize': 2.2.7 - '@formatjs/icu-messageformat-parser': 2.11.2 + '@formatjs/icu-messageformat-parser': 2.11.3 tslib: 2.8.1 invariant@2.2.4: dependencies: loose-envify: 1.4.0 - ioredis@5.7.0: + ioredis@5.8.1: dependencies: - '@ioredis/commands': 1.3.0 + '@ioredis/commands': 1.4.0 cluster-key-slot: 1.1.2 debug: 4.4.3 denque: 2.1.0 @@ -19975,8 +19627,6 @@ snapshots: is-arrayish@0.2.1: {} - is-arrayish@0.3.2: {} - is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 @@ -19999,6 +19649,8 @@ snapshots: is-docker@2.2.1: {} + is-docker@3.0.0: {} + is-extendable@0.1.1: {} is-extglob@2.1.1: {} @@ -20011,6 +19663,10 @@ snapshots: is-hexadecimal@2.0.1: {} + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + is-installed-globally@0.4.0: dependencies: global-dirs: 3.0.1 @@ -20020,6 +19676,8 @@ snapshots: is-interactive@2.0.0: {} + is-network-error@1.3.0: {} + is-npm@6.0.0: {} is-number@7.0.0: {} @@ -20071,6 +19729,10 @@ snapshots: dependencies: is-docker: 2.2.1 + is-wsl@3.1.0: + dependencies: + is-inside-container: 1.0.0 + is-yarn-global@0.4.1: {} isarray@0.0.1: {} @@ -20093,7 +19755,7 @@ snapshots: istanbul-lib-source-maps@5.0.6: dependencies: - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 debug: 4.4.3 istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: @@ -20119,7 +19781,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.18.5 + '@types/node': 22.18.10 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -20127,13 +19789,13 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -20142,7 +19804,7 @@ snapshots: jiti@2.4.2: {} - jiti@2.5.1: {} + jiti@2.6.1: {} joi@17.13.3: dependencies: @@ -20273,38 +19935,6 @@ snapshots: json-parse-even-better-errors@2.3.1: {} - json-pointer@0.6.2: - dependencies: - foreach: 2.0.6 - - json-refs@3.0.15: - dependencies: - commander: 4.1.1 - graphlib: 2.1.8 - js-yaml: 3.14.1 - lodash: 4.17.21 - native-promise-only: 0.8.1 - path-loader: 1.0.12 - slash: 3.0.0 - uri-js: 4.4.1 - transitivePeerDependencies: - - supports-color - - json-schema-compare@0.2.2: - dependencies: - lodash: 4.17.21 - - json-schema-merge-allof@0.8.1: - dependencies: - compute-lcm: 1.1.2 - json-schema-compare: 0.2.2 - lodash: 4.17.21 - - json-schema-resolve-allof@1.5.0: - dependencies: - get-stdin: 5.0.1 - lodash: 4.17.21 - json-schema-traverse@0.4.1: {} json-schema-traverse@1.0.0: {} @@ -20370,9 +20000,10 @@ snapshots: type-is: 2.0.1 vary: 1.1.2 - kysely-postgres-js@2.0.0(kysely@0.28.2)(postgres@3.4.7): + kysely-postgres-js@3.0.0(kysely@0.28.2)(postgres@3.4.7): dependencies: kysely: 0.28.2 + optionalDependencies: postgres: 3.4.7 kysely@0.28.2: {} @@ -20433,7 +20064,7 @@ snapshots: lightningcss@1.30.1: dependencies: - detect-libc: 2.1.0 + detect-libc: 2.1.2 optionalDependencies: lightningcss-darwin-arm64: 1.30.1 lightningcss-darwin-x64: 1.30.1 @@ -20452,13 +20083,11 @@ snapshots: lines-and-columns@1.2.4: {} - liquid-json@0.3.1: {} - load-esm@1.0.2: {} load-tsconfig@0.2.5: {} - loader-runner@4.3.0: {} + loader-runner@4.3.1: {} loader-utils@2.0.4: dependencies: @@ -20545,8 +20174,6 @@ snapshots: lunr@2.3.9: {} - luxon@3.6.1: {} - luxon@3.7.2: {} lz-string@1.5.0: {} @@ -20571,7 +20198,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.7.2 + semver: 7.7.3 make-fetch-happen@14.0.3: dependencies: @@ -20614,7 +20241,7 @@ snapshots: tinyqueue: 2.0.3 vt-pbf: 3.1.3 - maplibre-gl@5.7.1: + maplibre-gl@5.8.0: dependencies: '@mapbox/geojson-rewind': 0.5.2 '@mapbox/jsonlint-lines-primitives': 2.0.2 @@ -20623,7 +20250,7 @@ snapshots: '@mapbox/unitbezier': 0.0.1 '@mapbox/vector-tile': 2.0.4 '@mapbox/whoots-js': 3.1.0 - '@maplibre/maplibre-gl-style-spec': 23.3.0 + '@maplibre/maplibre-gl-style-spec': 24.2.0 '@maplibre/vt-pbf': 4.0.3 '@types/geojson': 7946.0.16 '@types/geojson-vt': 3.2.5 @@ -20649,16 +20276,14 @@ snapshots: markdown-table@3.0.4: {} - marked@11.2.0: {} - marked@7.0.4: {} math-intrinsics@1.1.0: {} - md-to-react-email@5.0.5(react@19.1.1): + md-to-react-email@5.0.5(react@19.2.0): dependencies: marked: 7.0.4 - react: 19.1.1 + react: 19.2.0 mdast-util-directive@3.1.0: dependencies: @@ -20790,7 +20415,7 @@ snapshots: parse-entities: 4.0.2 stringify-entities: 4.0.4 unist-util-stringify-position: 4.0.0 - vfile-message: 4.0.2 + vfile-message: 4.0.3 transitivePeerDependencies: - supports-color @@ -20860,6 +20485,15 @@ snapshots: dependencies: fs-monkey: 1.1.0 + memfs@4.49.0: + dependencies: + '@jsonjoy.com/json-pack': 1.20.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + glob-to-regex.js: 1.2.0(tslib@2.8.1) + thingies: 2.5.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + memoizee@0.4.17: dependencies: d: 1.0.2 @@ -20997,7 +20631,7 @@ snapshots: micromark-util-events-to-acorn: 2.0.3 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - vfile-message: 4.0.2 + vfile-message: 4.0.3 micromark-extension-mdx-md@2.0.0: dependencies: @@ -21013,7 +20647,7 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 unist-util-position-from-estree: 2.0.0 - vfile-message: 4.0.2 + vfile-message: 4.0.3 micromark-extension-mdxjs@3.0.0: dependencies: @@ -21049,7 +20683,7 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 unist-util-position-from-estree: 2.0.0 - vfile-message: 4.0.2 + vfile-message: 4.0.3 micromark-factory-space@1.1.0: dependencies: @@ -21121,7 +20755,7 @@ snapshots: estree-util-visit: 2.0.0 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - vfile-message: 4.0.2 + vfile-message: 4.0.3 micromark-util-html-tag-name@2.0.1: {} @@ -21187,10 +20821,6 @@ snapshots: mime-db@1.54.0: {} - mime-format@2.0.1: - dependencies: - charset: 1.0.1 - mime-types@2.1.18: dependencies: mime-db: 1.33.0 @@ -21220,11 +20850,11 @@ snapshots: min-indent@1.0.1: {} - mini-css-extract-plugin@2.9.2(webpack@5.100.2): + mini-css-extract-plugin@2.9.2(webpack@5.102.1): dependencies: - schema-utils: 4.3.2 - tapable: 2.2.2 - webpack: 5.100.2 + schema-utils: 4.3.3 + tapable: 2.3.0 + webpack: 5.102.1 minimalistic-assert@1.0.1: {} @@ -21254,7 +20884,7 @@ snapshots: dependencies: minipass: 7.1.2 minipass-sized: 1.0.3 - minizlib: 3.0.2 + minizlib: 3.1.0 optionalDependencies: encoding: 0.1.13 @@ -21283,7 +20913,7 @@ snapshots: minipass: 3.3.6 yallist: 4.0.0 - minizlib@3.0.2: + minizlib@3.1.0: dependencies: minipass: 7.1.2 @@ -21297,8 +20927,6 @@ snapshots: mkdirp@1.0.4: {} - mkdirp@3.0.1: {} - mnemonist@0.40.3: dependencies: obliterator: 2.0.5 @@ -21307,8 +20935,6 @@ snapshots: module-details-from-path@1.0.4: {} - monaco-editor@0.31.1: {} - moo@0.5.2: {} mri@1.2.0: {} @@ -21369,8 +20995,6 @@ snapshots: nanoid@5.1.5: {} - native-promise-only@0.8.1: {} - natural-compare@1.4.0: {} nearley@2.20.1: @@ -21388,18 +21012,16 @@ snapshots: neo-async@2.6.2: {} - neotraverse@0.6.15: {} - - nest-commander@3.19.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@types/inquirer@8.2.11)(@types/node@22.18.5)(typescript@5.9.2): + nest-commander@3.20.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@types/inquirer@8.2.11)(@types/node@22.18.10)(typescript@5.9.3): dependencies: '@fig/complete-commander': 3.2.0(commander@11.1.0) - '@golevelup/nestjs-discovery': 4.0.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6) + '@golevelup/nestjs-discovery': 5.0.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6) '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@types/inquirer': 8.2.11 commander: 11.1.0 - cosmiconfig: 8.3.6(typescript@5.9.2) - inquirer: 8.2.7(@types/node@22.18.5) + cosmiconfig: 8.3.6(typescript@5.9.3) + inquirer: 8.2.7(@types/node@22.18.10) transitivePeerDependencies: - '@types/node' - typescript @@ -21452,10 +21074,6 @@ snapshots: emojilib: 2.4.0 skin-tone: 2.0.0 - node-fetch-h2@2.3.0: - dependencies: - http2-client: 1.3.5 - node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 @@ -21471,7 +21089,7 @@ snapshots: node-gyp-build-optional-packages@5.2.2: dependencies: - detect-libc: 2.1.0 + detect-libc: 2.1.2 optional: true node-gyp-build@4.8.4: {} @@ -21484,20 +21102,16 @@ snapshots: make-fetch-happen: 14.0.3 nopt: 8.1.0 proc-log: 5.0.0 - semver: 7.7.2 - tar: 7.4.3 + semver: 7.7.3 + tar: 7.5.1 tinyglobby: 0.2.15 which: 5.0.0 transitivePeerDependencies: - supports-color - node-readfiles@0.2.0: - dependencies: - es6-promise: 3.3.1 + node-releases@2.0.23: {} - node-releases@2.0.19: {} - - nodemailer@7.0.6: {} + nodemailer@7.0.9: {} nopt@1.0.10: dependencies: @@ -21538,11 +21152,11 @@ snapshots: dependencies: boolbase: 1.0.0 - null-loader@4.0.1(webpack@5.100.2): + null-loader@4.0.1(webpack@5.102.1): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.100.2 + webpack: 5.102.1 nwsapi@2.2.22: optional: true @@ -21555,47 +21169,7 @@ snapshots: pkg-types: 2.3.0 tinyexec: 0.3.2 - oas-kit-common@1.0.8: - dependencies: - fast-safe-stringify: 2.1.1 - - oas-linter@3.2.2: - dependencies: - '@exodus/schemasafe': 1.3.0 - should: 13.2.3 - yaml: 1.10.2 - - oas-resolver-browser@2.5.6: - dependencies: - node-fetch-h2: 2.3.0 - oas-kit-common: 1.0.8 - path-browserify: 1.0.1 - reftools: 1.1.9 - yaml: 1.10.2 - yargs: 17.7.2 - - oas-resolver@2.5.6: - dependencies: - node-fetch-h2: 2.3.0 - oas-kit-common: 1.0.8 - reftools: 1.1.9 - yaml: 1.10.2 - yargs: 17.7.2 - - oas-schema-walker@1.1.5: {} - - oas-validator@5.0.8: - dependencies: - call-me-maybe: 1.0.2 - oas-kit-common: 1.0.8 - oas-linter: 3.2.2 - oas-resolver: 2.5.6 - oas-schema-walker: 1.1.5 - reftools: 1.1.9 - should: 13.2.3 - yaml: 1.10.2 - - oauth4webapi@3.8.1: {} + oauth4webapi@3.8.2: {} object-assign@4.1.1: {} @@ -21651,40 +21225,25 @@ snapshots: dependencies: mimic-function: 5.0.1 + open@10.2.0: + dependencies: + default-browser: 5.2.1 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + wsl-utils: 0.1.0 + open@8.4.2: dependencies: define-lazy-prop: 2.0.0 is-docker: 2.2.1 is-wsl: 2.2.0 - openapi-to-postmanv2@4.25.0(encoding@0.1.13): - dependencies: - ajv: 8.11.0 - ajv-draft-04: 1.0.0(ajv@8.11.0) - ajv-formats: 2.1.1(ajv@8.11.0) - async: 3.2.4 - commander: 2.20.3 - graphlib: 2.1.8 - js-yaml: 4.1.0 - json-pointer: 0.6.2 - json-schema-merge-allof: 0.8.1 - lodash: 4.17.21 - neotraverse: 0.6.15 - oas-resolver-browser: 2.5.6 - object-hash: 3.0.0 - path-browserify: 1.0.1 - postman-collection: 4.5.0 - swagger2openapi: 7.0.8(encoding@0.1.13) - yaml: 1.10.2 - transitivePeerDependencies: - - encoding - opener@1.5.2: {} - openid-client@6.8.0: + openid-client@6.8.1: dependencies: jose: 6.1.0 - oauth4webapi: 3.8.1 + oauth4webapi: 3.8.2 optionator@0.9.4: dependencies: @@ -21758,9 +21317,10 @@ snapshots: eventemitter3: 4.0.7 p-timeout: 3.2.0 - p-retry@4.6.2: + p-retry@6.2.1: dependencies: - '@types/retry': 0.12.0 + '@types/retry': 0.12.2 + is-network-error: 1.3.0 retry: 0.13.1 p-timeout@3.2.0: @@ -21776,7 +21336,7 @@ snapshots: got: 12.6.1 registry-auth-token: 5.1.0 registry-url: 6.0.1 - semver: 7.7.2 + semver: 7.7.3 param-case@3.0.4: dependencies: @@ -21831,8 +21391,6 @@ snapshots: no-case: 3.0.4 tslib: 2.8.1 - path-browserify@1.0.1: {} - path-exists@4.0.0: {} path-exists@5.0.0: {} @@ -21843,13 +21401,6 @@ snapshots: path-key@3.1.1: {} - path-loader@1.0.12: - dependencies: - native-promise-only: 0.8.1 - superagent: 7.1.6 - transitivePeerDependencies: - - supports-color - path-parse@1.0.7: {} path-scurry@1.11.1: @@ -21881,11 +21432,6 @@ snapshots: path-type@4.0.0: {} - path@0.12.7: - dependencies: - process: 0.11.10 - util: 0.10.4 - pathe@2.0.3: {} pathval@2.0.1: {} @@ -21958,11 +21504,11 @@ snapshots: exsolve: 1.0.7 pathe: 2.0.3 - playwright-core@1.55.0: {} + playwright-core@1.56.0: {} - playwright@1.55.0: + playwright@1.56.0: dependencies: - playwright-core: 1.55.0 + playwright-core: 1.56.0 optionalDependencies: fsevents: 2.3.2 @@ -22024,7 +21570,7 @@ snapshots: postcss-colormin@6.1.0(postcss@8.5.6): dependencies: - browserslist: 4.25.3 + browserslist: 4.26.3 caniuse-api: 3.0.0 colord: 2.9.3 postcss: 8.5.6 @@ -22032,7 +21578,7 @@ snapshots: postcss-convert-values@6.1.0(postcss@8.5.6): dependencies: - browserslist: 4.25.3 + browserslist: 4.26.3 postcss: 8.5.6 postcss-value-parser: 4.2.0 @@ -22146,20 +21692,21 @@ snapshots: optionalDependencies: postcss: 8.5.6 - postcss-load-config@4.0.2(postcss@8.5.6): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6)(yaml@2.8.1): dependencies: lilconfig: 3.1.3 - yaml: 2.8.1 optionalDependencies: - postcss: 8.5.6 - - postcss-loader@7.3.4(postcss@8.5.6)(typescript@5.9.2)(webpack@5.100.2): - dependencies: - cosmiconfig: 8.3.6(typescript@5.9.2) jiti: 1.21.7 postcss: 8.5.6 - semver: 7.7.2 - webpack: 5.100.2 + yaml: 2.8.1 + + postcss-loader@7.3.4(postcss@8.5.6)(typescript@5.9.3)(webpack@5.102.1): + dependencies: + cosmiconfig: 8.3.6(typescript@5.9.3) + jiti: 1.21.7 + postcss: 8.5.6 + semver: 7.7.3 + webpack: 5.102.1 transitivePeerDependencies: - typescript @@ -22182,7 +21729,7 @@ snapshots: postcss-merge-rules@6.1.1(postcss@8.5.6): dependencies: - browserslist: 4.25.3 + browserslist: 4.26.3 caniuse-api: 3.0.0 cssnano-utils: 4.0.2(postcss@8.5.6) postcss: 8.5.6 @@ -22202,7 +21749,7 @@ snapshots: postcss-minify-params@6.1.0(postcss@8.5.6): dependencies: - browserslist: 4.25.3 + browserslist: 4.26.3 cssnano-utils: 4.0.2(postcss@8.5.6) postcss: 8.5.6 postcss-value-parser: 4.2.0 @@ -22276,7 +21823,7 @@ snapshots: postcss-normalize-unicode@6.1.0(postcss@8.5.6): dependencies: - browserslist: 4.25.3 + browserslist: 4.26.3 postcss: 8.5.6 postcss-value-parser: 4.2.0 @@ -22350,7 +21897,7 @@ snapshots: '@csstools/postcss-trigonometric-functions': 4.0.9(postcss@8.5.6) '@csstools/postcss-unset-value': 4.0.0(postcss@8.5.6) autoprefixer: 10.4.21(postcss@8.5.6) - browserslist: 4.25.3 + browserslist: 4.26.3 css-blank-pseudo: 7.0.1(postcss@8.5.6) css-has-pseudo: 7.0.2(postcss@8.5.6) css-prefers-color-scheme: 10.0.0(postcss@8.5.6) @@ -22394,7 +21941,7 @@ snapshots: postcss-reduce-initial@6.1.0(postcss@8.5.6): dependencies: - browserslist: 4.25.3 + browserslist: 4.26.3 caniuse-api: 3.0.0 postcss: 8.5.6 @@ -22470,33 +22017,6 @@ snapshots: postgres@3.4.7: {} - postman-code-generators@1.14.2: - dependencies: - async: 3.2.2 - detect-package-manager: 3.0.2 - lodash: 4.17.21 - path: 0.12.7 - postman-collection: 4.5.0 - shelljs: 0.8.5 - - postman-collection@4.5.0: - dependencies: - '@faker-js/faker': 5.5.3 - file-type: 3.9.0 - http-reasons: 0.1.0 - iconv-lite: 0.6.3 - liquid-json: 0.3.1 - lodash: 4.17.21 - mime-format: 2.0.1 - mime-types: 2.1.35 - postman-url-encoder: 3.0.5 - semver: 7.6.3 - uuid: 8.3.2 - - postman-url-encoder@3.0.5: - dependencies: - punycode: 2.3.1 - potpack@1.0.2: {} potpack@2.1.0: {} @@ -22507,19 +22027,19 @@ snapshots: dependencies: fast-diff: 1.3.0 - prettier-plugin-organize-imports@4.2.0(prettier@3.6.2)(typescript@5.9.2): + prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.3): dependencies: prettier: 3.6.2 - typescript: 5.9.2 + typescript: 5.9.3 prettier-plugin-sort-json@4.1.1(prettier@3.6.2): dependencies: prettier: 3.6.2 - prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.38.10): + prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.39.11): dependencies: prettier: 3.6.2 - svelte: 5.38.10 + svelte: 5.39.11 prettier@3.6.2: {} @@ -22598,7 +22118,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 22.18.5 + '@types/node': 22.18.10 long: 5.3.2 protocol-buffers-schema@3.6.0: {} @@ -22682,11 +22202,11 @@ snapshots: iconv-lite: 0.7.0 unpipe: 1.0.0 - raw-loader@4.0.2(webpack@5.100.2): + raw-loader@4.0.2(webpack@5.102.1): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.100.2 + webpack: 5.102.1 rc@1.2.8: dependencies: @@ -22701,19 +22221,19 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 - react-dom@19.1.1(react@19.1.1): + react-dom@19.2.0(react@19.2.0): dependencies: - react: 19.1.1 - scheduler: 0.26.0 + react: 19.2.0 + scheduler: 0.27.0 - react-email@4.2.11: + react-email@4.3.0: dependencies: '@babel/parser': 7.28.4 '@babel/traverse': 7.28.4 chokidar: 4.0.3 commander: 13.1.0 debounce: 2.2.0 - esbuild: 0.25.9 + esbuild: 0.25.10 glob: 11.0.3 jiti: 2.4.2 log-symbols: 7.0.1 @@ -22739,30 +22259,16 @@ snapshots: dependencies: react: 18.3.1 - react-loadable-ssr-addon-v5-slorber@1.0.1(@docusaurus/react-loadable@6.0.0(react@18.3.1))(webpack@5.100.2): + react-loadable-ssr-addon-v5-slorber@1.0.1(@docusaurus/react-loadable@6.0.0(react@18.3.1))(webpack@5.102.1): dependencies: '@babel/runtime': 7.28.4 react-loadable: '@docusaurus/react-loadable@6.0.0(react@18.3.1)' - webpack: 5.100.2 - - react-magic-dropzone@1.0.1: {} + webpack: 5.102.1 react-promise-suspense@0.3.4: dependencies: fast-deep-equal: 2.0.1 - react-redux@7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.28.4 - '@types/react-redux': 7.1.34 - hoist-non-react-statics: 3.3.2 - loose-envify: 1.4.0 - prop-types: 15.8.1 - react: 18.3.1 - react-is: 17.0.2 - optionalDependencies: - react-dom: 18.3.1(react@18.3.1) - react-router-config@5.1.1(react-router@5.3.4(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.28.4 @@ -22797,7 +22303,7 @@ snapshots: dependencies: loose-envify: 1.4.0 - react@19.1.1: {} + react@19.2.0: {} read-cache@1.0.0: dependencies: @@ -22837,25 +22343,20 @@ snapshots: readdirp@4.1.2: {} - rechoir@0.6.2: - dependencies: - resolve: 1.22.10 - recma-build-jsx@1.0.0: dependencies: '@types/estree': 1.0.8 estree-util-build-jsx: 3.0.1 vfile: 6.0.3 - recma-jsx@1.0.0(acorn@8.15.0): + recma-jsx@1.0.1(acorn@8.15.0): dependencies: + acorn: 8.15.0 acorn-jsx: 5.3.2(acorn@8.15.0) estree-util-to-js: 2.0.0 recma-parse: 1.0.0 recma-stringify: 1.0.0 unified: 11.0.5 - transitivePeerDependencies: - - acorn recma-parse@1.0.0: dependencies: @@ -22882,29 +22383,8 @@ snapshots: dependencies: redis-errors: 1.2.0 - redux-devtools-extension@2.13.9(redux@4.2.1): - dependencies: - redux: 4.2.1 - - redux-thunk@2.4.2(redux@4.2.1): - dependencies: - redux: 4.2.1 - - redux@4.2.1: - dependencies: - '@babel/runtime': 7.28.4 - reflect-metadata@0.2.2: {} - refractor@4.9.0: - dependencies: - '@types/hast': 2.3.10 - '@types/prismjs': 1.26.5 - hastscript: 7.2.0 - parse-entities: 4.0.2 - - reftools@1.1.9: {} - regenerate-unicode-properties@10.2.0: dependencies: regenerate: 1.4.2 @@ -22994,7 +22474,7 @@ snapshots: transitivePeerDependencies: - supports-color - remark-mdx@3.1.0: + remark-mdx@3.1.1: dependencies: mdast-util-mdx: 3.0.0 micromark-extension-mdxjs: 3.0.0 @@ -23038,11 +22518,10 @@ snapshots: require-from-string@2.0.2: {} - require-in-the-middle@7.5.2: + require-in-the-middle@8.0.0: dependencies: debug: 4.4.3 module-details-from-path: 1.0.4 - resolve: 1.22.10 transitivePeerDependencies: - supports-color @@ -23052,8 +22531,6 @@ snapshots: requires-port@1.0.0: {} - reselect@4.1.8: {} - resolve-alpn@1.2.1: {} resolve-from@4.0.0: {} @@ -23103,40 +22580,41 @@ snapshots: robust-predicates@3.0.2: {} - rollup-plugin-visualizer@6.0.3(rollup@4.50.1): + rollup-plugin-visualizer@6.0.4(rollup@4.52.4): dependencies: open: 8.4.2 picomatch: 4.0.3 - source-map: 0.7.4 + source-map: 0.7.6 yargs: 17.7.2 optionalDependencies: - rollup: 4.50.1 + rollup: 4.52.4 - rollup@4.50.1: + rollup@4.52.4: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.50.1 - '@rollup/rollup-android-arm64': 4.50.1 - '@rollup/rollup-darwin-arm64': 4.50.1 - '@rollup/rollup-darwin-x64': 4.50.1 - '@rollup/rollup-freebsd-arm64': 4.50.1 - '@rollup/rollup-freebsd-x64': 4.50.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.50.1 - '@rollup/rollup-linux-arm-musleabihf': 4.50.1 - '@rollup/rollup-linux-arm64-gnu': 4.50.1 - '@rollup/rollup-linux-arm64-musl': 4.50.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.50.1 - '@rollup/rollup-linux-ppc64-gnu': 4.50.1 - '@rollup/rollup-linux-riscv64-gnu': 4.50.1 - '@rollup/rollup-linux-riscv64-musl': 4.50.1 - '@rollup/rollup-linux-s390x-gnu': 4.50.1 - '@rollup/rollup-linux-x64-gnu': 4.50.1 - '@rollup/rollup-linux-x64-musl': 4.50.1 - '@rollup/rollup-openharmony-arm64': 4.50.1 - '@rollup/rollup-win32-arm64-msvc': 4.50.1 - '@rollup/rollup-win32-ia32-msvc': 4.50.1 - '@rollup/rollup-win32-x64-msvc': 4.50.1 + '@rollup/rollup-android-arm-eabi': 4.52.4 + '@rollup/rollup-android-arm64': 4.52.4 + '@rollup/rollup-darwin-arm64': 4.52.4 + '@rollup/rollup-darwin-x64': 4.52.4 + '@rollup/rollup-freebsd-arm64': 4.52.4 + '@rollup/rollup-freebsd-x64': 4.52.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.52.4 + '@rollup/rollup-linux-arm-musleabihf': 4.52.4 + '@rollup/rollup-linux-arm64-gnu': 4.52.4 + '@rollup/rollup-linux-arm64-musl': 4.52.4 + '@rollup/rollup-linux-loong64-gnu': 4.52.4 + '@rollup/rollup-linux-ppc64-gnu': 4.52.4 + '@rollup/rollup-linux-riscv64-gnu': 4.52.4 + '@rollup/rollup-linux-riscv64-musl': 4.52.4 + '@rollup/rollup-linux-s390x-gnu': 4.52.4 + '@rollup/rollup-linux-x64-gnu': 4.52.4 + '@rollup/rollup-linux-x64-musl': 4.52.4 + '@rollup/rollup-openharmony-arm64': 4.52.4 + '@rollup/rollup-win32-arm64-msvc': 4.52.4 + '@rollup/rollup-win32-ia32-msvc': 4.52.4 + '@rollup/rollup-win32-x64-gnu': 4.52.4 + '@rollup/rollup-win32-x64-msvc': 4.52.4 fsevents: 2.3.3 router@2.2.0: @@ -23159,16 +22637,18 @@ snapshots: postcss: 8.5.6 strip-json-comments: 3.1.1 + run-applescript@7.1.0: {} + run-async@2.4.1: {} run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 - runed@0.29.2(svelte@5.38.10): + runed@0.29.2(svelte@5.39.11): dependencies: esm-env: 1.2.2 - svelte: 5.38.10 + svelte: 5.39.11 rw@1.3.3: {} @@ -23214,7 +22694,7 @@ snapshots: dependencies: loose-envify: 1.4.0 - scheduler@0.26.0: {} + scheduler@0.27.0: {} schema-dts@1.1.5: {} @@ -23224,7 +22704,7 @@ snapshots: ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) - schema-utils@4.3.2: + schema-utils@4.3.3: dependencies: '@types/json-schema': 7.0.15 ajv: 8.17.1 @@ -23251,13 +22731,11 @@ snapshots: semver-diff@4.0.0: dependencies: - semver: 7.7.2 + semver: 7.7.3 semver@6.3.1: {} - semver@7.6.3: {} - - semver@7.7.2: {} + semver@7.7.3: {} send@0.19.0: dependencies: @@ -23369,36 +22847,36 @@ snapshots: stream-source: 0.3.5 text-encoding: 0.6.4 - sharp@0.34.3: + sharp@0.34.4: dependencies: - color: 4.2.3 - detect-libc: 2.1.0 + '@img/colour': 1.0.0 + detect-libc: 2.1.2 node-addon-api: 8.5.0 node-gyp: 11.4.2 - semver: 7.7.2 + semver: 7.7.3 optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.3 - '@img/sharp-darwin-x64': 0.34.3 - '@img/sharp-libvips-darwin-arm64': 1.2.0 - '@img/sharp-libvips-darwin-x64': 1.2.0 - '@img/sharp-libvips-linux-arm': 1.2.0 - '@img/sharp-libvips-linux-arm64': 1.2.0 - '@img/sharp-libvips-linux-ppc64': 1.2.0 - '@img/sharp-libvips-linux-s390x': 1.2.0 - '@img/sharp-libvips-linux-x64': 1.2.0 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 - '@img/sharp-libvips-linuxmusl-x64': 1.2.0 - '@img/sharp-linux-arm': 0.34.3 - '@img/sharp-linux-arm64': 0.34.3 - '@img/sharp-linux-ppc64': 0.34.3 - '@img/sharp-linux-s390x': 0.34.3 - '@img/sharp-linux-x64': 0.34.3 - '@img/sharp-linuxmusl-arm64': 0.34.3 - '@img/sharp-linuxmusl-x64': 0.34.3 - '@img/sharp-wasm32': 0.34.3 - '@img/sharp-win32-arm64': 0.34.3 - '@img/sharp-win32-ia32': 0.34.3 - '@img/sharp-win32-x64': 0.34.3 + '@img/sharp-darwin-arm64': 0.34.4 + '@img/sharp-darwin-x64': 0.34.4 + '@img/sharp-libvips-darwin-arm64': 1.2.3 + '@img/sharp-libvips-darwin-x64': 1.2.3 + '@img/sharp-libvips-linux-arm': 1.2.3 + '@img/sharp-libvips-linux-arm64': 1.2.3 + '@img/sharp-libvips-linux-ppc64': 1.2.3 + '@img/sharp-libvips-linux-s390x': 1.2.3 + '@img/sharp-libvips-linux-x64': 1.2.3 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 + '@img/sharp-libvips-linuxmusl-x64': 1.2.3 + '@img/sharp-linux-arm': 0.34.4 + '@img/sharp-linux-arm64': 0.34.4 + '@img/sharp-linux-ppc64': 0.34.4 + '@img/sharp-linux-s390x': 0.34.4 + '@img/sharp-linux-x64': 0.34.4 + '@img/sharp-linuxmusl-arm64': 0.34.4 + '@img/sharp-linuxmusl-x64': 0.34.4 + '@img/sharp-wasm32': 0.34.4 + '@img/sharp-win32-arm64': 0.34.4 + '@img/sharp-win32-ia32': 0.34.4 + '@img/sharp-win32-x64': 0.34.4 transitivePeerDependencies: - supports-color @@ -23410,38 +22888,6 @@ snapshots: shell-quote@1.8.3: {} - shelljs@0.8.5: - dependencies: - glob: 7.2.3 - interpret: 1.4.0 - rechoir: 0.6.2 - - should-equal@2.0.0: - dependencies: - should-type: 1.4.0 - - should-format@3.0.3: - dependencies: - should-type: 1.4.0 - should-type-adaptors: 1.1.0 - - should-type-adaptors@1.1.0: - dependencies: - should-type: 1.4.0 - should-util: 1.0.1 - - should-type@1.4.0: {} - - should-util@1.0.1: {} - - should@13.2.3: - dependencies: - should-equal: 2.0.0 - should-format: 3.0.3 - should-type: 1.4.0 - should-type-adaptors: 1.1.0 - should-util: 1.0.1 - side-channel-list@1.0.0: dependencies: es-errors: 1.3.0 @@ -23486,11 +22932,7 @@ snapshots: simple-concat: 1.0.1 optional: true - simple-icons@15.15.0: {} - - simple-swizzle@0.2.2: - dependencies: - is-arrayish: 0.3.2 + simple-icons@15.16.1: {} sirv@2.0.4: dependencies: @@ -23603,6 +23045,8 @@ snapshots: source-map@0.7.4: {} + source-map@0.7.6: {} + space-separated-tokens@1.1.5: {} space-separated-tokens@2.0.2: {} @@ -23634,7 +23078,7 @@ snapshots: sprintf-js@1.0.3: {} - sql-formatter@15.6.9: + sql-formatter@15.6.10: dependencies: argparse: 2.0.1 nearley: 2.20.1 @@ -23644,9 +23088,9 @@ snapshots: ssh-remote-port-forward@1.0.4: dependencies: '@types/ssh2': 0.5.52 - ssh2: 1.16.0 + ssh2: 1.17.0 - ssh2@1.16.0: + ssh2@1.17.0: dependencies: asn1: 0.2.6 bcrypt-pbkdf: 1.0.2 @@ -23662,8 +23106,6 @@ snapshots: standard-as-callback@2.1.0: {} - state-local@1.0.7: {} - statuses@1.5.0: {} statuses@2.0.1: {} @@ -23678,12 +23120,11 @@ snapshots: streamsearch@1.1.0: {} - streamx@2.22.1: + streamx@2.23.0: dependencies: + events-universal: 1.0.1 fast-fifo: 1.3.2 text-decoder: 1.2.3 - optionalDependencies: - bare-events: 2.6.1 string-width@4.2.3: dependencies: @@ -23699,7 +23140,7 @@ snapshots: string-width@7.2.0: dependencies: - emoji-regex: 10.5.0 + emoji-regex: 10.6.0 get-east-asian-width: 1.4.0 strip-ansi: 7.1.2 @@ -23752,8 +23193,6 @@ snapshots: dependencies: js-tokens: 9.0.1 - striptags@3.2.0: {} - strnum@2.1.1: {} strtok3@10.3.4: @@ -23770,7 +23209,7 @@ snapshots: stylehacks@6.1.1(postcss@8.5.6): dependencies: - browserslist: 4.25.3 + browserslist: 4.26.3 postcss: 8.5.6 postcss-selector-parser: 6.1.2 @@ -23798,22 +23237,6 @@ snapshots: transitivePeerDependencies: - supports-color - superagent@7.1.6: - dependencies: - component-emitter: 1.3.1 - cookiejar: 2.1.4 - debug: 4.4.3 - fast-safe-stringify: 2.1.1 - form-data: 4.0.4 - formidable: 2.1.5 - methods: 1.1.2 - mime: 2.6.0 - qs: 6.14.0 - readable-stream: 3.6.2 - semver: 7.7.2 - transitivePeerDependencies: - - supports-color - supercluster@7.1.5: dependencies: kdbush: 3.0.0 @@ -23839,19 +23262,19 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-check@4.3.1(picomatch@4.0.3)(svelte@5.38.10)(typescript@5.9.2): + svelte-check@4.3.3(picomatch@4.0.3)(svelte@5.39.11)(typescript@5.9.3): dependencies: - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 chokidar: 4.0.3 fdir: 6.5.0(picomatch@4.0.3) picocolors: 1.1.1 sade: 1.8.1 - svelte: 5.38.10 - typescript: 5.9.2 + svelte: 5.39.11 + typescript: 5.9.3 transitivePeerDependencies: - picomatch - svelte-eslint-parser@1.3.2(svelte@5.38.10): + svelte-eslint-parser@1.3.3(svelte@5.39.11): dependencies: eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -23860,50 +23283,54 @@ snapshots: postcss-scss: 4.0.9(postcss@8.5.6) postcss-selector-parser: 7.1.0 optionalDependencies: - svelte: 5.38.10 + svelte: 5.39.11 svelte-gestures@5.2.2: {} - svelte-i18n@4.0.1(svelte@5.38.10): + svelte-highlight@7.8.4: + dependencies: + highlight.js: 11.11.1 + + svelte-i18n@4.0.1(svelte@5.39.11): dependencies: cli-color: 2.0.4 deepmerge: 4.3.1 esbuild: 0.19.12 estree-walker: 2.0.2 - intl-messageformat: 10.7.16 + intl-messageformat: 10.7.17 sade: 1.8.1 - svelte: 5.38.10 + svelte: 5.39.11 tiny-glob: 0.2.9 - svelte-maplibre@1.2.1(svelte@5.38.10): + svelte-maplibre@1.2.1(svelte@5.39.11): dependencies: d3-geo: 3.1.1 dequal: 2.0.3 just-compare: 2.3.0 - maplibre-gl: 5.7.1 + maplibre-gl: 5.8.0 pmtiles: 3.2.1 - svelte: 5.38.10 + svelte: 5.39.11 - svelte-parse-markup@0.1.5(svelte@5.38.10): + svelte-parse-markup@0.1.5(svelte@5.39.11): dependencies: - svelte: 5.38.10 + svelte: 5.39.11 - svelte-persisted-store@0.12.0(svelte@5.38.10): + svelte-persisted-store@0.12.0(svelte@5.39.11): dependencies: - svelte: 5.38.10 + svelte: 5.39.11 - svelte-toolbelt@0.9.3(svelte@5.38.10): + svelte-toolbelt@0.9.3(svelte@5.39.11): dependencies: clsx: 2.1.1 - runed: 0.29.2(svelte@5.38.10) + runed: 0.29.2(svelte@5.39.11) style-to-object: 1.0.9 - svelte: 5.38.10 + svelte: 5.39.11 - svelte@5.38.10: + svelte@5.39.11: dependencies: '@jridgewell/remapping': 2.3.5 '@jridgewell/sourcemap-codec': 1.5.5 - '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) + '@sveltejs/acorn-typescript': 1.0.6(acorn@8.15.0) '@types/estree': 1.0.8 acorn: 8.15.0 aria-query: 5.3.2 @@ -23914,7 +23341,7 @@ snapshots: is-reference: 3.0.3 locate-character: 3.0.0 magic-string: 0.30.19 - zimmerframe: 1.1.2 + zimmerframe: 1.1.4 svg-parser@2.0.4: {} @@ -23932,22 +23359,6 @@ snapshots: dependencies: '@scarf/scarf': 1.4.0 - swagger2openapi@7.0.8(encoding@0.1.13): - dependencies: - call-me-maybe: 1.0.2 - node-fetch: 2.7.0(encoding@0.1.13) - node-fetch-h2: 2.3.0 - node-readfiles: 0.2.0 - oas-kit-common: 1.0.8 - oas-resolver: 2.5.6 - oas-schema-walker: 1.1.5 - oas-validator: 5.0.8 - reftools: 1.1.9 - yaml: 1.10.2 - yargs: 17.7.2 - transitivePeerDependencies: - - encoding - symbol-observable@4.0.0: {} symbol-tree@3.2.4: @@ -23963,27 +23374,27 @@ snapshots: tailwind-merge@3.3.1: {} - tailwind-variants@3.1.1(tailwind-merge@3.3.1)(tailwindcss@4.1.13): + tailwind-variants@3.1.1(tailwind-merge@3.3.1)(tailwindcss@4.1.14): dependencies: - tailwindcss: 4.1.13 + tailwindcss: 4.1.14 optionalDependencies: tailwind-merge: 3.3.1 - tailwindcss-email-variants@3.0.4(tailwindcss@3.4.17): + tailwindcss-email-variants@3.0.4(tailwindcss@3.4.18(yaml@2.8.1)): dependencies: - tailwindcss: 3.4.17 + tailwindcss: 3.4.18(yaml@2.8.1) - tailwindcss-mso@2.0.2(tailwindcss@3.4.17): + tailwindcss-mso@2.0.2(tailwindcss@3.4.18(yaml@2.8.1)): dependencies: - tailwindcss: 3.4.17 + tailwindcss: 3.4.18(yaml@2.8.1) - tailwindcss-preset-email@1.4.0(tailwindcss@3.4.17): + tailwindcss-preset-email@1.4.0(tailwindcss@3.4.18(yaml@2.8.1)): dependencies: - tailwindcss: 3.4.17 - tailwindcss-email-variants: 3.0.4(tailwindcss@3.4.17) - tailwindcss-mso: 2.0.2(tailwindcss@3.4.17) + tailwindcss: 3.4.18(yaml@2.8.1) + tailwindcss-email-variants: 3.0.4(tailwindcss@3.4.18(yaml@2.8.1)) + tailwindcss-mso: 2.0.2(tailwindcss@3.4.18(yaml@2.8.1)) - tailwindcss@3.4.17: + tailwindcss@3.4.18(yaml@2.8.1): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -24002,31 +23413,32 @@ snapshots: postcss: 8.5.6 postcss-import: 15.1.0(postcss@8.5.6) postcss-js: 4.1.0(postcss@8.5.6) - postcss-load-config: 4.0.2(postcss@8.5.6) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)(yaml@2.8.1) postcss-nested: 6.2.0(postcss@8.5.6) postcss-selector-parser: 6.1.2 resolve: 1.22.10 sucrase: 3.35.0 transitivePeerDependencies: - - ts-node + - tsx + - yaml - tailwindcss@4.1.13: {} + tailwindcss@4.1.14: {} - tapable@2.2.2: {} + tapable@2.3.0: {} - tar-fs@2.1.3: + tar-fs@2.1.4: dependencies: chownr: 1.1.4 mkdirp-classic: 0.5.3 pump: 3.0.3 tar-stream: 2.2.0 - tar-fs@3.1.0: + tar-fs@3.1.1: dependencies: pump: 3.0.3 tar-stream: 3.1.7 optionalDependencies: - bare-fs: 4.2.0 + bare-fs: 4.4.7 bare-path: 3.0.0 transitivePeerDependencies: - bare-buffer @@ -24043,7 +23455,7 @@ snapshots: dependencies: b4a: 1.6.7 fast-fifo: 1.3.2 - streamx: 2.22.1 + streamx: 2.23.0 tar@6.2.1: dependencies: @@ -24054,34 +23466,33 @@ snapshots: mkdirp: 1.0.4 yallist: 4.0.0 - tar@7.4.3: + tar@7.5.1: dependencies: '@isaacs/fs-minipass': 4.0.1 chownr: 3.0.0 minipass: 7.1.2 - minizlib: 3.0.2 - mkdirp: 3.0.1 + minizlib: 3.1.0 yallist: 5.0.0 terser-webpack-plugin@5.3.14(@swc/core@1.13.5(@swc/helpers@0.5.17))(webpack@5.100.2(@swc/core@1.13.5(@swc/helpers@0.5.17))): dependencies: - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 - schema-utils: 4.3.2 + schema-utils: 4.3.3 serialize-javascript: 6.0.2 terser: 5.43.1 webpack: 5.100.2(@swc/core@1.13.5(@swc/helpers@0.5.17)) optionalDependencies: '@swc/core': 1.13.5(@swc/helpers@0.5.17) - terser-webpack-plugin@5.3.14(webpack@5.100.2): + terser-webpack-plugin@5.3.14(webpack@5.102.1): dependencies: - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 - schema-utils: 4.3.2 + schema-utils: 4.3.3 serialize-javascript: 6.0.2 terser: 5.43.1 - webpack: 5.100.2 + webpack: 5.102.1 terser@5.43.1: dependencies: @@ -24096,21 +23507,21 @@ snapshots: glob: 10.4.5 minimatch: 9.0.5 - testcontainers@11.5.1: + testcontainers@11.7.1: dependencies: '@balena/dockerignore': 1.0.2 - '@types/dockerode': 3.3.42 + '@types/dockerode': 3.3.44 archiver: 7.0.1 async-lock: 1.4.1 byline: 5.0.0 debug: 4.4.3 - docker-compose: 1.2.0 - dockerode: 4.0.7 + docker-compose: 1.3.0 + dockerode: 4.0.9 get-port: 7.1.0 proper-lockfile: 4.1.2 properties-reader: 2.3.0 ssh-remote-port-forward: 1.0.4 - tar-fs: 3.1.0 + tar-fs: 3.1.1 tmp: 0.2.5 undici: 7.16.0 transitivePeerDependencies: @@ -24131,6 +23542,10 @@ snapshots: dependencies: any-promise: 1.3.0 + thingies@2.5.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + three@0.179.1: {} three@0.180.0: {} @@ -24228,6 +23643,10 @@ snapshots: punycode: 2.3.1 optional: true + tree-dump@1.1.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + tree-kill@1.2.2: {} trim-lines@3.0.1: {} @@ -24240,21 +23659,21 @@ snapshots: dependencies: utf8-byte-length: 1.0.5 - ts-api-utils@2.1.0(typescript@5.9.2): + ts-api-utils@2.1.0(typescript@5.9.3): dependencies: - typescript: 5.9.2 + typescript: 5.9.3 ts-interface-checker@0.1.13: {} - tsconfck@3.1.6(typescript@5.9.2): + tsconfck@3.1.6(typescript@5.9.3): optionalDependencies: - typescript: 5.9.2 + typescript: 5.9.3 tsconfig-paths-webpack-plugin@4.2.0: dependencies: chalk: 4.1.2 enhanced-resolve: 5.18.3 - tapable: 2.2.2 + tapable: 2.3.0 tsconfig-paths: 4.2.0 tsconfig-paths@4.2.0: @@ -24298,20 +23717,20 @@ snapshots: typedarray@0.0.6: {} - typescript-eslint@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2): + typescript-eslint@8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/parser': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.9.2) - '@typescript-eslint/utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) - eslint: 9.35.0(jiti@2.5.1) - typescript: 5.9.2 + '@typescript-eslint/eslint-plugin': 8.46.0(@typescript-eslint/parser@8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.46.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.37.0(jiti@2.6.1) + typescript: 5.9.3 transitivePeerDependencies: - supports-color typescript@5.8.3: {} - typescript@5.9.2: {} + typescript@5.9.3: {} ua-is-frozen@0.1.2: {} @@ -24337,7 +23756,7 @@ snapshots: undici-types@6.21.0: {} - undici-types@7.12.0: + undici-types@7.14.0: optional: true undici@7.16.0: {} @@ -24442,9 +23861,9 @@ snapshots: unpipe@1.0.0: {} - unplugin-swc@1.5.7(@swc/core@1.13.5(@swc/helpers@0.5.17))(rollup@4.50.1): + unplugin-swc@1.5.7(@swc/core@1.13.5(@swc/helpers@0.5.17))(rollup@4.52.4): dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.50.1) + '@rollup/pluginutils': 5.3.0(rollup@4.52.4) '@swc/core': 1.13.5(@swc/helpers@0.5.17) load-tsconfig: 0.2.5 unplugin: 2.3.10 @@ -24458,9 +23877,9 @@ snapshots: picomatch: 4.0.3 webpack-virtual-modules: 0.6.2 - update-browserslist-db@1.1.3(browserslist@4.25.3): + update-browserslist-db@1.1.3(browserslist@4.26.3): dependencies: - browserslist: 4.25.3 + browserslist: 4.26.3 escalade: 3.2.0 picocolors: 1.1.1 @@ -24477,7 +23896,7 @@ snapshots: is-yarn-global: 0.4.1 latest-version: 7.0.0 pupa: 3.1.0 - semver: 7.7.2 + semver: 7.7.3 semver-diff: 4.0.0 xdg-basedir: 5.1.0 @@ -24485,14 +23904,14 @@ snapshots: dependencies: punycode: 2.3.1 - url-loader@4.1.1(file-loader@6.2.0(webpack@5.100.2))(webpack@5.100.2): + url-loader@4.1.1(file-loader@6.2.0(webpack@5.102.1))(webpack@5.102.1): dependencies: loader-utils: 2.0.4 mime-types: 2.1.35 schema-utils: 3.3.0 - webpack: 5.100.2 + webpack: 5.102.1 optionalDependencies: - file-loader: 6.2.0(webpack@5.100.2) + file-loader: 6.2.0(webpack@5.102.1) url-parse@1.5.10: dependencies: @@ -24509,10 +23928,6 @@ snapshots: util-deprecate@1.0.2: {} - util@0.10.4: - dependencies: - inherits: 2.0.3 - utila@0.4.0: {} utility-types@3.11.0: {} @@ -24533,23 +23948,6 @@ snapshots: uuid@8.3.2: {} - uuid@9.0.1: {} - - validate.io-array@1.0.6: {} - - validate.io-function@1.0.2: {} - - validate.io-integer-array@1.0.0: - dependencies: - validate.io-array: 1.0.6 - validate.io-integer: 1.0.5 - - validate.io-integer@1.0.5: - dependencies: - validate.io-number: 1.0.3 - - validate.io-number@1.0.3: {} - validator@13.15.15: {} value-equal@1.0.1: {} @@ -24568,7 +23966,7 @@ snapshots: '@types/unist': 2.0.11 unist-util-stringify-position: 2.0.3 - vfile-message@4.0.2: + vfile-message@4.0.3: dependencies: '@types/unist': 3.0.3 unist-util-stringify-position: 4.0.0 @@ -24583,24 +23981,24 @@ snapshots: vfile@6.0.3: dependencies: '@types/unist': 3.0.3 - vfile-message: 4.0.2 + vfile-message: 4.0.3 - vite-imagetools@8.0.0(rollup@4.50.1): + vite-imagetools@8.0.0(rollup@4.52.4): dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.50.1) + '@rollup/pluginutils': 5.3.0(rollup@4.52.4) imagetools-core: 8.0.0 - sharp: 0.34.3 + sharp: 0.34.4 transitivePeerDependencies: - rollup - supports-color - vite-node@3.2.4(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + vite-node@3.2.4(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti @@ -24615,13 +24013,13 @@ snapshots: - tsx - yaml - vite-node@3.2.4(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + vite-node@3.2.4(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti @@ -24636,62 +24034,62 @@ snapshots: - tsx - yaml - vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): + vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): dependencies: debug: 4.4.3 globrex: 0.1.2 - tsconfck: 3.1.6(typescript@5.9.2) + tsconfck: 3.1.6(typescript@5.9.3) optionalDependencies: - vite: 7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - supports-color - typescript - vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + vite@7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): dependencies: - esbuild: 0.25.9 + esbuild: 0.25.10 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.50.1 + rollup: 4.52.4 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 22.18.5 + '@types/node': 22.18.10 fsevents: 2.3.3 - jiti: 2.5.1 + jiti: 2.6.1 lightningcss: 1.30.1 terser: 5.43.1 yaml: 2.8.1 - vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): dependencies: - esbuild: 0.25.9 + esbuild: 0.25.10 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.50.1 + rollup: 4.52.4 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 24.5.1 + '@types/node': 24.7.2 fsevents: 2.3.3 - jiti: 2.5.1 + jiti: 2.6.1 lightningcss: 1.30.1 terser: 5.43.1 yaml: 2.8.1 - vitefu@1.1.1(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): + vitefu@1.1.1(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): optionalDependencies: - vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - vitest-fetch-mock@0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): + vitest-fetch-mock@0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): dependencies: - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -24709,13 +24107,13 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - vite-node: 3.2.4(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 - '@types/node': 22.18.5 - happy-dom: 18.0.1 + '@types/node': 22.18.10 + happy-dom: 20.0.2 jsdom: 26.1.0(canvas@2.11.2(encoding@0.1.13)) transitivePeerDependencies: - jiti @@ -24731,11 +24129,11 @@ snapshots: - tsx - yaml - vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -24753,13 +24151,13 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - vite-node: 3.2.4(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 - '@types/node': 22.18.5 - happy-dom: 18.0.1 + '@types/node': 22.18.10 + happy-dom: 20.0.2 jsdom: 26.1.0(canvas@2.11.2) transitivePeerDependencies: - jiti @@ -24775,11 +24173,11 @@ snapshots: - tsx - yaml - vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.5.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.7.2)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -24797,13 +24195,13 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - vite-node: 3.2.4(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 - '@types/node': 24.5.1 - happy-dom: 18.0.1 + '@types/node': 24.7.2 + happy-dom: 20.0.2 jsdom: 26.1.0(canvas@2.11.2) transitivePeerDependencies: - jiti @@ -24875,20 +24273,23 @@ snapshots: - bufferutil - utf-8-validate - webpack-dev-middleware@5.3.4(webpack@5.100.2): + webpack-dev-middleware@7.4.5(webpack@5.102.1): dependencies: colorette: 2.0.20 - memfs: 3.5.3 - mime-types: 2.1.35 + memfs: 4.49.0 + mime-types: 3.0.1 + on-finished: 2.4.1 range-parser: 1.2.1 - schema-utils: 4.3.2 - webpack: 5.100.2 + schema-utils: 4.3.3 + optionalDependencies: + webpack: 5.102.1 - webpack-dev-server@4.15.2(webpack@5.100.2): + webpack-dev-server@5.2.2(webpack@5.102.1): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 '@types/express': 4.17.23 + '@types/express-serve-static-core': 4.19.6 '@types/serve-index': 1.9.4 '@types/serve-static': 1.15.8 '@types/sockjs': 0.3.36 @@ -24899,25 +24300,22 @@ snapshots: colorette: 2.0.20 compression: 1.8.1 connect-history-api-fallback: 2.0.0 - default-gateway: 6.0.3 express: 4.21.2 graceful-fs: 4.2.11 - html-entities: 2.6.0 http-proxy-middleware: 2.0.9(@types/express@4.17.23) ipaddr.js: 2.2.0 launch-editor: 2.10.0 - open: 8.4.2 - p-retry: 4.6.2 - rimraf: 3.0.2 - schema-utils: 4.3.2 + open: 10.2.0 + p-retry: 6.2.1 + schema-utils: 4.3.3 selfsigned: 2.4.1 serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 5.3.4(webpack@5.100.2) + webpack-dev-middleware: 7.4.5(webpack@5.102.1) ws: 8.18.3 optionalDependencies: - webpack: 5.100.2 + webpack: 5.102.1 transitivePeerDependencies: - bufferutil - debug @@ -24942,38 +24340,6 @@ snapshots: webpack-virtual-modules@0.6.2: {} - webpack@5.100.2: - dependencies: - '@types/eslint-scope': 3.7.7 - '@types/estree': 1.0.8 - '@types/json-schema': 7.0.15 - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/wasm-edit': 1.14.1 - '@webassemblyjs/wasm-parser': 1.14.1 - acorn: 8.15.0 - acorn-import-phases: 1.0.4(acorn@8.15.0) - browserslist: 4.25.3 - chrome-trace-event: 1.0.4 - enhanced-resolve: 5.18.3 - es-module-lexer: 1.7.0 - eslint-scope: 5.1.1 - events: 3.3.0 - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - json-parse-even-better-errors: 2.3.1 - loader-runner: 4.3.0 - mime-types: 2.1.35 - neo-async: 2.6.2 - schema-utils: 4.3.2 - tapable: 2.2.2 - terser-webpack-plugin: 5.3.14(webpack@5.100.2) - watchpack: 2.4.4 - webpack-sources: 3.3.3 - transitivePeerDependencies: - - '@swc/core' - - esbuild - - uglify-js - webpack@5.100.2(@swc/core@1.13.5(@swc/helpers@0.5.17)): dependencies: '@types/eslint-scope': 3.7.7 @@ -24984,7 +24350,7 @@ snapshots: '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.15.0 acorn-import-phases: 1.0.4(acorn@8.15.0) - browserslist: 4.25.3 + browserslist: 4.26.3 chrome-trace-event: 1.0.4 enhanced-resolve: 5.18.3 es-module-lexer: 1.7.0 @@ -24993,11 +24359,11 @@ snapshots: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 json-parse-even-better-errors: 2.3.1 - loader-runner: 4.3.0 + loader-runner: 4.3.1 mime-types: 2.1.35 neo-async: 2.6.2 - schema-utils: 4.3.2 - tapable: 2.2.2 + schema-utils: 4.3.3 + tapable: 2.3.0 terser-webpack-plugin: 5.3.14(@swc/core@1.13.5(@swc/helpers@0.5.17))(webpack@5.100.2(@swc/core@1.13.5(@swc/helpers@0.5.17))) watchpack: 2.4.4 webpack-sources: 3.3.3 @@ -25006,7 +24372,39 @@ snapshots: - esbuild - uglify-js - webpackbar@6.0.1(webpack@5.100.2): + webpack@5.102.1: + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.15.0 + acorn-import-phases: 1.0.4(acorn@8.15.0) + browserslist: 4.26.3 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.18.3 + es-module-lexer: 1.7.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.1 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.0 + terser-webpack-plugin: 5.3.14(webpack@5.102.1) + watchpack: 2.4.4 + webpack-sources: 3.3.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + + webpackbar@6.0.1(webpack@5.102.1): dependencies: ansi-escapes: 4.3.2 chalk: 4.1.2 @@ -25015,7 +24413,7 @@ snapshots: markdown-table: 2.0.0 pretty-time: 1.1.0 std-env: 3.9.0 - webpack: 5.100.2 + webpack: 5.102.1 wrap-ansi: 7.0.0 websocket-driver@0.7.4: @@ -25124,6 +24522,10 @@ snapshots: ws@8.18.3: {} + wsl-utils@0.1.0: + dependencies: + is-wsl: 3.1.0 + xdg-basedir@5.1.0: {} xml-js@1.6.11: @@ -25196,7 +24598,7 @@ snapshots: yoctocolors@2.1.2: {} - zimmerframe@1.1.2: {} + zimmerframe@1.1.4: {} zip-stream@6.0.1: dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 880d115880..5fd79faba3 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -27,7 +27,7 @@ onlyBuiltDependencies: - '@tailwindcss/oxide' overrides: canvas: 2.11.2 - sharp: ^0.34.3 + sharp: ^0.34.4 packageExtensions: nestjs-kysely: dependencies: diff --git a/readme_i18n/README_ar_JO.md b/readme_i18n/README_ar_JO.md index 4e7ba99dd2..e0e13eeaf6 100644 --- a/readme_i18n/README_ar_JO.md +++ b/readme_i18n/README_ar_JO.md @@ -28,7 +28,8 @@ Deutsch Nederlands Türkçe - 中文 + 简体中文 + 正體中文 Українська Русский Português Brasileiro @@ -46,13 +47,13 @@ ## محتوى -- [الوثائق الرسمية](https://immich.app/docs) +- [الوثائق الرسمية](https://docs.immich.app) - [خريطة الطريق](https://github.com/orgs/immich-app/projects/1) - [تجريبي](#demo) - [سمات](#features) -- [مقدمة](https://immich.app/docs/overview/introduction) -- [تعليمات التحميل](https://immich.app/docs/install/requirements) -- [قواعد المساهمة](https://immich.app/docs/overview/support-the-project) +- [مقدمة](https://docs.immich.app/overview/introduction) +- [تعليمات التحميل](https://docs.immich.app/install/requirements) +- [قواعد المساهمة](https://docs.immich.app/overview/support-the-project) ## توثيق diff --git a/readme_i18n/README_ca_ES.md b/readme_i18n/README_ca_ES.md index 0b8dd5999b..d09362aa0f 100644 --- a/readme_i18n/README_ca_ES.md +++ b/readme_i18n/README_ca_ES.md @@ -27,7 +27,8 @@ Deutsch Nederlands Türkçe - 中文 + 简体中文 + 正體中文 Українська Русский Português Brasileiro @@ -44,13 +45,13 @@ ## Contingut -- [Documentació oficial](https://immich.app/docs) +- [Documentació oficial](https://docs.immich.app) - [Mapa de ruta](https://github.com/orgs/immich-app/projects/1) - [Demo](#demo) - [Funcionalitats](#funcionalitats) -- [Introducció](https://immich.app/docs/overview/introduction) -- [Instal·lació](https://immich.app/docs/install/requirements) -- [Directrius de contribució](https://immich.app/docs/overview/support-the-project) +- [Introducció](https://docs.immich.app/overview/introduction) +- [Instal·lació](https://docs.immich.app/install/requirements) +- [Directrius de contribució](https://docs.immich.app/overview/support-the-project) ## Documentació diff --git a/readme_i18n/README_de_DE.md b/readme_i18n/README_de_DE.md index d24818b881..a8685e0902 100644 --- a/readme_i18n/README_de_DE.md +++ b/readme_i18n/README_de_DE.md @@ -27,7 +27,8 @@ 한국어 Nederlands Türkçe - 中文 + 简体中文 + 正體中文 Українська Русский Português Brasileiro @@ -50,14 +51,14 @@ ## Inhalt -- [Offizielle Dokumentation](https://immich.app/docs) -- [Über Immich](https://immich.app/docs/overview/introduction) -- [Installation](https://immich.app/docs/install/requirements) +- [Offizielle Dokumentation](https://docs.immich.app) +- [Über Immich](https://docs.immich.app/overview/introduction) +- [Installation](https://docs.immich.app/install/requirements) - [Roadmap](https://github.com/orgs/immich-app/projects/1) - [Demo](#demo) - [Funktionen](#funktionen) -- [Übersetzungen](https://immich.app/docs/developer/translations) -- [Beitragsrichtlinien](https://immich.app/docs/overview/support-the-project) +- [Übersetzungen](https://docs.immich.app/developer/translations) +- [Beitragsrichtlinien](https://docs.immich.app/overview/support-the-project) ## Demo @@ -107,7 +108,7 @@ Die Web-Demo kannst Du unter https://demo.immich.app finden. Für die Handy-App ## Übersetzungen -Mehr zum Thema Übersetzungen kannst du [hier](https://immich.app/docs/developer/translations) erfahren. +Mehr zum Thema Übersetzungen kannst du [hier](https://docs.immich.app/developer/translations) erfahren. Translation status diff --git a/readme_i18n/README_es_ES.md b/readme_i18n/README_es_ES.md index 00417c9188..032f8c50a8 100644 --- a/readme_i18n/README_es_ES.md +++ b/readme_i18n/README_es_ES.md @@ -27,7 +27,8 @@ Deutsch Nederlands Türkçe - 中文 + 简体中文 + 正體中文 Українська Русский Português Brasileiro @@ -45,13 +46,13 @@ ## Contenido -- [Documentación oficial](https://immich.app/docs) +- [Documentación oficial](https://docs.immich.app) - [Hoja de ruta](https://github.com/orgs/immich-app/projects/1) - [Demo](#demo) - [Funciones](#funciones) -- [Introducción](https://immich.app/docs/overview/introduction) -- [Instalación](https://immich.app/docs/install/requirements) -- [Directrices para contribuir](https://immich.app/docs/overview/support-the-project) +- [Introducción](https://docs.immich.app/overview/introduction) +- [Instalación](https://docs.immich.app/install/requirements) +- [Directrices para contribuir](https://docs.immich.app/overview/support-the-project) ## Documentación @@ -99,7 +100,7 @@ contraseña: demo ## Traducciones -Lea mas acerca de las traducciones [acá](https://immich.app/docs/developer/translations). +Lea mas acerca de las traducciones [acá](https://docs.immich.app/developer/translations). Translation status diff --git a/readme_i18n/README_fr_FR.md b/readme_i18n/README_fr_FR.md index e1d4fb1cbc..349a0c49ce 100644 --- a/readme_i18n/README_fr_FR.md +++ b/readme_i18n/README_fr_FR.md @@ -27,7 +27,8 @@ Deutsch Nederlands Türkçe - 中文 + 简体中文 + 正體中文 Українська Русский Português Brasileiro @@ -45,13 +46,13 @@ ## Sommaire -- [Documentation officielle](https://immich.app/docs) +- [Documentation officielle](https://docs.immich.app) - [Feuille de route](https://github.com/orgs/immich-app/projects/1) - [Démo](#démo) - [Fonctionnalités](#fonctionnalités) -- [Introduction](https://immich.app/docs/overview/introduction) -- [Installation](https://immich.app/docs/install/requirements) -- [Contribution](https://immich.app/docs/overview/support-the-project) +- [Introduction](https://docs.immich.app/overview/introduction) +- [Installation](https://docs.immich.app/install/requirements) +- [Contribution](https://docs.immich.app/overview/support-the-project) ## Documentation diff --git a/readme_i18n/README_it_IT.md b/readme_i18n/README_it_IT.md index 5a368fe1f9..711840fd9d 100644 --- a/readme_i18n/README_it_IT.md +++ b/readme_i18n/README_it_IT.md @@ -28,7 +28,8 @@ Deutsch Nederlands Türkçe - 中文 + 简体中文 + 正體中文 Українська Русский Português Brasileiro @@ -49,14 +50,14 @@ ## Link utili -- [Documentazione](https://immich.app/docs) -- [Informazioni](https://immich.app/docs/overview/introduction) -- [Installazione](https://immich.app/docs/install/requirements) +- [Documentazione](https://docs.immich.app) +- [Informazioni](https://docs.immich.app/overview/introduction) +- [Installazione](https://docs.immich.app/install/requirements) - [Roadmap](https://immich.app/roadmap) - [Demo](#demo) - [Funzionalità](#funzionalità) -- [Traduzioni](https://immich.app/docs/developer/translations) -- [Contribuire](https://immich.app/docs/overview/support-the-project) +- [Traduzioni](https://docs.immich.app/developer/translations) +- [Contribuire](https://docs.immich.app/overview/support-the-project) ## Demo @@ -106,7 +107,7 @@ Per l’app mobile puoi usare `https://demo.immich.app` come `Server Endpoint UR ## Traduzioni -Scopri di più sulle traduzioni [qui](https://immich.app/docs/developer/translations). +Scopri di più sulle traduzioni [qui](https://docs.immich.app/developer/translations). Stato traduzioni diff --git a/readme_i18n/README_ja_JP.md b/readme_i18n/README_ja_JP.md index 60dd0f3ed7..0e74077895 100644 --- a/readme_i18n/README_ja_JP.md +++ b/readme_i18n/README_ja_JP.md @@ -27,7 +27,8 @@ Deutsch Nederlands Türkçe - 中文 + 简体中文 + 正體中文 Русский Português Brasileiro Svenska @@ -44,13 +45,13 @@ ## コンテンツ -- [公式ドキュメント](https://immich.app/docs) +- [公式ドキュメント](https://docs.immich.app) - [ロードマップ](https://github.com/orgs/immich-app/projects/1) - [デモ](#デモ) - [機能](#機能) -- [紹介](https://immich.app/docs/overview/introduction) -- [インストール](https://immich.app/docs/install/requirements) -- [コントリビューションガイド](https://immich.app/docs/overview/support-the-project) +- [紹介](https://docs.immich.app/overview/introduction) +- [インストール](https://docs.immich.app/install/requirements) +- [コントリビューションガイド](https://docs.immich.app/overview/support-the-project) ## ドキュメント diff --git a/readme_i18n/README_ko_KR.md b/readme_i18n/README_ko_KR.md index 031e2fd9ca..c2dfd11dd3 100644 --- a/readme_i18n/README_ko_KR.md +++ b/readme_i18n/README_ko_KR.md @@ -28,7 +28,8 @@ Deutsch Nederlands Türkçe -中文 +简体中文 +正體中文 Українська Русский Português Brasileiro @@ -50,14 +51,14 @@ ## 링크 -- [문서](https://immich.app/docs) -- [소개](https://immich.app/docs/overview/introduction) -- [설치](https://immich.app/docs/install/requirements) +- [문서](https://docs.immich.app) +- [소개](https://docs.immich.app/overview/introduction) +- [설치](https://docs.immich.app/install/requirements) - [로드맵](https://immich.app/roadmap) - [데모](#데모) - [기능](#기능) -- [번역](https://immich.app/docs/developer/tranlations) -- [기여](https://immich.app/docs/overview/support-the-project) +- [번역](https://docs.immich.app/developer/tranlations) +- [기여](https://docs.immich.app/overview/support-the-project) ## 데모 @@ -104,7 +105,7 @@ ## 번역 -번역에 대한 자세한 정보는 [이곳](https://immich.app/docs/developer/translations)에서 확인하세요. +번역에 대한 자세한 정보는 [이곳](https://docs.immich.app/developer/translations)에서 확인하세요. 번역 현황 diff --git a/readme_i18n/README_nl_NL.md b/readme_i18n/README_nl_NL.md index 46692bc612..ac72e9d238 100644 --- a/readme_i18n/README_nl_NL.md +++ b/readme_i18n/README_nl_NL.md @@ -27,7 +27,8 @@ 한국어 Deutsch Türkçe - 中文 + 简体中文 + 正體中文 Українська Русский Português Brasileiro @@ -45,13 +46,13 @@ ## Inhoud -- [Officiële documentatie](https://immich.app/docs) +- [Officiële documentatie](https://docs.immich.app) - [Toekomstplannen](https://github.com/orgs/immich-app/projects/1) - [Demo](#demo) - [Functies](#functies) -- [Introductie](https://immich.app/docs/overview/introduction) -- [Installatie](https://immich.app/docs/install/requirements) -- [Richtlijnen voor bijdragen](https://immich.app/docs/overview/support-the-project) +- [Introductie](https://docs.immich.app/overview/introduction) +- [Installatie](https://docs.immich.app/install/requirements) +- [Richtlijnen voor bijdragen](https://docs.immich.app/overview/support-the-project) ## Documentatie @@ -102,7 +103,7 @@ Je kunt de demo [hier](https://demo.immich.app/) bekijken. Voor de mobiele app k ## Vertalingen -Je kunt [hier](https://immich.app/docs/developer/translations) meer over vertalingen lezen. +Je kunt [hier](https://docs.immich.app/developer/translations) meer over vertalingen lezen. ## Repository activiteit diff --git a/readme_i18n/README_pt_BR.md b/readme_i18n/README_pt_BR.md index 2320e8fd6f..d6f51cd779 100644 --- a/readme_i18n/README_pt_BR.md +++ b/readme_i18n/README_pt_BR.md @@ -29,7 +29,8 @@ Deutsch Nederlands Türkçe -中文 +简体中文 +正體中文 Українська Русский Svenska @@ -55,14 +56,14 @@ ## Links -- [Documentação](https://immich.app/docs) -- [Sobre](https://immich.app/docs/overview/introduction) -- [Instalação](https://immich.app/docs/install/requirements) +- [Documentação](https://docs.immich.app) +- [Sobre](https://docs.immich.app/overview/introduction) +- [Instalação](https://docs.immich.app/install/requirements) - [Roadmap](https://github.com/orgs/immich-app/projects/1) - [Demonstração](#demonstração) - [Funcionalidades](#funcionalidades) -- [Traduções](https://immich.app/docs/developer/translations) -- [Diretrizes de Contribuição](https://immich.app/docs/overview/support-the-project) +- [Traduções](https://docs.immich.app/developer/translations) +- [Diretrizes de Contribuição](https://docs.immich.app/overview/support-the-project) ## Demonstração @@ -115,7 +116,7 @@ Acesse a demonstração [aqui](https://demo.immich.app). No aplicativo para disp ## Traduções Leia mais sobre as traduções -[aqui](https://immich.app/docs/developer/translations). +[aqui](https://docs.immich.app/developer/translations). Status da tradução diff --git a/readme_i18n/README_ru_RU.md b/readme_i18n/README_ru_RU.md index be97259acc..e29adde9c1 100644 --- a/readme_i18n/README_ru_RU.md +++ b/readme_i18n/README_ru_RU.md @@ -28,7 +28,8 @@ Deutsch Nederlands Türkçe - 中文 + 简体中文 + 正體中文 Українська Português Brasileiro Svenska @@ -51,14 +52,14 @@ ## Содержание -- [Официальная документация](https://immich.app/docs) -- [Введение](https://immich.app/docs/overview/introduction) -- [Установка](https://immich.app/docs/install/requirements) +- [Официальная документация](https://docs.immich.app) +- [Введение](https://docs.immich.app/overview/introduction) +- [Установка](https://docs.immich.app/install/requirements) - [План разработки](https://github.com/orgs/immich-app/projects/1) - [Демо](#demo) - [Возможности](#features) -- [Перевод](https://immich.app/docs/developer/translations) -- [Гид по участию и поддержке проекта](https://immich.app/docs/overview/support-the-project) +- [Перевод](https://docs.immich.app/developer/translations) +- [Гид по участию и поддержке проекта](https://docs.immich.app/overview/support-the-project) ## Демо @@ -107,7 +108,7 @@ ## Перевод -Всё про перевод проекта [Здесь](https://immich.app/docs/developer/translations). +Всё про перевод проекта [Здесь](https://docs.immich.app/developer/translations). Translation status diff --git a/readme_i18n/README_sv_SE.md b/readme_i18n/README_sv_SE.md index daa68b9874..a421c23c2e 100644 --- a/readme_i18n/README_sv_SE.md +++ b/readme_i18n/README_sv_SE.md @@ -29,7 +29,8 @@ Deutsch Nederlands Türkçe - 中文 + 简体中文 + 正體中文 Українська Русский Português Brasileiro @@ -46,13 +47,13 @@ ## Innehåll -- [Officiell Dokumentation](https://immich.app/docs) +- [Officiell Dokumentation](https://docs.immich.app) - [Roadmap](https://github.com/orgs/immich-app/projects/1) - [Demo](#demo) - [Funktioner](#features) -- [Introduktion](https://immich.app/docs/overview/introduction) -- [Installation](https://immich.app/docs/install/requirements) -- [Riktlinjer för Bidrag](https://immich.app/docs/overview/support-the-project) +- [Introduktion](https://docs.immich.app/overview/introduction) +- [Installation](https://docs.immich.app/install/requirements) +- [Riktlinjer för Bidrag](https://docs.immich.app/overview/support-the-project) ## Dokumentation diff --git a/readme_i18n/README_th_TH.md b/readme_i18n/README_th_TH.md index bdb6db868d..cdc28b14e6 100644 --- a/readme_i18n/README_th_TH.md +++ b/readme_i18n/README_th_TH.md @@ -31,7 +31,8 @@ Deutsch Nederlands Türkçe - 中文 + 简体中文 + 正體中文 Українська Русский Português Brasileiro @@ -52,14 +53,14 @@ ## ลิงก์ -- [คู่มือ](https://immich.app/docs) -- [เกี่ยวกับ](https://immich.app/docs/overview/introduction) -- [การติดตั้ง](https://immich.app/docs/install/requirements) +- [คู่มือ](https://docs.immich.app) +- [เกี่ยวกับ](https://docs.immich.app/overview/introduction) +- [การติดตั้ง](https://docs.immich.app/install/requirements) - [โรดแมป](https://immich.app/roadmap) - [สาธิต](#สาธิต) - [คุณสมบัติ](#คุณสมบัติ) -- [การแปลภาษา](https://immich.app/docs/developer/translations) -- [สนับสนุนโพรเจกต์](https://immich.app/docs/overview/support-the-project) +- [การแปลภาษา](https://docs.immich.app/developer/translations) +- [สนับสนุนโพรเจกต์](https://docs.immich.app/overview/support-the-project) ## สาธิต @@ -106,7 +107,7 @@ ## การแปลภาษา -อ่านเพิ่มเติมเกี่ยวกับการแปล [ที่นี่](https://immich.app/docs/developer/translations) +อ่านเพิ่มเติมเกี่ยวกับการแปล [ที่นี่](https://docs.immich.app/developer/translations) สถานะการแปล diff --git a/readme_i18n/README_tr_TR.md b/readme_i18n/README_tr_TR.md index 6285ab55a2..46aef49745 100644 --- a/readme_i18n/README_tr_TR.md +++ b/readme_i18n/README_tr_TR.md @@ -27,7 +27,8 @@ 한국어 Deutsch Nederlands - 中文 + 简体中文 + 正體中文 Українська Русский Português Brasileiro @@ -44,13 +45,13 @@ ## Content -- [Resmi Belgeler](https://immich.app/docs) +- [Resmi Belgeler](https://docs.immich.app) - [Yol Haritası](https://github.com/orgs/immich-app/projects/1) - [Demo](#demo) - [Özellikler](#özellikler) -- [Giriş](https://immich.app/docs/overview/introduction) -- [Kurulum](https://immich.app/docs/install/requirements) -- [Katkı Sağlama Rehberi](https://immich.app/docs/overview/support-the-project) +- [Giriş](https://docs.immich.app/overview/introduction) +- [Kurulum](https://docs.immich.app/install/requirements) +- [Katkı Sağlama Rehberi](https://docs.immich.app/overview/support-the-project) ## Belgeler diff --git a/readme_i18n/README_uk_UA.md b/readme_i18n/README_uk_UA.md index 33687bbc50..297054ee42 100644 --- a/readme_i18n/README_uk_UA.md +++ b/readme_i18n/README_uk_UA.md @@ -29,7 +29,8 @@ Deutsch Nederlands Türkçe - 中文 + 简体中文 + 正體中文 Русский Português Brasileiro Svenska @@ -50,14 +51,14 @@ ## Посилання -- [Документація](https://immich.app/docs) -- [Про проєкт](https://immich.app/docs/overview/introduction) -- [Встановлення](https://immich.app/docs/install/requirements) +- [Документація](https://docs.immich.app) +- [Про проєкт](https://docs.immich.app/overview/introduction) +- [Встановлення](https://docs.immich.app/install/requirements) - [Дорожня карта](https://immich.app/roadmap) - [Демо](#демо) - [Функції](#функції) -- [Переклади](https://immich.app/docs/developer/translations) -- [Гід для розробки проєкту](https://immich.app/docs/overview/support-the-project) +- [Переклади](https://docs.immich.app/developer/translations) +- [Гід для розробки проєкту](https://docs.immich.app/overview/support-the-project) ## Демо @@ -106,7 +107,7 @@ ## Переклади -Більше про переклади [тут](https://immich.app/docs/developer/translations). +Більше про переклади [тут](https://docs.immich.app/developer/translations). Статус перекладів diff --git a/readme_i18n/README_vi_VN.md b/readme_i18n/README_vi_VN.md index fd04bd9fa1..b6b22ff610 100644 --- a/readme_i18n/README_vi_VN.md +++ b/readme_i18n/README_vi_VN.md @@ -29,7 +29,8 @@ Deutsch Nederlands Türkçe -中文 +简体中文 +正體中文 Українська Русский Português Brasileiro @@ -52,14 +53,14 @@ ## Liên kết -- [Tài liệu](https://immich.app/docs) -- [Giới thiệu](https://immich.app/docs/overview/introduction) -- [Cài đặt](https://immich.app/docs/install/requirements) +- [Tài liệu](https://docs.immich.app) +- [Giới thiệu](https://docs.immich.app/overview/introduction) +- [Cài đặt](https://docs.immich.app/install/requirements) - [Lộ trình](https://immich.app/roadmap) - [Demo](#demo) - [Tính năng](#Tính-năng) -- [Dịch thuật](https://immich.app/docs/developer/translations) -- [Đóng góp](https://immich.app/docs/overview/support-the-project) +- [Dịch thuật](https://docs.immich.app/developer/translations) +- [Đóng góp](https://docs.immich.app/overview/support-the-project) ## Demo @@ -106,7 +107,7 @@ Truy cập bản demo [tại đây](https://demo.immich.app). Đối với ứng ## Dịch thuật -Đọc thêm về dịch thuật [tại đây](https://immich.app/docs/developer/translations). +Đọc thêm về dịch thuật [tại đây](https://docs.immich.app/developer/translations). Tình trạng dịch thuật diff --git a/readme_i18n/README_zh_CN.md b/readme_i18n/README_zh_CN.md index 5151e35379..b48e69f94d 100644 --- a/readme_i18n/README_zh_CN.md +++ b/readme_i18n/README_zh_CN.md @@ -32,6 +32,7 @@ Deutsch Nederlands Türkçe + 正體中文 Українська Русский Português Brasileiro @@ -54,14 +55,14 @@ ## 目录 -- [官方文档](https://immich.app/docs) -- [项目总览](https://immich.app/docs/overview/introduction) -- [安装教程](https://immich.app/docs/install/requirements) +- [官方文档](https://docs.immich.app) +- [项目总览](https://docs.immich.app/overview/introduction) +- [安装教程](https://docs.immich.app/install/requirements) - [路线图](https://immich.app/roadmap) - [在线演示](#示例) - [功能特性](#功能特性) -- [多语言](https://immich.app/docs/developer/translations) -- [贡献者](https://immich.app/docs/overview/support-the-project) +- [多语言](https://docs.immich.app/developer/translations) +- [贡献者](https://docs.immich.app/overview/support-the-project) ## 示例 @@ -110,7 +111,7 @@ ## 多语言 -关于翻译的更多信息请参见[此处](https://immich.app/docs/developer/translations)。 +关于翻译的更多信息请参见[此处](https://docs.immich.app/developer/translations)。 翻译进度 diff --git a/readme_i18n/README_zh_TW.md b/readme_i18n/README_zh_TW.md new file mode 100644 index 0000000000..bf14ce2dc0 --- /dev/null +++ b/readme_i18n/README_zh_TW.md @@ -0,0 +1,132 @@ +

+
+
授權條款:AGPLv3 + + Discord + +
+
+

+ +

+ +

+

高效能的自架照片和影片管理解決方案

+
+ + + +
+ +

+ English + Català + Español + Français + Italiano + 日本語 + 한국어 + Deutsch + Nederlands + Türkçe + 简体中文 + 正體中文 + Українська + Русский + Português Brasileiro + Svenska + العربية + Tiếng Việt + ภาษาไทย +

+ +> [!WARNING] +> ⚠️ 請務必遵循 [3-2-1 備份原則](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/),守護珍貴的照片與影片! +> + +> [!NOTE] +> 主要說明文件(包含安裝指南)可於 https://immich.app/ 取得。 + +## 連結 + +- [說明文件](https://docs.immich.app/) +- [關於](https://docs.immich.app/overview/introduction) +- [安裝](https://docs.immich.app/install/requirements) +- [發展藍圖](https://immich.app/roadmap) +- [線上體驗](#線上體驗) +- [功能](#功能) +- [翻譯](https://docs.immich.app/developer/translations) +- [貢獻指南](https://docs.immich.app/overview/support-the-project) + +## 線上體驗 + +請前往 [Demoo 網站](https://demo.immich.app) 立即體驗 Immich。若要在手機 App 試用,請在 `伺服器端點 URL` 欄位輸入 `https://demo.immich.app`。 + +### 登入資訊 + +| 電子郵件 | 密碼 | +| --------------- | ------ | +| demo@immich.app | demo | + +## 功能 + +| 功能 | 手機版 | 網頁版 | +| :----------------------------------------- | ------ | ------ | +| 上傳與檢視照片與影片 | 是 | 是 | +| 開啟 App 時自動備份 | 是 | 不適用 | +| 避免重複媒體 | 是 | 是 | +| 選擇要備份的相簿 | 是 | 不適用 | +| 下載照片與影片到本機裝置 | 是 | 是 | +| 多使用者支援 | 是 | 是 | +| 相簿與共享相簿 | 是 | 是 | +| 可拖曳的捲軸 | 是 | 是 | +| 支援 RAW 格式 | 是 | 是 | +| 中繼資料檢視(EXIF、地圖) | 是 | 是 | +| 依中繼資料、物件、臉孔與 CLIP 搜尋 | 是 | 是 | +| 管理功能(使用者管理) | 否 | 是 | +| 背景備份 | 是 | 不適用 | +| 虛擬滾動 | 是 | 是 | +| 支援 OAuth | 是 | 是 | +| API 金鑰 | 不適用 | 是 | +| Live Photo/動態照片備份與播放 | 是 | 是 | +| 支援 360 度全景照片顯示 | 否 | 是 | +| 使用者自訂儲存結構 | 是 | 是 | +| 公開分享 | 是 | 是 | +| 封存與收藏 | 是 | 是 | +| 世界地圖 | 是 | 是 | +| 親朋好友分享 | 是 | 是 | +| 臉部辨識與分群 | 是 | 是 | +| 回憶(x 年前) | 是 | 是 | +| 離線支援 | 是 | 否 | +| 唯讀媒體庫 | 是 | 是 | +| 照片堆疊 | 是 | 是 | +| 標籤 | 否 | 是 | +| 資料夾檢視 | 是 | 是 | + +## 翻譯 + +更多翻譯相關資訊請見 [此處](https://docs.immich.app/developer/translations)。 + + +翻譯狀態 + + +## 專案活躍度 + +![專案活躍度](https://repobeats.axiom.co/api/embed/9e86d9dc3ddd137161f2f6d2e758d7863b1789cb.svg "Repobeats 分析圖片") + +## Star 數量歷史紀錄 + + + + + + Star 歷史紀錄圖表 + + + +## 貢獻者 + + + + diff --git a/renovate.json b/renovate.json index 5fe45d61f7..3a889f4789 100644 --- a/renovate.json +++ b/renovate.json @@ -21,6 +21,11 @@ "addLabels": [ "📱mobile" ] + }, + { + "matchPackageNames": ["ghcr.io/immich-app/postgres"], + "matchUpdateTypes": ["major"], + "enabled": false } ], "ignorePaths": [ diff --git a/server/.nvmrc b/server/.nvmrc index e2228113dd..442c7587a9 100644 --- a/server/.nvmrc +++ b/server/.nvmrc @@ -1 +1 @@ -22.19.0 +22.20.0 diff --git a/server/Dockerfile b/server/Dockerfile index 6702b338c5..2ff62f9ab4 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,39 +1,54 @@ -FROM ghcr.io/immich-app/base-server-dev:202509210934@sha256:b5ce2d7eaf379d4cf15efd4bab180d8afc8a80d20b36c9800f4091aca6ae267e AS builder +FROM ghcr.io/immich-app/base-server-dev:202510092146@sha256:124ec9659cba4a206924de5e3691f84acde16d75fa2b10b7007542424b696b96 AS builder ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \ CI=1 \ - COREPACK_HOME=/tmp + COREPACK_HOME=/tmp \ + PNPM_HOME=/buildcache/pnpm-store \ + PATH="/buildcache/pnpm-store:$PATH" RUN npm install --global corepack@latest && \ - corepack enable pnpm + corepack enable pnpm && \ + pnpm config set store-dir "$PNPM_HOME" FROM builder AS server WORKDIR /usr/src/app -COPY ./package* ./pnpm* .pnpmfile.cjs ./ COPY ./server ./server/ -RUN SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile build && \ +RUN --mount=type=cache,id=pnpm-server,target=/buildcache/pnpm-store \ + --mount=type=bind,source=package.json,target=package.json \ + --mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \ + --mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \ + --mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \ + SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile build && \ SHARP_FORCE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile --prod --no-optional deploy /output/server-pruned FROM builder AS web WORKDIR /usr/src/app -COPY ./package* ./pnpm* .pnpmfile.cjs ./ COPY ./web ./web/ COPY ./i18n ./i18n/ COPY ./open-api ./open-api/ -RUN SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter @immich/sdk --filter immich-web --frozen-lockfile --force install && \ +RUN --mount=type=cache,id=pnpm-web,target=/buildcache/pnpm-store \ + --mount=type=bind,source=package.json,target=package.json \ + --mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \ + --mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \ + --mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \ + SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter @immich/sdk --filter immich-web --frozen-lockfile --force install && \ pnpm --filter @immich/sdk --filter immich-web build FROM builder AS cli -COPY ./package* ./pnpm* .pnpmfile.cjs ./ COPY ./cli ./cli/ COPY ./open-api ./open-api/ -RUN pnpm --filter @immich/sdk --filter @immich/cli --frozen-lockfile install && \ +RUN --mount=type=cache,id=pnpm-cli,target=/buildcache/pnpm-store \ + --mount=type=bind,source=package.json,target=package.json \ + --mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \ + --mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \ + --mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \ + pnpm --filter @immich/sdk --filter @immich/cli --frozen-lockfile install && \ pnpm --filter @immich/sdk --filter @immich/cli build && \ pnpm --filter @immich/cli --prod --no-optional deploy /output/cli-pruned -FROM ghcr.io/immich-app/base-server-prod:202509210934@sha256:0c7eacf0ba88ca52e1a267cfc62d20d07792ea2c604818c2cbd37dc7dcefdac9 +FROM ghcr.io/immich-app/base-server-prod:202510092146@sha256:c39b9ad949e7777bce415e6931334aeff7331e04cb7f9df93f9ae44f6ff36b9e WORKDIR /usr/src/app ENV NODE_ENV=production \ diff --git a/server/Dockerfile.dev b/server/Dockerfile.dev index dd2e931745..c37d9d3149 100644 --- a/server/Dockerfile.dev +++ b/server/Dockerfile.dev @@ -1,5 +1,5 @@ # dev build -FROM ghcr.io/immich-app/base-server-dev:202509210934@sha256:b5ce2d7eaf379d4cf15efd4bab180d8afc8a80d20b36c9800f4091aca6ae267e AS dev +FROM ghcr.io/immich-app/base-server-dev:202510092146@sha256:124ec9659cba4a206924de5e3691f84acde16d75fa2b10b7007542424b696b96 AS dev ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \ CI=1 \ @@ -27,8 +27,8 @@ ENTRYPOINT ["tini", "--", "/bin/bash", "-c"] FROM dev AS dev-container-server RUN apt-get update --allow-releaseinfo-change && \ - apt-get install sudo inetutils-ping openjdk-11-jre-headless \ - vim nano \ + apt-get install sudo inetutils-ping openjdk-21-jre-headless \ + vim nano curl \ -y --no-install-recommends --fix-missing RUN usermod -aG sudo node && \ @@ -44,13 +44,18 @@ FROM dev-container-server AS dev-container-mobile USER root # Enable multiarch for arm64 if necessary RUN if [ "$(dpkg --print-architecture)" = "arm64" ]; then \ - dpkg --add-architecture amd64 && \ - apt-get update && \ - apt-get install -y --no-install-recommends \ - qemu-user-static \ - libc6:amd64 \ - libstdc++6:amd64 \ - libgcc1:amd64; \ + dpkg --add-architecture amd64 && \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + gnupg \ + qemu-user-static \ + libc6:amd64 \ + libstdc++6:amd64 \ + libgcc1:amd64; \ + else \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + gnupg; \ fi # Flutter SDK @@ -65,11 +70,11 @@ RUN mkdir -p ${FLUTTER_HOME} \ && curl -C - --output flutter.tar.xz https://storage.googleapis.com/flutter_infra_release/releases/${FLUTTER_CHANNEL}/linux/flutter_linux_${FLUTTER_VERSION}-${FLUTTER_CHANNEL}.tar.xz \ && tar -xf flutter.tar.xz --strip-components=1 -C ${FLUTTER_HOME} \ && rm flutter.tar.xz \ - && chown -R node ${FLUTTER_HOME} + && chown -R node ${FLUTTER_HOME} \ + && git config --global --add safe.directory ${FLUTTER_HOME} -RUN apt-get update \ - && wget -qO- https://dcm.dev/pgp-key.public | gpg --dearmor -o /usr/share/keyrings/dcm.gpg \ +RUN wget -qO- https://dcm.dev/pgp-key.public | gpg --dearmor -o /usr/share/keyrings/dcm.gpg \ && echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | tee /etc/apt/sources.list.d/dart_stable.list \ && apt-get update \ && apt-get install dcm -y diff --git a/server/bin/start.sh b/server/bin/start.sh index 15390ae158..0afff4c3a8 100755 --- a/server/bin/start.sh +++ b/server/bin/start.sh @@ -1,12 +1,13 @@ #!/usr/bin/env bash echo "Initializing Immich $IMMICH_SOURCE_REF" -lib_path="/usr/lib/$(arch)-linux-gnu/libmimalloc.so.2" -if [ -f "$lib_path" ]; then - export LD_PRELOAD="$lib_path" -else - echo "skipping libmimalloc - path not found $lib_path" -fi +# TODO: Update to mimalloc v3 when verified memory isn't released issue is fixed +# lib_path="/usr/lib/$(arch)-linux-gnu/libmimalloc.so.3" +# if [ -f "$lib_path" ]; then +# export LD_PRELOAD="$lib_path" +# else +# echo "skipping libmimalloc - path not found $lib_path" +# fi export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/lib/jellyfin-ffmpeg/lib" SERVER_HOME="$(readlink -f "$(dirname "$0")/..")" diff --git a/server/package.json b/server/package.json index 850431f406..080452c615 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "immich", - "version": "1.143.1", + "version": "2.1.0", "description": "", "author": "", "private": true, @@ -44,14 +44,14 @@ "@nestjs/websockets": "^11.0.4", "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^2.0.0", - "@opentelemetry/exporter-prometheus": "^0.205.0", - "@opentelemetry/instrumentation-http": "^0.205.0", - "@opentelemetry/instrumentation-ioredis": "^0.53.0", - "@opentelemetry/instrumentation-nestjs-core": "^0.51.0", - "@opentelemetry/instrumentation-pg": "^0.58.0", + "@opentelemetry/exporter-prometheus": "^0.206.0", + "@opentelemetry/instrumentation-http": "^0.206.0", + "@opentelemetry/instrumentation-ioredis": "^0.54.0", + "@opentelemetry/instrumentation-nestjs-core": "^0.53.0", + "@opentelemetry/instrumentation-pg": "^0.59.0", "@opentelemetry/resources": "^2.0.1", "@opentelemetry/sdk-metrics": "^2.0.1", - "@opentelemetry/sdk-node": "^0.205.0", + "@opentelemetry/sdk-node": "^0.206.0", "@opentelemetry/semantic-conventions": "^1.34.0", "@react-email/components": "^0.5.0", "@react-email/render": "^1.1.2", @@ -67,7 +67,7 @@ "compression": "^1.8.0", "cookie": "^1.0.2", "cookie-parser": "^1.4.7", - "cron": "4.3.0", + "cron": "4.3.3", "exiftool-vendored": "^28.8.0", "express": "^5.1.0", "fast-glob": "^3.3.2", @@ -78,14 +78,14 @@ "ioredis": "^5.3.2", "js-yaml": "^4.1.0", "kysely": "0.28.2", - "kysely-postgres-js": "^2.0.0", + "kysely-postgres-js": "^3.0.0", "lodash": "^4.17.21", "luxon": "^3.4.2", "mnemonist": "^0.40.3", "multer": "^2.0.2", "nest-commander": "^3.16.0", "nestjs-cls": "^5.0.0", - "nestjs-kysely": "^3.0.0", + "nestjs-kysely": "3.0.0", "nestjs-otel": "^7.0.0", "nodemailer": "^7.0.0", "openid-client": "^6.3.3", @@ -101,7 +101,7 @@ "sanitize-filename": "^1.6.3", "sanitize-html": "^2.14.0", "semver": "^7.6.2", - "sharp": "^0.34.3", + "sharp": "^0.34.4", "sirv": "^3.0.0", "socket.io": "^4.8.1", "tailwindcss-preset-email": "^1.4.0", @@ -129,7 +129,7 @@ "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", "@types/multer": "^2.0.0", - "@types/node": "^22.18.1", + "@types/node": "^22.18.8", "@types/nodemailer": "^7.0.0", "@types/picomatch": "^4.0.0", "@types/pngjs": "^6.0.5", @@ -161,9 +161,9 @@ "vitest": "^3.0.0" }, "volta": { - "node": "22.19.0" + "node": "22.20.0" }, "overrides": { - "sharp": "^0.34.3" + "sharp": "^0.34.4" } } diff --git a/server/src/app.module.ts b/server/src/app.module.ts index 8d261463e7..a1cd1edfdf 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -19,6 +19,7 @@ import { ConfigRepository } from 'src/repositories/config.repository'; import { EventRepository } from 'src/repositories/event.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { teardownTelemetry, TelemetryRepository } from 'src/repositories/telemetry.repository'; +import { UserRepository } from 'src/repositories/user.repository'; import { services } from 'src/services'; import { AuthService } from 'src/services/auth.service'; import { CliService } from 'src/services/cli.service'; @@ -55,6 +56,7 @@ class BaseModule implements OnModuleInit, OnModuleDestroy { private jobService: JobService, private telemetryRepository: TelemetryRepository, private authService: AuthService, + private userRepository: UserRepository, ) { logger.setAppName(this.worker); } diff --git a/server/src/config.ts b/server/src/config.ts index 66c03450fa..1a88b0a2d3 100644 --- a/server/src/config.ts +++ b/server/src/config.ts @@ -159,6 +159,7 @@ export interface SystemConfig { ignoreCert: boolean; host: string; port: number; + secure: boolean; username: string; password: string; }; @@ -356,6 +357,7 @@ export const defaults = Object.freeze({ ignoreCert: false, host: '', port: 587, + secure: false, username: '', password: '', }, diff --git a/server/src/constants.ts b/server/src/constants.ts index 1bae521a9f..3b75ca9f7e 100644 --- a/server/src/constants.ts +++ b/server/src/constants.ts @@ -5,7 +5,7 @@ import { SemVer } from 'semver'; import { DatabaseExtension, ExifOrientation, VectorIndex } from 'src/enum'; export const POSTGRES_VERSION_RANGE = '>=14.0.0'; -export const VECTORCHORD_VERSION_RANGE = '>=0.3 <0.5'; +export const VECTORCHORD_VERSION_RANGE = '>=0.3 <0.6'; export const VECTORS_VERSION_RANGE = '>=0.2 <0.4'; export const VECTOR_VERSION_RANGE = '>=0.5 <1'; diff --git a/server/src/decorators.ts b/server/src/decorators.ts index 2f1e76d097..8a8e23d880 100644 --- a/server/src/decorators.ts +++ b/server/src/decorators.ts @@ -87,7 +87,7 @@ export function Chunked( return Promise.all( chunks(argument, chunkSize).map(async (chunk) => { - await Reflect.apply(originalMethod, this, [ + return await Reflect.apply(originalMethod, this, [ ...arguments_.slice(0, parameterIndex), chunk, ...arguments_.slice(parameterIndex + 1), @@ -103,7 +103,7 @@ export function ChunkedArray(options?: { paramIndex?: number }): MethodDecorator } export function ChunkedSet(options?: { paramIndex?: number }): MethodDecorator { - return Chunked({ ...options, mergeFn: setUnion }); + return Chunked({ ...options, mergeFn: (args: Set[]) => setUnion(...args) }); } const UUID = '00000000-0000-4000-a000-000000000000'; diff --git a/server/src/dtos/album.dto.ts b/server/src/dtos/album.dto.ts index 00f5759aac..2f3f22099a 100644 --- a/server/src/dtos/album.dto.ts +++ b/server/src/dtos/album.dto.ts @@ -128,6 +128,14 @@ export class AlbumUserResponseDto { role!: AlbumUserRole; } +export class ContributorCountResponseDto { + @ApiProperty() + userId!: string; + + @ApiProperty({ type: 'integer' }) + assetCount!: number; +} + export class AlbumResponseDto { id!: string; ownerId!: string; @@ -149,6 +157,11 @@ export class AlbumResponseDto { isActivityEnabled!: boolean; @ValidateEnum({ enum: AssetOrder, name: 'AssetOrder', optional: true }) order?: AssetOrder; + + // Optional per-user contribution counts for shared albums + @Type(() => ContributorCountResponseDto) + @ApiProperty({ type: [ContributorCountResponseDto], required: false }) + contributorCounts?: ContributorCountResponseDto[]; } export type MapAlbumDto = { diff --git a/server/src/dtos/asset-response.dto.ts b/server/src/dtos/asset-response.dto.ts index f60f2a8824..b3fecf2f8f 100644 --- a/server/src/dtos/asset-response.dto.ts +++ b/server/src/dtos/asset-response.dto.ts @@ -212,7 +212,7 @@ export function mapAsset(entity: MapAsset, options: AssetMapOptions = {}): Asset fileModifiedAt: entity.fileModifiedAt, localDateTime: entity.localDateTime, updatedAt: entity.updatedAt, - isFavorite: options.auth?.user.id === entity.ownerId ? entity.isFavorite : false, + isFavorite: options.auth?.user.id === entity.ownerId && entity.isFavorite, isArchived: entity.visibility === AssetVisibility.Archive, isTrashed: !!entity.deletedAt, visibility: entity.visibility, diff --git a/server/src/dtos/system-config.dto.ts b/server/src/dtos/system-config.dto.ts index 1facc6c331..f12846fc9a 100644 --- a/server/src/dtos/system-config.dto.ts +++ b/server/src/dtos/system-config.dto.ts @@ -463,6 +463,9 @@ class SystemConfigSmtpTransportDto { @Max(65_535) port!: number; + @ValidateBoolean() + secure!: boolean; + @IsString() username!: string; diff --git a/server/src/emails/album-update.email.tsx b/server/src/emails/album-update.email.tsx index 3bed3a5b36..6fd2abb055 100644 --- a/server/src/emails/album-update.email.tsx +++ b/server/src/emails/album-update.email.tsx @@ -29,8 +29,8 @@ export const AlbumUpdateEmail = ({ - New media has been added to {albumName}, -
check it out! + New media has been added to {albumName}. +
Check it out!
); diff --git a/server/src/emails/components/button.component.tsx b/server/src/emails/components/button.component.tsx index b490e36650..a1fc4636cc 100644 --- a/server/src/emails/components/button.component.tsx +++ b/server/src/emails/components/button.component.tsx @@ -1,12 +1,12 @@ import React from 'react'; -import { Button, ButtonProps } from '@react-email/components'; +import { Button, ButtonProps, Text } from '@react-email/components'; export const ImmichButton = ({ children, ...props }: ButtonProps) => ( ); diff --git a/server/src/enum.ts b/server/src/enum.ts index 646138b060..b8e6e5209f 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -722,6 +722,8 @@ export enum NotificationType { JobFailed = 'JobFailed', BackupFailed = 'BackupFailed', SystemMessage = 'SystemMessage', + AlbumInvite = 'AlbumInvite', + AlbumUpdate = 'AlbumUpdate', Custom = 'Custom', } diff --git a/server/src/queries/access.repository.sql b/server/src/queries/access.repository.sql index 9aecaafb52..e98c5c6d98 100644 --- a/server/src/queries/access.repository.sql +++ b/server/src/queries/access.repository.sql @@ -71,6 +71,11 @@ where and "shared_link"."albumId" in ($2) -- AccessRepository.asset.checkAlbumAccess +with + "target" as ( + select + array[$1]::uuid[] as "ids" + ) select "asset"."id", "asset"."livePhotoVideoId" @@ -82,8 +87,12 @@ from left join "album_user" as "albumUsers" on "albumUsers"."albumsId" = "album"."id" left join "user" on "user"."id" = "albumUsers"."usersId" and "user"."deletedAt" is null + cross join "target" where - array["asset"."id", "asset"."livePhotoVideoId"] && array[$1]::uuid[] + ( + "asset"."id" = any (target.ids) + or "asset"."livePhotoVideoId" = any (target.ids) + ) and ( "album"."ownerId" = $2 or "user"."id" = $3 diff --git a/server/src/queries/album.repository.sql b/server/src/queries/album.repository.sql index 36c44414db..0087217738 100644 --- a/server/src/queries/album.repository.sql +++ b/server/src/queries/album.repository.sql @@ -407,3 +407,18 @@ from where "album_asset"."albumsId" = $1 and "album_asset"."assetsId" in ($2) + +-- AlbumRepository.getContributorCounts +select + "asset"."ownerId" as "userId", + count(*) as "assetCount" +from + "album_asset" + inner join "asset" on "asset"."id" = "assetsId" +where + "asset"."deletedAt" is null + and "album_asset"."albumsId" = $1 +group by + "asset"."ownerId" +order by + "assetCount" desc diff --git a/server/src/queries/asset.repository.sql b/server/src/queries/asset.repository.sql index 1283ff0a66..e3a0eb8c06 100644 --- a/server/src/queries/asset.repository.sql +++ b/server/src/queries/asset.repository.sql @@ -296,7 +296,8 @@ with "asset"."duration", "asset"."id", "asset"."visibility", - "asset"."isFavorite", + asset."isFavorite" + and asset."ownerId" = $1 as "isFavorite", asset.type = 'IMAGE' as "isImage", asset."deletedAt" is not null as "isTrashed", "asset"."livePhotoVideoId", @@ -341,14 +342,14 @@ with where "stacked"."stackId" = "asset"."stackId" and "stacked"."deletedAt" is null - and "stacked"."visibility" = $1 + and "stacked"."visibility" = $2 group by "stacked"."stackId" ) as "stacked_assets" on true where "asset"."deletedAt" is null and "asset"."visibility" in ('archive', 'timeline') - and date_trunc('MONTH', "localDateTime" AT TIME ZONE 'UTC') AT TIME ZONE 'UTC' = $2 + and date_trunc('MONTH', "localDateTime" AT TIME ZONE 'UTC') AT TIME ZONE 'UTC' = $3 and not exists ( select from diff --git a/server/src/queries/user.repository.sql b/server/src/queries/user.repository.sql index 6a02654781..c5a4f139a7 100644 --- a/server/src/queries/user.repository.sql +++ b/server/src/queries/user.repository.sql @@ -363,6 +363,14 @@ group by order by "user"."createdAt" asc +-- UserRepository.getCount +select + count(*) as "count" +from + "user" +where + "user"."deletedAt" is null + -- UserRepository.updateUsage update "user" set diff --git a/server/src/repositories/access.repository.ts b/server/src/repositories/access.repository.ts index 5cceb6dbe0..ca12ff040b 100644 --- a/server/src/repositories/access.repository.ts +++ b/server/src/repositories/access.repository.ts @@ -136,6 +136,7 @@ class AssetAccess { } return this.db + .with('target', (qb) => qb.selectNoFrom(sql`array[${sql.join([...assetIds])}]::uuid[]`.as('ids'))) .selectFrom('album') .innerJoin('album_asset as albumAssets', 'album.id', 'albumAssets.albumsId') .innerJoin('asset', (join) => @@ -143,11 +144,13 @@ class AssetAccess { ) .leftJoin('album_user as albumUsers', 'albumUsers.albumsId', 'album.id') .leftJoin('user', (join) => join.onRef('user.id', '=', 'albumUsers.usersId').on('user.deletedAt', 'is', null)) + .crossJoin('target') .select(['asset.id', 'asset.livePhotoVideoId']) - .where( - sql`array["asset"."id", "asset"."livePhotoVideoId"]`, - '&&', - sql`array[${sql.join([...assetIds])}]::uuid[] `, + .where((eb) => + eb.or([ + eb('asset.id', '=', sql`any(target.ids)`), + eb('asset.livePhotoVideoId', '=', sql`any(target.ids)`), + ]), ) .where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('user.id', '=', userId)])) .where('album.deletedAt', 'is', null) diff --git a/server/src/repositories/album.repository.ts b/server/src/repositories/album.repository.ts index b023068f16..00c1dfda7f 100644 --- a/server/src/repositories/album.repository.ts +++ b/server/src/repositories/album.repository.ts @@ -379,4 +379,22 @@ export class AlbumRepository { ) .whereRef('album_asset.albumsId', '=', 'album.id'); } + + /** + * Get per-user asset contribution counts for a single album. + * Excludes deleted assets, orders by count desc. + */ + @GenerateSql({ params: [DummyValue.UUID] }) + getContributorCounts(id: string) { + return this.db + .selectFrom('album_asset') + .innerJoin('asset', 'asset.id', 'assetsId') + .where('asset.deletedAt', 'is', sql.lit(null)) + .where('album_asset.albumsId', '=', id) + .select('asset.ownerId as userId') + .select((eb) => eb.fn.countAll().as('assetCount')) + .groupBy('asset.ownerId') + .orderBy('assetCount', 'desc') + .execute(); + } } diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index 5c3bd8996c..73d3a485dd 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -4,6 +4,7 @@ import { isEmpty, isUndefined, omitBy } from 'lodash'; import { InjectKysely } from 'nestjs-kysely'; import { Stack } from 'src/database'; import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; +import { AuthDto } from 'src/dtos/auth.dto'; import { AssetFileType, AssetMetadataKey, AssetOrder, AssetStatus, AssetType, AssetVisibility } from 'src/enum'; import { DB } from 'src/schema'; import { AssetExifTable } from 'src/schema/tables/asset-exif.table'; @@ -589,9 +590,9 @@ export class AssetRepository { } @GenerateSql({ - params: [DummyValue.TIME_BUCKET, { withStacked: true }], + params: [DummyValue.TIME_BUCKET, { withStacked: true }, { user: { id: DummyValue.UUID } }], }) - getTimeBucket(timeBucket: string, options: TimeBucketOptions) { + getTimeBucket(timeBucket: string, options: TimeBucketOptions, auth: AuthDto) { const query = this.db .with('cte', (qb) => qb @@ -601,7 +602,7 @@ export class AssetRepository { 'asset.duration', 'asset.id', 'asset.visibility', - 'asset.isFavorite', + sql`asset."isFavorite" and asset."ownerId" = ${auth.user.id}`.as('isFavorite'), sql`asset.type = 'IMAGE'`.as('isImage'), sql`asset."deletedAt" is not null`.as('isTrashed'), 'asset.livePhotoVideoId', diff --git a/server/src/repositories/email.repository.ts b/server/src/repositories/email.repository.ts index 78c89b4a9d..1bc4f0981a 100644 --- a/server/src/repositories/email.repository.ts +++ b/server/src/repositories/email.repository.ts @@ -23,6 +23,7 @@ export type SendEmailOptions = { export type SmtpOptions = { host: string; port?: number; + secure?: boolean; username?: string; password?: string; ignoreCert?: boolean; diff --git a/server/src/repositories/index.ts b/server/src/repositories/index.ts index a01b46f3bd..d2e1aa08c8 100644 --- a/server/src/repositories/index.ts +++ b/server/src/repositories/index.ts @@ -31,6 +31,7 @@ import { ProcessRepository } from 'src/repositories/process.repository'; import { SearchRepository } from 'src/repositories/search.repository'; import { ServerInfoRepository } from 'src/repositories/server-info.repository'; import { SessionRepository } from 'src/repositories/session.repository'; +import { SharedLinkAssetRepository } from 'src/repositories/shared-link-asset.repository'; import { SharedLinkRepository } from 'src/repositories/shared-link.repository'; import { StackRepository } from 'src/repositories/stack.repository'; import { StorageRepository } from 'src/repositories/storage.repository'; @@ -79,6 +80,7 @@ export const repositories = [ SessionRepository, ServerInfoRepository, SharedLinkRepository, + SharedLinkAssetRepository, StackRepository, StorageRepository, SyncRepository, diff --git a/server/src/repositories/media.repository.ts b/server/src/repositories/media.repository.ts index fa36c41798..d98e018efb 100644 --- a/server/src/repositories/media.repository.ts +++ b/server/src/repositories/media.repository.ts @@ -203,6 +203,9 @@ export class MediaRepository { isHDR: stream.color_transfer === 'smpte2084' || stream.color_transfer === 'arib-std-b67', bitrate: this.parseInt(stream.bit_rate), pixelFormat: stream.pix_fmt || 'yuv420p', + colorPrimaries: stream.color_primaries, + colorSpace: stream.color_space, + colorTransfer: stream.color_transfer, })), audioStreams: results.streams .filter((stream) => stream.codec_type === 'audio') diff --git a/server/src/repositories/shared-link-asset.repository.ts b/server/src/repositories/shared-link-asset.repository.ts new file mode 100644 index 0000000000..45085c4a8d --- /dev/null +++ b/server/src/repositories/shared-link-asset.repository.ts @@ -0,0 +1,18 @@ +import { Kysely } from 'kysely'; +import { InjectKysely } from 'nestjs-kysely'; +import { DB } from 'src/schema'; + +export class SharedLinkAssetRepository { + constructor(@InjectKysely() private db: Kysely) {} + + async remove(sharedLinkId: string, assetsId: string[]) { + const deleted = await this.db + .deleteFrom('shared_link_asset') + .where('shared_link_asset.sharedLinksId', '=', sharedLinkId) + .where('shared_link_asset.assetsId', 'in', assetsId) + .returning('assetsId') + .execute(); + + return deleted.map((row) => row.assetsId); + } +} diff --git a/server/src/repositories/tag.repository.ts b/server/src/repositories/tag.repository.ts index 9bbb62bd8b..d9c44f4ba4 100644 --- a/server/src/repositories/tag.repository.ts +++ b/server/src/repositories/tag.repository.ts @@ -163,22 +163,22 @@ export class TagRepository { } async deleteEmptyTags() { - // TODO rewrite as a single statement - await this.db.transaction().execute(async (tx) => { - const result = await tx - .selectFrom('asset') - .innerJoin('tag_asset', 'tag_asset.assetsId', 'asset.id') - .innerJoin('tag_closure', 'tag_closure.id_descendant', 'tag_asset.tagsId') - .innerJoin('tag', 'tag.id', 'tag_closure.id_descendant') - .select((eb) => ['tag.id', eb.fn.count('asset.id').as('count')]) - .groupBy('tag.id') - .execute(); + const result = await this.db + .deleteFrom('tag') + .where(({ not, exists, selectFrom }) => + not( + exists( + selectFrom('tag_closure') + .whereRef('tag.id', '=', 'tag_closure.id_ancestor') + .innerJoin('tag_asset', 'tag_closure.id_descendant', 'tag_asset.tagsId'), + ), + ), + ) + .executeTakeFirst(); - const ids = result.filter(({ count }) => count === 0).map(({ id }) => id); - if (ids.length > 0) { - await this.db.deleteFrom('tag').where('id', 'in', ids).execute(); - this.logger.log(`Deleted ${ids.length} empty tags`); - } - }); + const deletedRows = Number(result.numDeletedRows); + if (deletedRows > 0) { + this.logger.log(`Deleted ${deletedRows} empty tags`); + } } } diff --git a/server/src/repositories/user.repository.ts b/server/src/repositories/user.repository.ts index 44f4a2bb9c..20b41c80f8 100644 --- a/server/src/repositories/user.repository.ts +++ b/server/src/repositories/user.repository.ts @@ -286,6 +286,16 @@ export class UserRepository { .execute(); } + @GenerateSql() + async getCount(): Promise { + const result = await this.db + .selectFrom('user') + .select((eb) => eb.fn.countAll().as('count')) + .where('user.deletedAt', 'is', null) + .executeTakeFirstOrThrow(); + return Number(result.count); + } + @GenerateSql({ params: [DummyValue.UUID, DummyValue.NUMBER] }) async updateUsage(id: string, delta: number): Promise { await this.db diff --git a/server/src/schema/migrations/1744910873969-InitialMigration.ts b/server/src/schema/migrations/1744910873969-InitialMigration.ts index 53a55d860e..b703a47536 100644 --- a/server/src/schema/migrations/1744910873969-InitialMigration.ts +++ b/server/src/schema/migrations/1744910873969-InitialMigration.ts @@ -16,7 +16,9 @@ export async function up(db: Kysely): Promise { rows: [lastMigration], } = await lastMigrationSql.execute(db); if (lastMigration?.name !== 'AddMissingIndex1744910873956') { - throw new Error('Invalid upgrade path. For more information, see https://immich.app/errors#typeorm-upgrade'); + throw new Error( + 'Invalid upgrade path. For more information, see https://docs.immich.app/errors/#typeorm-upgrade', + ); } logger.log('Database has up to date TypeORM migrations, skipping initial Kysely migration'); return; diff --git a/server/src/services/album.service.ts b/server/src/services/album.service.ts index d7b857d666..dd12e31892 100644 --- a/server/src/services/album.service.ts +++ b/server/src/services/album.service.ts @@ -79,12 +79,17 @@ export class AlbumService extends BaseService { const album = await this.findOrFail(id, { withAssets }); const [albumMetadataForIds] = await this.albumRepository.getMetadataForIds([album.id]); + const hasSharedUsers = album.albumUsers && album.albumUsers.length > 0; + const hasSharedLink = album.sharedLinks && album.sharedLinks.length > 0; + const isShared = hasSharedUsers || hasSharedLink; + return { ...mapAlbum(album, withAssets, auth), startDate: albumMetadataForIds?.startDate ?? undefined, endDate: albumMetadataForIds?.endDate ?? undefined, assetCount: albumMetadataForIds?.assetCount ?? 0, lastModifiedAssetTimestamp: albumMetadataForIds?.lastModifiedAssetTimestamp ?? undefined, + contributorCounts: isShared ? await this.albumRepository.getContributorCounts(album.id) : undefined, }; } diff --git a/server/src/services/base.service.ts b/server/src/services/base.service.ts index 2f0e272883..5a2dd42c3c 100644 --- a/server/src/services/base.service.ts +++ b/server/src/services/base.service.ts @@ -38,6 +38,7 @@ import { ProcessRepository } from 'src/repositories/process.repository'; import { SearchRepository } from 'src/repositories/search.repository'; import { ServerInfoRepository } from 'src/repositories/server-info.repository'; import { SessionRepository } from 'src/repositories/session.repository'; +import { SharedLinkAssetRepository } from 'src/repositories/shared-link-asset.repository'; import { SharedLinkRepository } from 'src/repositories/shared-link.repository'; import { StackRepository } from 'src/repositories/stack.repository'; import { StorageRepository } from 'src/repositories/storage.repository'; @@ -89,6 +90,7 @@ export const BASE_SERVICE_DEPENDENCIES = [ ServerInfoRepository, SessionRepository, SharedLinkRepository, + SharedLinkAssetRepository, StackRepository, StorageRepository, SyncRepository, @@ -141,6 +143,7 @@ export class BaseService { protected serverInfoRepository: ServerInfoRepository, protected sessionRepository: SessionRepository, protected sharedLinkRepository: SharedLinkRepository, + protected sharedLinkAssetRepository: SharedLinkAssetRepository, protected stackRepository: StackRepository, protected storageRepository: StorageRepository, protected syncRepository: SyncRepository, @@ -215,6 +218,7 @@ export class BaseService { payload.storageLabel = sanitize(payload.storageLabel.replaceAll('.', '')); } + this.telemetryRepository.api.addToGauge(`immich.users.total`, 1); return this.userRepository.create(payload); } } diff --git a/server/src/services/database.service.ts b/server/src/services/database.service.ts index 758198a197..2ff0e0ca27 100644 --- a/server/src/services/database.service.ts +++ b/server/src/services/database.service.ts @@ -22,7 +22,7 @@ const messages = { The ${name} extension version is ${version}, which means it is a nightly release. Please run 'DROP EXTENSION IF EXISTS ${extension}' and switch to a release version. - See https://immich.app/docs/guides/database-queries for how to query the database.`, + See https://docs.immich.app/guides/database-queries for how to query the database.`, outOfRange: ({ name, version, range }: OutOfRangeArgs) => `The ${name} extension version is ${version}, but Immich only supports ${range}. Please change ${name} to a compatible version in the Postgres instance.`, @@ -32,20 +32,20 @@ const messages = { If the Postgres instance already has ${name} installed, Immich may not have the necessary permissions to activate it. In this case, please run 'CREATE EXTENSION IF NOT EXISTS ${extension} CASCADE' manually as a superuser. - See https://immich.app/docs/guides/database-queries for how to query the database.`, + See https://docs.immich.app/guides/database-queries for how to query the database.`, updateFailed: ({ name, extension, availableVersion }: UpdateFailedArgs) => `The ${name} extension can be updated to ${availableVersion}. Immich attempted to update the extension, but failed to do so. This may be because Immich does not have the necessary permissions to update the extension. Please run 'ALTER EXTENSION ${extension} UPDATE' manually as a superuser. - See https://immich.app/docs/guides/database-queries for how to query the database.`, + See https://docs.immich.app/guides/database-queries for how to query the database.`, dropFailed: ({ name, extension }: DropFailedArgs) => `The ${name} extension is no longer needed, but could not be dropped. This may be because Immich does not have the necessary permissions to drop the extension. Please run 'DROP EXTENSION ${extension};' manually as a superuser. - See https://immich.app/docs/guides/database-queries for how to query the database.`, + See https://docs.immich.app/guides/database-queries for how to query the database.`, restartRequired: ({ name, availableVersion }: RestartRequiredArgs) => `The ${name} extension has been updated to ${availableVersion}. Please restart the Postgres instance to complete the update.`, @@ -55,7 +55,7 @@ const messages = { If ${name} ${installedVersion} is compatible with Immich, please ensure the Postgres instance has this available.`, deprecatedExtension: (name: string) => `DEPRECATION WARNING: The ${name} extension is deprecated and support for it will be removed very soon. - See https://immich.app/docs/install/upgrading#migrating-to-vectorchord in order to switch to the VectorChord extension instead.`, + See https://docs.immich.app/install/upgrading#migrating-to-vectorchord in order to switch to the VectorChord extension instead.`, }; @Injectable() diff --git a/server/src/services/media.service.spec.ts b/server/src/services/media.service.spec.ts index e494dd9bcc..ad52b0e8b0 100644 --- a/server/src/services/media.service.spec.ts +++ b/server/src/services/media.service.spec.ts @@ -445,6 +445,7 @@ describe(MediaService.name, () => { }), ); }); + it('should not skip intra frames for MTS file', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamMTS); mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.video); @@ -462,6 +463,25 @@ describe(MediaService.name, () => { ); }); + it('should override reserved color metadata', async () => { + mocks.media.probe.mockResolvedValue(probeStub.videoStreamReserved); + mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.video); + await sut.handleGenerateThumbnails({ id: assetStub.video.id }); + + expect(mocks.media.transcode).toHaveBeenCalledWith( + '/original/path.ext', + expect.any(String), + expect.objectContaining({ + inputOptions: expect.arrayContaining([ + '-bsf:v hevc_metadata=colour_primaries=1:matrix_coefficients=1:transfer_characteristics=1', + ]), + outputOptions: expect.any(Array), + progress: expect.any(Object), + twoPass: false, + }), + ); + }); + it('should use scaling divisible by 2 even when using quick sync', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStream2160p); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHardwareAcceleration.Qsv } }); @@ -841,6 +861,37 @@ describe(MediaService.name, () => { ); }); + it('should always generate full-size preview from non-web-friendly panoramas', async () => { + mocks.systemMetadata.get.mockResolvedValue({ image: { fullsize: { enabled: false } } }); + mocks.media.extract.mockResolvedValue({ buffer: extractedBuffer, format: RawExtractedFormat.Jpeg }); + mocks.media.getImageDimensions.mockResolvedValue({ width: 3840, height: 2160 }); + + mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.panoramaTif); + + await sut.handleGenerateThumbnails({ id: assetStub.image.id }); + + expect(mocks.media.decodeImage).toHaveBeenCalledOnce(); + expect(mocks.media.decodeImage).toHaveBeenCalledWith(assetStub.panoramaTif.originalPath, { + colorspace: Colorspace.Srgb, + orientation: undefined, + processInvalidImages: false, + size: undefined, + }); + + expect(mocks.media.generateThumbnail).toHaveBeenCalledTimes(3); + expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( + rawBuffer, + { + colorspace: Colorspace.Srgb, + format: ImageFormat.Jpeg, + quality: 80, + processInvalidImages: false, + raw: rawInfo, + }, + expect.any(String), + ); + }); + it('should respect encoding options when generating full-size preview', async () => { mocks.systemMetadata.get.mockResolvedValue({ image: { fullsize: { enabled: true, format: ImageFormat.Webp, quality: 90 } }, diff --git a/server/src/services/media.service.ts b/server/src/services/media.service.ts index a2c3c5ed42..6caa682f5e 100644 --- a/server/src/services/media.service.ts +++ b/server/src/services/media.service.ts @@ -271,7 +271,9 @@ export class MediaService extends BaseService { // Handle embedded preview extraction for RAW files const extractEmbedded = image.extractEmbedded && mimeTypes.isRaw(asset.originalFileName); const extracted = extractEmbedded ? await this.extractImage(asset.originalPath, image.preview.size) : null; - const generateFullsize = image.fullsize.enabled && !mimeTypes.isWebSupportedImage(asset.originalPath); + const generateFullsize = + (image.fullsize.enabled || asset.exifInfo.projectionType == 'EQUIRECTANGULAR') && + !mimeTypes.isWebSupportedImage(asset.originalPath); const convertFullsize = generateFullsize && (!extracted || !mimeTypes.isWebSupportedImage(` .${extracted.format}`)); const { info, data, colorspace } = await this.decodeImage( diff --git a/server/src/services/notification-admin.service.spec.ts b/server/src/services/notification-admin.service.spec.ts index 4a747d41a3..c200897719 100644 --- a/server/src/services/notification-admin.service.spec.ts +++ b/server/src/services/notification-admin.service.spec.ts @@ -14,6 +14,7 @@ const smtpTransport = Object.freeze({ ignoreCert: false, host: 'localhost', port: 587, + secure: false, username: 'test', password: 'test', }, diff --git a/server/src/services/notification.service.spec.ts b/server/src/services/notification.service.spec.ts index 11c385b1e2..403ed44631 100644 --- a/server/src/services/notification.service.spec.ts +++ b/server/src/services/notification.service.spec.ts @@ -7,6 +7,7 @@ import { NotificationService } from 'src/services/notification.service'; import { INotifyAlbumUpdateJob } from 'src/types'; import { albumStub } from 'test/fixtures/album.stub'; import { assetStub } from 'test/fixtures/asset.stub'; +import { notificationStub } from 'test/fixtures/notification.stub'; import { userStub } from 'test/fixtures/user.stub'; import { newTestService, ServiceMocks } from 'test/utils'; @@ -39,6 +40,7 @@ const configs = { ignoreCert: false, host: 'localhost', port: 587, + secure: false, username: 'test', password: 'test', }, @@ -282,6 +284,7 @@ describe(NotificationService.name, () => { }, ], }); + mocks.notification.create.mockResolvedValue(notificationStub.albumEvent); await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.Skipped); }); @@ -297,6 +300,7 @@ describe(NotificationService.name, () => { }, ], }); + mocks.notification.create.mockResolvedValue(notificationStub.albumEvent); await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.Skipped); }); @@ -313,6 +317,7 @@ describe(NotificationService.name, () => { ], }); mocks.systemMetadata.get.mockResolvedValue({ server: {} }); + mocks.notification.create.mockResolvedValue(notificationStub.albumEvent); mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.Success); @@ -334,6 +339,7 @@ describe(NotificationService.name, () => { ], }); mocks.systemMetadata.get.mockResolvedValue({ server: {} }); + mocks.notification.create.mockResolvedValue(notificationStub.albumEvent); mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([]); @@ -363,6 +369,7 @@ describe(NotificationService.name, () => { ], }); mocks.systemMetadata.get.mockResolvedValue({ server: {} }); + mocks.notification.create.mockResolvedValue(notificationStub.albumEvent); mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([ { id: '1', type: AssetFileType.Thumbnail, path: 'path-to-thumb.jpg' }, @@ -394,6 +401,7 @@ describe(NotificationService.name, () => { ], }); mocks.systemMetadata.get.mockResolvedValue({ server: {} }); + mocks.notification.create.mockResolvedValue(notificationStub.albumEvent); mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([assetStub.image.files[2]]); @@ -431,6 +439,7 @@ describe(NotificationService.name, () => { albumUsers: [{ user: { id: userStub.user1.id } } as AlbumUser], }); mocks.user.get.mockResolvedValueOnce(userStub.user1); + mocks.notification.create.mockResolvedValue(notificationStub.albumEvent); mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([]); @@ -453,6 +462,7 @@ describe(NotificationService.name, () => { }, ], }); + mocks.notification.create.mockResolvedValue(notificationStub.albumEvent); mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([]); @@ -475,6 +485,7 @@ describe(NotificationService.name, () => { }, ], }); + mocks.notification.create.mockResolvedValue(notificationStub.albumEvent); mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([]); @@ -489,6 +500,7 @@ describe(NotificationService.name, () => { albumUsers: [{ user: { id: userStub.user1.id } } as AlbumUser], }); mocks.user.get.mockResolvedValue(userStub.user1); + mocks.notification.create.mockResolvedValue(notificationStub.albumEvent); mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([]); diff --git a/server/src/services/notification.service.ts b/server/src/services/notification.service.ts index 91a043d405..af0c1b981e 100644 --- a/server/src/services/notification.service.ts +++ b/server/src/services/notification.service.ts @@ -1,5 +1,6 @@ import { BadRequestException, Injectable } from '@nestjs/common'; import { OnEvent, OnJob } from 'src/decorators'; +import { MapAlbumDto } from 'src/dtos/album.dto'; import { mapAsset } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { @@ -161,7 +162,11 @@ export class NotificationService extends BaseService { const [asset] = await this.assetRepository.getByIdsWithAllRelationsButStacks([assetId]); if (asset) { - this.eventRepository.clientSend('on_asset_update', userId, mapAsset(asset)); + this.eventRepository.clientSend( + 'on_asset_update', + userId, + mapAsset(asset, { auth: { user: { id: userId } } as AuthDto }), + ); } } @@ -295,6 +300,8 @@ export class NotificationService extends BaseService { return JobStatus.Skipped; } + await this.sendAlbumLocalNotification(album, recipientId, NotificationType.AlbumInvite, album.owner.name); + const { emailNotifications } = getPreferences(recipient.metadata); if (!emailNotifications.enabled || !emailNotifications.albumInvite) { @@ -344,6 +351,8 @@ export class NotificationService extends BaseService { return JobStatus.Skipped; } + await this.sendAlbumLocalNotification(album, recipientId, NotificationType.AlbumUpdate); + const attachment = await this.getAlbumThumbnailAttachment(album); const { server, templates } = await this.getConfig({ withCache: false }); @@ -431,4 +440,25 @@ export class NotificationService extends BaseService { cid: 'album-thumbnail', }; } + + private async sendAlbumLocalNotification( + album: MapAlbumDto, + userId: string, + type: NotificationType.AlbumInvite | NotificationType.AlbumUpdate, + senderName?: string, + ) { + const isInvite = type === NotificationType.AlbumInvite; + const item = await this.notificationRepository.create({ + userId, + type, + level: isInvite ? NotificationLevel.Success : NotificationLevel.Info, + title: isInvite ? 'Shared Album Invitation' : 'Shared Album Update', + description: isInvite + ? `${senderName} shared an album (${album.albumName}) with you` + : `New media has been added to the album (${album.albumName})`, + data: JSON.stringify({ albumId: album.id }), + }); + + this.eventRepository.clientSend('on_notification', userId, mapNotification(item)); + } } diff --git a/server/src/services/shared-link.service.spec.ts b/server/src/services/shared-link.service.spec.ts index 9483cdddff..062214b975 100644 --- a/server/src/services/shared-link.service.spec.ts +++ b/server/src/services/shared-link.service.spec.ts @@ -300,6 +300,7 @@ describe(SharedLinkService.name, () => { mocks.sharedLink.get.mockResolvedValue(_.cloneDeep(sharedLinkStub.individual)); mocks.sharedLink.create.mockResolvedValue(sharedLinkStub.individual); mocks.sharedLink.update.mockResolvedValue(sharedLinkStub.individual); + mocks.sharedLinkAsset.remove.mockResolvedValue([assetStub.image.id]); await expect( sut.removeAssets(authStub.admin, 'link-1', { assetIds: [assetStub.image.id, 'asset-2'] }), @@ -308,6 +309,7 @@ describe(SharedLinkService.name, () => { { assetId: 'asset-2', success: false, error: AssetIdErrorReason.NOT_FOUND }, ]); + expect(mocks.sharedLinkAsset.remove).toHaveBeenCalledWith('link-1', [assetStub.image.id, 'asset-2']); expect(mocks.sharedLink.update).toHaveBeenCalledWith({ ...sharedLinkStub.individual, assets: [] }); }); }); diff --git a/server/src/services/shared-link.service.ts b/server/src/services/shared-link.service.ts index 096739d056..3c1a6083e9 100644 --- a/server/src/services/shared-link.service.ts +++ b/server/src/services/shared-link.service.ts @@ -175,10 +175,12 @@ export class SharedLinkService extends BaseService { throw new BadRequestException('Invalid shared link type'); } + const removedAssetIds = await this.sharedLinkAssetRepository.remove(id, dto.assetIds); + const results: AssetIdsResponseDto[] = []; for (const assetId of dto.assetIds) { - const hasAsset = sharedLink.assets.find((asset) => asset.id === assetId); - if (!hasAsset) { + const wasRemoved = removedAssetIds.find((id) => id === assetId); + if (!wasRemoved) { results.push({ assetId, success: false, error: AssetIdErrorReason.NOT_FOUND }); continue; } diff --git a/server/src/services/storage.service.ts b/server/src/services/storage.service.ts index b983c34f62..71cf0d0ce8 100644 --- a/server/src/services/storage.service.ts +++ b/server/src/services/storage.service.ts @@ -15,7 +15,7 @@ import { BaseService } from 'src/services/base.service'; import { JobOf, SystemFlags } from 'src/types'; import { ImmichStartupError } from 'src/utils/misc'; -const docsMessage = `Please see https://immich.app/docs/administration/system-integrity#folder-checks for more information.`; +const docsMessage = `Please see https://docs.immich.app/administration/system-integrity#folder-checks for more information.`; @Injectable() export class StorageService extends BaseService { @@ -115,7 +115,7 @@ export class StorageService extends BaseService { if (!path.startsWith(previous)) { throw new Error( - 'Detected an inconsistent media location. For more information, see https://immich.app/errors#inconsistent-media-location', + 'Detected an inconsistent media location. For more information, see https://docs.immich.app/errors#inconsistent-media-location', ); } diff --git a/server/src/services/system-config.service.spec.ts b/server/src/services/system-config.service.spec.ts index 5a9c7f4df3..c2dff1aaed 100644 --- a/server/src/services/system-config.service.spec.ts +++ b/server/src/services/system-config.service.spec.ts @@ -197,6 +197,7 @@ const updatedConfig = Object.freeze({ transport: { host: '', port: 587, + secure: false, username: '', password: '', ignoreCert: false, diff --git a/server/src/services/timeline.service.spec.ts b/server/src/services/timeline.service.spec.ts index 11df30a7d4..3301e61318 100644 --- a/server/src/services/timeline.service.spec.ts +++ b/server/src/services/timeline.service.spec.ts @@ -36,10 +36,14 @@ describe(TimelineService.name, () => { ); expect(mocks.access.album.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['album-id'])); - expect(mocks.asset.getTimeBucket).toHaveBeenCalledWith('bucket', { - timeBucket: 'bucket', - albumId: 'album-id', - }); + expect(mocks.asset.getTimeBucket).toHaveBeenCalledWith( + 'bucket', + { + timeBucket: 'bucket', + albumId: 'album-id', + }, + authStub.admin, + ); }); it('should return the assets for a archive time bucket if user has archive.read', async () => { @@ -60,6 +64,7 @@ describe(TimelineService.name, () => { visibility: AssetVisibility.Archive, userIds: [authStub.admin.user.id], }), + authStub.admin, ); }); @@ -76,12 +81,16 @@ describe(TimelineService.name, () => { withPartners: true, }), ).resolves.toEqual(json); - expect(mocks.asset.getTimeBucket).toHaveBeenCalledWith('bucket', { - timeBucket: 'bucket', - visibility: AssetVisibility.Timeline, - withPartners: true, - userIds: [authStub.admin.user.id], - }); + expect(mocks.asset.getTimeBucket).toHaveBeenCalledWith( + 'bucket', + { + timeBucket: 'bucket', + visibility: AssetVisibility.Timeline, + withPartners: true, + userIds: [authStub.admin.user.id], + }, + authStub.admin, + ); }); it('should check permissions to read tag', async () => { @@ -96,11 +105,15 @@ describe(TimelineService.name, () => { tagId: 'tag-123', }), ).resolves.toEqual(json); - expect(mocks.asset.getTimeBucket).toHaveBeenCalledWith('bucket', { - tagId: 'tag-123', - timeBucket: 'bucket', - userIds: [authStub.admin.user.id], - }); + expect(mocks.asset.getTimeBucket).toHaveBeenCalledWith( + 'bucket', + { + tagId: 'tag-123', + timeBucket: 'bucket', + userIds: [authStub.admin.user.id], + }, + authStub.admin, + ); }); it('should return the assets for a library time bucket if user has library.read', async () => { @@ -119,6 +132,7 @@ describe(TimelineService.name, () => { timeBucket: 'bucket', userIds: [authStub.admin.user.id], }), + authStub.admin, ); }); diff --git a/server/src/services/timeline.service.ts b/server/src/services/timeline.service.ts index d8cac3a205..b840883fa9 100644 --- a/server/src/services/timeline.service.ts +++ b/server/src/services/timeline.service.ts @@ -21,7 +21,7 @@ export class TimelineService extends BaseService { const timeBucketOptions = await this.buildTimeBucketOptions(auth, { ...dto }); // TODO: use id cursor for pagination - const bucket = await this.assetRepository.getTimeBucket(dto.timeBucket, timeBucketOptions); + const bucket = await this.assetRepository.getTimeBucket(dto.timeBucket, timeBucketOptions, auth); return bucket.assets; } diff --git a/server/src/services/user-admin.service.ts b/server/src/services/user-admin.service.ts index ce70419ff6..a57072e496 100644 --- a/server/src/services/user-admin.service.ts +++ b/server/src/services/user-admin.service.ts @@ -102,6 +102,7 @@ export class UserAdminService extends BaseService { const status = force ? UserStatus.Removing : UserStatus.Deleted; const user = await this.userRepository.update(id, { status, deletedAt: new Date() }); + this.telemetryRepository.api.addToGauge(`immich.users.total`, -1); if (force) { await this.jobRepository.queue({ name: JobName.UserDelete, data: { id: user.id, force } }); @@ -114,6 +115,7 @@ export class UserAdminService extends BaseService { await this.findOrFail(id, { withDeleted: true }); await this.albumRepository.restoreAll(id); const user = await this.userRepository.restore(id); + this.telemetryRepository.api.addToGauge('immich.users.total', 1); return mapUserAdmin(user); } diff --git a/server/src/services/user.service.ts b/server/src/services/user.service.ts index 6849b17ac3..fc71777673 100644 --- a/server/src/services/user.service.ts +++ b/server/src/services/user.service.ts @@ -3,14 +3,14 @@ import { Updateable } from 'kysely'; import { DateTime } from 'luxon'; import { SALT_ROUNDS } from 'src/constants'; import { StorageCore } from 'src/cores/storage.core'; -import { OnJob } from 'src/decorators'; +import { OnEvent, OnJob } from 'src/decorators'; import { AuthDto } from 'src/dtos/auth.dto'; import { LicenseKeyDto, LicenseResponseDto } from 'src/dtos/license.dto'; import { OnboardingDto, OnboardingResponseDto } from 'src/dtos/onboarding.dto'; import { UserPreferencesResponseDto, UserPreferencesUpdateDto, mapPreferences } from 'src/dtos/user-preferences.dto'; import { CreateProfileImageResponseDto } from 'src/dtos/user-profile.dto'; import { UserAdminResponseDto, UserResponseDto, UserUpdateMeDto, mapUser, mapUserAdmin } from 'src/dtos/user.dto'; -import { CacheControl, JobName, JobStatus, QueueName, StorageFolder, UserMetadataKey } from 'src/enum'; +import { CacheControl, ImmichWorker, JobName, JobStatus, QueueName, StorageFolder, UserMetadataKey } from 'src/enum'; import { UserFindOptions } from 'src/repositories/user.repository'; import { UserTable } from 'src/schema/tables/user.table'; import { BaseService } from 'src/services/base.service'; @@ -213,6 +213,12 @@ export class UserService extends BaseService { }; } + @OnEvent({ name: 'AppBootstrap', workers: [ImmichWorker.Api] }) + async onBootstrap(): Promise { + const userCount = await this.userRepository.getCount(); + this.telemetryRepository.api.addToGauge('immich.users.total', userCount); + } + @OnJob({ name: JobName.UserSyncUsage, queue: QueueName.BackgroundTask }) async handleUserSyncUsage(): Promise { await this.userRepository.syncUsage(); diff --git a/server/src/types.ts b/server/src/types.ts index ce0baa1672..da3889ef7c 100644 --- a/server/src/types.ts +++ b/server/src/types.ts @@ -88,6 +88,9 @@ export interface VideoStreamInfo { isHDR: boolean; bitrate: number; pixelFormat: string; + colorPrimaries?: string; + colorSpace?: string; + colorTransfer?: string; } export interface AudioStreamInfo { diff --git a/server/src/utils/media.ts b/server/src/utils/media.ts index 1ecd005315..f678a32b0f 100644 --- a/server/src/utils/media.ts +++ b/server/src/utils/media.ts @@ -392,9 +392,30 @@ export class ThumbnailConfig extends BaseConfig { getBaseInputOptions(videoStream: VideoStreamInfo, format?: VideoFormat): string[] { // skip_frame nointra skips all frames for some MPEG-TS files. Look at ffmpeg tickets 7950 and 7895 for more details. - return format?.formatName === 'mpegts' - ? ['-sws_flags accurate_rnd+full_chroma_int'] - : ['-skip_frame nointra', '-sws_flags accurate_rnd+full_chroma_int']; + const options = + format?.formatName === 'mpegts' + ? ['-sws_flags accurate_rnd+full_chroma_int'] + : ['-skip_frame nointra', '-sws_flags accurate_rnd+full_chroma_int']; + + const metadataOverrides = []; + if (videoStream.colorPrimaries === 'reserved') { + metadataOverrides.push('colour_primaries=1'); + } + + if (videoStream.colorSpace === 'reserved') { + metadataOverrides.push('matrix_coefficients=1'); + } + + if (videoStream.colorTransfer === 'reserved') { + metadataOverrides.push('transfer_characteristics=1'); + } + + if (metadataOverrides.length > 0) { + // workaround for https://fftrac-bg.ffmpeg.org/ticket/11020 + options.push(`-bsf:v ${videoStream.codecName}_metadata=${metadataOverrides.join(':')}`); + } + + return options; } getBaseOutputOptions() { diff --git a/server/test/fixtures/asset.stub.ts b/server/test/fixtures/asset.stub.ts index dd1f98d6eb..0fd2189268 100644 --- a/server/test/fixtures/asset.stub.ts +++ b/server/test/fixtures/asset.stub.ts @@ -866,4 +866,43 @@ export const assetStub = { stackId: null, visibility: AssetVisibility.Timeline, }), + panoramaTif: Object.freeze({ + id: 'asset-id', + status: AssetStatus.Active, + deviceAssetId: 'device-asset-id', + fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'), + fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'), + owner: userStub.user1, + ownerId: 'user-id', + deviceId: 'device-id', + originalPath: '/original/path.tif', + checksum: Buffer.from('file hash', 'utf8'), + type: AssetType.Image, + files, + thumbhash: Buffer.from('blablabla', 'base64'), + encodedVideoPath: null, + createdAt: new Date('2023-02-23T05:06:29.716Z'), + updatedAt: new Date('2023-02-23T05:06:29.716Z'), + localDateTime: new Date('2023-02-23T05:06:29.716Z'), + isFavorite: true, + duration: null, + isExternal: false, + livePhotoVideo: null, + livePhotoVideoId: null, + sharedLinks: [], + originalFileName: 'asset-id.tif', + faces: [], + deletedAt: null, + sidecarPath: null, + exifInfo: { + fileSizeInByte: 5000, + projectionType: 'EQUIRECTANGULAR', + } as Exif, + duplicateId: null, + isOffline: false, + updateId: '42', + libraryId: null, + stackId: null, + visibility: AssetVisibility.Timeline, + }), }; diff --git a/server/test/fixtures/media.stub.ts b/server/test/fixtures/media.stub.ts index efbf21d317..727f5ae7cf 100644 --- a/server/test/fixtures/media.stub.ts +++ b/server/test/fixtures/media.stub.ts @@ -261,4 +261,15 @@ export const probeStub = { bitrate: 0, }, }), + videoStreamReserved: Object.freeze({ + ...probeStubDefault, + videoStreams: [ + { + ...probeStubDefaultVideoStream[0], + colorPrimaries: 'reserved', + colorSpace: 'reserved', + colorTransfer: 'reserved', + }, + ], + }), }; diff --git a/server/test/fixtures/notification.stub.ts b/server/test/fixtures/notification.stub.ts new file mode 100644 index 0000000000..b5a436a622 --- /dev/null +++ b/server/test/fixtures/notification.stub.ts @@ -0,0 +1,14 @@ +import { NotificationLevel, NotificationType } from 'src/enum'; + +export const notificationStub = { + albumEvent: { + id: 'notification-album-event', + type: NotificationType.AlbumInvite, + description: 'You have been invited to a shared album', + title: 'Album Invitation', + createdAt: new Date('2024-01-01'), + data: { albumId: 'album-id' }, + level: NotificationLevel.Success, + readAt: null, + }, +}; diff --git a/server/test/medium.factory.ts b/server/test/medium.factory.ts index a169d96322..3f021f3eb7 100644 --- a/server/test/medium.factory.ts +++ b/server/test/medium.factory.ts @@ -33,12 +33,15 @@ import { PartnerRepository } from 'src/repositories/partner.repository'; import { PersonRepository } from 'src/repositories/person.repository'; import { SearchRepository } from 'src/repositories/search.repository'; import { SessionRepository } from 'src/repositories/session.repository'; +import { SharedLinkAssetRepository } from 'src/repositories/shared-link-asset.repository'; import { SharedLinkRepository } from 'src/repositories/shared-link.repository'; import { StackRepository } from 'src/repositories/stack.repository'; import { StorageRepository } from 'src/repositories/storage.repository'; import { SyncCheckpointRepository } from 'src/repositories/sync-checkpoint.repository'; import { SyncRepository } from 'src/repositories/sync.repository'; import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; +import { TagRepository } from 'src/repositories/tag.repository'; +import { TelemetryRepository } from 'src/repositories/telemetry.repository'; import { UserRepository } from 'src/repositories/user.repository'; import { VersionHistoryRepository } from 'src/repositories/version-history.repository'; import { DB } from 'src/schema'; @@ -51,9 +54,12 @@ import { MemoryTable } from 'src/schema/tables/memory.table'; import { PersonTable } from 'src/schema/tables/person.table'; import { SessionTable } from 'src/schema/tables/session.table'; import { StackTable } from 'src/schema/tables/stack.table'; +import { TagAssetTable } from 'src/schema/tables/tag-asset.table'; +import { TagTable } from 'src/schema/tables/tag.table'; import { UserTable } from 'src/schema/tables/user.table'; import { BASE_SERVICE_DEPENDENCIES, BaseService } from 'src/services/base.service'; import { SyncService } from 'src/services/sync.service'; +import { newTelemetryRepositoryMock } from 'test/repositories/telemetry.repository.mock'; import { factory, newDate, newEmbedding, newUuid } from 'test/small.factory'; import { automock, wait } from 'test/utils'; import { Mocked } from 'vitest'; @@ -238,6 +244,18 @@ export class MediumTestContext { user, }; } + + async newTagAsset(tagBulkAssets: { tagIds: string[]; assetIds: string[] }) { + const tagsAssets: Insertable[] = []; + for (const tagsId of tagBulkAssets.tagIds) { + for (const assetsId of tagBulkAssets.assetIds) { + tagsAssets.push({ tagsId, assetsId }); + } + } + + const result = await this.get(TagRepository).upsertAssetIds(tagsAssets); + return { tagsAssets, result }; + } } export class SyncTestContext extends MediumTestContext { @@ -294,6 +312,7 @@ const newRealRepository = (key: ClassConstructor, db: Kysely): T => { case SearchRepository: case SessionRepository: case SharedLinkRepository: + case SharedLinkAssetRepository: case StackRepository: case SyncRepository: case SyncCheckpointRepository: @@ -316,6 +335,10 @@ const newRealRepository = (key: ClassConstructor, db: Kysely): T => { return new key(LoggingRepository.create()); } + case TagRepository: { + return new key(db, LoggingRepository.create()); + } + case LoggingRepository as unknown as ClassConstructor: { return new key() as unknown as T; } @@ -343,10 +366,15 @@ const newMockRepository = (key: ClassConstructor) => { case SyncCheckpointRepository: case SystemMetadataRepository: case UserRepository: - case VersionHistoryRepository: { + case VersionHistoryRepository: + case TagRepository: { return automock(key); } + case TelemetryRepository: { + return newTelemetryRepositoryMock(); + } + case DatabaseRepository: { return automock(DatabaseRepository, { args: [undefined, { setContext: () => {} }, { getEnv: () => ({ database: { vectorExtension: '' } }) }], @@ -561,6 +589,23 @@ const memoryInsert = (memory: Partial> = {}) => { return { ...defaults, ...memory, id }; }; +const tagInsert = (tag: Partial>) => { + const id = tag.id || newUuid(); + + const defaults: Insertable = { + id, + userId: '', + value: '', + createdAt: newDate(), + updatedAt: newDate(), + color: '', + parentId: null, + updateId: newUuid(), + }; + + return { ...defaults, ...tag, id }; +}; + class CustomWritable extends Writable { private data = ''; @@ -613,4 +658,5 @@ export const mediumFactory = { memoryInsert, loginDetails, loginResponse, + tagInsert, }; diff --git a/server/test/medium/specs/services/auth.service.spec.ts b/server/test/medium/specs/services/auth.service.spec.ts index 14ea1451f2..60d3210f4c 100644 --- a/server/test/medium/specs/services/auth.service.spec.ts +++ b/server/test/medium/specs/services/auth.service.spec.ts @@ -11,6 +11,7 @@ import { LoggingRepository } from 'src/repositories/logging.repository'; import { SessionRepository } from 'src/repositories/session.repository'; import { StorageRepository } from 'src/repositories/storage.repository'; import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; +import { TelemetryRepository } from 'src/repositories/telemetry.repository'; import { UserRepository } from 'src/repositories/user.repository'; import { DB } from 'src/schema'; import { AuthService } from 'src/services/auth.service'; @@ -32,7 +33,7 @@ const setup = (db?: Kysely) => { SystemMetadataRepository, UserRepository, ], - mock: [LoggingRepository, StorageRepository, EventRepository], + mock: [LoggingRepository, StorageRepository, EventRepository, TelemetryRepository], }); }; diff --git a/server/test/medium/specs/services/shared-link.service.spec.ts b/server/test/medium/specs/services/shared-link.service.spec.ts index 88e7e86df5..acc51374d1 100644 --- a/server/test/medium/specs/services/shared-link.service.spec.ts +++ b/server/test/medium/specs/services/shared-link.service.spec.ts @@ -4,6 +4,7 @@ import { SharedLinkType } from 'src/enum'; import { AccessRepository } from 'src/repositories/access.repository'; import { DatabaseRepository } from 'src/repositories/database.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; +import { SharedLinkAssetRepository } from 'src/repositories/shared-link-asset.repository'; import { SharedLinkRepository } from 'src/repositories/shared-link.repository'; import { StorageRepository } from 'src/repositories/storage.repository'; import { DB } from 'src/schema'; @@ -17,7 +18,7 @@ let defaultDatabase: Kysely; const setup = (db?: Kysely) => { return newMediumService(SharedLinkService, { database: db || defaultDatabase, - real: [AccessRepository, DatabaseRepository, SharedLinkRepository], + real: [AccessRepository, DatabaseRepository, SharedLinkRepository, SharedLinkAssetRepository], mock: [LoggingRepository, StorageRepository], }); }; @@ -62,4 +63,65 @@ describe(SharedLinkService.name, () => { }); }); }); + + it('should share individually assets', async () => { + const { sut, ctx } = setup(); + + const { user } = await ctx.newUser(); + + const assets = await Promise.all([ + ctx.newAsset({ ownerId: user.id }), + ctx.newAsset({ ownerId: user.id }), + ctx.newAsset({ ownerId: user.id }), + ]); + + for (const { asset } of assets) { + await ctx.newExif({ assetId: asset.id, make: 'Canon' }); + } + + const sharedLinkRepo = ctx.get(SharedLinkRepository); + + const sharedLink = await sharedLinkRepo.create({ + key: randomBytes(16), + id: factory.uuid(), + userId: user.id, + allowUpload: false, + type: SharedLinkType.Individual, + assetIds: assets.map(({ asset }) => asset.id), + }); + + await expect(sut.getMine({ user, sharedLink }, {})).resolves.toMatchObject({ + assets: assets.map(({ asset }) => expect.objectContaining({ id: asset.id })), + }); + }); + + it('should remove individually shared asset', async () => { + const { sut, ctx } = setup(); + + const { user } = await ctx.newUser(); + const auth = factory.auth({ user }); + const { asset } = await ctx.newAsset({ ownerId: user.id }); + await ctx.newExif({ assetId: asset.id, make: 'Canon' }); + + const sharedLinkRepo = ctx.get(SharedLinkRepository); + + const sharedLink = await sharedLinkRepo.create({ + key: randomBytes(16), + id: factory.uuid(), + userId: user.id, + allowUpload: false, + type: SharedLinkType.Individual, + assetIds: [asset.id], + }); + + await expect(sut.getMine({ user, sharedLink }, {})).resolves.toMatchObject({ + assets: [expect.objectContaining({ id: asset.id })], + }); + + await sut.removeAssets(auth, sharedLink.id, { + assetIds: [asset.id], + }); + + await expect(sut.getMine({ user, sharedLink }, {})).resolves.toHaveProperty('assets', []); + }); }); diff --git a/server/test/medium/specs/services/tag.service.spec.ts b/server/test/medium/specs/services/tag.service.spec.ts new file mode 100644 index 0000000000..2ec498e56d --- /dev/null +++ b/server/test/medium/specs/services/tag.service.spec.ts @@ -0,0 +1,116 @@ +import { Kysely } from 'kysely'; +import { JobStatus } from 'src/enum'; +import { AccessRepository } from 'src/repositories/access.repository'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { TagRepository } from 'src/repositories/tag.repository'; +import { DB } from 'src/schema'; +import { TagService } from 'src/services/tag.service'; +import { upsertTags } from 'src/utils/tag'; +import { newMediumService } from 'test/medium.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = (db?: Kysely) => { + return newMediumService(TagService, { + database: db || defaultDatabase, + real: [TagRepository, AccessRepository], + mock: [LoggingRepository], + }); +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(TagService.name, () => { + describe('deleteEmptyTags', () => { + it('single tag exists, not connected to any assets, and is deleted', async () => { + const { sut, ctx } = setup(); + const { user } = await ctx.newUser(); + const tagRepo = ctx.get(TagRepository); + const [tag] = await upsertTags(tagRepo, { userId: user.id, tags: ['tag-1'] }); + + await expect(tagRepo.getByValue(user.id, 'tag-1')).resolves.toEqual(expect.objectContaining({ id: tag.id })); + await expect(sut.handleTagCleanup()).resolves.toBe(JobStatus.Success); + await expect(tagRepo.getByValue(user.id, 'tag-1')).resolves.toBeUndefined(); + }); + + it('single tag exists, connected to one asset, and is not deleted', async () => { + const { sut, ctx } = setup(); + const { user } = await ctx.newUser(); + const { asset } = await ctx.newAsset({ ownerId: user.id }); + const tagRepo = ctx.get(TagRepository); + const [tag] = await upsertTags(tagRepo, { userId: user.id, tags: ['tag-1'] }); + + await ctx.newTagAsset({ tagIds: [tag.id], assetIds: [asset.id] }); + + await expect(tagRepo.getByValue(user.id, 'tag-1')).resolves.toEqual(expect.objectContaining({ id: tag.id })); + await expect(sut.handleTagCleanup()).resolves.toBe(JobStatus.Success); + await expect(tagRepo.getByValue(user.id, 'tag-1')).resolves.toEqual(expect.objectContaining({ id: tag.id })); + }); + + it('hierarchical tag exists, and the parent is connected to an asset, and the child is deleted', async () => { + const { sut, ctx } = setup(); + const { user } = await ctx.newUser(); + const { asset } = await ctx.newAsset({ ownerId: user.id }); + const tagRepo = ctx.get(TagRepository); + const [parentTag, childTag] = await upsertTags(tagRepo, { userId: user.id, tags: ['parent', 'parent/child'] }); + + await ctx.newTagAsset({ tagIds: [parentTag.id], assetIds: [asset.id] }); + + await expect(tagRepo.getByValue(user.id, 'parent')).resolves.toEqual( + expect.objectContaining({ id: parentTag.id }), + ); + await expect(tagRepo.getByValue(user.id, 'parent/child')).resolves.toEqual( + expect.objectContaining({ id: childTag.id }), + ); + await expect(sut.handleTagCleanup()).resolves.toBe(JobStatus.Success); + await expect(tagRepo.getByValue(user.id, 'parent')).resolves.toEqual( + expect.objectContaining({ id: parentTag.id }), + ); + await expect(tagRepo.getByValue(user.id, 'parent/child')).resolves.toBeUndefined(); + }); + + it('hierarchical tag exists, and only the child is connected to an asset, and nothing is deleted', async () => { + const { sut, ctx } = setup(); + const { user } = await ctx.newUser(); + const { asset } = await ctx.newAsset({ ownerId: user.id }); + const tagRepo = ctx.get(TagRepository); + const [parentTag, childTag] = await upsertTags(tagRepo, { userId: user.id, tags: ['parent', 'parent/child'] }); + + await ctx.newTagAsset({ tagIds: [childTag.id], assetIds: [asset.id] }); + + await expect(tagRepo.getByValue(user.id, 'parent')).resolves.toEqual( + expect.objectContaining({ id: parentTag.id }), + ); + await expect(tagRepo.getByValue(user.id, 'parent/child')).resolves.toEqual( + expect.objectContaining({ id: childTag.id }), + ); + await expect(sut.handleTagCleanup()).resolves.toBe(JobStatus.Success); + await expect(tagRepo.getByValue(user.id, 'parent')).resolves.toEqual( + expect.objectContaining({ id: parentTag.id }), + ); + await expect(tagRepo.getByValue(user.id, 'parent/child')).resolves.toEqual( + expect.objectContaining({ id: childTag.id }), + ); + }); + + it('hierarchical tag exists, and neither parent nor child is connected to an asset, and both are deleted', async () => { + const { sut, ctx } = setup(); + const { user } = await ctx.newUser(); + const tagRepo = ctx.get(TagRepository); + const [parentTag, childTag] = await upsertTags(tagRepo, { userId: user.id, tags: ['parent', 'parent/child'] }); + + await expect(tagRepo.getByValue(user.id, 'parent')).resolves.toEqual( + expect.objectContaining({ id: parentTag.id }), + ); + await expect(tagRepo.getByValue(user.id, 'parent/child')).resolves.toEqual( + expect.objectContaining({ id: childTag.id }), + ); + await expect(sut.handleTagCleanup()).resolves.toBe(JobStatus.Success); + await expect(tagRepo.getByValue(user.id, 'parent/child')).resolves.toBeUndefined(); + await expect(tagRepo.getByValue(user.id, 'parent')).resolves.toBeUndefined(); + }); + }); +}); diff --git a/server/test/medium/specs/services/timeline.service.spec.ts b/server/test/medium/specs/services/timeline.service.spec.ts index fa4a75e869..eaca4dcc14 100644 --- a/server/test/medium/specs/services/timeline.service.spec.ts +++ b/server/test/medium/specs/services/timeline.service.spec.ts @@ -4,6 +4,7 @@ import { AssetVisibility } from 'src/enum'; import { AccessRepository } from 'src/repositories/access.repository'; import { AssetRepository } from 'src/repositories/asset.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; +import { PartnerRepository } from 'src/repositories/partner.repository'; import { DB } from 'src/schema'; import { TimelineService } from 'src/services/timeline.service'; import { newMediumService } from 'test/medium.factory'; @@ -15,7 +16,7 @@ let defaultDatabase: Kysely; const setup = (db?: Kysely) => { return newMediumService(TimelineService, { database: db || defaultDatabase, - real: [AssetRepository, AccessRepository], + real: [AssetRepository, AccessRepository, PartnerRepository], mock: [LoggingRepository], }); }; @@ -155,5 +156,54 @@ describe(TimelineService.name, () => { const response = JSON.parse(rawResponse); expect(response).toEqual(expect.objectContaining({ isTrashed: [true] })); }); + + it('should return false for favorite status unless asset owner', async () => { + const { sut, ctx } = setup(); + const [{ asset: asset1 }, { asset: asset2 }] = await Promise.all([ + ctx.newUser().then(async ({ user }) => { + const result = await ctx.newAsset({ + ownerId: user.id, + fileCreatedAt: new Date('1970-02-12'), + localDateTime: new Date('1970-02-12'), + isFavorite: true, + }); + await ctx.newExif({ assetId: result.asset.id, make: 'Canon' }); + return result; + }), + ctx.newUser().then(async ({ user }) => { + const result = await ctx.newAsset({ + ownerId: user.id, + fileCreatedAt: new Date('1970-02-13'), + localDateTime: new Date('1970-02-13'), + isFavorite: true, + }); + await ctx.newExif({ assetId: result.asset.id, make: 'Canon' }); + return result; + }), + ]); + + await Promise.all([ + ctx.newPartner({ sharedById: asset1.ownerId, sharedWithId: asset2.ownerId }), + ctx.newPartner({ sharedById: asset2.ownerId, sharedWithId: asset1.ownerId }), + ]); + + const auth1 = factory.auth({ user: { id: asset1.ownerId } }); + const rawResponse1 = await sut.getTimeBucket(auth1, { + timeBucket: '1970-02-01', + withPartners: true, + visibility: AssetVisibility.Timeline, + }); + const response1 = JSON.parse(rawResponse1); + expect(response1).toEqual(expect.objectContaining({ id: [asset2.id, asset1.id], isFavorite: [false, true] })); + + const auth2 = factory.auth({ user: { id: asset2.ownerId } }); + const rawResponse2 = await sut.getTimeBucket(auth2, { + timeBucket: '1970-02-01', + withPartners: true, + visibility: AssetVisibility.Timeline, + }); + const response2 = JSON.parse(rawResponse2); + expect(response2).toEqual(expect.objectContaining({ id: [asset2.id, asset1.id], isFavorite: [true, false] })); + }); }); }); diff --git a/server/test/medium/specs/services/user.service.spec.ts b/server/test/medium/specs/services/user.service.spec.ts index 7643be292e..0d72d39950 100644 --- a/server/test/medium/specs/services/user.service.spec.ts +++ b/server/test/medium/specs/services/user.service.spec.ts @@ -6,6 +6,7 @@ import { CryptoRepository } from 'src/repositories/crypto.repository'; import { JobRepository } from 'src/repositories/job.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; +import { TelemetryRepository } from 'src/repositories/telemetry.repository'; import { UserRepository } from 'src/repositories/user.repository'; import { DB } from 'src/schema'; import { UserService } from 'src/services/user.service'; @@ -21,7 +22,7 @@ const setup = (db?: Kysely) => { return newMediumService(UserService, { database: db || defaultDatabase, real: [CryptoRepository, ConfigRepository, SystemMetadataRepository, UserRepository], - mock: [LoggingRepository, JobRepository], + mock: [LoggingRepository, JobRepository, TelemetryRepository], }); }; diff --git a/server/test/utils.ts b/server/test/utils.ts index c23341d64c..bae9163b80 100644 --- a/server/test/utils.ts +++ b/server/test/utils.ts @@ -47,6 +47,7 @@ import { ProcessRepository } from 'src/repositories/process.repository'; import { SearchRepository } from 'src/repositories/search.repository'; import { ServerInfoRepository } from 'src/repositories/server-info.repository'; import { SessionRepository } from 'src/repositories/session.repository'; +import { SharedLinkAssetRepository } from 'src/repositories/shared-link-asset.repository'; import { SharedLinkRepository } from 'src/repositories/shared-link.repository'; import { StackRepository } from 'src/repositories/stack.repository'; import { StorageRepository } from 'src/repositories/storage.repository'; @@ -236,6 +237,7 @@ export type ServiceOverrides = { serverInfo: ServerInfoRepository; session: SessionRepository; sharedLink: SharedLinkRepository; + sharedLinkAsset: SharedLinkAssetRepository; stack: StackRepository; storage: StorageRepository; sync: SyncRepository; @@ -307,6 +309,7 @@ export const newTestService = ( serverInfo: automock(ServerInfoRepository, { args: [, loggerMock], strict: false }), session: automock(SessionRepository), sharedLink: automock(SharedLinkRepository), + sharedLinkAsset: automock(SharedLinkAssetRepository), stack: automock(StackRepository), storage: newStorageRepositoryMock(), sync: automock(SyncRepository), @@ -357,6 +360,7 @@ export const newTestService = ( overrides.serverInfo || (mocks.serverInfo as As), overrides.session || (mocks.session as As), overrides.sharedLink || (mocks.sharedLink as As), + overrides.sharedLinkAsset || (mocks.sharedLinkAsset as As), overrides.stack || (mocks.stack as As), overrides.storage || (mocks.storage as As), overrides.sync || (mocks.sync as As), diff --git a/web/.nvmrc b/web/.nvmrc index e2228113dd..442c7587a9 100644 --- a/web/.nvmrc +++ b/web/.nvmrc @@ -1 +1 @@ -22.19.0 +22.20.0 diff --git a/web/eslint.config.js b/web/eslint.config.js index d2ef7631e7..792ff90e0c 100644 --- a/web/eslint.config.js +++ b/web/eslint.config.js @@ -121,6 +121,7 @@ export default typescriptEslint.config( 'unicorn/filename-case': 'off', 'unicorn/prefer-top-level-await': 'off', 'unicorn/import-style': 'off', + 'unicorn/no-array-sort': 'off', 'svelte/button-has-type': 'error', '@typescript-eslint/await-thenable': 'error', '@typescript-eslint/no-floating-promises': 'error', diff --git a/web/package.json b/web/package.json index 425c1027c6..5eacb72b85 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "immich-web", - "version": "1.143.1", + "version": "2.1.0", "license": "GNU Affero General Public License version 3", "type": "module", "scripts": { @@ -14,12 +14,11 @@ "check:watch": "npm run check:svelte -- --watch", "check:code": "npm run format && npm run lint:p && npm run check:svelte && npm run check:typescript", "check:all": "npm run check:code && npm run test:cov", - "lint": "eslint . --max-warnings 0", - "lint:p": "eslint-p . --max-warnings 0 --concurrency=4", + "lint": "eslint . --max-warnings 0 --concurrency 4", "lint:fix": "npm run lint -- --fix", "format": "prettier --check .", "format:fix": "prettier --write . && npm run format:i18n", - "format:i18n": "pnpx sort-json ../i18n/*.json", + "format:i18n": "pnpm dlx sort-json ../i18n/*.json", "test": "vitest --run", "test:cov": "vitest --coverage", "test:watch": "vitest dev", @@ -28,7 +27,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.29.0", + "@immich/ui": "^0.34.0", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", @@ -45,7 +44,7 @@ "geo-coordinates-parser": "^1.7.4", "geojson": "^0.5.0", "handlebars": "^4.7.8", - "happy-dom": "^18.0.1", + "happy-dom": "^20.0.0", "intl-messageformat": "^10.7.11", "justified-layout": "^4.1.0", "lodash-es": "^4.17.21", @@ -63,14 +62,14 @@ "thumbhash": "^0.1.1" }, "devDependencies": { - "@eslint/js": "^9.18.0", + "@eslint/js": "^9.36.0", "@faker-js/faker": "^10.0.0", "@koddsson/eslint-plugin-tscompat": "^0.2.0", "@socket.io/component-emitter": "^3.1.0", "@sveltejs/adapter-static": "^3.0.8", "@sveltejs/enhanced-img": "^0.8.0", "@sveltejs/kit": "^2.27.1", - "@sveltejs/vite-plugin-svelte": "6.2.0", + "@sveltejs/vite-plugin-svelte": "6.2.1", "@tailwindcss/vite": "^4.1.7", "@testing-library/jest-dom": "^6.4.2", "@testing-library/svelte": "^5.2.8", @@ -83,30 +82,29 @@ "@types/qrcode": "^1.5.5", "@vitest/coverage-v8": "^3.0.0", "dotenv": "^17.0.0", - "eslint": "^9.18.0", + "eslint": "^9.36.0", "eslint-config-prettier": "^10.1.8", - "eslint-p": "^0.26.0", "eslint-plugin-compat": "^6.0.2", - "eslint-plugin-svelte": "^3.9.0", - "eslint-plugin-unicorn": "^60.0.0", + "eslint-plugin-svelte": "^3.12.4", + "eslint-plugin-unicorn": "^61.0.2", "factory.ts": "^1.4.1", "globals": "^16.0.0", - "happy-dom": "^18.0.1", + "happy-dom": "^20.0.0", "prettier": "^3.4.2", "prettier-plugin-organize-imports": "^4.0.0", "prettier-plugin-sort-json": "^4.1.1", "prettier-plugin-svelte": "^3.3.3", "rollup-plugin-visualizer": "^6.0.0", - "svelte": "5.38.10", + "svelte": "5.39.11", "svelte-check": "^4.1.5", - "svelte-eslint-parser": "^1.2.0", + "svelte-eslint-parser": "^1.3.3", "tailwindcss": "^4.1.7", "typescript": "^5.8.3", - "typescript-eslint": "^8.28.0", + "typescript-eslint": "^8.45.0", "vite": "^7.1.2", "vitest": "^3.0.0" }, "volta": { - "node": "22.19.0" + "node": "22.20.0" } } diff --git a/web/src/lib/components/ServerAboutItem.svelte b/web/src/lib/components/ServerAboutItem.svelte index 9e169a9839..415c047732 100644 --- a/web/src/lib/components/ServerAboutItem.svelte +++ b/web/src/lib/components/ServerAboutItem.svelte @@ -16,7 +16,7 @@ {#if versionHref} - {version} + {version} {:else} {version} {/if} diff --git a/web/src/lib/components/admin-settings/AuthSettings.svelte b/web/src/lib/components/admin-settings/AuthSettings.svelte index d7b73430e3..dbc96ac02a 100644 --- a/web/src/lib/components/admin-settings/AuthSettings.svelte +++ b/web/src/lib/components/admin-settings/AuthSettings.svelte @@ -86,7 +86,7 @@ {#snippet children({ message })} {#snippet children({ message })} + + {:else if tag === 'implications-link'} void timelineManager.updateOptions({ albumId: album.id, order: album.order })); - onDestroy(() => timelineManager.destroy()); + const options = $derived({ albumId: album.id, order: album.order }); + let timelineManager = $state() as TimelineManager; const assetInteraction = new AssetInteraction(); @@ -61,7 +59,7 @@ />
- +

diff --git a/web/src/lib/components/asset-viewer/actions/action.ts b/web/src/lib/components/asset-viewer/actions/action.ts index 680dced7ca..6a807d2766 100644 --- a/web/src/lib/components/asset-viewer/actions/action.ts +++ b/web/src/lib/components/asset-viewer/actions/action.ts @@ -12,6 +12,7 @@ type ActionMap = { [AssetAction.RESTORE]: { asset: TimelineAsset }; [AssetAction.ADD]: { asset: TimelineAsset }; [AssetAction.ADD_TO_ALBUM]: { asset: TimelineAsset; album: AlbumResponseDto }; + [AssetAction.STACK]: { stack: StackResponseDto }; [AssetAction.UNSTACK]: { assets: TimelineAsset[] }; [AssetAction.KEEP_THIS_DELETE_OTHERS]: { asset: TimelineAsset }; [AssetAction.SET_STACK_PRIMARY_ASSET]: { stack: StackResponseDto }; diff --git a/web/src/lib/components/asset-viewer/actions/add-to-stack-action.svelte b/web/src/lib/components/asset-viewer/actions/add-to-stack-action.svelte new file mode 100644 index 0000000000..5b30beff6e --- /dev/null +++ b/web/src/lib/components/asset-viewer/actions/add-to-stack-action.svelte @@ -0,0 +1,37 @@ + + + diff --git a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte index bbdd0dcd5f..d61af04db6 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte @@ -4,6 +4,7 @@ import CastButton from '$lib/cast/cast-button.svelte'; import type { OnAction, PreAction } from '$lib/components/asset-viewer/actions/action'; import AddToAlbumAction from '$lib/components/asset-viewer/actions/add-to-album-action.svelte'; + import AddToStackAction from '$lib/components/asset-viewer/actions/add-to-stack-action.svelte'; import ArchiveAction from '$lib/components/asset-viewer/actions/archive-action.svelte'; import CloseAction from '$lib/components/asset-viewer/actions/close-action.svelte'; import DeleteAction from '$lib/components/asset-viewer/actions/delete-action.svelte'; @@ -196,6 +197,7 @@ {/if} {#if isOwner} + {#if stack} diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte index 5484cd2c08..0fe8610b3d 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte @@ -22,8 +22,8 @@ import { AssetJobName, AssetTypeEnum, - getAssetInfo, getAllAlbums, + getAssetInfo, getStack, runAssetJobs, type AlbumResponseDto, @@ -303,10 +303,8 @@ const handleStopSlideshow = async () => { try { - // eslint-disable-next-line tscompat/tscompat if (document.fullscreenElement) { document.body.style.cursor = ''; - // eslint-disable-next-line tscompat/tscompat await document.exitFullscreen(); } } catch (error) { @@ -336,6 +334,7 @@ } break; } + case AssetAction.STACK: case AssetAction.SET_STACK_PRIMARY_ASSET: { stack = action.stack; break; diff --git a/web/src/lib/components/asset-viewer/detail-panel.svelte b/web/src/lib/components/asset-viewer/detail-panel.svelte index c968c986a8..17cba69d99 100644 --- a/web/src/lib/components/asset-viewer/detail-panel.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel.svelte @@ -5,12 +5,9 @@ import DetailPanelLocation from '$lib/components/asset-viewer/detail-panel-location.svelte'; import DetailPanelRating from '$lib/components/asset-viewer/detail-panel-star-rating.svelte'; import DetailPanelTags from '$lib/components/asset-viewer/detail-panel-tags.svelte'; - import ChangeDate, { - type AbsoluteResult, - type RelativeResult, - } from '$lib/components/shared-components/change-date.svelte'; import { AppRoute, QueryParameter, timeToLoadTheMap } from '$lib/constants'; import { authManager } from '$lib/managers/auth-manager.svelte'; + import AssetChangeDateModal from '$lib/modals/AssetChangeDateModal.svelte'; import { isFaceEditMode } from '$lib/stores/face-edit.svelte'; import { boundingBoxesArray } from '$lib/stores/people.store'; import { locale } from '$lib/stores/preferences.store'; @@ -19,12 +16,11 @@ import { getAssetThumbnailUrl, getPeopleThumbnailUrl } from '$lib/utils'; import { delay, getDimensions } from '$lib/utils/asset-utils'; import { getByteUnitString } from '$lib/utils/byte-units'; - import { handleError } from '$lib/utils/handle-error'; import { getMetadataSearchQuery } from '$lib/utils/metadata-search'; - import { fromISODateTime, fromISODateTimeUTC } from '$lib/utils/timeline-util'; + import { fromISODateTime, fromISODateTimeUTC, toTimelineAsset } from '$lib/utils/timeline-util'; import { getParentPath } from '$lib/utils/tree-utils'; - import { AssetMediaSize, getAssetInfo, updateAsset, type AlbumResponseDto, type AssetResponseDto } from '@immich/sdk'; - import { Icon, IconButton, LoadingSpinner } from '@immich/ui'; + import { AssetMediaSize, getAssetInfo, type AlbumResponseDto, type AssetResponseDto } from '@immich/sdk'; + import { Icon, IconButton, LoadingSpinner, modalManager } from '@immich/ui'; import { mdiCalendar, mdiCameraIris, @@ -59,7 +55,7 @@ let people = $derived(asset.people || []); let unassignedFaces = $derived(asset.unassignedFaces || []); let showingHiddenPeople = $state(false); - let timeZone = $derived(asset.exifInfo?.timeZone); + let timeZone = $derived(asset.exifInfo?.timeZone ?? undefined); let dateTime = $derived( timeZone && asset.exifInfo?.dateTimeOriginal ? fromISODateTime(asset.exifInfo.dateTimeOriginal, timeZone) @@ -112,18 +108,13 @@ const toggleAssetPath = () => (showAssetPath = !showAssetPath); - let isShowChangeDate = $state(false); - - async function handleConfirmChangeDate(result: AbsoluteResult | RelativeResult) { - isShowChangeDate = false; - try { - if (result.mode === 'absolute') { - await updateAsset({ id: asset.id, updateAssetDto: { dateTimeOriginal: result.date } }); - } - } catch (error) { - handleError(error, $t('errors.unable_to_change_date')); + const handleChangeDate = async () => { + if (!isOwner) { + return; } - } + + await modalManager.show(AssetChangeDateModal, { asset: toTimelineAsset(asset), initialDate: dateTime }); + };
@@ -280,7 +271,7 @@ + + +

{$t('mobile_app_download_onboarding_note')}

diff --git a/web/src/lib/components/onboarding-page/onboarding-storage-template.svelte b/web/src/lib/components/onboarding-page/onboarding-storage-template.svelte index 4e266e410c..0a349af6f5 100644 --- a/web/src/lib/components/onboarding-page/onboarding-storage-template.svelte +++ b/web/src/lib/components/onboarding-page/onboarding-storage-template.svelte @@ -21,7 +21,7 @@

{#snippet children({ message })} - {message} + {message} {/snippet}

diff --git a/web/src/lib/components/server-statistics/ServerStatisticsCard.svelte b/web/src/lib/components/server-statistics/ServerStatisticsCard.svelte index fc9853ca7d..c44839ae38 100644 --- a/web/src/lib/components/server-statistics/ServerStatisticsCard.svelte +++ b/web/src/lib/components/server-statistics/ServerStatisticsCard.svelte @@ -29,7 +29,7 @@
{zeros()}{value} {#if unit} - {unit} + {unit} {/if}
diff --git a/web/src/lib/components/shared-components/change-date.svelte b/web/src/lib/components/shared-components/change-date.svelte deleted file mode 100644 index f7acf06890..0000000000 --- a/web/src/lib/components/shared-components/change-date.svelte +++ /dev/null @@ -1,284 +0,0 @@ - - - (confirmed ? handleConfirm() : onCancel())} -> - {#snippet promptSnippet()} - {#if withDuration} -
- - - -
- {/if} -
-
-
- - -
-
-
- - -
-
- {#if timezoneInput} -
- handleOnSelect(option)} - /> -
- {/if} -
- {$t('edit_date_and_time_by_offset_interval', { values: { from: intervalFrom, to: intervalTo } })} -
-
-
- {/snippet} -
diff --git a/web/src/lib/components/shared-components/combobox.svelte b/web/src/lib/components/shared-components/combobox.svelte index 725b456912..955ca64565 100644 --- a/web/src/lib/components/shared-components/combobox.svelte +++ b/web/src/lib/components/shared-components/combobox.svelte @@ -23,7 +23,7 @@ import { focusOutside } from '$lib/actions/focus-outside'; import { shortcuts } from '$lib/actions/shortcut'; import { generateId } from '$lib/utils/generate-id'; - import { Icon, IconButton } from '@immich/ui'; + import { Icon, IconButton, Label } from '@immich/ui'; import { mdiClose, mdiMagnify, mdiUnfoldMoreHorizontal } from '@mdi/js'; import { onMount, tick } from 'svelte'; import { t } from 'svelte-i18n'; @@ -251,7 +251,7 @@ - +
0); + + onMount(async () => { + try { + await notificationManager.refresh(); + } catch (error) { + console.error('Failed to load notifications on mount', error); + } + }); @@ -125,15 +134,25 @@ onEscape: () => (shouldShowNotificationPanel = false), }} > - (shouldShowNotificationPanel = !shouldShowNotificationPanel)} - aria-label={$t('notifications')} - /> +
+ (shouldShowNotificationPanel = !shouldShowNotificationPanel)} + aria-label={$t('notifications')} + /> + + {#if hasUnreadNotifications} +
+ {notificationManager.notifications.length} +
+ {/if} +
{#if shouldShowNotificationPanel} diff --git a/web/src/lib/components/shared-components/navigation-bar/notification-item.svelte b/web/src/lib/components/shared-components/navigation-bar/notification-item.svelte index 0d05e2d6d7..e713eb7743 100644 --- a/web/src/lib/components/shared-components/navigation-bar/notification-item.svelte +++ b/web/src/lib/components/shared-components/navigation-bar/notification-item.svelte @@ -1,12 +1,19 @@
{#each notificationManager.notifications as notification (notification.id)}
- markAsRead(id)} /> +
{/each} diff --git a/web/src/lib/components/shared-components/side-bar/side-bar-link.svelte b/web/src/lib/components/shared-components/side-bar/side-bar-link.svelte index f0cb21b402..b3c44da61a 100644 --- a/web/src/lib/components/shared-components/side-bar/side-bar-link.svelte +++ b/web/src/lib/components/shared-components/side-bar/side-bar-link.svelte @@ -57,7 +57,9 @@ draggable="false" aria-current={isSelected ? 'page' : undefined} class="flex w-full place-items-center gap-4 rounded-e-full py-3 transition-[padding] delay-100 duration-100 hover:cursor-pointer hover:bg-subtle hover:text-immich-primary dark:text-immich-dark-fg dark:hover:bg-immich-dark-gray dark:hover:text-immich-dark-primary - {isSelected ? 'bg-immich-primary/10 text-primary hover:bg-immich-primary/10 dark:bg-immich-dark-primary/10' : ''}" + {isSelected + ? 'bg-immich-primary/10 dark:text-primary text-primary hover:bg-immich-primary/10 dark:bg-immich-dark-primary/10' + : ''}" >
diff --git a/web/src/lib/components/timeline/Scrubber.svelte b/web/src/lib/components/timeline/Scrubber.svelte index d6e5bb0833..2c22af98ad 100644 --- a/web/src/lib/components/timeline/Scrubber.svelte +++ b/web/src/lib/components/timeline/Scrubber.svelte @@ -1,6 +1,6 @@ - + - + timelineManager.destroy()} /> -{#if isShowDeleteConfirmation} - (isShowDeleteConfirmation = false)} - onConfirm={() => handlePromiseError(trashOrDelete(true))} - /> -{/if} - -{#if isShowSelectDate} - { - isShowSelectDate = false; - if (dateString.mode == 'absolute') { - const asset = await timelineManager.getClosestAssetToDate( - (DateTime.fromISO(dateString.date) as DateTime).toObject(), - ); - if (asset) { - setFocusAsset(asset); - } - } - }} - onCancel={() => (isShowSelectDate = false)} - /> -{/if} + scrollToAsset(asset) ?? false} + {timelineManager} + {assetInteraction} + bind:isShowDeleteConfirmation + {onEscape} +/> {#if timelineManager.months.length > 0} { @@ -869,10 +527,10 @@ if (evt.key === 'ArrowUp') { amount = -amount; if (shiftKeyIsDown) { - element?.scrollBy({ top: amount, behavior: 'smooth' }); + scrollableElement?.scrollBy({ top: amount, behavior: 'smooth' }); } } else if (evt.key === 'ArrowDown') { - element?.scrollBy({ top: amount, behavior: 'smooth' }); + scrollableElement?.scrollBy({ top: amount, behavior: 'smooth' }); } }} /> @@ -885,19 +543,19 @@ style:margin-right={(usingMobileDevice ? 0 : scrubberWidth) + 'px'} tabindex="-1" bind:clientHeight={timelineManager.viewportHeight} - bind:clientWidth={null, (v: number) => ((timelineManager.viewportWidth = v), updateSlidingWindow())} - bind:this={element} - onscroll={() => (handleTimelineScroll(), updateSlidingWindow(), updateIsScrolling())} + bind:clientWidth={timelineManager.viewportWidth} + bind:this={scrollableElement} + onscroll={() => (handleTimelineScroll(), timelineManager.updateSlidingWindow(), updateIsScrolling())} >
- +
{:else if display}
handleGroupSelect(timelineManager, title, assets)} onSelectAssetCandidates={handleSelectAssetCandidates} onSelectAssets={handleSelectAssets} - onScrollCompensation={handleScrollCompensation} {customLayout} {onThumbnailClick} /> @@ -953,33 +607,18 @@ {/each}

{#if $showAssetViewer} - {#await import('../asset-viewer/asset-viewer.svelte') then { default: AssetViewer }} - - {/await} + {/if} diff --git a/web/src/lib/components/timeline/TimelineAssetViewer.svelte b/web/src/lib/components/timeline/TimelineAssetViewer.svelte new file mode 100644 index 0000000000..9459eef25b --- /dev/null +++ b/web/src/lib/components/timeline/TimelineAssetViewer.svelte @@ -0,0 +1,187 @@ + + +{#await import('../asset-viewer/asset-viewer.svelte') then { default: AssetViewer }} + +{/await} diff --git a/web/src/lib/components/timeline/TimelineDateGroup.svelte b/web/src/lib/components/timeline/TimelineDateGroup.svelte index 30f6e65edb..cd0dc9a212 100644 --- a/web/src/lib/components/timeline/TimelineDateGroup.svelte +++ b/web/src/lib/components/timeline/TimelineDateGroup.svelte @@ -14,7 +14,7 @@ import { fromTimelinePlainDate, getDateLocaleString } from '$lib/utils/timeline-util'; import { Icon } from '@immich/ui'; - import type { Snippet } from 'svelte'; + import { type Snippet } from 'svelte'; import { flip } from 'svelte/animate'; import { scale } from 'svelte/transition'; @@ -33,7 +33,6 @@ onSelect: ({ title, assets }: { title: string; assets: TimelineAsset[] }) => void; onSelectAssets: (asset: TimelineAsset) => void; onSelectAssetCandidates: (asset: TimelineAsset | null) => void; - onScrollCompensation: (compensation: { heightDelta?: number; scrollTop?: number }) => void; onThumbnailClick?: ( asset: TimelineAsset, timelineManager: TimelineManager, @@ -59,7 +58,7 @@ onSelect, onSelectAssets, onSelectAssetCandidates, - onScrollCompensation, + onThumbnailClick, }: Props = $props(); @@ -134,13 +133,6 @@ }); return getDateLocaleString(date); }; - - $effect.root(() => { - if (timelineManager.scrollCompensation.monthGroup === monthGroup) { - onScrollCompensation(timelineManager.scrollCompensation); - timelineManager.clearScrollCompensation(); - } - }); {#each filterIntersecting(monthGroup.dayGroups) as dayGroup, groupIndex (dayGroup.day)} diff --git a/web/src/lib/components/timeline/actions/ChangeDateAction.svelte b/web/src/lib/components/timeline/actions/ChangeDateAction.svelte index 5e869050d3..fdfbf48749 100644 --- a/web/src/lib/components/timeline/actions/ChangeDateAction.svelte +++ b/web/src/lib/components/timeline/actions/ChangeDateAction.svelte @@ -1,18 +1,11 @@ {#if menuItem} - (isShowChangeDate = true)} /> -{/if} -{#if isShowChangeDate} - (isShowChangeDate = false)} - /> + {/if} diff --git a/web/src/lib/components/timeline/actions/ChangeDescriptionAction.svelte b/web/src/lib/components/timeline/actions/ChangeDescriptionAction.svelte index cf16d01172..6a4da927c3 100644 --- a/web/src/lib/components/timeline/actions/ChangeDescriptionAction.svelte +++ b/web/src/lib/components/timeline/actions/ChangeDescriptionAction.svelte @@ -2,7 +2,7 @@ import { getAssetControlContext } from '$lib/components/timeline/AssetSelectControlBar.svelte'; import AssetUpdateDescriptionConfirmModal from '$lib/modals/AssetUpdateDescriptionConfirmModal.svelte'; import { user } from '$lib/stores/user.store'; - import { getSelectedAssets } from '$lib/utils/asset-utils'; + import { getOwnedAssetsWithWarning } from '$lib/utils/asset-utils'; import { handleError } from '$lib/utils/handle-error'; import { updateAssets } from '@immich/sdk'; import { modalManager } from '@immich/ui'; @@ -20,7 +20,7 @@ const handleUpdateDescription = async () => { const description = await modalManager.show(AssetUpdateDescriptionConfirmModal); if (description) { - const ids = getSelectedAssets(getOwnedAssets(), $user); + const ids = getOwnedAssetsWithWarning(getOwnedAssets(), $user); try { await updateAssets({ assetBulkUpdateDto: { ids, description } }); diff --git a/web/src/lib/components/timeline/actions/ChangeLocationAction.svelte b/web/src/lib/components/timeline/actions/ChangeLocationAction.svelte index 2ccfe3704d..bfb10bb382 100644 --- a/web/src/lib/components/timeline/actions/ChangeLocationAction.svelte +++ b/web/src/lib/components/timeline/actions/ChangeLocationAction.svelte @@ -2,7 +2,7 @@ import ChangeLocation from '$lib/components/shared-components/change-location.svelte'; import { getAssetControlContext } from '$lib/components/timeline/AssetSelectControlBar.svelte'; import { user } from '$lib/stores/user.store'; - import { getSelectedAssets } from '$lib/utils/asset-utils'; + import { getOwnedAssetsWithWarning } from '$lib/utils/asset-utils'; import { handleError } from '$lib/utils/handle-error'; import { updateAssets } from '@immich/sdk'; import { mdiMapMarkerMultipleOutline } from '@mdi/js'; @@ -25,7 +25,7 @@ return; } - const ids = getSelectedAssets(getOwnedAssets(), $user); + const ids = getOwnedAssetsWithWarning(getOwnedAssets(), $user); try { await updateAssets({ assetBulkUpdateDto: { ids, latitude: point.lat, longitude: point.lng } }); diff --git a/web/src/lib/components/timeline/actions/TimelineKeyboardActions.svelte b/web/src/lib/components/timeline/actions/TimelineKeyboardActions.svelte new file mode 100644 index 0000000000..d3f151a974 --- /dev/null +++ b/web/src/lib/components/timeline/actions/TimelineKeyboardActions.svelte @@ -0,0 +1,202 @@ + + + + +{#if isShowDeleteConfirmation} + (isShowDeleteConfirmation = false)} + onConfirm={() => handlePromiseError(trashOrDelete(true))} + /> +{/if} diff --git a/web/src/lib/components/user-settings-page/app-settings.svelte b/web/src/lib/components/user-settings-page/app-settings.svelte index ffb0263c94..138a5555d4 100644 --- a/web/src/lib/components/user-settings-page/app-settings.svelte +++ b/web/src/lib/components/user-settings-page/app-settings.svelte @@ -7,6 +7,7 @@ import { themeManager } from '$lib/managers/theme-manager.svelte'; import { alwaysLoadOriginalFile, + autoPlayVideo, locale, loopVideo, playVideoThumbnailOnHover, @@ -108,6 +109,13 @@ bind:checked={$playVideoThumbnailOnHover} /> +
+ +
diff --git a/web/src/lib/components/utilities-page/utilities-menu.svelte b/web/src/lib/components/utilities-page/utilities-menu.svelte index fc747dc6af..bf7090e310 100644 --- a/web/src/lib/components/utilities-page/utilities-menu.svelte +++ b/web/src/lib/components/utilities-page/utilities-menu.svelte @@ -1,7 +1,15 @@ -
-
- {title} -
-
+
+ {#if title} +
+ {title} +
+ {/if} +
diff --git a/web/src/lib/managers/VirtualScrollManager/VirtualScrollManager.svelte.ts b/web/src/lib/managers/VirtualScrollManager/VirtualScrollManager.svelte.ts new file mode 100644 index 0000000000..c5f48f735a --- /dev/null +++ b/web/src/lib/managers/VirtualScrollManager/VirtualScrollManager.svelte.ts @@ -0,0 +1,168 @@ +import { debounce } from 'lodash-es'; + +type LayoutOptions = { + headerHeight: number; + rowHeight: number; + gap: number; +}; +export abstract class VirtualScrollManager { + topSectionHeight = $state(0); + bodySectionHeight = $state(0); + bottomSectionHeight = $state(0); + totalViewerHeight = $derived.by(() => this.topSectionHeight + this.bodySectionHeight + this.bottomSectionHeight); + + visibleWindow = $derived.by(() => ({ + top: this.#scrollTop, + bottom: this.#scrollTop + this.viewportHeight, + })); + + #viewportHeight = $state(0); + #viewportWidth = $state(0); + #scrollTop = $state(0); + #rowHeight = $state(235); + #headerHeight = $state(48); + #gap = $state(12); + #scrolling = $state(false); + #suspendTransitions = $state(false); + #resetScrolling = debounce(() => (this.#scrolling = false), 1000); + #resetSuspendTransitions = debounce(() => (this.suspendTransitions = false), 1000); + #justifiedLayoutOptions = $derived.by(() => ({ + spacing: 2, + heightTolerance: 0.15, + rowHeight: this.#rowHeight, + rowWidth: Math.floor(this.viewportWidth), + })); + + constructor() { + this.setLayoutOptions(); + } + + get scrollTop() { + return 0; + } + + get justifiedLayoutOptions() { + return this.#justifiedLayoutOptions; + } + + get maxScrollPercent() { + const totalHeight = this.totalViewerHeight; + return (totalHeight - this.viewportHeight) / totalHeight; + } + + get maxScroll() { + return this.totalViewerHeight - this.viewportHeight; + } + + #setHeaderHeight(value: number) { + if (this.#headerHeight == value) { + return false; + } + this.#headerHeight = value; + return true; + } + + get headerHeight() { + return this.#headerHeight; + } + + #setGap(value: number) { + if (this.#gap == value) { + return false; + } + this.#gap = value; + return true; + } + + get gap() { + return this.#gap; + } + + #setRowHeight(value: number) { + if (this.#rowHeight == value) { + return false; + } + this.#rowHeight = value; + return true; + } + + get rowHeight() { + return this.#rowHeight; + } + + set scrolling(value: boolean) { + this.#scrolling = value; + if (value) { + this.suspendTransitions = true; + this.#resetScrolling(); + } + } + + get scrolling() { + return this.#scrolling; + } + + set suspendTransitions(value: boolean) { + this.#suspendTransitions = value; + if (value) { + this.#resetSuspendTransitions(); + } + } + + get suspendTransitions() { + return this.#suspendTransitions; + } + + set viewportWidth(value: number) { + const changed = value !== this.#viewportWidth; + this.#viewportWidth = value; + this.suspendTransitions = true; + void this.updateViewportGeometry(changed); + } + + get viewportWidth() { + return this.#viewportWidth; + } + + set viewportHeight(value: number) { + this.#viewportHeight = value; + this.#suspendTransitions = true; + void this.updateViewportGeometry(false); + } + + get viewportHeight() { + return this.#viewportHeight; + } + + get hasEmptyViewport() { + return this.viewportWidth === 0 || this.viewportHeight === 0; + } + + protected updateIntersections(): void {} + + protected updateViewportGeometry(_: boolean) {} + + setLayoutOptions({ headerHeight = 48, rowHeight = 235, gap = 12 }: Partial = {}) { + let changed = false; + changed ||= this.#setHeaderHeight(headerHeight); + changed ||= this.#setGap(gap); + changed ||= this.#setRowHeight(rowHeight); + if (changed) { + this.refreshLayout(); + } + } + + updateSlidingWindow() { + const scrollTop = this.scrollTop; + if (this.#scrollTop !== scrollTop) { + this.#scrollTop = scrollTop; + this.updateIntersections(); + } + } + + refreshLayout() { + this.updateIntersections(); + } + + destroy(): void {} +} diff --git a/web/src/lib/managers/theme-manager.svelte.ts b/web/src/lib/managers/theme-manager.svelte.ts index 4efac4a44d..394c9850de 100644 --- a/web/src/lib/managers/theme-manager.svelte.ts +++ b/web/src/lib/managers/theme-manager.svelte.ts @@ -67,11 +67,7 @@ class ThemeManager { const theme: ThemeSetting = value === 'system' ? { system: true, value: getDefaultTheme() } : { system: false, value }; - if (theme.value === Theme.LIGHT) { - document.documentElement.classList.remove('dark'); - } else { - document.documentElement.classList.add('dark'); - } + document.documentElement.classList.toggle('dark', !(theme.value === Theme.LIGHT)); this.#theme.current = theme; diff --git a/web/src/lib/managers/timeline-manager/day-group.svelte.ts b/web/src/lib/managers/timeline-manager/day-group.svelte.ts index 9d5008bf83..57cf513a7d 100644 --- a/web/src/lib/managers/timeline-manager/day-group.svelte.ts +++ b/web/src/lib/managers/timeline-manager/day-group.svelte.ts @@ -82,11 +82,6 @@ export class DayGroup { return this.viewerAssets[0]?.asset; } - getRandomAsset() { - const random = Math.floor(Math.random() * this.viewerAssets.length); - return this.viewerAssets[random]; - } - *assetsIterator(options: { startAsset?: TimelineAsset; direction?: Direction } = {}) { const isEarlier = (options?.direction ?? 'earlier') === 'earlier'; let assetIndex = options?.startAsset diff --git a/web/src/lib/managers/timeline-manager/internal/intersection-support.svelte.ts b/web/src/lib/managers/timeline-manager/internal/intersection-support.svelte.ts index 185d74e0b0..bdf2b17cbe 100644 --- a/web/src/lib/managers/timeline-manager/internal/intersection-support.svelte.ts +++ b/web/src/lib/managers/timeline-manager/internal/intersection-support.svelte.ts @@ -55,7 +55,7 @@ export function calculateMonthGroupIntersecting( } /** - * Calculate intersection for viewer assets with additional parameters like header height and scroll compensation + * Calculate intersection for viewer assets with additional parameters like header height */ export function calculateViewerAssetIntersecting( timelineManager: TimelineManager, @@ -64,12 +64,8 @@ export function calculateViewerAssetIntersecting( expandTop: number = INTERSECTION_EXPAND_TOP, expandBottom: number = INTERSECTION_EXPAND_BOTTOM, ) { - const scrollCompensationHeightDelta = timelineManager.scrollCompensation?.heightDelta ?? 0; - - const topWindow = - timelineManager.visibleWindow.top - timelineManager.headerHeight - expandTop + scrollCompensationHeightDelta; - const bottomWindow = - timelineManager.visibleWindow.bottom + timelineManager.headerHeight + expandBottom + scrollCompensationHeightDelta; + const topWindow = timelineManager.visibleWindow.top - timelineManager.headerHeight - expandTop; + const bottomWindow = timelineManager.visibleWindow.bottom + timelineManager.headerHeight + expandBottom; const positionBottom = positionTop + positionHeight; diff --git a/web/src/lib/managers/timeline-manager/internal/layout-support.svelte.ts b/web/src/lib/managers/timeline-manager/internal/layout-support.svelte.ts index 9ba09c5a45..434b2d1847 100644 --- a/web/src/lib/managers/timeline-manager/internal/layout-support.svelte.ts +++ b/web/src/lib/managers/timeline-manager/internal/layout-support.svelte.ts @@ -28,7 +28,7 @@ export function layoutMonthGroup(timelineManager: TimelineManager, month: MonthG let dayGroupRow = 0; let dayGroupCol = 0; - const options = timelineManager.createLayoutOptions(); + const options = timelineManager.justifiedLayoutOptions; for (const dayGroup of month.dayGroups) { dayGroup.layout(options, noDefer); diff --git a/web/src/lib/managers/timeline-manager/internal/load-support.svelte.ts b/web/src/lib/managers/timeline-manager/internal/load-support.svelte.ts index 82a9e8083d..e6a80afc7f 100644 --- a/web/src/lib/managers/timeline-manager/internal/load-support.svelte.ts +++ b/web/src/lib/managers/timeline-manager/internal/load-support.svelte.ts @@ -4,7 +4,6 @@ import { getTimeBucket } from '@immich/sdk'; import type { MonthGroup } from '../month-group.svelte'; import type { TimelineManager } from '../timeline-manager.svelte'; import type { TimelineManagerOptions } from '../types'; -import { layoutMonthGroup } from './layout-support.svelte'; export async function loadFromTimeBuckets( timelineManager: TimelineManager, @@ -55,6 +54,4 @@ export async function loadFromTimeBuckets( )}`, ); } - - layoutMonthGroup(timelineManager, monthGroup); } diff --git a/web/src/lib/managers/timeline-manager/internal/search-support.svelte.spec.ts b/web/src/lib/managers/timeline-manager/internal/search-support.svelte.spec.ts new file mode 100644 index 0000000000..2b8ca930eb --- /dev/null +++ b/web/src/lib/managers/timeline-manager/internal/search-support.svelte.spec.ts @@ -0,0 +1,75 @@ +import { describe, expect, it } from 'vitest'; +import type { MonthGroup } from '../month-group.svelte'; +import { findClosestGroupForDate } from './search-support.svelte'; + +function createMockMonthGroup(year: number, month: number): MonthGroup { + return { + yearMonth: { year, month }, + } as MonthGroup; +} + +describe('findClosestGroupForDate', () => { + it('should return undefined for empty months array', () => { + const result = findClosestGroupForDate([], { year: 2024, month: 1 }); + expect(result).toBeUndefined(); + }); + + it('should return the only month when there is only one month', () => { + const months = [createMockMonthGroup(2024, 6)]; + const result = findClosestGroupForDate(months, { year: 2025, month: 1 }); + expect(result?.yearMonth).toEqual({ year: 2024, month: 6 }); + }); + + it('should return exact match when available', () => { + const months = [createMockMonthGroup(2024, 1), createMockMonthGroup(2024, 6), createMockMonthGroup(2024, 12)]; + const result = findClosestGroupForDate(months, { year: 2024, month: 6 }); + expect(result?.yearMonth).toEqual({ year: 2024, month: 6 }); + }); + + it('should find closest month when target is between two months', () => { + const months = [createMockMonthGroup(2024, 1), createMockMonthGroup(2024, 6), createMockMonthGroup(2024, 12)]; + const result = findClosestGroupForDate(months, { year: 2024, month: 4 }); + expect(result?.yearMonth).toEqual({ year: 2024, month: 6 }); + }); + + it('should handle year boundaries correctly (2023-12 vs 2024-01)', () => { + const months = [createMockMonthGroup(2023, 12), createMockMonthGroup(2024, 2)]; + const result = findClosestGroupForDate(months, { year: 2024, month: 1 }); + // 2024-01 is 1 month from 2023-12 and 1 month from 2024-02 + // Should return first encountered with min distance (2023-12) + expect(result?.yearMonth).toEqual({ year: 2023, month: 12 }); + }); + + it('should correctly calculate distance across years', () => { + const months = [createMockMonthGroup(2022, 6), createMockMonthGroup(2024, 6)]; + const result = findClosestGroupForDate(months, { year: 2023, month: 6 }); + // Both are exactly 12 months away, should return first encountered + expect(result?.yearMonth).toEqual({ year: 2022, month: 6 }); + }); + + it('should handle target before all months', () => { + const months = [createMockMonthGroup(2024, 6), createMockMonthGroup(2024, 12)]; + const result = findClosestGroupForDate(months, { year: 2024, month: 1 }); + expect(result?.yearMonth).toEqual({ year: 2024, month: 6 }); + }); + + it('should handle target after all months', () => { + const months = [createMockMonthGroup(2024, 1), createMockMonthGroup(2024, 6)]; + const result = findClosestGroupForDate(months, { year: 2025, month: 1 }); + expect(result?.yearMonth).toEqual({ year: 2024, month: 6 }); + }); + + it('should handle multiple years correctly', () => { + const months = [createMockMonthGroup(2020, 1), createMockMonthGroup(2022, 1), createMockMonthGroup(2024, 1)]; + const result = findClosestGroupForDate(months, { year: 2023, month: 1 }); + // 2023-01 is 12 months from 2022-01 and 12 months from 2024-01 + expect(result?.yearMonth).toEqual({ year: 2022, month: 1 }); + }); + + it('should prefer closer month when one is clearly closer', () => { + const months = [createMockMonthGroup(2024, 1), createMockMonthGroup(2024, 10)]; + const result = findClosestGroupForDate(months, { year: 2024, month: 11 }); + // 2024-11 is 1 month from 2024-10 and 10 months from 2024-01 + expect(result?.yearMonth).toEqual({ year: 2024, month: 10 }); + }); +}); diff --git a/web/src/lib/managers/timeline-manager/internal/search-support.svelte.ts b/web/src/lib/managers/timeline-manager/internal/search-support.svelte.ts index 7e6ae734dc..52a37b52d0 100644 --- a/web/src/lib/managers/timeline-manager/internal/search-support.svelte.ts +++ b/web/src/lib/managers/timeline-manager/internal/search-support.svelte.ts @@ -1,5 +1,6 @@ import { plainDateTimeCompare, type TimelineYearMonth } from '$lib/utils/timeline-util'; import { AssetOrder } from '@immich/sdk'; +import { DateTime } from 'luxon'; import type { MonthGroup } from '../month-group.svelte'; import type { TimelineManager } from '../timeline-manager.svelte'; import type { AssetDescriptor, Direction, TimelineAsset } from '../types'; @@ -143,3 +144,22 @@ export function findMonthGroupForDate(timelineManager: TimelineManager, targetYe } } } + +export function findClosestGroupForDate(months: MonthGroup[], targetYearMonth: TimelineYearMonth) { + const targetDate = DateTime.fromObject({ year: targetYearMonth.year, month: targetYearMonth.month }); + + let closestMonth: MonthGroup | undefined; + let minDifference = Number.MAX_SAFE_INTEGER; + + for (const month of months) { + const monthDate = DateTime.fromObject({ year: month.yearMonth.year, month: month.yearMonth.month }); + const totalDiff = Math.abs(monthDate.diff(targetDate, 'months').months); + + if (totalDiff < minDifference) { + minDifference = totalDiff; + closestMonth = month; + } + } + + return closestMonth; +} diff --git a/web/src/lib/managers/timeline-manager/month-group.svelte.ts b/web/src/lib/managers/timeline-manager/month-group.svelte.ts index e406972900..d244df9f81 100644 --- a/web/src/lib/managers/timeline-manager/month-group.svelte.ts +++ b/web/src/lib/managers/timeline-manager/month-group.svelte.ts @@ -233,15 +233,6 @@ export class MonthGroup { addContext.changedDayGroups.add(dayGroup); } - getRandomDayGroup() { - const random = Math.floor(Math.random() * this.dayGroups.length); - return this.dayGroups[random]; - } - - getRandomAsset() { - return this.getRandomDayGroup()?.getRandomAsset()?.asset; - } - get viewId() { const { year, month } = this.yearMonth; return year + '-' + month; @@ -251,42 +242,40 @@ export class MonthGroup { if (this.#height === height) { return; } - const { timelineManager: store, percent } = this; - const index = store.months.indexOf(this); + const timelineManager = this.timelineManager; + const index = timelineManager.months.indexOf(this); const heightDelta = height - this.#height; this.#height = height; - const prevMonthGroup = store.months[index - 1]; + const prevMonthGroup = timelineManager.months[index - 1]; if (prevMonthGroup) { const newTop = prevMonthGroup.#top + prevMonthGroup.#height; if (this.#top !== newTop) { this.#top = newTop; } } - for (let cursor = index + 1; cursor < store.months.length; cursor++) { + if (heightDelta === 0) { + return; + } + for (let cursor = index + 1; cursor < timelineManager.months.length; cursor++) { const monthGroup = this.timelineManager.months[cursor]; const newTop = monthGroup.#top + heightDelta; if (monthGroup.#top !== newTop) { monthGroup.#top = newTop; } } - if (store.topIntersectingMonthGroup) { - const currentIndex = store.months.indexOf(store.topIntersectingMonthGroup); - if (currentIndex > 0) { - if (index < currentIndex) { - store.scrollCompensation = { - heightDelta, - scrollTop: undefined, - monthGroup: this, - }; - } else if (percent > 0) { - const top = this.top + height * percent; - store.scrollCompensation = { - heightDelta: undefined, - scrollTop: top, - monthGroup: this, - }; - } - } + if (!timelineManager.viewportTopMonthIntersection) { + return; + } + const { month, monthBottomViewportRatio, viewportTopRatioInMonth } = timelineManager.viewportTopMonthIntersection; + const currentIndex = month ? timelineManager.months.indexOf(month) : -1; + if (!month || currentIndex <= 0 || index > currentIndex) { + return; + } + if (index < currentIndex || monthBottomViewportRatio < 1) { + timelineManager.scrollBy(heightDelta); + } else if (index === currentIndex) { + const scrollTo = this.top + height * viewportTopRatioInMonth; + timelineManager.scrollTo(scrollTo); } } diff --git a/web/src/lib/managers/timeline-manager/timeline-manager.svelte.spec.ts b/web/src/lib/managers/timeline-manager/timeline-manager.svelte.spec.ts index f845caa119..c9ac596e34 100644 --- a/web/src/lib/managers/timeline-manager/timeline-manager.svelte.spec.ts +++ b/web/src/lib/managers/timeline-manager/timeline-manager.svelte.spec.ts @@ -4,6 +4,7 @@ import { AbortError } from '$lib/utils'; import { fromISODateTimeUTCToObject } from '$lib/utils/timeline-util'; import { type AssetResponseDto, type TimeBucketAssetResponseDto } from '@immich/sdk'; import { timelineAssetFactory, toResponseDto } from '@test-data/factories/asset-factory'; +import { tick } from 'svelte'; import { TimelineManager } from './timeline-manager.svelte'; import type { TimelineAsset } from './types'; @@ -64,6 +65,7 @@ describe('TimelineManager', () => { sdkMock.getTimeBucket.mockImplementation(({ timeBucket }) => Promise.resolve(bucketAssetsResponse[timeBucket])); await timelineManager.updateViewport({ width: 1588, height: 1000 }); + await tick(); }); it('should load months in viewport', () => { @@ -88,7 +90,7 @@ describe('TimelineManager', () => { }); it('calculates timeline height', () => { - expect(timelineManager.timelineHeight).toBe(12_447.5); + expect(timelineManager.totalViewerHeight).toBe(12_507.5); }); }); @@ -580,4 +582,60 @@ describe('TimelineManager', () => { expect(timelineManager.getMonthGroupByAssetId(assetOne.id)?.yearMonth.month).toEqual(1); }); }); + + describe('getRandomAsset', () => { + let timelineManager: TimelineManager; + const bucketAssets: Record = { + '2024-03-01T00:00:00.000Z': timelineAssetFactory.buildList(1).map((asset) => + deriveLocalDateTimeFromFileCreatedAt({ + ...asset, + fileCreatedAt: fromISODateTimeUTCToObject('2024-03-01T00:00:00.000Z'), + }), + ), + '2024-02-01T00:00:00.000Z': timelineAssetFactory.buildList(10).map((asset, idx) => + deriveLocalDateTimeFromFileCreatedAt({ + ...asset, + // here we make sure that not all assets are on the first day of the month + fileCreatedAt: fromISODateTimeUTCToObject(`2024-02-0${idx < 7 ? 1 : 2}T00:00:00.000Z`), + }), + ), + '2024-01-01T00:00:00.000Z': timelineAssetFactory.buildList(3).map((asset) => + deriveLocalDateTimeFromFileCreatedAt({ + ...asset, + fileCreatedAt: fromISODateTimeUTCToObject('2024-01-01T00:00:00.000Z'), + }), + ), + }; + + const bucketAssetsResponse: Record = Object.fromEntries( + Object.entries(bucketAssets).map(([key, assets]) => [key, toResponseDto(...assets)]), + ); + + beforeEach(async () => { + timelineManager = new TimelineManager(); + sdkMock.getTimeBuckets.mockResolvedValue([ + { count: 1, timeBucket: '2024-03-01' }, + { count: 10, timeBucket: '2024-02-01' }, + { count: 3, timeBucket: '2024-01-01' }, + ]); + + sdkMock.getTimeBucket.mockImplementation(({ timeBucket }) => Promise.resolve(bucketAssetsResponse[timeBucket])); + await timelineManager.updateViewport({ width: 1588, height: 0 }); + }); + + it('gets all assets once', async () => { + const assetCount = timelineManager.assetCount; + expect(assetCount).toBe(14); + const discoveredAssets: Set = new Set(); + for (let idx = 0; idx < assetCount; idx++) { + const asset = await timelineManager.getRandomAsset(idx); + expect(asset).toBeDefined(); + const id = asset!.id; + expect(discoveredAssets.has(id)).toBeFalsy(); + discoveredAssets.add(id); + } + + expect(discoveredAssets.size).toBe(assetCount); + }); + }); }); diff --git a/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts b/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts index 172cd07a02..9f879fe1b1 100644 --- a/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts +++ b/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts @@ -1,13 +1,5 @@ -import { AssetOrder, getAssetInfo, getTimeBuckets } from '@immich/sdk'; - +import { VirtualScrollManager } from '$lib/managers/VirtualScrollManager/VirtualScrollManager.svelte'; import { authManager } from '$lib/managers/auth-manager.svelte'; - -import { CancellableTask } from '$lib/utils/cancellable-task'; -import { toTimelineAsset, type TimelineDateTime, type TimelineYearMonth } from '$lib/utils/timeline-util'; - -import { clamp, debounce, isEqual } from 'lodash-es'; -import { SvelteDate, SvelteMap, SvelteSet } from 'svelte/reactivity'; - import { updateIntersectionMonthGroup } from '$lib/managers/timeline-manager/internal/intersection-support.svelte'; import { updateGeometry } from '$lib/managers/timeline-manager/internal/layout-support.svelte'; import { loadFromTimeBuckets } from '$lib/managers/timeline-manager/internal/load-support.svelte'; @@ -16,6 +8,7 @@ import { runAssetOperation, } from '$lib/managers/timeline-manager/internal/operations-support.svelte'; import { + findClosestGroupForDate, findMonthGroupForAsset as findMonthGroupForAssetUtil, findMonthGroupForDate, getAssetWithOffset, @@ -23,6 +16,11 @@ import { retrieveRange as retrieveRangeUtil, } from '$lib/managers/timeline-manager/internal/search-support.svelte'; import { WebsocketSupport } from '$lib/managers/timeline-manager/internal/websocket-support.svelte'; +import { CancellableTask } from '$lib/utils/cancellable-task'; +import { toTimelineAsset, type TimelineDateTime, type TimelineYearMonth } from '$lib/utils/timeline-util'; +import { AssetOrder, getAssetInfo, getTimeBuckets } from '@immich/sdk'; +import { clamp, isEqual } from 'lodash-es'; +import { SvelteDate, SvelteMap, SvelteSet } from 'svelte/reactivity'; import { DayGroup } from './day-group.svelte'; import { isMismatched, updateObject } from './internal/utils.svelte'; import { MonthGroup } from './month-group.svelte'; @@ -32,30 +30,43 @@ import type { Direction, ScrubberMonth, TimelineAsset, - TimelineManagerLayoutOptions, TimelineManagerOptions, Viewport, } from './types'; -export class TimelineManager { +type ViewportTopMonthIntersection = { + month: MonthGroup | undefined; + // Where viewport top intersects month (0 = month top, 1 = month bottom) + viewportTopRatioInMonth: number; + // Where month bottom is in viewport (0 = viewport top, 1 = viewport bottom) + monthBottomViewportRatio: number; +}; +export class TimelineManager extends VirtualScrollManager { + override bottomSectionHeight = $state(60); + + override bodySectionHeight = $derived.by(() => { + let height = 0; + for (const month of this.months) { + height += month.height; + } + return height; + }); + + assetCount = $derived.by(() => { + let count = 0; + for (const month of this.months) { + count += month.assetsCount; + } + return count; + }); + isInitialized = $state(false); months: MonthGroup[] = $state([]); - topSectionHeight = $state(0); - timelineHeight = $derived(this.months.reduce((accumulator, b) => accumulator + b.height, 0) + this.topSectionHeight); - assetCount = $derived(this.months.reduce((accumulator, b) => accumulator + b.assetsCount, 0)); - albumAssets: Set = new SvelteSet(); - scrubberMonths: ScrubberMonth[] = $state([]); scrubberTimelineHeight: number = $state(0); - - topIntersectingMonthGroup: MonthGroup | undefined = $state(); - - visibleWindow = $derived.by(() => ({ - top: this.#scrollTop, - bottom: this.#scrollTop + this.viewportHeight, - })); - + viewportTopMonthIntersection: ViewportTopMonthIntersection | undefined; + limitedScroll = $derived(this.maxScrollPercent < 0.5); initTask = new CancellableTask( () => { this.isInitialized = true; @@ -72,121 +83,31 @@ export class TimelineManager { ); static #INIT_OPTIONS = {}; - #viewportHeight = $state(0); - #viewportWidth = $state(0); - #scrollTop = $state(0); #websocketSupport: WebsocketSupport | undefined; - - #rowHeight = $state(235); - #headerHeight = $state(48); - #gap = $state(12); - #options: TimelineManagerOptions = TimelineManager.#INIT_OPTIONS; + #updatingIntersections = false; + #scrollableElement: HTMLElement | undefined = $state(); - #scrolling = $state(false); - #suspendTransitions = $state(false); - #resetScrolling = debounce(() => (this.#scrolling = false), 1000); - #resetSuspendTransitions = debounce(() => (this.suspendTransitions = false), 1000); - scrollCompensation: { - heightDelta: number | undefined; - scrollTop: number | undefined; - monthGroup: MonthGroup | undefined; - } = $state({ - heightDelta: 0, - scrollTop: 0, - monthGroup: undefined, - }); - - constructor() {} - - setLayoutOptions({ headerHeight = 48, rowHeight = 235, gap = 12 }: TimelineManagerLayoutOptions) { - let changed = false; - changed ||= this.#setHeaderHeight(headerHeight); - changed ||= this.#setGap(gap); - changed ||= this.#setRowHeight(rowHeight); - if (changed) { - this.refreshLayout(); - } + constructor() { + super(); } - #setHeaderHeight(value: number) { - if (this.#headerHeight == value) { - return false; - } - this.#headerHeight = value; - return true; + override get scrollTop(): number { + return this.#scrollableElement?.scrollTop ?? 0; } - get headerHeight() { - return this.#headerHeight; + set scrollableElement(element: HTMLElement | undefined) { + this.#scrollableElement = element; } - #setGap(value: number) { - if (this.#gap == value) { - return false; - } - this.#gap = value; - return true; + scrollTo(top: number) { + this.#scrollableElement?.scrollTo({ top }); + this.updateSlidingWindow(); } - get gap() { - return this.#gap; - } - - #setRowHeight(value: number) { - if (this.#rowHeight == value) { - return false; - } - this.#rowHeight = value; - return true; - } - - get rowHeight() { - return this.#rowHeight; - } - - set scrolling(value: boolean) { - this.#scrolling = value; - if (value) { - this.suspendTransitions = true; - this.#resetScrolling(); - } - } - - get scrolling() { - return this.#scrolling; - } - - set suspendTransitions(value: boolean) { - this.#suspendTransitions = value; - if (value) { - this.#resetSuspendTransitions(); - } - } - - get suspendTransitions() { - return this.#suspendTransitions; - } - - set viewportWidth(value: number) { - const changed = value !== this.#viewportWidth; - this.#viewportWidth = value; - this.suspendTransitions = true; - void this.#updateViewportGeometry(changed); - } - - get viewportWidth() { - return this.#viewportWidth; - } - - set viewportHeight(value: number) { - this.#viewportHeight = value; - this.#suspendTransitions = true; - void this.#updateViewportGeometry(false); - } - - get viewportHeight() { - return this.#viewportHeight; + scrollBy(y: number) { + this.#scrollableElement?.scrollBy(0, y); + this.updateSlidingWindow(); } async *assetsIterator(options?: { @@ -234,46 +155,44 @@ export class TimelineManager { this.#websocketSupport = undefined; } - updateSlidingWindow(scrollTop: number) { - if (this.#scrollTop !== scrollTop) { - this.#scrollTop = scrollTop; - this.updateIntersections(); + #calculateMonthBottomViewportRatio(month: MonthGroup | undefined) { + if (!month) { + return 0; } + const windowHeight = this.visibleWindow.bottom - this.visibleWindow.top; + const bottomOfMonth = month.top + month.height; + const bottomOfMonthInViewport = bottomOfMonth - this.visibleWindow.top; + return clamp(bottomOfMonthInViewport / windowHeight, 0, 1); } - clearScrollCompensation() { - this.scrollCompensation = { - heightDelta: undefined, - scrollTop: undefined, - monthGroup: undefined, - }; + #calculateVewportTopRatioInMonth(month: MonthGroup | undefined) { + if (!month) { + return 0; + } + return clamp((this.visibleWindow.top - month.top) / month.height, 0, 1); } - updateIntersections() { - if (!this.isInitialized || this.visibleWindow.bottom === this.visibleWindow.top) { + override updateIntersections() { + if (this.#updatingIntersections || !this.isInitialized || this.visibleWindow.bottom === this.visibleWindow.top) { return; } - let topIntersectingMonthGroup = undefined; + this.#updatingIntersections = true; + for (const month of this.months) { updateIntersectionMonthGroup(this, month); - if (!topIntersectingMonthGroup && month.actuallyIntersecting) { - topIntersectingMonthGroup = month; - } - } - if (topIntersectingMonthGroup !== undefined && this.topIntersectingMonthGroup !== topIntersectingMonthGroup) { - this.topIntersectingMonthGroup = topIntersectingMonthGroup; - } - for (const month of this.months) { - if (month === this.topIntersectingMonthGroup) { - this.topIntersectingMonthGroup.percent = clamp( - (this.visibleWindow.top - this.topIntersectingMonthGroup.top) / this.topIntersectingMonthGroup.height, - 0, - 1, - ); - } else { - month.percent = 0; - } } + + const month = this.months.find((month) => month.actuallyIntersecting); + const viewportTopRatioInMonth = this.#calculateVewportTopRatioInMonth(month); + const monthBottomViewportRatio = this.#calculateMonthBottomViewportRatio(month); + + this.viewportTopMonthIntersection = { + month, + monthBottomViewportRatio, + viewportTopRatioInMonth, + }; + + this.#updatingIntersections = false; } clearDeferredLayout(month: MonthGroup) { @@ -302,7 +221,7 @@ export class TimelineManager { ); }); this.albumAssets.clear(); - this.#updateViewportGeometry(false); + this.updateViewportGeometry(false); } async updateOptions(options: TimelineManagerOptions) { @@ -314,7 +233,7 @@ export class TimelineManager { } await this.initTask.reset(); await this.#init(options); - this.#updateViewportGeometry(false); + this.updateViewportGeometry(false); } async #init(options: TimelineManagerOptions) { @@ -327,9 +246,10 @@ export class TimelineManager { }, true); } - public destroy() { + public override destroy() { this.disconnect(); this.isInitialized = false; + super.destroy(); } async updateViewport(viewport: Viewport) { @@ -348,21 +268,20 @@ export class TimelineManager { const changedWidth = viewport.width !== this.viewportWidth; this.viewportHeight = viewport.height; this.viewportWidth = viewport.width; - this.#updateViewportGeometry(changedWidth); + this.updateViewportGeometry(changedWidth); } - #updateViewportGeometry(changedWidth: boolean) { - if (!this.isInitialized) { - return; - } - if (this.viewportWidth === 0 || this.viewportHeight === 0) { + protected override updateViewportGeometry(changedWidth: boolean) { + if (!this.isInitialized || this.hasEmptyViewport) { return; } for (const month of this.months) { updateGeometry(this, month, { invalidateHeight: changedWidth }); } this.updateIntersections(); - this.#createScrubberMonths(); + if (changedWidth) { + this.#createScrubberMonths(); + } } #createScrubberMonths() { @@ -373,18 +292,7 @@ export class TimelineManager { title: month.monthGroupTitle, height: month.height, })); - this.scrubberTimelineHeight = this.timelineHeight; - } - - createLayoutOptions() { - const viewportWidth = this.viewportWidth; - - return { - spacing: 2, - heightTolerance: 0.15, - rowHeight: this.#rowHeight, - rowWidth: Math.floor(viewportWidth), - }; + this.scrubberTimelineHeight = this.totalViewerHeight; } async loadMonthGroup(yearMonth: TimelineYearMonth, options?: { cancelable: boolean }): Promise { @@ -401,11 +309,12 @@ export class TimelineManager { return; } - const result = await monthGroup.loader?.execute(async (signal: AbortSignal) => { + const executionStatus = await monthGroup.loader?.execute(async (signal: AbortSignal) => { await loadFromTimeBuckets(this, monthGroup, this.#options, signal); }, cancelable); - if (result === 'LOADED') { - updateIntersectionMonthGroup(this, monthGroup); + if (executionStatus === 'LOADED') { + updateGeometry(this, monthGroup, { invalidateHeight: false }); + this.updateIntersections(); } } @@ -451,16 +360,42 @@ export class TimelineManager { return monthGroupInfo?.monthGroup; } - async getRandomMonthGroup() { - const random = Math.floor(Math.random() * this.months.length); - const month = this.months[random]; - await this.loadMonthGroup(month.yearMonth, { cancelable: false }); - return month; - } + // note: the `index` input is expected to be in the range [0, assetCount). This + // value can be passed to make the method deterministic, which is mainly useful + // for testing. + async getRandomAsset(index?: number): Promise { + const randomAssetIndex = index ?? Math.floor(Math.random() * this.assetCount); - async getRandomAsset() { - const month = await this.getRandomMonthGroup(); - return month?.getRandomAsset(); + let accumulatedCount = 0; + + let randomMonth: MonthGroup | undefined = undefined; + for (const month of this.months) { + if (randomAssetIndex < accumulatedCount + month.assetsCount) { + randomMonth = month; + break; + } + + accumulatedCount += month.assetsCount; + } + if (!randomMonth) { + return; + } + await this.loadMonthGroup(randomMonth.yearMonth, { cancelable: false }); + + let randomDay: DayGroup | undefined = undefined; + for (const day of randomMonth.dayGroups) { + if (randomAssetIndex < accumulatedCount + day.viewerAssets.length) { + randomDay = day; + break; + } + + accumulatedCount += day.viewerAssets.length; + } + if (!randomDay) { + return; + } + + return randomDay.viewerAssets[randomAssetIndex - accumulatedCount].asset; } updateAssetOperation(ids: string[], operation: AssetOperation) { @@ -497,7 +432,7 @@ export class TimelineManager { return [...unprocessedIds]; } - refreshLayout() { + override refreshLayout() { for (const month of this.months) { updateGeometry(this, month, { invalidateHeight: true }); } @@ -523,9 +458,13 @@ export class TimelineManager { } async getClosestAssetToDate(dateTime: TimelineDateTime) { - const monthGroup = findMonthGroupForDate(this, dateTime); + let monthGroup = findMonthGroupForDate(this, dateTime); if (!monthGroup) { - return; + // if exact match not found, find closest + monthGroup = findClosestGroupForDate(this.months, dateTime); + if (!monthGroup) { + return; + } } await this.loadMonthGroup(dateTime, { cancelable: false }); const asset = monthGroup.findClosest(dateTime); diff --git a/web/src/lib/managers/timeline-manager/types.ts b/web/src/lib/managers/timeline-manager/types.ts index fea62084b2..27c27dcb63 100644 --- a/web/src/lib/managers/timeline-manager/types.ts +++ b/web/src/lib/managers/timeline-manager/types.ts @@ -1,6 +1,8 @@ -import type { TimelineDate, TimelineDateTime } from '$lib/utils/timeline-util'; +import type { TimelineDate, TimelineDateTime, TimelineYearMonth } from '$lib/utils/timeline-util'; import type { AssetStackResponseDto, AssetVisibility } from '@immich/sdk'; +export type ViewportTopMonth = TimelineYearMonth | undefined | 'lead-in' | 'lead-out'; + export type AssetApiGetTimeBucketsRequest = Parameters[0]; export type TimelineManagerOptions = Omit & { diff --git a/web/src/lib/modals/AlbumUsersModal.svelte b/web/src/lib/modals/AlbumUsersModal.svelte index 3cc0f03d5e..7dd7442249 100644 --- a/web/src/lib/modals/AlbumUsersModal.svelte +++ b/web/src/lib/modals/AlbumUsersModal.svelte @@ -31,6 +31,14 @@ let isOwned = $derived(currentUser?.id == album.ownerId); + // Build a map of contributor counts by user id; avoid casts/derived + const contributorCounts: Record = {}; + if (album.contributorCounts) { + for (const { userId, assetCount } of album.contributorCounts) { + contributorCounts[userId] = assetCount; + } + } + onMount(async () => { try { currentUser = await getMyUser(); @@ -111,6 +119,10 @@ {:else} {$t('role_editor')} {/if} + {#if user.id in contributorCounts} + - + {$t('items_count', { values: { count: contributorCounts[user.id] } })} + {/if}
diff --git a/web/src/lib/modals/ApiKeySecretModal.svelte b/web/src/lib/modals/ApiKeySecretModal.svelte index e3c8d13fb8..66c58543c2 100644 --- a/web/src/lib/modals/ApiKeySecretModal.svelte +++ b/web/src/lib/modals/ApiKeySecretModal.svelte @@ -1,6 +1,6 @@ + + + +
+
+ + + Get it on F-Droid + +
+ +
+ + + Get it on Google Play + +
+ +
+ + + Download on the App Store + +
+
+
+
diff --git a/web/src/lib/modals/AssetChangeDateModal.svelte b/web/src/lib/modals/AssetChangeDateModal.svelte new file mode 100644 index 0000000000..7034493924 --- /dev/null +++ b/web/src/lib/modals/AssetChangeDateModal.svelte @@ -0,0 +1,73 @@ + + + onClose(false)} size="small"> + + + + {#if timezoneInput} +
+ +
+ {/if} +
+ + + + + + +
diff --git a/web/src/lib/components/shared-components/change-date.spec.ts b/web/src/lib/modals/AssetSelectionChangeDateModal.spec.ts similarity index 61% rename from web/src/lib/components/shared-components/change-date.spec.ts rename to web/src/lib/modals/AssetSelectionChangeDateModal.spec.ts index 63926a44a6..ab7f24db25 100644 --- a/web/src/lib/components/shared-components/change-date.spec.ts +++ b/web/src/lib/modals/AssetSelectionChangeDateModal.spec.ts @@ -1,33 +1,30 @@ +import { getAnimateMock } from '$lib/__mocks__/animate.mock'; import { getIntersectionObserverMock } from '$lib/__mocks__/intersection-observer.mock'; +import { sdkMock } from '$lib/__mocks__/sdk.mock'; import { getVisualViewportMock } from '$lib/__mocks__/visual-viewport.mock'; +import { calcNewDate } from '$lib/modals/timezone-utils'; import { fireEvent, render, screen, waitFor } from '@testing-library/svelte'; import userEvent from '@testing-library/user-event'; import { DateTime } from 'luxon'; -import ChangeDate from './change-date.svelte'; +import AssetSelectionChangeDateModal from './AssetSelectionChangeDateModal.svelte'; -describe('ChangeDate component', () => { +describe('DateSelectionModal component', () => { const initialDate = DateTime.fromISO('2024-01-01'); const initialTimeZone = 'Europe/Berlin'; - const currentInterval = { - start: DateTime.fromISO('2000-02-01T14:00:00+01:00'), - end: DateTime.fromISO('2001-02-01T14:00:00+01:00'), - }; - const onCancel = vi.fn(); - const onConfirm = vi.fn(); + + const onClose = vi.fn(); const getRelativeInputToggle = () => screen.getByTestId('edit-by-offset-switch'); const getDateInput = () => screen.getByLabelText('date_and_time') as HTMLInputElement; const getTimeZoneInput = () => screen.getByLabelText('timezone') as HTMLInputElement; - const getCancelButton = () => screen.getByText('Cancel'); - const getConfirmButton = () => screen.getByText('Confirm'); + const getCancelButton = () => screen.getByText('cancel'); + const getConfirmButton = () => screen.getByText('confirm'); beforeEach(() => { vi.stubGlobal('IntersectionObserver', getIntersectionObserverMock()); vi.stubGlobal('visualViewport', getVisualViewportMock()); - }); - - afterEach(() => { vi.resetAllMocks(); + Element.prototype.animate = getAnimateMock(); }); afterAll(async () => { @@ -38,54 +35,75 @@ describe('ChangeDate component', () => { }); test('should render correct values', () => { - render(ChangeDate, { initialDate, initialTimeZone, onCancel, onConfirm }); + render(AssetSelectionChangeDateModal, { + initialDate, + initialTimeZone, + assets: [], + + onClose, + }); expect(getDateInput().value).toBe('2024-01-01T00:00'); expect(getTimeZoneInput().value).toBe('Europe/Berlin (+01:00)'); }); test('calls onConfirm with correct date on confirm', async () => { - render(ChangeDate, { - props: { initialDate, initialTimeZone, onCancel, onConfirm }, + render(AssetSelectionChangeDateModal, { + props: { initialDate, initialTimeZone, assets: [], onClose }, }); await fireEvent.click(getConfirmButton()); - expect(onConfirm).toHaveBeenCalledWith({ mode: 'absolute', date: '2024-01-01T00:00:00.000+01:00' }); + expect(sdkMock.updateAssets).toHaveBeenCalledWith({ + assetBulkUpdateDto: { + ids: [], + dateTimeOriginal: '2024-01-01T00:00:00.000+01:00', + }, + }); }); test('calls onCancel on cancel', async () => { - render(ChangeDate, { - props: { initialDate, initialTimeZone, onCancel, onConfirm }, + render(AssetSelectionChangeDateModal, { + props: { initialDate, initialTimeZone, assets: [], onClose }, }); await fireEvent.click(getCancelButton()); - expect(onCancel).toHaveBeenCalled(); + expect(onClose).toHaveBeenCalled(); }); describe('when date is in daylight saving time', () => { const dstDate = DateTime.fromISO('2024-07-01'); test('should render correct timezone with offset', () => { - render(ChangeDate, { initialDate: dstDate, initialTimeZone, onCancel, onConfirm }); + render(AssetSelectionChangeDateModal, { + initialDate: dstDate, + initialTimeZone, + assets: [], + onClose, + }); expect(getTimeZoneInput().value).toBe('Europe/Berlin (+02:00)'); }); test('calls onConfirm with correct date on confirm', async () => { - render(ChangeDate, { - props: { initialDate: dstDate, initialTimeZone, onCancel, onConfirm }, + render(AssetSelectionChangeDateModal, { + props: { initialDate: dstDate, initialTimeZone, assets: [], onClose }, }); await fireEvent.click(getConfirmButton()); - expect(onConfirm).toHaveBeenCalledWith({ mode: 'absolute', date: '2024-07-01T00:00:00.000+02:00' }); + expect(sdkMock.updateAssets).toHaveBeenCalledWith({ + assetBulkUpdateDto: { + ids: [], + dateTimeOriginal: '2024-07-01T00:00:00.000+02:00', + }, + }); }); }); test('calls onConfirm with correct offset in relative mode', async () => { - render(ChangeDate, { - props: { initialDate, initialTimeZone, currentInterval, onCancel, onConfirm }, + render(AssetSelectionChangeDateModal, { + props: { initialDate, initialTimeZone, assets: [], onClose }, }); await fireEvent.click(getRelativeInputToggle()); @@ -104,17 +122,19 @@ describe('ChangeDate component', () => { await fireEvent.click(getConfirmButton()); - expect(onConfirm).toHaveBeenCalledWith({ - mode: 'relative', - duration: days * 60 * 24 + hours * 60 + minutes, - timeZone: undefined, + expect(sdkMock.updateAssets).toHaveBeenCalledWith({ + assetBulkUpdateDto: { + ids: [], + dateTimeRelative: days * 60 * 24 + hours * 60 + minutes, + timeZone: 'Europe/Berlin', + }, }); }); test('calls onConfirm with correct timeZone in relative mode', async () => { const user = userEvent.setup(); - render(ChangeDate, { - props: { initialDate, initialTimeZone, currentInterval, onCancel, onConfirm }, + render(AssetSelectionChangeDateModal, { + props: { initialDate, initialTimeZone, assets: [], onClose }, }); await user.click(getRelativeInputToggle()); @@ -123,10 +143,13 @@ describe('ChangeDate component', () => { await user.keyboard('{Enter}'); await user.click(getConfirmButton()); - expect(onConfirm).toHaveBeenCalledWith({ - mode: 'relative', - duration: 0, - timeZone: initialTimeZone, + + expect(sdkMock.updateAssets).toHaveBeenCalledWith({ + assetBulkUpdateDto: { + ids: [], + dateTimeRelative: 0, + timeZone: 'Europe/Berlin', + }, }); }); @@ -136,55 +159,50 @@ describe('ChangeDate component', () => { timestamp: DateTime.fromISO('2024-01-01T00:00:00.000+01:00', { setZone: true }), duration: 0, timezone: undefined, - expectedResult: 'Jan 1, 2024, 12:00 AM GMT+01:00', + expectedResult: '2024-01-01T00:00:00.000', }, { timestamp: DateTime.fromISO('2024-01-01T04:00:00.000+05:00', { setZone: true }), duration: 0, timezone: undefined, - expectedResult: 'Jan 1, 2024, 4:00 AM GMT+05:00', + expectedResult: '2024-01-01T04:00:00.000', }, { timestamp: DateTime.fromISO('2024-01-01T00:00:00.000+00:00', { setZone: true }), duration: 0, timezone: 'Europe/Berlin', - expectedResult: 'Jan 1, 2024, 1:00 AM GMT+01:00', + expectedResult: '2024-01-01T01:00:00.000', }, { timestamp: DateTime.fromISO('2024-07-01T00:00:00.000+00:00', { setZone: true }), duration: 0, timezone: 'Europe/Berlin', - expectedResult: 'Jul 1, 2024, 2:00 AM GMT+02:00', + expectedResult: '2024-07-01T02:00:00.000', }, { timestamp: DateTime.fromISO('2024-01-01T00:00:00.000+01:00', { setZone: true }), duration: 1440, timezone: undefined, - expectedResult: 'Jan 2, 2024, 12:00 AM GMT+01:00', + expectedResult: '2024-01-02T00:00:00.000', }, { timestamp: DateTime.fromISO('2024-01-01T00:00:00.000+01:00', { setZone: true }), duration: -1440, timezone: undefined, - expectedResult: 'Dec 31, 2023, 12:00 AM GMT+01:00', + expectedResult: '2023-12-31T00:00:00.000', }, { timestamp: DateTime.fromISO('2024-01-01T00:00:00.000-01:00', { setZone: true }), duration: -1440, timezone: 'America/Anchorage', - expectedResult: 'Dec 30, 2023, 4:00 PM GMT-09:00', + expectedResult: '2023-12-30T16:00:00.000', }, ]; - const component = render(ChangeDate, { - props: { initialDate, initialTimeZone, currentInterval, onCancel, onConfirm }, - }); - for (const testCase of testCases) { - expect( - component.component.calcNewDate(testCase.timestamp, testCase.duration, testCase.timezone), - JSON.stringify(testCase), - ).toBe(testCase.expectedResult); + expect(calcNewDate(testCase.timestamp, testCase.duration, testCase.timezone), JSON.stringify(testCase)).toBe( + testCase.expectedResult, + ); } }); }); diff --git a/web/src/lib/modals/AssetSelectionChangeDateModal.svelte b/web/src/lib/modals/AssetSelectionChangeDateModal.svelte new file mode 100644 index 0000000000..8eb1a481cc --- /dev/null +++ b/web/src/lib/modals/AssetSelectionChangeDateModal.svelte @@ -0,0 +1,128 @@ + + + onClose(false)} size="small"> + + + + + {#if showRelative} + + + {:else} + + + {/if} +
+ (lastSelectedTimezone = option as ZoneOption)} + > +
+ +
+ + + + + + +
diff --git a/web/src/lib/modals/AuthDisableLoginConfirmModal.svelte b/web/src/lib/modals/AuthDisableLoginConfirmModal.svelte index 35d6ae2e83..7199decfd9 100644 --- a/web/src/lib/modals/AuthDisableLoginConfirmModal.svelte +++ b/web/src/lib/modals/AuthDisableLoginConfirmModal.svelte @@ -19,7 +19,7 @@ {#snippet children({ message })} {$t('official_immich_resources')}

- +

{$t('documentation')} diff --git a/web/src/lib/modals/NavigateToDateModal.svelte b/web/src/lib/modals/NavigateToDateModal.svelte new file mode 100644 index 0000000000..4b83c66bc6 --- /dev/null +++ b/web/src/lib/modals/NavigateToDateModal.svelte @@ -0,0 +1,61 @@ + + + onClose()}> + + + + + + + + + + + + + + + + + diff --git a/web/src/lib/modals/ObtainiumConfigModal.svelte b/web/src/lib/modals/ObtainiumConfigModal.svelte new file mode 100644 index 0000000000..d7132f2f85 --- /dev/null +++ b/web/src/lib/modals/ObtainiumConfigModal.svelte @@ -0,0 +1,94 @@ + + + + +

+ + diff --git a/web/src/lib/modals/ShortcutsModal.svelte b/web/src/lib/modals/ShortcutsModal.svelte index 9bd7b29b94..ebb5ea3c60 100644 --- a/web/src/lib/modals/ShortcutsModal.svelte +++ b/web/src/lib/modals/ShortcutsModal.svelte @@ -27,6 +27,7 @@ { key: ['D', 'd'], action: $t('previous_or_next_day') }, { key: ['M', 'm'], action: $t('previous_or_next_month') }, { key: ['Y', 'y'], action: $t('previous_or_next_year') }, + { key: ['g'], action: $t('navigate_to_time') }, { key: ['x'], action: $t('select') }, { key: ['Esc'], action: $t('back_close_deselect') }, { key: ['Ctrl', 'k'], action: $t('search_your_photos') }, diff --git a/web/src/lib/modals/timezone-utils.ts b/web/src/lib/modals/timezone-utils.ts new file mode 100644 index 0000000000..c7bb00fd69 --- /dev/null +++ b/web/src/lib/modals/timezone-utils.ts @@ -0,0 +1,149 @@ +import { DateTime, Duration } from 'luxon'; + +export type ZoneOption = { + /** + * Timezone name with offset + * + * e.g. Asia/Jerusalem (+03:00) + */ + label: string; + + /** + * Timezone name + * + * e.g. Asia/Jerusalem + */ + value: string; + + /** + * Timezone offset in minutes + * + * e.g. 300 + */ + offsetMinutes: number; + + /** + * True iff the date is valid + * + * Dates may be invalid for various reasons, for example setting a day that does not exist (30 Feb 2024). + * Due to daylight saving time, 2:30am is invalid for Europe/Berlin on Mar 31 2024.The two following local times + * are one second apart: + * + * - Mar 31 2024 01:59:59 (GMT+0100, unix timestamp 1725058799) + * - Mar 31 2024 03:00:00 (GMT+0200, unix timestamp 1711846800) + * + * Mar 31 2024 02:30:00 does not exist in Europe/Berlin, this is an invalid date/time/time zone combination. + */ + valid: boolean; +}; + +const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; +const knownTimezones = Intl.supportedValuesOf('timeZone'); + +export function getTimezones(selectedDate: string) { + // Use a fixed modern date to calculate stable timezone offsets for the list + // This ensures that the offsets shown in the combobox are always current, + // regardless of the historical date selected by the user. + return knownTimezones + .map((zone) => zoneOptionForDate(zone, selectedDate)) + .filter((zone) => zone.valid) + .sort((zoneA, zoneB) => sortTwoZones(zoneA, zoneB)); +} + +export function getModernOffsetForZoneAndDate( + zone: string, + dateString: string, +): { offsetMinutes: number; offsetFormat: string } { + const dt = DateTime.fromISO(dateString, { zone }); + + // we determine the *modern* offset for this zone based on its current rules. + // To do this, we "move" the date to the current year, keeping the local time components. + // This allows Luxon to apply current-year DST rules. + const modernYearDt = dt.set({ year: DateTime.now().year }); + + // Calculate the offset at that modern year's date. + const modernOffsetMinutes = modernYearDt.setZone(zone, { keepLocalTime: true }).offset; + const modernOffsetFormat = modernYearDt.setZone(zone, { keepLocalTime: true }).toFormat('ZZ'); + + return { offsetMinutes: modernOffsetMinutes, offsetFormat: modernOffsetFormat }; +} + +function zoneOptionForDate(zone: string, date: string) { + const { offsetMinutes, offsetFormat: zoneOffsetAtDate } = getModernOffsetForZoneAndDate(zone, date); + // For validity, we still need to check if the exact date/time exists in the *original* timezone (for gaps/overlaps). + const dateForValidity = DateTime.fromISO(date, { zone }); + const valid = dateForValidity.isValid && date === dateForValidity.toFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); + return { + value: zone, + offsetMinutes, + label: zone + ' (' + zoneOffsetAtDate + ')' + (valid ? '' : ' [invalid date!]'), + valid, + }; +} + +function sortTwoZones(zoneA: ZoneOption, zoneB: ZoneOption) { + const offsetDifference = zoneA.offsetMinutes - zoneB.offsetMinutes; + if (offsetDifference != 0) { + return offsetDifference; + } + return zoneA.value.localeCompare(zoneB.value, undefined, { sensitivity: 'base' }); +} + +/* + * If the time zone is not given, find the timezone to select for a given time, date, and offset (e.g. +02:00). + * + * This is done so that the list shown to the user includes more helpful names like "Europe/Berlin (+02:00)" + * instead of just the raw offset or something like "UTC+02:00". + * + * The provided information (initialDate, from some asset) includes the offset (e.g. +02:00), but no information about + * the actual time zone. As several countries/regions may share the same offset, for example Berlin (Germany) and + * Blantyre (Malawi) sharing +02:00 in summer, we have to guess and somehow pick a suitable time zone. + * + * If the time zone configured by the user (in the browser) provides the same offset for the given date (accounting + * for daylight saving time and other weirdness), we prefer to show it. This way, for German users, we might be able + * to show "Europe/Berlin" instead of the lexicographically first entry "Africa/Blantyre". + */ +export function getPreferredTimeZone( + date: DateTime, + initialTimeZone: string | undefined, + timezones: ZoneOption[], + selectedOption?: ZoneOption, +) { + const offset = date.offset; + const previousSelection = timezones.find((item) => item.value === selectedOption?.value); + const fromInitialTimeZone = timezones.find((item) => item.value === initialTimeZone); + const sameAsUserTimeZone = timezones.find((item) => item.offsetMinutes === offset && item.value === userTimeZone); + const firstWithSameOffset = timezones.find((item) => item.offsetMinutes === offset); + const utcFallback = { + label: 'UTC (+00:00)', + offsetMinutes: 0, + value: 'UTC', + valid: true, + }; + return previousSelection ?? fromInitialTimeZone ?? sameAsUserTimeZone ?? firstWithSameOffset ?? utcFallback; +} + +export function toDatetime(selectedDate: string, selectedZone: ZoneOption) { + const dtComponents = DateTime.fromISO(selectedDate, { zone: 'utc' }); + + // Determine the modern, DST-aware offset for the selected IANA zone + const { offsetMinutes } = getModernOffsetForZoneAndDate(selectedZone.value, selectedDate); + + // Construct the final ISO string with a fixed-offset zone. + const fixedOffsetZone = `UTC${offsetMinutes >= 0 ? '+' : ''}${Duration.fromObject({ minutes: offsetMinutes }).toFormat('hh:mm')}`; + + // Create a DateTime object in this fixed-offset zone, preserving the local time. + return DateTime.fromObject(dtComponents.toObject(), { zone: fixedOffsetZone }); +} + +export function toIsoDate(selectedDate: string, selectedZone: ZoneOption) { + return toDatetime(selectedDate, selectedZone).toISO({ includeOffset: true })!; +} + +export const calcNewDate = (timestamp: DateTime, selectedDuration: number, timezone?: string) => { + let newDateTime = timestamp.plus({ minutes: selectedDuration }); + if (timezone) { + newDateTime = newDateTime.setZone(timezone); + } + return newDateTime.toFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); +}; diff --git a/web/src/lib/stores/preferences.store.ts b/web/src/lib/stores/preferences.store.ts index f0689e5d6e..bd3fccf68f 100644 --- a/web/src/lib/stores/preferences.store.ts +++ b/web/src/lib/stores/preferences.store.ts @@ -146,4 +146,6 @@ export const playVideoThumbnailOnHover = persisted('play-video-thumbnai export const loopVideo = persisted('loop-video', true, {}); +export const autoPlayVideo = persisted('auto-play-video', true, {}); + export const recentAlbumsDropdown = persisted('recent-albums-open', true, {}); diff --git a/web/src/lib/stores/websocket.ts b/web/src/lib/stores/websocket.ts index 6e896e8000..c543fdb4ff 100644 --- a/web/src/lib/stores/websocket.ts +++ b/web/src/lib/stores/websocket.ts @@ -25,7 +25,7 @@ export interface Events { on_person_thumbnail: (personId: string) => void; on_server_version: (serverVersion: ServerVersionResponseDto) => void; on_config_update: () => void; - on_new_release: (newRelase: ReleaseEvent) => void; + on_new_release: (newRelease: ReleaseEvent) => void; on_session_delete: (sessionId: string) => void; on_notification: (notification: NotificationDto) => void; } diff --git a/web/src/lib/utils.spec.ts b/web/src/lib/utils.spec.ts new file mode 100644 index 0000000000..169f42409c --- /dev/null +++ b/web/src/lib/utils.spec.ts @@ -0,0 +1,25 @@ +import { getReleaseType } from '$lib/utils'; + +describe('utils', () => { + describe(getReleaseType.name, () => { + it('should return "major" for major version changes', () => { + expect(getReleaseType({ major: 1, minor: 0, patch: 0 }, { major: 2, minor: 0, patch: 0 })).toBe('major'); + expect(getReleaseType({ major: 1, minor: 0, patch: 0 }, { major: 3, minor: 2, patch: 1 })).toBe('major'); + }); + + it('should return "minor" for minor version changes', () => { + expect(getReleaseType({ major: 1, minor: 0, patch: 0 }, { major: 1, minor: 1, patch: 0 })).toBe('minor'); + expect(getReleaseType({ major: 1, minor: 0, patch: 0 }, { major: 1, minor: 2, patch: 1 })).toBe('minor'); + }); + + it('should return "patch" for patch version changes', () => { + expect(getReleaseType({ major: 1, minor: 0, patch: 0 }, { major: 1, minor: 0, patch: 1 })).toBe('patch'); + expect(getReleaseType({ major: 1, minor: 0, patch: 0 }, { major: 1, minor: 0, patch: 5 })).toBe('patch'); + }); + + it('should return "none" for matching versions', () => { + expect(getReleaseType({ major: 1, minor: 0, patch: 0 }, { major: 1, minor: 0, patch: 0 })).toBe('none'); + expect(getReleaseType({ major: 1, minor: 2, patch: 3 }, { major: 1, minor: 2, patch: 3 })).toBe('none'); + }); + }); +}); diff --git a/web/src/lib/utils.ts b/web/src/lib/utils.ts index 09205a2fc9..9604ecbbc9 100644 --- a/web/src/lib/utils.ts +++ b/web/src/lib/utils.ts @@ -21,6 +21,7 @@ import { unlinkOAuthAccount, type MemoryResponseDto, type PersonResponseDto, + type ServerVersionResponseDto, type SharedLinkResponseDto, type UserResponseDto, } from '@immich/sdk'; @@ -59,14 +60,14 @@ interface UploadRequestOptions { } export class AbortError extends Error { - name = 'AbortError'; + override name = 'AbortError'; } class ApiError extends Error { - name = 'ApiError'; + override name = 'ApiError'; constructor( - public message: string, + public override message: string, public statusCode: number, public details: string, ) { @@ -385,3 +386,22 @@ export function createDateFormatter(localeCode: string | undefined): DateFormatt }, }; } + +export const getReleaseType = ( + current: ServerVersionResponseDto, + newVersion: ServerVersionResponseDto, +): 'major' | 'minor' | 'patch' | 'none' => { + if (current.major !== newVersion.major) { + return 'major'; + } + + if (current.minor !== newVersion.minor) { + return 'minor'; + } + + if (current.patch !== newVersion.patch) { + return 'patch'; + } + + return 'none'; +}; diff --git a/web/src/lib/utils/asset-utils.ts b/web/src/lib/utils/asset-utils.ts index 25e045c8a1..5211f0bf72 100644 --- a/web/src/lib/utils/asset-utils.ts +++ b/web/src/lib/utils/asset-utils.ts @@ -405,7 +405,7 @@ export const getAssetType = (type: AssetTypeEnum) => { } }; -export const getSelectedAssets = (assets: TimelineAsset[], user: UserResponseDto | null): string[] => { +export const getOwnedAssetsWithWarning = (assets: TimelineAsset[], user: UserResponseDto | null): string[] => { const ids = [...assets].filter((a) => user && a.ownerId === user.id).map((a) => a.id); const numberOfIssues = [...assets].filter((a) => user && a.ownerId !== user.id).length; diff --git a/web/src/lib/utils/file-uploader.ts b/web/src/lib/utils/file-uploader.ts index c572ec1760..3f602bdb29 100644 --- a/web/src/lib/utils/file-uploader.ts +++ b/web/src/lib/utils/file-uploader.ts @@ -52,7 +52,7 @@ export const openFileUploadDialog = async (options: FileUploadParam = {}) => { const { albumId, multiple = true, assetId } = options; const extensions = uploadManager.getExtensions(); - return new Promise<(string | undefined)[]>((resolve, reject) => { + return new Promise((resolve, reject) => { try { const fileSelector = document.createElement('input'); diff --git a/web/src/lib/utils/timeline-util.ts b/web/src/lib/utils/timeline-util.ts index 9cf4428da6..60811c24f0 100644 --- a/web/src/lib/utils/timeline-util.ts +++ b/web/src/lib/utils/timeline-util.ts @@ -1,4 +1,4 @@ -import type { TimelineAsset } from '$lib/managers/timeline-manager/types'; +import type { TimelineAsset, ViewportTopMonth } from '$lib/managers/timeline-manager/types'; import { locale } from '$lib/stores/preferences.store'; import { getAssetRatio } from '$lib/utils/asset-utils'; import { AssetTypeEnum, type AssetResponseDto } from '@immich/sdk'; @@ -23,11 +23,11 @@ export type TimelineDateTime = TimelineDate & { millisecond: number; }; -export type ScrubberListener = ( - scrubberMonth: { year: number; month: number }, - overallScrollPercent: number, - scrubberMonthScrollPercent: number, -) => void | Promise; +export type ScrubberListener = (scrubberData: { + scrubberMonth: ViewportTopMonth; + overallScrollPercent: number; + scrubberMonthScrollPercent: number; +}) => void | Promise; // used for AssetResponseDto.dateTimeOriginal, amongst others export const fromISODateTime = (isoDateTime: string, timeZone: string): DateTime => @@ -154,12 +154,6 @@ export function formatGroupTitle(_date: DateTime): string { export const getDateLocaleString = (date: DateTime, opts?: LocaleOptions): string => date.toLocaleString(DateTime.DATE_MED_WITH_WEEKDAY, opts); -export const getDateTimeOffsetLocaleString = (date: DateTime, opts?: LocaleOptions): string => - date.toLocaleString( - { year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit', timeZoneName: 'longOffset' }, - opts, - ); - export const toTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset): TimelineAsset => { if (isTimelineAsset(unknownAsset)) { return unknownAsset; diff --git a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte index 8c8b84ce16..8441362b64 100644 --- a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -328,18 +328,19 @@ } }); - let timelineManager = new TimelineManager(); - - $effect(() => { + let timelineManager = $state() as TimelineManager; + const options = $derived.by(() => { if (viewMode === AlbumPageViewMode.VIEW) { - void timelineManager.updateOptions({ albumId, order: albumOrder }); - } else if (viewMode === AlbumPageViewMode.SELECT_ASSETS) { - void timelineManager.updateOptions({ + return { albumId, order: albumOrder }; + } + if (viewMode === AlbumPageViewMode.SELECT_ASSETS) { + return { visibility: AssetVisibility.Timeline, withPartners: true, timelineAlbumId: albumId, - }); + }; } + return {}; }); const isShared = $derived(viewMode === AlbumPageViewMode.SELECT_ASSETS ? false : album.albumUsers.length > 0); @@ -352,10 +353,7 @@ handlePromiseError(activityManager.init(album.id)); }); - onDestroy(() => { - activityManager.reset(); - timelineManager.destroy(); - }); + onDestroy(() => activityManager.reset()); let isOwned = $derived($user.id == album.ownerId); @@ -446,7 +444,8 @@ timelineManager.destroy()); + let timelineManager = $state() as TimelineManager; + const options = { visibility: AssetVisibility.Archive }; const assetInteraction = new AssetInteraction(); @@ -49,7 +47,8 @@ timelineManager.destroy()); + let timelineManager = $state() as TimelineManager; + const options = { isFavorite: true, withStacked: true }; const assetInteraction = new AssetInteraction(); @@ -54,7 +52,8 @@ timelineManager.destroy()); + let timelineManager = $state() as TimelineManager; + const options = { visibility: AssetVisibility.Locked }; const assetInteraction = new AssetInteraction(); @@ -60,7 +58,8 @@ - void timelineManager.updateOptions({ - userId: data.partner.id, - visibility: AssetVisibility.Timeline, - withStacked: true, - }), - ); - onDestroy(() => timelineManager.destroy()); + const options = $derived({ + userId: data.partner.id, + visibility: AssetVisibility.Timeline, + withStacked: true, + }); + const assetInteraction = new AssetInteraction(); const handleEscape = () => { @@ -43,7 +37,7 @@
- +
{#if assetInteraction.selectionActive} diff --git a/web/src/routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]/+page.svelte index 5de52fc689..31c599f19b 100644 --- a/web/src/routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -63,7 +63,7 @@ mdiPlus, } from '@mdi/js'; import { DateTime } from 'luxon'; - import { onDestroy, onMount } from 'svelte'; + import { onMount } from 'svelte'; import { t } from 'svelte-i18n'; import type { PageData } from './$types'; @@ -76,10 +76,8 @@ let numberOfAssets = $state(data.statistics.assets); let { isViewing: showAssetViewer } = assetViewingStore; - const timelineManager = new TimelineManager(); - $effect(() => void timelineManager.updateOptions({ visibility: AssetVisibility.Timeline, personId: data.person.id })); - onDestroy(() => timelineManager.destroy()); - + let timelineManager = $state() as TimelineManager; + const options = $derived({ visibility: AssetVisibility.Timeline, personId: data.person.id }); const assetInteraction = new AssetInteraction(); let viewMode: PersonPageViewMode = $state(PersonPageViewMode.VIEW_ASSETS); @@ -388,7 +386,8 @@ timelineManager.destroy()); + let timelineManager = $state() as TimelineManager; + const options = { visibility: AssetVisibility.Timeline, withStacked: true, withPartners: true }; const assetInteraction = new AssetInteraction(); @@ -91,7 +90,8 @@ { if ($showAssetViewer) { return; @@ -129,7 +126,6 @@ }; const handleSetVisibility = (assetIds: string[]) => { - timelineManager.removeAssets(assetIds); assetInteraction.clearMultiselect(); onAssetDelete(assetIds); }; diff --git a/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.svelte index 20db2b23bb..442d3cef6c 100644 --- a/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -16,7 +16,6 @@ import { deleteTag, getAllTags, type TagResponseDto } from '@immich/sdk'; import { Button, HStack, modalManager, Text } from '@immich/ui'; import { mdiPencil, mdiPlus, mdiTag, mdiTagMultiple, mdiTrashCanOutline } from '@mdi/js'; - import { onDestroy } from 'svelte'; import { t } from 'svelte-i18n'; import type { PageData } from './$types'; @@ -28,14 +27,13 @@ const assetInteraction = new AssetInteraction(); - const timelineManager = new TimelineManager(); - $effect(() => void timelineManager.updateOptions({ deferInit: !tag, tagId: tag?.id })); - onDestroy(() => timelineManager.destroy()); - let tags = $derived(data.tags); const tree = $derived(TreeNode.fromTags(tags)); const tag = $derived(tree.traverse(data.path)); + let timelineManager = $state() as TimelineManager; + const options = $derived({ deferInit: !tag, tagId: tag?.id }); + const handleNavigation = (tag: string) => navigateToView(joinPaths(data.path, tag)); const getLink = (path: string) => { @@ -117,7 +115,13 @@
{#if tag.hasAssets} - + {#snippet empty()} {/snippet} diff --git a/web/src/routes/(user)/trash/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/trash/[[photos=photos]]/[[assetId=id]]/+page.svelte index 20a9746a4b..9615618445 100644 --- a/web/src/routes/(user)/trash/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/trash/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -21,7 +21,6 @@ import { emptyTrash, restoreTrash } from '@immich/sdk'; import { Button, HStack, modalManager, Text } from '@immich/ui'; import { mdiDeleteForeverOutline, mdiHistory } from '@mdi/js'; - import { onDestroy } from 'svelte'; import { t } from 'svelte-i18n'; import type { PageData } from './$types'; @@ -35,9 +34,8 @@ handlePromiseError(goto(AppRoute.PHOTOS)); } - const timelineManager = new TimelineManager(); - void timelineManager.updateOptions({ isTrashed: true }); - onDestroy(() => timelineManager.destroy()); + let timelineManager = $state() as TimelineManager; + const options = { isTrashed: true }; const assetInteraction = new AssetInteraction(); @@ -116,7 +114,7 @@ {/snippet} - +

{$t('trashed_items_will_be_permanently_deleted_after', { values: { days: $serverConfig.trashDays } })}

diff --git a/web/src/routes/(user)/utilities/geolocation/+page.svelte b/web/src/routes/(user)/utilities/geolocation/+page.svelte index 5441257df4..732e0625ab 100644 --- a/web/src/routes/(user)/utilities/geolocation/+page.svelte +++ b/web/src/routes/(user)/utilities/geolocation/+page.svelte @@ -30,13 +30,13 @@ let location = $state<{ latitude: number; longitude: number }>({ latitude: 0, longitude: 0 }); let locationUpdated = $state(false); - const timelineManager = new TimelineManager(); - void timelineManager.updateOptions({ + let timelineManager = $state() as TimelineManager; + const options = { visibility: AssetVisibility.Timeline, withStacked: true, withPartners: true, withCoordinates: true, - }); + }; const handleUpdate = async () => { const confirmed = await modalManager.show(GeolocationUpdateConfirmModal, { @@ -188,7 +188,8 @@ { await init({ fallbackLocale: 'dev' }); + Element.prototype.animate = vi.fn().mockImplementation(() => ({ cancel: () => {}, finished: Promise.resolve() })); }); Object.defineProperty(globalThis, 'matchMedia', { @@ -16,3 +17,11 @@ Object.defineProperty(globalThis, 'matchMedia', { dispatchEvent: vi.fn(), })), }); + +vi.mock('$env/dynamic/public', () => { + return { + env: { + PUBLIC_IMMICH_HOSTNAME: '', + }, + }; +}); diff --git a/web/svelte.config.js b/web/svelte.config.js index 1d6527fdf0..00472f9c13 100644 --- a/web/svelte.config.js +++ b/web/svelte.config.js @@ -10,7 +10,8 @@ process.env.PUBLIC_IMMICH_PAY_HOST = process.env.PUBLIC_IMMICH_PAY_HOST || 'http /** @type {import('@sveltejs/kit').Config} */ const config = { compilerOptions: { - runes: true, + // TODO pending `@immich/ui` to enable it + // runes: true, }, preprocess: vitePreprocess(), kit: { diff --git a/web/tsconfig.json b/web/tsconfig.json index c7bc16f52b..afebee51ad 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -6,6 +6,7 @@ "forceConsistentCasingInFileNames": true, "module": "es2022", "moduleResolution": "bundler", + "noImplicitOverride": true, "resolveJsonModule": true, "skipLibCheck": true, "sourceMap": true,