diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 231ad141e4..c4c522fe3f 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -96,7 +96,7 @@ jobs: type=raw,value=latest,enable=${{ github.event_name == 'release' }} - name: Build and push image - uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0 + uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0 with: file: cli/Dockerfile platforms: linux/amd64,linux/arm64 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7d07785de7..69ef63efe9 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -50,7 +50,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@45775bd8235c68ba998cffa5171334d58593da47 # v3 + uses: github/codeql-action/init@28deaeda66b76a05916b6923827895f2b14ab387 # v3 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@45775bd8235c68ba998cffa5171334d58593da47 # v3 + uses: github/codeql-action/autobuild@28deaeda66b76a05916b6923827895f2b14ab387 # v3 # âšī¸ 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@45775bd8235c68ba998cffa5171334d58593da47 # v3 + uses: github/codeql-action/analyze@28deaeda66b76a05916b6923827895f2b14ab387 # v3 with: category: '/language:${{matrix.language}}' diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index a78d3c25dc..1746c93bc7 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -205,7 +205,7 @@ jobs: - name: Build and push image id: build - uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0 + uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0 with: context: ${{ env.context }} file: ${{ env.file }} @@ -224,7 +224,7 @@ jobs: BUILD_SOURCE_COMMIT=${{ github.sha }} - name: Export digest - run: | + run: | # zizmor: ignore[template-injection] mkdir -p ${{ runner.temp }}/digests digest="${{ steps.build.outputs.digest }}" touch "${{ runner.temp }}/digests/${digest#sha256:}" @@ -266,7 +266,7 @@ jobs: - build_and_push_ml steps: - name: Download digests - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: path: ${{ runner.temp }}/digests pattern: ml-digests-${{ matrix.device }}-* @@ -407,7 +407,7 @@ jobs: - name: Build and push image id: build - uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0 + uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0 with: context: ${{ env.context }} file: ${{ env.file }} @@ -426,7 +426,7 @@ jobs: BUILD_SOURCE_COMMIT=${{ github.sha }} - name: Export digest - run: | + run: | # zizmor: ignore[template-injection] mkdir -p ${{ runner.temp }}/digests digest="${{ steps.build.outputs.digest }}" touch "${{ runner.temp }}/digests/${digest#sha256:}" @@ -454,7 +454,7 @@ jobs: - build_and_push_server steps: - name: Download digests - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: path: ${{ runner.temp }}/digests pattern: server-digests-* @@ -535,6 +535,7 @@ jobs: run: exit 1 - name: All jobs passed or skipped if: ${{ !(contains(needs.*.result, 'failure')) }} + # zizmor: ignore[template-injection] run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}" success-check-ml: @@ -549,4 +550,5 @@ jobs: run: exit 1 - name: All jobs passed or skipped if: ${{ !(contains(needs.*.result, 'failure')) }} + # zizmor: ignore[template-injection] run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}" diff --git a/.github/workflows/docs-build.yml b/.github/workflows/docs-build.yml index ece3bbd248..aaa1780657 100644 --- a/.github/workflows/docs-build.yml +++ b/.github/workflows/docs-build.yml @@ -72,4 +72,5 @@ jobs: with: name: docs-build-output path: docs/build/ + include-hidden-files: true retention-days: 1 diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index 10277a0c5e..fd12423fd9 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -1,6 +1,6 @@ name: Docs deploy on: - workflow_run: + workflow_run: # zizmor: ignore[dangerous-triggers] no attacker inputs are used here workflows: ['Docs build'] types: - completed @@ -115,22 +115,22 @@ jobs: - name: Load parameters id: parameters uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + env: + PARAM_JSON: ${{ needs.checks.outputs.parameters }} with: script: | - const json = `${{ needs.checks.outputs.parameters }}`; - const parameters = JSON.parse(json); + const parameters = JSON.parse(process.env.PARAM_JSON); core.setOutput("event", parameters.event); core.setOutput("name", parameters.name); core.setOutput("shouldDeploy", parameters.shouldDeploy); - - run: | - echo "Starting docs deployment for ${{ steps.parameters.outputs.event }} ${{ steps.parameters.outputs.name }}" - - name: Download artifact uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + env: + ARTIFACT_JSON: ${{ needs.checks.outputs.artifact }} with: script: | - let artifact = ${{ needs.checks.outputs.artifact }}; + let artifact = JSON.parse(process.env.ARTIFACT_JSON); let download = await github.rest.actions.downloadArtifact({ owner: context.repo.owner, repo: context.repo.repo, diff --git a/.github/workflows/docs-destroy.yml b/.github/workflows/docs-destroy.yml index 9d1e4b6612..0da258de09 100644 --- a/.github/workflows/docs-destroy.yml +++ b/.github/workflows/docs-destroy.yml @@ -1,6 +1,6 @@ name: Docs destroy on: - pull_request_target: + pull_request_target: # zizmor: ignore[dangerous-triggers] no attacker inputs are used here types: [closed] permissions: {} diff --git a/.github/workflows/pr-label-validation.yml b/.github/workflows/pr-label-validation.yml index 8d34597a08..c5e5131920 100644 --- a/.github/workflows/pr-label-validation.yml +++ b/.github/workflows/pr-label-validation.yml @@ -1,7 +1,7 @@ name: PR Label Validation on: - pull_request_target: + pull_request_target: # zizmor: ignore[dangerous-triggers] no attacker inputs are used here types: [opened, labeled, unlabeled, synchronize] permissions: {} diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index 5704f4275f..75c6836ab9 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -1,6 +1,6 @@ name: 'Pull Request Labeler' on: - - pull_request_target + - pull_request_target # zizmor: ignore[dangerous-triggers] no attacker inputs are used here permissions: {} diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 7971f7574a..27957ca4a3 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -44,10 +44,13 @@ jobs: persist-credentials: true - name: Install uv - uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5 + uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 - name: Bump version - run: misc/release/pump-version.sh -s "${{ inputs.serverBump }}" -m "${{ inputs.mobileBump }}" + env: + SERVER_BUMP: ${{ inputs.serverBump }} + MOBILE_BUMP: ${{ inputs.mobileBump }} + run: misc/release/pump-version.sh -s "${SERVER_BUMP}" -m "${MOBILE_BUMP}" - name: Commit and tag id: push-tag @@ -61,6 +64,8 @@ jobs: build_mobile: uses: ./.github/workflows/build-mobile.yml needs: bump_version + permissions: + contents: read secrets: KEY_JKS: ${{ secrets.KEY_JKS }} ALIAS: ${{ secrets.ALIAS }} @@ -90,12 +95,12 @@ jobs: persist-credentials: false - name: Download APK - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: release-apk-signed - name: Create draft release - uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2 + uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2 with: draft: true tag_name: ${{ env.IMMICH_VERSION }} diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml index 1a3c11d3d5..3a0b702210 100644 --- a/.github/workflows/static_analysis.yml +++ b/.github/workflows/static_analysis.yml @@ -95,3 +95,30 @@ jobs: - name: Run dart custom_lint run: dart run custom_lint working-directory: ./mobile + + zizmor: + name: zizmor + runs-on: ubuntu-latest + permissions: + security-events: write + contents: read + actions: read + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false + + - name: Install the latest version of uv + uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 + + - name: Run zizmor đ + run: uvx zizmor --format=sarif . > results.sarif + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@28deaeda66b76a05916b6923827895f2b14ab387 # v3 + with: + sarif_file: results.sarif + category: zizmor diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 91389c25ff..605993f5e9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -184,8 +184,49 @@ jobs: run: npm run test:cov if: ${{ !cancelled() }} + web-lint: + name: Lint Web + needs: pre-job + if: ${{ needs.pre-job.outputs.should_run_web == 'true' }} + runs-on: mich + permissions: + contents: read + defaults: + run: + working-directory: ./web + + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false + + - name: Setup Node + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version-file: './web/.nvmrc' + + - name: Run setup typescript-sdk + run: npm ci && npm run build + working-directory: ./open-api/typescript-sdk + + - name: Run npm install + run: npm ci + + - name: Run linter + run: npm run lint:p + if: ${{ !cancelled() }} + + - name: Run formatter + run: npm run format + if: ${{ !cancelled() }} + + - name: Run svelte checks + run: npm run check:svelte + if: ${{ !cancelled() }} + web-unit-tests: - name: Test & Lint Web + name: Test Web needs: pre-job if: ${{ needs.pre-job.outputs.should_run_web == 'true' }} runs-on: ubuntu-latest @@ -213,18 +254,6 @@ jobs: - name: Run npm install run: npm ci - - name: Run linter - run: npm run lint - if: ${{ !cancelled() }} - - - name: Run formatter - run: npm run format - if: ${{ !cancelled() }} - - - name: Run svelte checks - run: npm run check:svelte - if: ${{ !cancelled() }} - - name: Run tsc run: npm run check:typescript if: ${{ !cancelled() }} @@ -309,12 +338,15 @@ jobs: name: End-to-End Tests (Server & CLI) needs: pre-job if: ${{ needs.pre-job.outputs.should_run_e2e_server_cli == 'true' }} - runs-on: mich + runs-on: ${{ matrix.runner }} permissions: contents: read defaults: run: working-directory: ./e2e + strategy: + matrix: + runner: [mich, ubuntu-24.04-arm] steps: - name: Checkout code @@ -354,12 +386,15 @@ jobs: name: End-to-End Tests (Web) needs: pre-job if: ${{ needs.pre-job.outputs.should_run_e2e_web == 'true' }} - runs-on: mich + runs-on: ${{ matrix.runner }} permissions: contents: read defaults: run: working-directory: ./e2e + strategy: + matrix: + runner: [mich, ubuntu-24.04-arm] steps: - name: Checkout code @@ -394,6 +429,21 @@ jobs: run: npx playwright test if: ${{ !cancelled() }} + success-check-e2e: + name: End-to-End Tests Success + needs: [e2e-tests-server-cli, e2e-tests-web] + permissions: {} + runs-on: ubuntu-latest + if: always() + steps: + - name: Any jobs failed? + if: ${{ contains(needs.*.result, 'failure') }} + run: exit 1 + - name: All jobs passed or skipped + if: ${{ !(contains(needs.*.result, 'failure')) }} + # zizmor: ignore[template-injection] + run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}" + mobile-unit-tests: name: Unit Test Mobile needs: pre-job @@ -431,8 +481,8 @@ jobs: persist-credentials: false - name: Install uv - uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5 - - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5 + uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 # TODO: add caching when supported (https://github.com/actions/setup-python/pull/818) # with: # python-version: 3.11 diff --git a/.github/workflows/weblate-lock.yml b/.github/workflows/weblate-lock.yml index 2aef5c472a..2d644955bc 100644 --- a/.github/workflows/weblate-lock.yml +++ b/.github/workflows/weblate-lock.yml @@ -57,4 +57,5 @@ jobs: run: exit 1 - name: All jobs passed or skipped if: ${{ !(contains(needs.*.result, 'failure')) }} + # zizmor: ignore[template-injection] run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}" diff --git a/.vscode/settings.json b/.vscode/settings.json index 49692809bc..396755a634 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,45 +1,63 @@ { - "editor.formatOnSave": true, - "[javascript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.tabSize": 2, - "editor.formatOnSave": true - }, - "[typescript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.tabSize": 2, - "editor.formatOnSave": true - }, "[css]": { "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.tabSize": 2, - "editor.formatOnSave": true - }, - "[svelte]": { - "editor.defaultFormatter": "svelte.svelte-vscode", + "editor.formatOnSave": true, "editor.tabSize": 2 }, - "svelte.enable-ts-plugin": true, - "eslint.validate": [ - "javascript", - "svelte" - ], - "typescript.preferences.importModuleSpecifier": "non-relative", "[dart]": { + "editor.defaultFormatter": "Dart-Code.dart-code", "editor.formatOnSave": true, "editor.selectionHighlight": false, "editor.suggest.snippetsPreventQuickSuggestions": false, "editor.suggestSelection": "first", "editor.tabCompletion": "onlySnippets", - "editor.wordBasedSuggestions": "off", - "editor.defaultFormatter": "Dart-Code.dart-code" + "editor.wordBasedSuggestions": "off" }, - "cSpell.words": [ - "immich" - ], + "[javascript]": { + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit", + "source.removeUnusedImports": "explicit" + }, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.tabSize": 2 + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.tabSize": 2 + }, + "[jsonc]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.tabSize": 2 + }, + "[svelte]": { + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit", + "source.removeUnusedImports": "explicit" + }, + "editor.defaultFormatter": "svelte.svelte-vscode", + "editor.formatOnSave": true, + "editor.tabSize": 2 + }, + "[typescript]": { + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit", + "source.removeUnusedImports": "explicit" + }, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.tabSize": 2 + }, + "cSpell.words": ["immich"], + "editor.formatOnSave": true, + "eslint.validate": ["javascript", "svelte"], "explorer.fileNesting.enabled": true, "explorer.fileNesting.patterns": { - "*.ts": "${capture}.spec.ts,${capture}.mock.ts", - "*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart" - } -} \ No newline at end of file + "*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart", + "*.ts": "${capture}.spec.ts,${capture}.mock.ts" + }, + "svelte.enable-ts-plugin": true, + "typescript.preferences.importModuleSpecifier": "non-relative" +} diff --git a/Makefile b/Makefile index e15faa8051..1e7760ae68 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,9 @@ e2e: prod: docker compose -f ./docker/docker-compose.prod.yml up --build -V --remove-orphans +prod-down: + docker compose -f ./docker/docker-compose.prod.yml down --remove-orphans + prod-scale: docker compose -f ./docker/docker-compose.prod.yml up --build -V --scale immich-server=3 --scale immich-microservices=3 --remove-orphans diff --git a/cli/Dockerfile b/cli/Dockerfile index 356537213b..ce345c29a0 100644 --- a/cli/Dockerfile +++ b/cli/Dockerfile @@ -1,4 +1,4 @@ -FROM node:22.14.0-alpine3.20@sha256:40be979442621049f40b1d51a26b55e281246b5de4e5f51a18da7beb6e17e3f9 AS core +FROM node:22.15.0-alpine3.20@sha256:686b8892b69879ef5bfd6047589666933508f9a5451c67320df3070ba0e9807b AS core WORKDIR /usr/src/open-api/typescript-sdk COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./ diff --git a/cli/package-lock.json b/cli/package-lock.json index 22f8980754..cc8e88012f 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/cli", - "version": "2.2.61", + "version": "2.2.65", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/cli", - "version": "2.2.61", + "version": "2.2.65", "license": "GNU Affero General Public License version 3", "dependencies": { "chokidar": "^4.0.3", @@ -27,7 +27,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.14.0", + "@types/node": "^22.14.1", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", @@ -54,14 +54,14 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.131.3", + "version": "1.132.3", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.14.0", + "@types/node": "^22.14.1", "typescript": "^5.3.3" } }, @@ -647,9 +647,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", + "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -697,9 +697,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", - "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", + "version": "9.25.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.1.tgz", + "integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==", "dev": true, "license": "MIT", "engines": { @@ -730,19 +730,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1380,17 +1367,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", - "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.0.tgz", + "integrity": "sha512-evaQJZ/J/S4wisevDvC1KFZkPzRetH8kYZbkgcTRyql3mcKsf+ZFDV1BVWUGTCAW5pQHoqn5gK5b8kn7ou9aFQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/type-utils": "8.30.1", - "@typescript-eslint/utils": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/scope-manager": "8.31.0", + "@typescript-eslint/type-utils": "8.31.0", + "@typescript-eslint/utils": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1410,16 +1397,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", - "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.31.0.tgz", + "integrity": "sha512-67kYYShjBR0jNI5vsf/c3WG4u+zDnCTHTPqVMQguffaWWFs7artgwKmfwdifl+r6XyM5LYLas/dInj2T0SgJyw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/typescript-estree": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/scope-manager": "8.31.0", + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/typescript-estree": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0", "debug": "^4.3.4" }, "engines": { @@ -1435,14 +1422,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", - "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.31.0.tgz", + "integrity": "sha512-knO8UyF78Nt8O/B64i7TlGXod69ko7z6vJD9uhSlm0qkAbGeRUSudcm0+K/4CrRjrpiHfBCjMWlc08Vav1xwcw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1" + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1453,14 +1440,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", - "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.31.0.tgz", + "integrity": "sha512-DJ1N1GdjI7IS7uRlzJuEDCgDQix3ZVYVtgeWEyhyn4iaoitpMBX6Ndd488mXSx0xah/cONAkEaYyylDyAeHMHg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.30.1", - "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/typescript-estree": "8.31.0", + "@typescript-eslint/utils": "8.31.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, @@ -1477,9 +1464,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", - "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.31.0.tgz", + "integrity": "sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ==", "dev": true, "license": "MIT", "engines": { @@ -1491,14 +1478,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", - "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.0.tgz", + "integrity": "sha512-xLmgn4Yl46xi6aDSZ9KkyfhhtnYI15/CvHbpOy/eR5NWhK/BK8wc709KKwhAR0m4ZKRP7h07bm4BWUYOCuRpQQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1544,16 +1531,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", - "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.31.0.tgz", + "integrity": "sha512-qi6uPLt9cjTFxAb1zGNgTob4x9ur7xC6mHQJ8GwEzGMGE9tYniublmJaowOJ9V2jUzxrltTPfdG2nKlWsq0+Ww==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/typescript-estree": "8.30.1" + "@typescript-eslint/scope-manager": "8.31.0", + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/typescript-estree": "8.31.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1568,13 +1555,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", - "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.0.tgz", + "integrity": "sha512-QcGHmlRHWOl93o64ZUMNewCdwKGU6WItOU52H0djgNmn1EOrhVudrDzXz4OycCRSCPwFCDrE2iIt5vmuUdHxuQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/types": "8.31.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -1586,9 +1573,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.1.tgz", - "integrity": "sha512-MgV6D2dhpD6Hp/uroUoAIvFqA8AuvXEFBC2eepG3WFc1pxTfdk1LEqqkWoWhjz+rytoqrnUUCdf6Lzco3iHkLQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.2.tgz", + "integrity": "sha512-XDdaDOeaTMAMYW7N63AqoK32sYUWbXnTkC6tEbVcu3RlU1bB9of32T+PGf8KZvxqLNqeXhafDFqCkwpf2+dyaQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1601,7 +1588,7 @@ "istanbul-reports": "^3.1.7", "magic-string": "^0.30.17", "magicast": "^0.3.5", - "std-env": "^3.8.1", + "std-env": "^3.9.0", "test-exclude": "^7.0.1", "tinyrainbow": "^2.0.0" }, @@ -1609,8 +1596,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "3.1.1", - "vitest": "3.1.1" + "@vitest/browser": "3.1.2", + "vitest": "3.1.2" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -1619,14 +1606,14 @@ } }, "node_modules/@vitest/expect": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.1.tgz", - "integrity": "sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.2.tgz", + "integrity": "sha512-O8hJgr+zREopCAqWl3uCVaOdqJwZ9qaDwUP7vy3Xigad0phZe9APxKhPcDNqYYi0rX5oMvwJMSCAXY2afqeTSA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", + "@vitest/spy": "3.1.2", + "@vitest/utils": "3.1.2", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, @@ -1635,13 +1622,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.1.tgz", - "integrity": "sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.2.tgz", + "integrity": "sha512-kOtd6K2lc7SQ0mBqYv/wdGedlqPdM/B38paPY+OwJ1XiNi44w3Fpog82UfOibmHaV9Wod18A09I9SCKLyDMqgw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.1", + "@vitest/spy": "3.1.2", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -1662,9 +1649,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.1.tgz", - "integrity": "sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.2.tgz", + "integrity": "sha512-R0xAiHuWeDjTSB3kQ3OQpT8Rx3yhdOAIm/JM4axXxnG7Q/fS8XUwggv/A4xzbQA+drYRjzkMnpYnOGAc4oeq8w==", "dev": true, "license": "MIT", "dependencies": { @@ -1675,13 +1662,13 @@ } }, "node_modules/@vitest/runner": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.1.tgz", - "integrity": "sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.2.tgz", + "integrity": "sha512-bhLib9l4xb4sUMPXnThbnhX2Yi8OutBMA8Yahxa7yavQsFDtwY/jrUZwpKp2XH9DhRFJIeytlyGpXCqZ65nR+g==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.1.1", + "@vitest/utils": "3.1.2", "pathe": "^2.0.3" }, "funding": { @@ -1689,13 +1676,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.1.tgz", - "integrity": "sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.2.tgz", + "integrity": "sha512-Q1qkpazSF/p4ApZg1vfZSQ5Yw6OCQxVMVrLjslbLFA1hMDrT2uxtqMaw8Tc/jy5DLka1sNs1Y7rBcftMiaSH/Q==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.1", + "@vitest/pretty-format": "3.1.2", "magic-string": "^0.30.17", "pathe": "^2.0.3" }, @@ -1704,9 +1691,9 @@ } }, "node_modules/@vitest/spy": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.1.tgz", - "integrity": "sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.2.tgz", + "integrity": "sha512-OEc5fSXMws6sHVe4kOFyDSj/+4MSwst0ib4un0DlcYgQvRuYQ0+M2HyqGaauUMnjq87tmUaMNDxKQx7wNfVqPA==", "dev": true, "license": "MIT", "dependencies": { @@ -1717,13 +1704,13 @@ } }, "node_modules/@vitest/utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.1.tgz", - "integrity": "sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.2.tgz", + "integrity": "sha512-5GGd0ytZ7BH3H6JTj9Kw7Prn1Nbg0wZVrIvou+UWxm54d+WoXXgAgjFJ8wn3LdagWLFSEfpPeyYrByZaGEZHLg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.1", + "@vitest/pretty-format": "3.1.2", "loupe": "^3.1.3", "tinyrainbow": "^2.0.0" }, @@ -2183,9 +2170,9 @@ "license": "MIT" }, "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", "dev": true, "license": "MIT" }, @@ -2254,20 +2241,20 @@ } }, "node_modules/eslint": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", - "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", + "version": "9.25.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.1.tgz", + "integrity": "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.20.0", - "@eslint/config-helpers": "^0.2.0", - "@eslint/core": "^0.12.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.13.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.24.0", - "@eslint/plugin-kit": "^0.2.7", + "@eslint/js": "9.25.1", + "@eslint/plugin-kit": "^0.2.8", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -4028,6 +4015,51 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tinypool": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", @@ -4152,15 +4184,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.30.1.tgz", - "integrity": "sha512-D7lC0kcehVH7Mb26MRQi64LMyRJsj3dToJxM1+JVTl53DQSV5/7oUGWQLcKl1C1KnoVHxMMU2FNQMffr7F3Row==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.31.0.tgz", + "integrity": "sha512-u+93F0sB0An8WEAPtwxVhFby573E8ckdjwUUQUj9QA4v8JAvgtoDdIyYR3XFwFHq2W1KJ1AurwJCO+w+Y1ixyQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.30.1", - "@typescript-eslint/parser": "8.30.1", - "@typescript-eslint/utils": "8.30.1" + "@typescript-eslint/eslint-plugin": "8.31.0", + "@typescript-eslint/parser": "8.31.0", + "@typescript-eslint/utils": "8.31.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4247,15 +4279,18 @@ } }, "node_modules/vite": { - "version": "6.2.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz", - "integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==", + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.3.tgz", + "integrity": "sha512-5nXH+QsELbFKhsEfWLkHrvgRpTdGJzqOZ+utSdmPTvwHmvU6ITTm3xx+mRusihkcI8GeC7lCDyn3kDtiki9scw==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", "postcss": "^8.5.3", - "rollup": "^4.30.1" + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" @@ -4319,9 +4354,9 @@ } }, "node_modules/vite-node": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.1.tgz", - "integrity": "sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.2.tgz", + "integrity": "sha512-/8iMryv46J3aK13iUXsei5G/A3CUlW4665THCPS+K8xAaqrVWiGB4RfXMQXCLjpK9P2eK//BczrVkn5JLAk6DA==", "dev": true, "license": "MIT", "dependencies": { @@ -4361,32 +4396,61 @@ } } }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/vitest": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.1.tgz", - "integrity": "sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.2.tgz", + "integrity": "sha512-WaxpJe092ID1C0mr+LH9MmNrhfzi8I65EX/NRU/Ld016KqQNRgxSOlGNP1hHN+a/F8L15Mh8klwaF77zR3GeDQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "3.1.1", - "@vitest/mocker": "3.1.1", - "@vitest/pretty-format": "^3.1.1", - "@vitest/runner": "3.1.1", - "@vitest/snapshot": "3.1.1", - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", + "@vitest/expect": "3.1.2", + "@vitest/mocker": "3.1.2", + "@vitest/pretty-format": "^3.1.2", + "@vitest/runner": "3.1.2", + "@vitest/snapshot": "3.1.2", + "@vitest/spy": "3.1.2", + "@vitest/utils": "3.1.2", "chai": "^5.2.0", "debug": "^4.4.0", - "expect-type": "^1.2.0", + "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", - "std-env": "^3.8.1", + "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.13", "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.1.1", + "vite-node": "3.1.2", "why-is-node-running": "^2.3.0" }, "bin": { @@ -4402,8 +4466,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.1.1", - "@vitest/ui": "3.1.1", + "@vitest/browser": "3.1.2", + "@vitest/ui": "3.1.2", "happy-dom": "*", "jsdom": "*" }, diff --git a/cli/package.json b/cli/package.json index 304c2acfbd..b2d29d6bb9 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@immich/cli", - "version": "2.2.61", + "version": "2.2.65", "description": "Command Line Interface (CLI) for Immich", "type": "module", "exports": "./dist/index.js", @@ -21,7 +21,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.14.0", + "@types/node": "^22.14.1", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index af7e2c52a9..01be1ef247 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -116,7 +116,7 @@ services: redis: container_name: immich_redis - image: docker.io/valkey/valkey:8-bookworm@sha256:42cba146593a5ea9a622002c1b7cba5da7be248650cbb64ecb9c6c33d29794b1 + image: docker.io/valkey/valkey:8-bookworm@sha256:c855f98e09d558a0d7cc1a4e56473231206a4c54c0114ada9c485b47aeb92ec8 healthcheck: test: redis-cli ping || exit 1 diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index f4a57ecbb9..c4fb086a09 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -56,7 +56,7 @@ services: redis: container_name: immich_redis - image: docker.io/valkey/valkey:8-bookworm@sha256:42cba146593a5ea9a622002c1b7cba5da7be248650cbb64ecb9c6c33d29794b1 + image: docker.io/valkey/valkey:8-bookworm@sha256:c855f98e09d558a0d7cc1a4e56473231206a4c54c0114ada9c485b47aeb92ec8 healthcheck: test: redis-cli ping || exit 1 restart: always @@ -90,7 +90,7 @@ services: container_name: immich_prometheus ports: - 9090:9090 - image: prom/prometheus@sha256:502ad90314c7485892ce696cb14a99fceab9fc27af29f4b427f41bd39701a199 + image: prom/prometheus@sha256:339ce86a59413be18d0e445472891d022725b4803fab609069110205e79fb2f1 volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - prometheus-data:/prometheus @@ -102,7 +102,7 @@ services: command: [ './run.sh', '-disable-reporting' ] ports: - 3000:3000 - image: grafana/grafana:11.6.0-ubuntu@sha256:fd8fa48213c624e1a95122f1d93abbf1cf1cbe85fc73212c1e599dbd76c63ff8 + image: grafana/grafana:11.6.1-ubuntu@sha256:6fc273288470ef499dd3c6b36aeade093170d4f608f864c5dd3a7fabeae77b50 volumes: - grafana-data:/var/lib/grafana diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 499673a383..b9fa6f8b02 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -49,7 +49,7 @@ services: redis: container_name: immich_redis - image: docker.io/valkey/valkey:8-bookworm@sha256:42cba146593a5ea9a622002c1b7cba5da7be248650cbb64ecb9c6c33d29794b1 + image: docker.io/valkey/valkey:8-bookworm@sha256:c855f98e09d558a0d7cc1a4e56473231206a4c54c0114ada9c485b47aeb92ec8 healthcheck: test: redis-cli ping || exit 1 restart: always diff --git a/docs/docs/administration/backup-and-restore.md b/docs/docs/administration/backup-and-restore.md index 817a7dca6d..7e55e4e88f 100644 --- a/docs/docs/administration/backup-and-restore.md +++ b/docs/docs/administration/backup-and-restore.md @@ -23,23 +23,32 @@ Refer to the official [postgres documentation](https://www.postgresql.org/docs/c It is not recommended to directly backup the `DB_DATA_LOCATION` folder. Doing so while the database is running can lead to a corrupted backup that cannot be restored. ::: -### Automatic Database Backups +### Automatic Database Dumps -For convenience, Immich will automatically create database backups by default. The backups are stored in `UPLOAD_LOCATION/backups`. -As mentioned above, you should make your own backup of these together with the asset folders as noted below. -You can adjust the schedule and amount of kept backups in the [admin settings](http://my.immich.app/admin/system-settings?isOpen=backup). -By default, Immich will keep the last 14 backups and create a new backup every day at 2:00 AM. +:::warning +The automatic database dumps can be used to restore the database in the event of damage to the Postgres database files. +There is no monitoring for these dumps and you will not be notified if they are unsuccessful. +::: -#### Trigger Backup +:::caution +The database dumps do **NOT** contain any pictures or videos, only metadata. They are only usable with a copy of the other files in `UPLOAD_LOCATION` as outlined below. +::: -You are able to trigger a backup in the [admin job status page](http://my.immich.app/admin/jobs-status). -Visit the page, open the "Create job" modal from the top right, select "Backup Database" and click "Confirm". -A job will run and trigger a backup, you can verify this worked correctly by checking the logs or the backup folder. -This backup will count towards the last X backups that will be kept based on your settings. +For disaster-recovery purposes, Immich will automatically create database dumps. The dumps are stored in `UPLOAD_LOCATION/backups`. +Please be sure to make your own, independent backup of the database together with the asset folders as noted below. +You can adjust the schedule and amount of kept database dumps in the [admin settings](http://my.immich.app/admin/system-settings?isOpen=backup). +By default, Immich will keep the last 14 database dumps and create a new dump every day at 2:00 AM. + +#### Trigger Dump + +You are able to trigger a database dump in the [admin job status page](http://my.immich.app/admin/jobs-status). +Visit the page, open the "Create job" modal from the top right, select "Create Database Dump" and click "Confirm". +A job will run and trigger a dump, you can verify this worked correctly by checking the logs or the `backups/` folder. +This dumps will count towards the last `X` dumps that will be kept based on your settings. #### Restoring -We hope to make restoring simpler in future versions, for now you can find the backups in the `UPLOAD_LOCATION/backups` folder on your host. +We hope to make restoring simpler in future versions, for now you can find the database dumps in the `UPLOAD_LOCATION/backups` folder on your host. Then please follow the steps in the following section for restoring the database. ### Manual Backup and Restore diff --git a/docs/docs/features/ml-hardware-acceleration.md b/docs/docs/features/ml-hardware-acceleration.md index 8371e726b9..a94f8c8c64 100644 --- a/docs/docs/features/ml-hardware-acceleration.md +++ b/docs/docs/features/ml-hardware-acceleration.md @@ -42,7 +42,7 @@ You do not need to redo any machine learning jobs after enabling hardware accele - The GPU must have compute capability 5.2 or greater. - The server must have the official NVIDIA driver installed. -- The installed driver must be >= 535 (it must support CUDA 12.2). +- The installed driver must be >= 545 (it must support CUDA 12.3). - On Linux (except for WSL2), you also need to have [NVIDIA Container Toolkit][nvct] installed. #### ROCm diff --git a/docs/docs/guides/custom-map-styles.md b/docs/docs/guides/custom-map-styles.md index 3f52937432..1a61afc324 100644 --- a/docs/docs/guides/custom-map-styles.md +++ b/docs/docs/guides/custom-map-styles.md @@ -14,14 +14,14 @@ online generators you can use. 2. Paste the link to your JSON style in either the **Light Style** or **Dark Style**. (You can add different styles which will help make the map style more appropriate depending on whether you set **Immich** to Light or Dark mode.) 3. Save your selections. Reload the map, and enjoy your custom map style! -## Use Maptiler to build a custom style +## Use MapTiler to build a custom style -Customizing the map style can be done easily using Maptiler, if you do not want to write an entire JSON document by hand. +Customizing the map style can be done easily using MapTiler, if you do not want to write an entire JSON document by hand. 1. Create a free account at https://cloud.maptiler.com 2. Once logged in, you can either create a brand new map by clicking on **New Map**, selecting a starter map, and then clicking **Customize**, OR by selecting a **Standard Map** and customizing it from there. 3. The **editor** interface is self-explanatory. You can change colors, remove visible layers, or add optional layers (e.g., administrative, topo, hydro, etc.) in the composer. 4. Once you have your map composed, click on **Save** at the top right. Give it a unique name to save it to your account. -5. Next, **Publish** your style using the **Publish** button at the top right. This will deploy it to production, which means it is able to be exposed over the Internet. Maptiler will present an interactive side-by-side map with the original and your changes prior to publication.<br/> -6. Maptiler will warn you that changing the map will change it across all apps using the map. Since no apps are using the map yet, this is okay. -7. Clicking on the name of your new map at the top left will bring you to the item's **details** page. From here, copy the link to the JSON style under **Use vector style**. This link will automatically contain your personal API key to Maptiler. +5. Next, **Publish** your style using the **Publish** button at the top right. This will deploy it to production, which means it is able to be exposed over the Internet. MapTiler will present an interactive side-by-side map with the original and your changes prior to publication.<br/> +6. MapTiler will warn you that changing the map will change it across all apps using the map. Since no apps are using the map yet, this is okay. +7. Clicking on the name of your new map at the top left will bring you to the item's **details** page. From here, copy the link to the JSON style under **Use vector style**. This link will automatically contain your personal API key to MapTiler. diff --git a/docs/docs/guides/database-queries.md b/docs/docs/guides/database-queries.md index 89a4f07bc0..209f673993 100644 --- a/docs/docs/guides/database-queries.md +++ b/docs/docs/guides/database-queries.md @@ -1,7 +1,7 @@ # Database Queries :::danger -Keep in mind that mucking around in the database might set the moon on fire. Avoid modifying the database directly when possible, and always have current backups. +Keep in mind that mucking around in the database might set the Moon on fire. Avoid modifying the database directly when possible, and always have current backups. ::: :::tip diff --git a/docs/src/components/community-projects.tsx b/docs/src/components/community-projects.tsx index b30544d461..e70b5af50f 100644 --- a/docs/src/components/community-projects.tsx +++ b/docs/src/components/community-projects.tsx @@ -40,8 +40,9 @@ const projects: CommunityProjectProps[] = [ }, { title: 'Lightroom Immich Plugin: lrc-immich-plugin', - description: 'Another Lightroom plugin to publish or export photos from Lightroom to Immich.', - url: 'https://github.com/bmachek/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 Duplicate Finder', diff --git a/docs/src/pages/index.tsx b/docs/src/pages/index.tsx index 2ffe1debc7..db299271aa 100644 --- a/docs/src/pages/index.tsx +++ b/docs/src/pages/index.tsx @@ -4,6 +4,7 @@ import Layout from '@theme/Layout'; import { discordPath, discordViewBox } from '@site/src/components/svg-paths'; import ThemedImage from '@theme/ThemedImage'; import Icon from '@mdi/react'; +import { mdiAndroid } from '@mdi/js'; function HomepageHeader() { return ( <header> @@ -88,11 +89,18 @@ function HomepageHeader() { <img className="h-24" alt="Get it on Google Play" src="/img/google-play-badge.png" /> </a> </div> + <div className="h-24"> <a href="https://apps.apple.com/sg/app/immich/id1613945652"> <img className="h-24 sm:p-3.5 p-3" alt="Download on the App Store" src="/img/ios-app-store-badge.svg" /> </a> </div> + + <div className="h-24"> + <a href="https://github.com/immich-app/immich/releases/latest"> + <img className="h-24 sm:p-3.5 p-3" alt="Download APK" src="/img/download-apk-github.svg" /> + </a> + </div> </div> <ThemedImage sources={{ dark: '/img/app-qr-code-dark.svg', light: '/img/app-qr-code-light.svg' }} diff --git a/docs/src/pages/roadmap.tsx b/docs/src/pages/roadmap.tsx index b7ded8e8c9..1e0914a651 100644 --- a/docs/src/pages/roadmap.tsx +++ b/docs/src/pages/roadmap.tsx @@ -76,6 +76,7 @@ import { mdiWeb, mdiDatabaseOutline, mdiLinkEdit, + mdiTagFaces, mdiMovieOpenPlayOutline, } from '@mdi/js'; import Layout from '@theme/Layout'; @@ -83,6 +84,8 @@ import React from 'react'; import { Item, Timeline } from '../components/timeline'; const releases = { + '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), @@ -242,6 +245,13 @@ const roadmap: Item[] = [ ]; const milestones: Item[] = [ + 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', @@ -249,6 +259,14 @@ const milestones: Item[] = [ 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', @@ -266,8 +284,8 @@ const milestones: Item[] = [ withRelease({ icon: mdiDatabaseOutline, iconColor: 'brown', - title: 'Automatic database backups', - description: 'Database backups are now integrated into the Immich server', + title: 'Automatic database dumps', + description: 'Database dumps are now integrated into the Immich server', release: 'v1.120.0', }), { @@ -300,7 +318,7 @@ const milestones: Item[] = [ withRelease({ icon: mdiFolderMultiple, iconColor: 'brown', - title: 'Folders', + title: 'Folders view', description: 'Browse your photos and videos in their folder structure', release: 'v1.113.0', }), diff --git a/docs/static/.well-known/security.txt b/docs/static/.well-known/security.txt new file mode 100644 index 0000000000..5a8414c3e2 --- /dev/null +++ b/docs/static/.well-known/security.txt @@ -0,0 +1,5 @@ +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/archived-versions.json b/docs/static/archived-versions.json index 247d5749e9..1e45c7a696 100644 --- a/docs/static/archived-versions.json +++ b/docs/static/archived-versions.json @@ -1,4 +1,20 @@ [ + { + "label": "v1.132.3", + "url": "https://v1.132.3.archive.immich.app" + }, + { + "label": "v1.132.2", + "url": "https://v1.132.2.archive.immich.app" + }, + { + "label": "v1.132.1", + "url": "https://v1.132.1.archive.immich.app" + }, + { + "label": "v1.132.0", + "url": "https://v1.132.0.archive.immich.app" + }, { "label": "v1.131.3", "url": "https://v1.131.3.archive.immich.app" diff --git a/docs/static/img/download-apk-github.svg b/docs/static/img/download-apk-github.svg new file mode 100644 index 0000000000..3fad724350 --- /dev/null +++ b/docs/static/img/download-apk-github.svg @@ -0,0 +1,13 @@ +<svg width="120" height="40" viewBox="0 0 120 40" fill="none" xmlns="http://www.w3.org/2000/svg"> +<rect x="0.5" y="0.5" width="119" height="39" rx="5.5" fill="black" stroke="#959797"/> +<path d="M39.576 16V10.08H41.704C42.184 10.08 42.6027 10.1653 42.96 10.336C43.3227 10.5013 43.624 10.7253 43.864 11.008C44.104 11.2907 44.2827 11.608 44.4 11.96C44.5227 12.312 44.584 12.672 44.584 13.04C44.584 13.408 44.5227 13.768 44.4 14.12C44.2827 14.472 44.104 14.7893 43.864 15.072C43.624 15.3547 43.3227 15.5813 42.96 15.752C42.6027 15.9173 42.184 16 41.704 16H39.576ZM41.656 11.032H40.664V15.04H41.656C42.072 15.04 42.416 14.944 42.688 14.752C42.96 14.5547 43.1627 14.304 43.296 14C43.4293 13.6907 43.496 13.368 43.496 13.032C43.496 12.7013 43.4293 12.384 43.296 12.08C43.1627 11.7707 42.96 11.52 42.688 11.328C42.416 11.1307 42.072 11.032 41.656 11.032ZM47.5533 16.08C47.0893 16.08 46.6946 15.976 46.3693 15.768C46.044 15.56 45.796 15.2907 45.6253 14.96C45.46 14.624 45.3773 14.264 45.3773 13.88C45.3773 13.496 45.46 13.1387 45.6253 12.808C45.796 12.472 46.044 12.2 46.3693 11.992C46.6946 11.784 47.0893 11.68 47.5533 11.68C48.0226 11.68 48.4173 11.784 48.7373 11.992C49.0626 12.2 49.308 12.472 49.4733 12.808C49.644 13.1387 49.7293 13.496 49.7293 13.88C49.7293 14.264 49.644 14.624 49.4733 14.96C49.308 15.2907 49.0626 15.56 48.7373 15.768C48.4173 15.976 48.0226 16.08 47.5533 16.08ZM47.5533 15.192C47.7933 15.192 47.9933 15.1307 48.1533 15.008C48.3186 14.8853 48.444 14.7253 48.5293 14.528C48.6146 14.3253 48.6573 14.1093 48.6573 13.88C48.6573 13.6453 48.6146 13.4293 48.5293 13.232C48.444 13.0347 48.3186 12.8747 48.1533 12.752C47.9933 12.6293 47.7933 12.568 47.5533 12.568C47.3186 12.568 47.1186 12.6293 46.9533 12.752C46.788 12.8747 46.6626 13.0347 46.5773 13.232C46.492 13.4293 46.4493 13.6453 46.4493 13.88C46.4493 14.1093 46.492 14.3253 46.5773 14.528C46.6626 14.7253 46.788 14.8853 46.9533 15.008C47.1186 15.1307 47.3186 15.192 47.5533 15.192ZM51.5128 16L50.1848 11.76H51.2488L52.0968 14.672L53.0008 11.76H53.9688L54.8728 14.672L55.7208 11.76H56.7848L55.4488 16H54.3208L53.4888 13.304L52.6488 16H51.5128ZM57.496 16V11.76H58.56V12.368H58.608C58.6507 12.2933 58.7173 12.2027 58.808 12.096C58.8987 11.984 59.024 11.888 59.184 11.808C59.344 11.7227 59.5467 11.68 59.792 11.68C60.1173 11.68 60.384 11.7547 60.592 11.904C60.8053 12.048 60.9627 12.24 61.064 12.48C61.1707 12.72 61.224 12.9787 61.224 13.256V16H60.16V13.4C60.16 13.1707 60.096 12.9733 59.968 12.808C59.84 12.6427 59.648 12.56 59.392 12.56C59.1947 12.56 59.0347 12.6107 58.912 12.712C58.7893 12.808 58.6987 12.9333 58.64 13.088C58.5867 13.2427 58.56 13.4053 58.56 13.576V16H57.496ZM62.2948 16L62.3028 9.92H63.3588V16H62.2948ZM66.5143 16.08C66.0503 16.08 65.6556 15.976 65.3303 15.768C65.0049 15.56 64.7569 15.2907 64.5863 14.96C64.4209 14.624 64.3383 14.264 64.3383 13.88C64.3383 13.496 64.4209 13.1387 64.5863 12.808C64.7569 12.472 65.0049 12.2 65.3303 11.992C65.6556 11.784 66.0503 11.68 66.5143 11.68C66.9836 11.68 67.3783 11.784 67.6983 11.992C68.0236 12.2 68.2689 12.472 68.4343 12.808C68.6049 13.1387 68.6903 13.496 68.6903 13.88C68.6903 14.264 68.6049 14.624 68.4343 14.96C68.2689 15.2907 68.0236 15.56 67.6983 15.768C67.3783 15.976 66.9836 16.08 66.5143 16.08ZM66.5143 15.192C66.7543 15.192 66.9543 15.1307 67.1143 15.008C67.2796 14.8853 67.4049 14.7253 67.4903 14.528C67.5756 14.3253 67.6183 14.1093 67.6183 13.88C67.6183 13.6453 67.5756 13.4293 67.4903 13.232C67.4049 13.0347 67.2796 12.8747 67.1143 12.752C66.9543 12.6293 66.7543 12.568 66.5143 12.568C66.2796 12.568 66.0796 12.6293 65.9143 12.752C65.7489 12.8747 65.6236 13.0347 65.5383 13.232C65.4529 13.4293 65.4103 13.6453 65.4103 13.88C65.4103 14.1093 65.4529 14.3253 65.5383 14.528C65.6236 14.7253 65.7489 14.8853 65.9143 15.008C66.0796 15.1307 66.2796 15.192 66.5143 15.192ZM71.3979 11.68C71.6699 11.68 71.8939 11.7253 72.0699 11.816C72.2459 11.9013 72.3846 11.9973 72.4859 12.104C72.5873 12.2053 72.6566 12.288 72.6939 12.352H72.7419V11.76H73.8059V16H72.7419V15.408H72.6939C72.6566 15.472 72.5873 15.5573 72.4859 15.664C72.3846 15.7707 72.2459 15.8667 72.0699 15.952C71.8939 16.0373 71.6699 16.08 71.3979 16.08C71.0833 16.08 70.8086 16.0187 70.5739 15.896C70.3393 15.768 70.1419 15.6 69.9819 15.392C69.8273 15.1787 69.7099 14.9413 69.6299 14.68C69.5553 14.4187 69.5179 14.152 69.5179 13.88C69.5179 13.608 69.5553 13.3413 69.6299 13.08C69.7099 12.8187 69.8273 12.584 69.9819 12.376C70.1419 12.1627 70.3393 11.9947 70.5739 11.872C70.8086 11.744 71.0833 11.68 71.3979 11.68ZM71.6779 12.552C71.4379 12.552 71.2353 12.6187 71.0699 12.752C70.9099 12.88 70.7899 13.0453 70.7099 13.248C70.6299 13.4507 70.5899 13.6613 70.5899 13.88C70.5899 14.0987 70.6299 14.3093 70.7099 14.512C70.7899 14.7147 70.9099 14.8827 71.0699 15.016C71.2353 15.144 71.4379 15.208 71.6779 15.208C71.9179 15.208 72.1179 15.144 72.2779 15.016C72.4379 14.8827 72.5579 14.7147 72.6379 14.512C72.7179 14.3093 72.7579 14.0987 72.7579 13.88C72.7579 13.6613 72.7179 13.4507 72.6379 13.248C72.5579 13.0453 72.4379 12.88 72.2779 12.752C72.1179 12.6187 71.9179 12.552 71.6779 12.552ZM76.6479 16.08C76.3333 16.08 76.0586 16.0187 75.8239 15.896C75.5893 15.768 75.3919 15.6 75.2319 15.392C75.0773 15.1787 74.9599 14.9413 74.8799 14.68C74.8053 14.4187 74.7679 14.152 74.7679 13.88C74.7679 13.608 74.8053 13.3413 74.8799 13.08C74.9599 12.8187 75.0773 12.584 75.2319 12.376C75.3919 12.1627 75.5893 11.9947 75.8239 11.872C76.0586 11.744 76.3333 11.68 76.6479 11.68C76.9199 11.68 77.1439 11.7253 77.3199 11.816C77.4959 11.9013 77.6346 11.9973 77.7359 12.104C77.8373 12.2053 77.9066 12.288 77.9439 12.352H77.9919V9.92H79.0559V16H77.9919V15.408H77.9439C77.9066 15.472 77.8373 15.5573 77.7359 15.664C77.6346 15.7707 77.4959 15.8667 77.3199 15.952C77.1439 16.0373 76.9199 16.08 76.6479 16.08ZM76.9279 15.208C77.1679 15.208 77.3679 15.144 77.5279 15.016C77.6879 14.8827 77.8079 14.7147 77.8879 14.512C77.9679 14.3093 78.0079 14.0987 78.0079 13.88C78.0079 13.6613 77.9679 13.4507 77.8879 13.248C77.8079 13.0453 77.6879 12.88 77.5279 12.752C77.3679 12.6187 77.1679 12.552 76.9279 12.552C76.6879 12.552 76.4853 12.6187 76.3199 12.752C76.1599 12.88 76.0399 13.0453 75.9599 13.248C75.8799 13.4507 75.8399 13.6613 75.8399 13.88C75.8399 14.0987 75.8799 14.3093 75.9599 14.512C76.0399 14.7147 76.1599 14.8827 76.3199 15.016C76.4853 15.144 76.6879 15.208 76.9279 15.208ZM81.7566 16L83.7966 10.08H85.2126L87.2606 16H86.1246L85.7006 14.752H83.3166L82.8926 16H81.7566ZM83.6286 13.808H85.3886L84.5086 11.192L83.6286 13.808ZM88.1151 16V10.08H90.4511C90.9257 10.08 91.3204 10.176 91.6351 10.368C91.9497 10.5547 92.1844 10.8 92.3391 11.104C92.4991 11.4027 92.5791 11.7147 92.5791 12.04C92.5791 12.3653 92.4991 12.68 92.3391 12.984C92.1844 13.2827 91.9497 13.528 91.6351 13.72C91.3204 13.912 90.9257 14.008 90.4511 14.008H89.2031V16H88.1151ZM90.4431 11.024H89.2031V13.064H90.4431C90.6777 13.064 90.8724 13.016 91.0271 12.92C91.1817 12.8187 91.2964 12.6907 91.3711 12.536C91.4511 12.376 91.4911 12.2133 91.4911 12.048C91.4911 11.8773 91.4511 11.7147 91.3711 11.56C91.2964 11.4053 91.1817 11.2773 91.0271 11.176C90.8724 11.0747 90.6777 11.024 90.4431 11.024ZM93.451 16V10.08H94.539V12.648L96.795 10.088H98.155L95.587 12.928L98.259 16H96.851L94.539 13.296V16H93.451ZM102.952 16.08C102.488 16.08 102.093 15.976 101.768 15.768C101.442 15.56 101.194 15.2907 101.024 14.96C100.858 14.624 100.776 14.264 100.776 13.88C100.776 13.496 100.858 13.1387 101.024 12.808C101.194 12.472 101.442 12.2 101.768 11.992C102.093 11.784 102.488 11.68 102.952 11.68C103.421 11.68 103.816 11.784 104.136 11.992C104.461 12.2 104.706 12.472 104.872 12.808C105.042 13.1387 105.128 13.496 105.128 13.88C105.128 14.264 105.042 14.624 104.872 14.96C104.706 15.2907 104.461 15.56 104.136 15.768C103.816 15.976 103.421 16.08 102.952 16.08ZM102.952 15.192C103.192 15.192 103.392 15.1307 103.552 15.008C103.717 14.8853 103.842 14.7253 103.928 14.528C104.013 14.3253 104.056 14.1093 104.056 13.88C104.056 13.6453 104.013 13.4293 103.928 13.232C103.842 13.0347 103.717 12.8747 103.552 12.752C103.392 12.6293 103.192 12.568 102.952 12.568C102.717 12.568 102.517 12.6293 102.352 12.752C102.186 12.8747 102.061 13.0347 101.976 13.232C101.89 13.4293 101.848 13.6453 101.848 13.88C101.848 14.1093 101.89 14.3253 101.976 14.528C102.061 14.7253 102.186 14.8853 102.352 15.008C102.517 15.1307 102.717 15.192 102.952 15.192ZM106.027 16V11.76H107.091V12.368H107.139C107.182 12.2933 107.249 12.2027 107.339 12.096C107.43 11.984 107.555 11.888 107.715 11.808C107.875 11.7227 108.078 11.68 108.323 11.68C108.649 11.68 108.915 11.7547 109.123 11.904C109.337 12.048 109.494 12.24 109.595 12.48C109.702 12.72 109.755 12.9787 109.755 13.256V16H108.691V13.4C108.691 13.1707 108.627 12.9733 108.499 12.808C108.371 12.6427 108.179 12.56 107.923 12.56C107.726 12.56 107.566 12.6107 107.443 12.712C107.321 12.808 107.23 12.9333 107.171 13.088C107.118 13.2427 107.091 13.4053 107.091 13.576V16H106.027Z" fill="white"/> +<path d="M59.944 30.12C59.2 30.12 58.552 29.992 58 29.736C57.448 29.48 56.988 29.136 56.62 28.704C56.26 28.264 55.988 27.776 55.804 27.24C55.628 26.696 55.54 26.14 55.54 25.572C55.54 25.004 55.628 24.448 55.804 23.904C55.988 23.36 56.264 22.872 56.632 22.44C57 22 57.464 21.652 58.024 21.396C58.584 21.132 59.236 21 59.98 21C60.564 21 61.064 21.076 61.48 21.228C61.904 21.372 62.256 21.56 62.536 21.792C62.824 22.024 63.052 22.272 63.22 22.536C63.396 22.792 63.528 23.036 63.616 23.268C63.704 23.5 63.764 23.692 63.796 23.844C63.828 23.988 63.844 24.06 63.844 24.06H62.212C62.212 24.06 62.196 24.008 62.164 23.904C62.14 23.792 62.084 23.656 61.996 23.496C61.908 23.336 61.78 23.176 61.612 23.016C61.452 22.856 61.24 22.724 60.976 22.62C60.712 22.508 60.384 22.452 59.992 22.452C59.52 22.452 59.108 22.54 58.756 22.716C58.412 22.892 58.124 23.128 57.892 23.424C57.66 23.72 57.488 24.052 57.376 24.42C57.264 24.788 57.208 25.164 57.208 25.548C57.208 26.068 57.312 26.568 57.52 27.048C57.728 27.528 58.036 27.92 58.444 28.224C58.86 28.528 59.372 28.68 59.98 28.68C60.684 28.68 61.24 28.488 61.648 28.104C62.064 27.72 62.3 27.208 62.356 26.568H59.932V25.272H63.94V25.932C63.94 26.852 63.764 27.624 63.412 28.248C63.06 28.864 62.58 29.332 61.972 29.652C61.372 29.964 60.696 30.12 59.944 30.12ZM66.401 22.656C66.121 22.656 65.889 22.564 65.705 22.38C65.529 22.196 65.441 21.976 65.441 21.72C65.441 21.464 65.529 21.244 65.705 21.06C65.889 20.876 66.121 20.784 66.401 20.784C66.681 20.784 66.913 20.876 67.097 21.06C67.281 21.244 67.373 21.464 67.373 21.72C67.373 21.976 67.281 22.196 67.097 22.38C66.913 22.564 66.681 22.656 66.401 22.656ZM65.609 30V23.64H67.205V30H65.609ZM71.9709 30.12C71.5389 30.12 71.1829 30.056 70.9029 29.928C70.6229 29.8 70.4029 29.632 70.2429 29.424C70.0909 29.216 69.9829 28.988 69.9189 28.74C69.8549 28.492 69.8229 28.248 69.8229 28.008V24.924H68.7309V23.64H69.8229V22.056H71.4189V23.64H72.8469V24.924H71.4189V27.972C71.4189 28.54 71.7389 28.824 72.3789 28.824C72.5309 28.824 72.6709 28.816 72.7989 28.8C72.9349 28.776 73.0029 28.764 73.0029 28.764V30.012C73.0029 30.012 72.8989 30.028 72.6909 30.06C72.4909 30.1 72.2509 30.12 71.9709 30.12ZM74.666 30V21.12H76.298V24.768H80.006V21.12H81.638V30H80.006V26.184H76.298V30H74.666ZM85.4973 30.12C84.8333 30.12 84.3293 29.916 83.9853 29.508C83.6493 29.1 83.4813 28.576 83.4813 27.936V23.64H85.0653V27.672C85.0653 28.016 85.1573 28.292 85.3413 28.5C85.5253 28.7 85.7853 28.8 86.1213 28.8C86.4173 28.8 86.6573 28.724 86.8413 28.572C87.0253 28.42 87.1573 28.224 87.2373 27.984C87.3253 27.744 87.3693 27.496 87.3693 27.24V23.64H88.9653V30H87.3693V29.088H87.2973C87.2173 29.232 87.1013 29.384 86.9493 29.544C86.8053 29.704 86.6133 29.84 86.3733 29.952C86.1413 30.064 85.8493 30.12 85.4973 30.12ZM94.426 30.12C94.018 30.12 93.682 30.056 93.418 29.928C93.154 29.792 92.946 29.644 92.794 29.484C92.65 29.324 92.55 29.2 92.494 29.112H92.41V30H90.814V20.88H92.41V24.528H92.494C92.55 24.432 92.65 24.304 92.794 24.144C92.946 23.984 93.154 23.84 93.418 23.712C93.682 23.584 94.018 23.52 94.426 23.52C94.906 23.52 95.322 23.616 95.674 23.808C96.026 23.992 96.318 24.244 96.55 24.564C96.782 24.876 96.954 25.228 97.066 25.62C97.186 26.012 97.246 26.412 97.246 26.82C97.246 27.228 97.186 27.628 97.066 28.02C96.954 28.412 96.782 28.768 96.55 29.088C96.318 29.4 96.026 29.652 95.674 29.844C95.322 30.028 94.906 30.12 94.426 30.12ZM94.006 28.812C94.374 28.812 94.678 28.716 94.918 28.524C95.158 28.324 95.338 28.072 95.458 27.768C95.578 27.464 95.638 27.148 95.638 26.82C95.638 26.492 95.578 26.176 95.458 25.872C95.338 25.568 95.158 25.32 94.918 25.128C94.678 24.928 94.374 24.828 94.006 24.828C93.646 24.828 93.346 24.928 93.106 25.128C92.866 25.32 92.686 25.568 92.566 25.872C92.446 26.176 92.386 26.492 92.386 26.82C92.386 27.148 92.446 27.464 92.566 27.768C92.686 28.072 92.866 28.324 93.106 28.524C93.346 28.716 93.646 28.812 94.006 28.812Z" fill="white"/> +<g clip-path="url(#clip0_10_61)"> +<path d="M19.5 8.98957C16.799 8.95068 14.1741 9.88499 12.1051 11.6218C10.0361 13.3585 8.66106 15.7818 8.23132 18.4487C7.80158 21.1156 8.3458 23.8481 9.76454 26.1469C11.1833 28.4457 13.3818 30.1572 15.9583 30.9687C16.4792 31.0729 16.8958 30.5 16.8958 30.0312V28.5208C13.7708 29.1979 12.7812 27.0104 12.7812 27.0104C12.5781 26.3511 12.1783 25.7696 11.6354 25.3437C10.5937 24.6146 11.6875 24.6667 11.6875 24.6667C12.0353 24.6944 12.3719 24.803 12.6703 24.9839C12.9687 25.1647 13.2207 25.4128 13.4062 25.7083C14.2917 27.2187 16.2708 27.0625 16.8958 26.8021C16.8411 26.2842 16.9313 25.7613 17.1562 25.2917C13.6146 24.6667 11.6875 22.5833 11.6875 19.7708C11.6502 18.304 12.193 16.8816 13.1979 15.8125C12.918 15.2953 12.7846 14.7115 12.8121 14.1241C12.8396 13.5366 13.0271 12.9679 13.3542 12.4792C13.9832 12.4915 14.6031 12.6318 15.1762 12.8915C15.7493 13.1512 16.2635 13.5248 16.6875 13.9896C17.5919 13.681 18.545 13.5398 19.5 13.5729C20.451 13.5434 21.4005 13.6665 22.3125 13.9375C22.7429 13.4845 23.2595 13.1222 23.8319 12.8717C24.4043 12.6213 25.0211 12.4878 25.6458 12.4792C25.9658 12.9693 26.1531 13.5341 26.1897 14.1183C26.2262 14.7026 26.1106 15.2863 25.8542 15.8125C26.8402 16.8899 27.3638 18.3113 27.3125 19.7708C27.3125 22.5833 25.3854 24.6667 21.8437 25.2917C22.0687 25.7613 22.1589 26.2842 22.1042 26.8021V30.0312C22.104 30.1589 22.1298 30.2852 22.1802 30.4025C22.2305 30.5197 22.3043 30.6255 22.3969 30.7133C22.4896 30.801 22.5991 30.869 22.719 30.9129C22.8388 30.9568 22.9663 30.9758 23.0937 30.9687C25.6089 30.0961 27.7353 28.3635 29.0981 26.0765C30.4609 23.7894 30.9725 21.0947 30.5429 18.4674C30.1133 15.84 28.7701 13.4486 26.7498 11.7147C24.7296 9.9808 22.1621 9.01572 19.5 8.98957Z" fill="white"/> +</g> +<defs> +<clipPath id="clip0_10_61"> +<rect width="25" height="25" fill="white" transform="translate(7 8)"/> +</clipPath> +</defs> +</svg> diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index e05d7734ed..48c17c828b 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -34,7 +34,7 @@ services: - 2285:2285 redis: - image: redis:6.2-alpine@sha256:148bb5411c184abd288d9aaed139c98123eeb8824c5d3fce03cf721db58066d8 + image: redis:6.2-alpine@sha256:3211c33a618c457e5d241922c975dbc4f446d0bdb2dc75694f5573ef8e2d01fa database: image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52 diff --git a/e2e/package-lock.json b/e2e/package-lock.json index d09a7e9701..a61324beff 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-e2e", - "version": "1.131.3", + "version": "1.132.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-e2e", - "version": "1.131.3", + "version": "1.132.3", "license": "GNU Affero General Public License version 3", "devDependencies": { "@eslint/eslintrc": "^3.1.0", @@ -15,7 +15,7 @@ "@immich/sdk": "file:../open-api/typescript-sdk", "@playwright/test": "^1.44.1", "@types/luxon": "^3.4.2", - "@types/node": "^22.14.0", + "@types/node": "^22.14.1", "@types/oidc-provider": "^8.5.1", "@types/pg": "^8.11.0", "@types/pngjs": "^6.0.4", @@ -44,7 +44,7 @@ }, "../cli": { "name": "@immich/cli", - "version": "2.2.61", + "version": "2.2.65", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { @@ -66,7 +66,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.14.0", + "@types/node": "^22.14.1", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", @@ -93,14 +93,14 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.131.3", + "version": "1.132.3", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.14.0", + "@types/node": "^22.14.1", "typescript": "^5.3.3" } }, @@ -194,9 +194,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", - "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz", + "integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==", "cpu": [ "ppc64" ], @@ -211,9 +211,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", - "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz", + "integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==", "cpu": [ "arm" ], @@ -228,9 +228,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", - "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz", + "integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==", "cpu": [ "arm64" ], @@ -245,9 +245,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", - "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz", + "integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==", "cpu": [ "x64" ], @@ -262,9 +262,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", - "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz", + "integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==", "cpu": [ "arm64" ], @@ -279,9 +279,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", - "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz", + "integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==", "cpu": [ "x64" ], @@ -296,9 +296,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", - "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz", + "integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==", "cpu": [ "arm64" ], @@ -313,9 +313,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", - "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz", + "integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==", "cpu": [ "x64" ], @@ -330,9 +330,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", - "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz", + "integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==", "cpu": [ "arm" ], @@ -347,9 +347,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", - "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz", + "integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==", "cpu": [ "arm64" ], @@ -364,9 +364,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", - "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz", + "integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==", "cpu": [ "ia32" ], @@ -381,9 +381,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", - "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz", + "integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==", "cpu": [ "loong64" ], @@ -398,9 +398,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", - "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz", + "integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==", "cpu": [ "mips64el" ], @@ -415,9 +415,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", - "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz", + "integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==", "cpu": [ "ppc64" ], @@ -432,9 +432,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", - "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz", + "integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==", "cpu": [ "riscv64" ], @@ -449,9 +449,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", - "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz", + "integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==", "cpu": [ "s390x" ], @@ -466,9 +466,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", - "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz", + "integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==", "cpu": [ "x64" ], @@ -483,9 +483,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", - "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz", + "integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==", "cpu": [ "arm64" ], @@ -500,9 +500,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", - "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz", + "integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==", "cpu": [ "x64" ], @@ -517,9 +517,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", - "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz", + "integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==", "cpu": [ "arm64" ], @@ -534,9 +534,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", - "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz", + "integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==", "cpu": [ "x64" ], @@ -551,9 +551,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", - "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz", + "integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==", "cpu": [ "x64" ], @@ -568,9 +568,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", - "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz", + "integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==", "cpu": [ "arm64" ], @@ -585,9 +585,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", - "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz", + "integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==", "cpu": [ "ia32" ], @@ -602,9 +602,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", - "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz", + "integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==", "cpu": [ "x64" ], @@ -686,9 +686,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", + "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -736,9 +736,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", - "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", + "version": "9.25.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.1.tgz", + "integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==", "dev": true, "license": "MIT", "engines": { @@ -769,19 +769,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1082,13 +1069,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.51.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.51.1.tgz", - "integrity": "sha512-nM+kEaTSAoVlXmMPH10017vn3FSiFqr/bh4fKg9vmAdMfd9SDqRZNvPSiAHADc/itWak+qPvMPZQOPwCBW7k7Q==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.52.0.tgz", + "integrity": "sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.51.1" + "playwright": "1.52.0" }, "bin": { "playwright": "cli.js" @@ -1098,9 +1085,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", - "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.1.tgz", + "integrity": "sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw==", "cpu": [ "arm" ], @@ -1112,9 +1099,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", - "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.1.tgz", + "integrity": "sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw==", "cpu": [ "arm64" ], @@ -1126,9 +1113,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", - "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.1.tgz", + "integrity": "sha512-VWXGISWFY18v/0JyNUy4A46KCFCb9NVsH+1100XP31lud+TzlezBbz24CYzbnA4x6w4hx+NYCXDfnvDVO6lcAA==", "cpu": [ "arm64" ], @@ -1140,9 +1127,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", - "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.1.tgz", + "integrity": "sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw==", "cpu": [ "x64" ], @@ -1154,9 +1141,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", - "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.1.tgz", + "integrity": "sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw==", "cpu": [ "arm64" ], @@ -1168,9 +1155,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", - "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.1.tgz", + "integrity": "sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q==", "cpu": [ "x64" ], @@ -1182,9 +1169,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", - "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.1.tgz", + "integrity": "sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg==", "cpu": [ "arm" ], @@ -1196,9 +1183,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", - "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.1.tgz", + "integrity": "sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg==", "cpu": [ "arm" ], @@ -1210,9 +1197,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", - "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.1.tgz", + "integrity": "sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg==", "cpu": [ "arm64" ], @@ -1224,9 +1211,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", - "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.1.tgz", + "integrity": "sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ==", "cpu": [ "arm64" ], @@ -1238,9 +1225,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", - "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.1.tgz", + "integrity": "sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ==", "cpu": [ "loong64" ], @@ -1252,9 +1239,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", - "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.1.tgz", + "integrity": "sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg==", "cpu": [ "ppc64" ], @@ -1266,9 +1253,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", - "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.1.tgz", + "integrity": "sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ==", "cpu": [ "riscv64" ], @@ -1280,9 +1267,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", - "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.1.tgz", + "integrity": "sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA==", "cpu": [ "riscv64" ], @@ -1294,9 +1281,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", - "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.1.tgz", + "integrity": "sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg==", "cpu": [ "s390x" ], @@ -1308,9 +1295,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", - "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.1.tgz", + "integrity": "sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ==", "cpu": [ "x64" ], @@ -1322,9 +1309,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", - "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.1.tgz", + "integrity": "sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ==", "cpu": [ "x64" ], @@ -1336,9 +1323,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", - "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.1.tgz", + "integrity": "sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg==", "cpu": [ "arm64" ], @@ -1350,9 +1337,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", - "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.1.tgz", + "integrity": "sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA==", "cpu": [ "ia32" ], @@ -1364,9 +1351,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", - "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.1.tgz", + "integrity": "sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA==", "cpu": [ "x64" ], @@ -1613,9 +1600,9 @@ } }, "node_modules/@types/pg": { - "version": "8.11.13", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.13.tgz", - "integrity": "sha512-6kXByGkvRvwXLuyaWzsebs2du6+XuAB2CuMsuzP7uaihQahshVgSmB22Pmh0vQMkQ1h5+PZU0d+Di1o+WpVWJg==", + "version": "8.11.14", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.14.tgz", + "integrity": "sha512-qyD11E5R3u0eJmd1lB0WnWKXJGA7s015nyARWljfz5DcX83TKAIlY+QrmvzQTsbIe+hkiFtkyL2gHC6qwF6Fbg==", "dev": true, "license": "MIT", "dependencies": { @@ -1696,17 +1683,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", - "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.0.tgz", + "integrity": "sha512-evaQJZ/J/S4wisevDvC1KFZkPzRetH8kYZbkgcTRyql3mcKsf+ZFDV1BVWUGTCAW5pQHoqn5gK5b8kn7ou9aFQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/type-utils": "8.30.1", - "@typescript-eslint/utils": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/scope-manager": "8.31.0", + "@typescript-eslint/type-utils": "8.31.0", + "@typescript-eslint/utils": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1726,16 +1713,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", - "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.31.0.tgz", + "integrity": "sha512-67kYYShjBR0jNI5vsf/c3WG4u+zDnCTHTPqVMQguffaWWFs7artgwKmfwdifl+r6XyM5LYLas/dInj2T0SgJyw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/typescript-estree": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/scope-manager": "8.31.0", + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/typescript-estree": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0", "debug": "^4.3.4" }, "engines": { @@ -1751,14 +1738,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", - "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.31.0.tgz", + "integrity": "sha512-knO8UyF78Nt8O/B64i7TlGXod69ko7z6vJD9uhSlm0qkAbGeRUSudcm0+K/4CrRjrpiHfBCjMWlc08Vav1xwcw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1" + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1769,14 +1756,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", - "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.31.0.tgz", + "integrity": "sha512-DJ1N1GdjI7IS7uRlzJuEDCgDQix3ZVYVtgeWEyhyn4iaoitpMBX6Ndd488mXSx0xah/cONAkEaYyylDyAeHMHg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.30.1", - "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/typescript-estree": "8.31.0", + "@typescript-eslint/utils": "8.31.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, @@ -1793,9 +1780,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", - "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.31.0.tgz", + "integrity": "sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ==", "dev": true, "license": "MIT", "engines": { @@ -1807,14 +1794,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", - "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.0.tgz", + "integrity": "sha512-xLmgn4Yl46xi6aDSZ9KkyfhhtnYI15/CvHbpOy/eR5NWhK/BK8wc709KKwhAR0m4ZKRP7h07bm4BWUYOCuRpQQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1860,16 +1847,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", - "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.31.0.tgz", + "integrity": "sha512-qi6uPLt9cjTFxAb1zGNgTob4x9ur7xC6mHQJ8GwEzGMGE9tYniublmJaowOJ9V2jUzxrltTPfdG2nKlWsq0+Ww==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/typescript-estree": "8.30.1" + "@typescript-eslint/scope-manager": "8.31.0", + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/typescript-estree": "8.31.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1884,13 +1871,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", - "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.0.tgz", + "integrity": "sha512-QcGHmlRHWOl93o64ZUMNewCdwKGU6WItOU52H0djgNmn1EOrhVudrDzXz4OycCRSCPwFCDrE2iIt5vmuUdHxuQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/types": "8.31.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -1902,9 +1889,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.1.tgz", - "integrity": "sha512-MgV6D2dhpD6Hp/uroUoAIvFqA8AuvXEFBC2eepG3WFc1pxTfdk1LEqqkWoWhjz+rytoqrnUUCdf6Lzco3iHkLQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.2.tgz", + "integrity": "sha512-XDdaDOeaTMAMYW7N63AqoK32sYUWbXnTkC6tEbVcu3RlU1bB9of32T+PGf8KZvxqLNqeXhafDFqCkwpf2+dyaQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1917,7 +1904,7 @@ "istanbul-reports": "^3.1.7", "magic-string": "^0.30.17", "magicast": "^0.3.5", - "std-env": "^3.8.1", + "std-env": "^3.9.0", "test-exclude": "^7.0.1", "tinyrainbow": "^2.0.0" }, @@ -1925,8 +1912,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "3.1.1", - "vitest": "3.1.1" + "@vitest/browser": "3.1.2", + "vitest": "3.1.2" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -1935,14 +1922,14 @@ } }, "node_modules/@vitest/expect": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.1.tgz", - "integrity": "sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.2.tgz", + "integrity": "sha512-O8hJgr+zREopCAqWl3uCVaOdqJwZ9qaDwUP7vy3Xigad0phZe9APxKhPcDNqYYi0rX5oMvwJMSCAXY2afqeTSA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", + "@vitest/spy": "3.1.2", + "@vitest/utils": "3.1.2", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, @@ -1951,13 +1938,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.1.tgz", - "integrity": "sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.2.tgz", + "integrity": "sha512-kOtd6K2lc7SQ0mBqYv/wdGedlqPdM/B38paPY+OwJ1XiNi44w3Fpog82UfOibmHaV9Wod18A09I9SCKLyDMqgw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.1", + "@vitest/spy": "3.1.2", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -1978,9 +1965,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.1.tgz", - "integrity": "sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.2.tgz", + "integrity": "sha512-R0xAiHuWeDjTSB3kQ3OQpT8Rx3yhdOAIm/JM4axXxnG7Q/fS8XUwggv/A4xzbQA+drYRjzkMnpYnOGAc4oeq8w==", "dev": true, "license": "MIT", "dependencies": { @@ -1991,13 +1978,13 @@ } }, "node_modules/@vitest/runner": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.1.tgz", - "integrity": "sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.2.tgz", + "integrity": "sha512-bhLib9l4xb4sUMPXnThbnhX2Yi8OutBMA8Yahxa7yavQsFDtwY/jrUZwpKp2XH9DhRFJIeytlyGpXCqZ65nR+g==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.1.1", + "@vitest/utils": "3.1.2", "pathe": "^2.0.3" }, "funding": { @@ -2005,13 +1992,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.1.tgz", - "integrity": "sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.2.tgz", + "integrity": "sha512-Q1qkpazSF/p4ApZg1vfZSQ5Yw6OCQxVMVrLjslbLFA1hMDrT2uxtqMaw8Tc/jy5DLka1sNs1Y7rBcftMiaSH/Q==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.1", + "@vitest/pretty-format": "3.1.2", "magic-string": "^0.30.17", "pathe": "^2.0.3" }, @@ -2020,9 +2007,9 @@ } }, "node_modules/@vitest/spy": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.1.tgz", - "integrity": "sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.2.tgz", + "integrity": "sha512-OEc5fSXMws6sHVe4kOFyDSj/+4MSwst0ib4un0DlcYgQvRuYQ0+M2HyqGaauUMnjq87tmUaMNDxKQx7wNfVqPA==", "dev": true, "license": "MIT", "dependencies": { @@ -2033,13 +2020,13 @@ } }, "node_modules/@vitest/utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.1.tgz", - "integrity": "sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.2.tgz", + "integrity": "sha512-5GGd0ytZ7BH3H6JTj9Kw7Prn1Nbg0wZVrIvou+UWxm54d+WoXXgAgjFJ8wn3LdagWLFSEfpPeyYrByZaGEZHLg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.1", + "@vitest/pretty-format": "3.1.2", "loupe": "^3.1.3", "tinyrainbow": "^2.0.0" }, @@ -2915,9 +2902,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", "dev": true, "license": "MIT" }, @@ -2951,9 +2938,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", - "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz", + "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2964,31 +2951,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.2", - "@esbuild/android-arm": "0.25.2", - "@esbuild/android-arm64": "0.25.2", - "@esbuild/android-x64": "0.25.2", - "@esbuild/darwin-arm64": "0.25.2", - "@esbuild/darwin-x64": "0.25.2", - "@esbuild/freebsd-arm64": "0.25.2", - "@esbuild/freebsd-x64": "0.25.2", - "@esbuild/linux-arm": "0.25.2", - "@esbuild/linux-arm64": "0.25.2", - "@esbuild/linux-ia32": "0.25.2", - "@esbuild/linux-loong64": "0.25.2", - "@esbuild/linux-mips64el": "0.25.2", - "@esbuild/linux-ppc64": "0.25.2", - "@esbuild/linux-riscv64": "0.25.2", - "@esbuild/linux-s390x": "0.25.2", - "@esbuild/linux-x64": "0.25.2", - "@esbuild/netbsd-arm64": "0.25.2", - "@esbuild/netbsd-x64": "0.25.2", - "@esbuild/openbsd-arm64": "0.25.2", - "@esbuild/openbsd-x64": "0.25.2", - "@esbuild/sunos-x64": "0.25.2", - "@esbuild/win32-arm64": "0.25.2", - "@esbuild/win32-ia32": "0.25.2", - "@esbuild/win32-x64": "0.25.2" + "@esbuild/aix-ppc64": "0.25.3", + "@esbuild/android-arm": "0.25.3", + "@esbuild/android-arm64": "0.25.3", + "@esbuild/android-x64": "0.25.3", + "@esbuild/darwin-arm64": "0.25.3", + "@esbuild/darwin-x64": "0.25.3", + "@esbuild/freebsd-arm64": "0.25.3", + "@esbuild/freebsd-x64": "0.25.3", + "@esbuild/linux-arm": "0.25.3", + "@esbuild/linux-arm64": "0.25.3", + "@esbuild/linux-ia32": "0.25.3", + "@esbuild/linux-loong64": "0.25.3", + "@esbuild/linux-mips64el": "0.25.3", + "@esbuild/linux-ppc64": "0.25.3", + "@esbuild/linux-riscv64": "0.25.3", + "@esbuild/linux-s390x": "0.25.3", + "@esbuild/linux-x64": "0.25.3", + "@esbuild/netbsd-arm64": "0.25.3", + "@esbuild/netbsd-x64": "0.25.3", + "@esbuild/openbsd-arm64": "0.25.3", + "@esbuild/openbsd-x64": "0.25.3", + "@esbuild/sunos-x64": "0.25.3", + "@esbuild/win32-arm64": "0.25.3", + "@esbuild/win32-ia32": "0.25.3", + "@esbuild/win32-x64": "0.25.3" } }, "node_modules/escalade": { @@ -3022,20 +3009,20 @@ } }, "node_modules/eslint": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", - "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", + "version": "9.25.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.1.tgz", + "integrity": "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.20.0", - "@eslint/config-helpers": "^0.2.0", - "@eslint/core": "^0.12.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.13.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.24.0", - "@eslint/plugin-kit": "^0.2.7", + "@eslint/js": "9.25.1", + "@eslint/plugin-kit": "^0.2.8", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -5235,15 +5222,15 @@ } }, "node_modules/pg": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.14.1.tgz", - "integrity": "sha512-0TdbqfjwIun9Fm/r89oB7RFQ0bLgduAhiIqIXOsyKoiC/L54DbuAAzIEN/9Op0f1Po9X7iCPXGoa/Ah+2aI8Xw==", + "version": "8.15.5", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.15.5.tgz", + "integrity": "sha512-EpAhHFQc+aH9VfeffWIVC+XXk6lmAhS9W1FxtxcPXs94yxhrI1I6w/zkWfIOII/OkBv3Be04X3xMOj0kQ78l6w==", "dev": true, "license": "MIT", "dependencies": { - "pg-connection-string": "^2.7.0", - "pg-pool": "^3.8.0", - "pg-protocol": "^1.8.0", + "pg-connection-string": "^2.8.5", + "pg-pool": "^3.9.5", + "pg-protocol": "^1.9.5", "pg-types": "^2.1.0", "pgpass": "1.x" }, @@ -5251,7 +5238,7 @@ "node": ">= 8.0.0" }, "optionalDependencies": { - "pg-cloudflare": "^1.1.1" + "pg-cloudflare": "^1.2.5" }, "peerDependencies": { "pg-native": ">=3.0.1" @@ -5263,17 +5250,17 @@ } }, "node_modules/pg-cloudflare": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", - "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.5.tgz", + "integrity": "sha512-OOX22Vt0vOSRrdoUPKJ8Wi2OpE/o/h9T8X1s4qSkCedbNah9ei2W2765be8iMVxQUsvgT7zIAT2eIa9fs5+vtg==", "dev": true, "license": "MIT", "optional": true }, "node_modules/pg-connection-string": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", - "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==", + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.8.5.tgz", + "integrity": "sha512-Ni8FuZ8yAF+sWZzojvtLE2b03cqjO5jNULcHFfM9ZZ0/JXrgom5pBREbtnAw7oxsxJqHw9Nz/XWORUEL3/IFow==", "dev": true, "license": "MIT" }, @@ -5298,9 +5285,9 @@ } }, "node_modules/pg-pool": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.8.0.tgz", - "integrity": "sha512-VBw3jiVm6ZOdLBTIcXLNdSotb6Iy3uOCwDGFAksZCXmi10nyRvnP2v3jl4d+IsLYRyXf6o9hIm/ZtUzlByNUdw==", + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.9.6.tgz", + "integrity": "sha512-rFen0G7adh1YmgvrmE5IPIqbb+IgEzENUm+tzm6MLLDSlPRoZVhzU1WdML9PV2W5GOdRA9qBKURlbt1OsXOsPw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -5308,9 +5295,9 @@ } }, "node_modules/pg-protocol": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.8.0.tgz", - "integrity": "sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g==", + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.9.5.tgz", + "integrity": "sha512-DYTWtWpfd5FOro3UnAfwvhD8jh59r2ig8bPtc9H8Ds7MscE/9NYruUQWFAOuraRl29jwcT2kyMFQ3MxeaVjUhg==", "dev": true, "license": "MIT" }, @@ -5424,13 +5411,13 @@ } }, "node_modules/playwright": { - "version": "1.51.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.51.1.tgz", - "integrity": "sha512-kkx+MB2KQRkyxjYPc3a0wLZZoDczmppyGJIvQ43l+aZihkaVvmu/21kiyaHeHjiFxjxNNFnUncKmcGIyOojsaw==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz", + "integrity": "sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.51.1" + "playwright-core": "1.52.0" }, "bin": { "playwright": "cli.js" @@ -5443,9 +5430,9 @@ } }, "node_modules/playwright-core": { - "version": "1.51.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.51.1.tgz", - "integrity": "sha512-/crRMj8+j/Nq5s8QcvegseuyeZPxpQCZb6HNk3Sos3BlZyAknRjoyJPFWkpNn8v0+P3WiwqFF8P+zQo4eqiNuw==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.52.0.tgz", + "integrity": "sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5878,9 +5865,9 @@ } }, "node_modules/rollup": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", - "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.1.tgz", + "integrity": "sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw==", "dev": true, "license": "MIT", "dependencies": { @@ -5894,26 +5881,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.40.0", - "@rollup/rollup-android-arm64": "4.40.0", - "@rollup/rollup-darwin-arm64": "4.40.0", - "@rollup/rollup-darwin-x64": "4.40.0", - "@rollup/rollup-freebsd-arm64": "4.40.0", - "@rollup/rollup-freebsd-x64": "4.40.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", - "@rollup/rollup-linux-arm-musleabihf": "4.40.0", - "@rollup/rollup-linux-arm64-gnu": "4.40.0", - "@rollup/rollup-linux-arm64-musl": "4.40.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", - "@rollup/rollup-linux-riscv64-gnu": "4.40.0", - "@rollup/rollup-linux-riscv64-musl": "4.40.0", - "@rollup/rollup-linux-s390x-gnu": "4.40.0", - "@rollup/rollup-linux-x64-gnu": "4.40.0", - "@rollup/rollup-linux-x64-musl": "4.40.0", - "@rollup/rollup-win32-arm64-msvc": "4.40.0", - "@rollup/rollup-win32-ia32-msvc": "4.40.0", - "@rollup/rollup-win32-x64-msvc": "4.40.0", + "@rollup/rollup-android-arm-eabi": "4.40.1", + "@rollup/rollup-android-arm64": "4.40.1", + "@rollup/rollup-darwin-arm64": "4.40.1", + "@rollup/rollup-darwin-x64": "4.40.1", + "@rollup/rollup-freebsd-arm64": "4.40.1", + "@rollup/rollup-freebsd-x64": "4.40.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.1", + "@rollup/rollup-linux-arm-musleabihf": "4.40.1", + "@rollup/rollup-linux-arm64-gnu": "4.40.1", + "@rollup/rollup-linux-arm64-musl": "4.40.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.1", + "@rollup/rollup-linux-riscv64-gnu": "4.40.1", + "@rollup/rollup-linux-riscv64-musl": "4.40.1", + "@rollup/rollup-linux-s390x-gnu": "4.40.1", + "@rollup/rollup-linux-x64-gnu": "4.40.1", + "@rollup/rollup-linux-x64-musl": "4.40.1", + "@rollup/rollup-win32-arm64-msvc": "4.40.1", + "@rollup/rollup-win32-ia32-msvc": "4.40.1", + "@rollup/rollup-win32-x64-msvc": "4.40.1", "fsevents": "~2.3.2" } }, @@ -6570,6 +6557,51 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tinypool": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", @@ -6715,15 +6747,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.30.1.tgz", - "integrity": "sha512-D7lC0kcehVH7Mb26MRQi64LMyRJsj3dToJxM1+JVTl53DQSV5/7oUGWQLcKl1C1KnoVHxMMU2FNQMffr7F3Row==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.31.0.tgz", + "integrity": "sha512-u+93F0sB0An8WEAPtwxVhFby573E8ckdjwUUQUj9QA4v8JAvgtoDdIyYR3XFwFHq2W1KJ1AurwJCO+w+Y1ixyQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.30.1", - "@typescript-eslint/parser": "8.30.1", - "@typescript-eslint/utils": "8.30.1" + "@typescript-eslint/eslint-plugin": "8.31.0", + "@typescript-eslint/parser": "8.31.0", + "@typescript-eslint/utils": "8.31.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6852,15 +6884,18 @@ } }, "node_modules/vite": { - "version": "6.2.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz", - "integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==", + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.3.tgz", + "integrity": "sha512-5nXH+QsELbFKhsEfWLkHrvgRpTdGJzqOZ+utSdmPTvwHmvU6ITTm3xx+mRusihkcI8GeC7lCDyn3kDtiki9scw==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", "postcss": "^8.5.3", - "rollup": "^4.30.1" + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" @@ -6924,9 +6959,9 @@ } }, "node_modules/vite-node": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.1.tgz", - "integrity": "sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.2.tgz", + "integrity": "sha512-/8iMryv46J3aK13iUXsei5G/A3CUlW4665THCPS+K8xAaqrVWiGB4RfXMQXCLjpK9P2eK//BczrVkn5JLAk6DA==", "dev": true, "license": "MIT", "dependencies": { @@ -6946,6 +6981,21 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/vite/node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -6961,32 +7011,46 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/vitest": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.1.tgz", - "integrity": "sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.2.tgz", + "integrity": "sha512-WaxpJe092ID1C0mr+LH9MmNrhfzi8I65EX/NRU/Ld016KqQNRgxSOlGNP1hHN+a/F8L15Mh8klwaF77zR3GeDQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "3.1.1", - "@vitest/mocker": "3.1.1", - "@vitest/pretty-format": "^3.1.1", - "@vitest/runner": "3.1.1", - "@vitest/snapshot": "3.1.1", - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", + "@vitest/expect": "3.1.2", + "@vitest/mocker": "3.1.2", + "@vitest/pretty-format": "^3.1.2", + "@vitest/runner": "3.1.2", + "@vitest/snapshot": "3.1.2", + "@vitest/spy": "3.1.2", + "@vitest/utils": "3.1.2", "chai": "^5.2.0", "debug": "^4.4.0", - "expect-type": "^1.2.0", + "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", - "std-env": "^3.8.1", + "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.13", "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.1.1", + "vite-node": "3.1.2", "why-is-node-running": "^2.3.0" }, "bin": { @@ -7002,8 +7066,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.1.1", - "@vitest/ui": "3.1.1", + "@vitest/browser": "3.1.2", + "@vitest/ui": "3.1.2", "happy-dom": "*", "jsdom": "*" }, diff --git a/e2e/package.json b/e2e/package.json index f141430c97..c4da9b8a4a 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,6 +1,6 @@ { "name": "immich-e2e", - "version": "1.131.3", + "version": "1.132.3", "description": "", "main": "index.js", "type": "module", @@ -25,7 +25,7 @@ "@immich/sdk": "file:../open-api/typescript-sdk", "@playwright/test": "^1.44.1", "@types/luxon": "^3.4.2", - "@types/node": "^22.14.0", + "@types/node": "^22.14.1", "@types/oidc-provider": "^8.5.1", "@types/pg": "^8.11.0", "@types/pngjs": "^6.0.4", diff --git a/e2e/src/api/specs/audit.e2e-spec.ts b/e2e/src/api/specs/audit.e2e-spec.ts deleted file mode 100644 index c6a2adbb0a..0000000000 --- a/e2e/src/api/specs/audit.e2e-spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { deleteAssets, getAuditFiles, updateAsset, type LoginResponseDto } from '@immich/sdk'; -import { asBearerAuth, utils } from 'src/utils'; -import { beforeAll, describe, expect, it } from 'vitest'; - -describe('/audits', () => { - let admin: LoginResponseDto; - - beforeAll(async () => { - await utils.resetDatabase(); - await utils.resetFilesystem(); - - admin = await utils.adminSetup(); - }); - - // TODO: Enable these tests again once #7436 is resolved as these were flaky - describe.skip('GET :/file-report', () => { - it('excludes assets without issues from report', async () => { - const [trashedAsset, archivedAsset] = await Promise.all([ - utils.createAsset(admin.accessToken), - utils.createAsset(admin.accessToken), - utils.createAsset(admin.accessToken), - ]); - - await Promise.all([ - deleteAssets({ assetBulkDeleteDto: { ids: [trashedAsset.id] } }, { headers: asBearerAuth(admin.accessToken) }), - updateAsset( - { - id: archivedAsset.id, - updateAssetDto: { isArchived: true }, - }, - { headers: asBearerAuth(admin.accessToken) }, - ), - ]); - - const body = await getAuditFiles({ - headers: asBearerAuth(admin.accessToken), - }); - - expect(body.orphans).toHaveLength(0); - expect(body.extras).toHaveLength(0); - }); - }); -}); diff --git a/e2e/src/api/specs/oauth.e2e-spec.ts b/e2e/src/api/specs/oauth.e2e-spec.ts index 9cd5f0252a..9e4d64892e 100644 --- a/e2e/src/api/specs/oauth.e2e-spec.ts +++ b/e2e/src/api/specs/oauth.e2e-spec.ts @@ -6,6 +6,7 @@ import { startOAuth, updateConfig, } from '@immich/sdk'; +import { createHash, randomBytes } from 'node:crypto'; import { errorDto } from 'src/responses'; import { OAuthClient, OAuthUser } from 'src/setup/auth-server'; import { app, asBearerAuth, baseUrl, utils } from 'src/utils'; @@ -21,18 +22,30 @@ const mobileOverrideRedirectUri = 'https://photos.immich.app/oauth/mobile-redire const redirect = async (url: string, cookies?: string[]) => { const { headers } = await request(url) - .get('/') + .get('') .set('Cookie', cookies || []); return { cookies: (headers['set-cookie'] as unknown as string[]) || [], location: headers.location }; }; +// Function to generate a code challenge from the verifier +const generateCodeChallenge = async (codeVerifier: string): Promise<string> => { + const hashed = createHash('sha256').update(codeVerifier).digest(); + return hashed.toString('base64url'); +}; + const loginWithOAuth = async (sub: OAuthUser | string, redirectUri?: string) => { - const { url } = await startOAuth({ oAuthConfigDto: { redirectUri: redirectUri ?? `${baseUrl}/auth/login` } }); + const state = randomBytes(16).toString('base64url'); + const codeVerifier = randomBytes(64).toString('base64url'); + const codeChallenge = await generateCodeChallenge(codeVerifier); + + const { url } = await startOAuth({ + oAuthConfigDto: { redirectUri: redirectUri ?? `${baseUrl}/auth/login`, state, codeChallenge }, + }); // login const response1 = await redirect(url.replace(authServer.internal, authServer.external)); const response2 = await request(authServer.external + response1.location) - .post('/') + .post('') .set('Cookie', response1.cookies) .type('form') .send({ prompt: 'login', login: sub, password: 'password' }); @@ -40,7 +53,7 @@ const loginWithOAuth = async (sub: OAuthUser | string, redirectUri?: string) => // approve const response3 = await redirect(response2.header.location, response1.cookies); const response4 = await request(authServer.external + response3.location) - .post('/') + .post('') .type('form') .set('Cookie', response3.cookies) .send({ prompt: 'consent' }); @@ -51,9 +64,9 @@ const loginWithOAuth = async (sub: OAuthUser | string, redirectUri?: string) => expect(redirectUrl).toBeDefined(); const params = new URL(redirectUrl).searchParams; expect(params.get('code')).toBeDefined(); - expect(params.get('state')).toBeDefined(); + expect(params.get('state')).toBe(state); - return redirectUrl; + return { url: redirectUrl, state, codeVerifier }; }; const setupOAuth = async (token: string, dto: Partial<SystemConfigOAuthDto>) => { @@ -119,9 +132,42 @@ describe(`/oauth`, () => { expect(body).toEqual(errorDto.badRequest(['url should not be empty'])); }); - it('should auto register the user by default', async () => { - const url = await loginWithOAuth('oauth-auto-register'); + it(`should throw an error if the state is not provided`, async () => { + const { url } = await loginWithOAuth('oauth-auto-register'); const { status, body } = await request(app).post('/oauth/callback').send({ url }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest('OAuth state is missing')); + }); + + it(`should throw an error if the state mismatches`, async () => { + const callbackParams = await loginWithOAuth('oauth-auto-register'); + const { state } = await loginWithOAuth('oauth-auto-register'); + const { status } = await request(app) + .post('/oauth/callback') + .send({ ...callbackParams, state }); + expect(status).toBeGreaterThanOrEqual(400); + }); + + it(`should throw an error if the codeVerifier is not provided`, async () => { + const { url, state } = await loginWithOAuth('oauth-auto-register'); + const { status, body } = await request(app).post('/oauth/callback').send({ url, state }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest('OAuth code verifier is missing')); + }); + + it(`should throw an error if the codeVerifier doesn't match the challenge`, async () => { + const callbackParams = await loginWithOAuth('oauth-auto-register'); + const { codeVerifier } = await loginWithOAuth('oauth-auto-register'); + const { status, body } = await request(app) + .post('/oauth/callback') + .send({ ...callbackParams, codeVerifier }); + console.log(body); + expect(status).toBeGreaterThanOrEqual(400); + }); + + it('should auto register the user by default', async () => { + const callbackParams = await loginWithOAuth('oauth-auto-register'); + const { status, body } = await request(app).post('/oauth/callback').send(callbackParams); expect(status).toBe(201); expect(body).toMatchObject({ accessToken: expect.any(String), @@ -132,16 +178,30 @@ describe(`/oauth`, () => { }); }); + it('should allow passing state and codeVerifier via cookies', async () => { + const { url, state, codeVerifier } = await loginWithOAuth('oauth-auto-register'); + const { status, body } = await request(app) + .post('/oauth/callback') + .set('Cookie', [`immich_oauth_state=${state}`, `immich_oauth_code_verifier=${codeVerifier}`]) + .send({ url }); + expect(status).toBe(201); + expect(body).toMatchObject({ + accessToken: expect.any(String), + userId: expect.any(String), + userEmail: 'oauth-auto-register@immich.app', + }); + }); + it('should handle a user without an email', async () => { - const url = await loginWithOAuth(OAuthUser.NO_EMAIL); - const { status, body } = await request(app).post('/oauth/callback').send({ url }); + const callbackParams = await loginWithOAuth(OAuthUser.NO_EMAIL); + const { status, body } = await request(app).post('/oauth/callback').send(callbackParams); expect(status).toBe(400); expect(body).toEqual(errorDto.badRequest('OAuth profile does not have an email address')); }); it('should set the quota from a claim', async () => { - const url = await loginWithOAuth(OAuthUser.WITH_QUOTA); - const { status, body } = await request(app).post('/oauth/callback').send({ url }); + const callbackParams = await loginWithOAuth(OAuthUser.WITH_QUOTA); + const { status, body } = await request(app).post('/oauth/callback').send(callbackParams); expect(status).toBe(201); expect(body).toMatchObject({ accessToken: expect.any(String), @@ -154,8 +214,8 @@ describe(`/oauth`, () => { }); it('should set the storage label from a claim', async () => { - const url = await loginWithOAuth(OAuthUser.WITH_USERNAME); - const { status, body } = await request(app).post('/oauth/callback').send({ url }); + const callbackParams = await loginWithOAuth(OAuthUser.WITH_USERNAME); + const { status, body } = await request(app).post('/oauth/callback').send(callbackParams); expect(status).toBe(201); expect(body).toMatchObject({ accessToken: expect.any(String), @@ -176,8 +236,8 @@ describe(`/oauth`, () => { buttonText: 'Login with Immich', signingAlgorithm: 'RS256', }); - const url = await loginWithOAuth('oauth-RS256-token'); - const { status, body } = await request(app).post('/oauth/callback').send({ url }); + const callbackParams = await loginWithOAuth('oauth-RS256-token'); + const { status, body } = await request(app).post('/oauth/callback').send(callbackParams); expect(status).toBe(201); expect(body).toMatchObject({ accessToken: expect.any(String), @@ -196,8 +256,8 @@ describe(`/oauth`, () => { buttonText: 'Login with Immich', profileSigningAlgorithm: 'RS256', }); - const url = await loginWithOAuth('oauth-signed-profile'); - const { status, body } = await request(app).post('/oauth/callback').send({ url }); + const callbackParams = await loginWithOAuth('oauth-signed-profile'); + const { status, body } = await request(app).post('/oauth/callback').send(callbackParams); expect(status).toBe(201); expect(body).toMatchObject({ userId: expect.any(String), @@ -213,8 +273,8 @@ describe(`/oauth`, () => { buttonText: 'Login with Immich', signingAlgorithm: 'something-that-does-not-work', }); - const url = await loginWithOAuth('oauth-signed-bad'); - const { status, body } = await request(app).post('/oauth/callback').send({ url }); + const callbackParams = await loginWithOAuth('oauth-signed-bad'); + const { status, body } = await request(app).post('/oauth/callback').send(callbackParams); expect(status).toBe(500); expect(body).toMatchObject({ error: 'Internal Server Error', @@ -235,8 +295,8 @@ describe(`/oauth`, () => { }); it('should not auto register the user', async () => { - const url = await loginWithOAuth('oauth-no-auto-register'); - const { status, body } = await request(app).post('/oauth/callback').send({ url }); + const callbackParams = await loginWithOAuth('oauth-no-auto-register'); + const { status, body } = await request(app).post('/oauth/callback').send(callbackParams); expect(status).toBe(400); expect(body).toEqual(errorDto.badRequest('User does not exist and auto registering is disabled.')); }); @@ -247,8 +307,8 @@ describe(`/oauth`, () => { email: 'oauth-user3@immich.app', password: 'password', }); - const url = await loginWithOAuth('oauth-user3'); - const { status, body } = await request(app).post('/oauth/callback').send({ url }); + const callbackParams = await loginWithOAuth('oauth-user3'); + const { status, body } = await request(app).post('/oauth/callback').send(callbackParams); expect(status).toBe(201); expect(body).toMatchObject({ userId, @@ -286,13 +346,15 @@ describe(`/oauth`, () => { }); it('should auto register the user by default', async () => { - const url = await loginWithOAuth('oauth-mobile-override', 'app.immich:///oauth-callback'); - expect(url).toEqual(expect.stringContaining(mobileOverrideRedirectUri)); + const callbackParams = await loginWithOAuth('oauth-mobile-override', 'app.immich:///oauth-callback'); + expect(callbackParams.url).toEqual(expect.stringContaining(mobileOverrideRedirectUri)); // simulate redirecting back to mobile app - const redirectUri = url.replace(mobileOverrideRedirectUri, 'app.immich:///oauth-callback'); + const url = callbackParams.url.replace(mobileOverrideRedirectUri, 'app.immich:///oauth-callback'); - const { status, body } = await request(app).post('/oauth/callback').send({ url: redirectUri }); + const { status, body } = await request(app) + .post('/oauth/callback') + .send({ ...callbackParams, url }); expect(status).toBe(201); expect(body).toMatchObject({ accessToken: expect.any(String), diff --git a/e2e/src/api/specs/user-admin.e2e-spec.ts b/e2e/src/api/specs/user-admin.e2e-spec.ts index 9299e62b79..1fbee84c3f 100644 --- a/e2e/src/api/specs/user-admin.e2e-spec.ts +++ b/e2e/src/api/specs/user-admin.e2e-spec.ts @@ -215,6 +215,19 @@ describe('/admin/users', () => { const user = await getMyUser({ headers: asBearerAuth(token.accessToken) }); expect(user).toMatchObject({ email: nonAdmin.userEmail }); }); + + it('should update the avatar color', async () => { + const { status, body } = await request(app) + .put(`/admin/users/${admin.userId}`) + .send({ avatarColor: 'orange' }) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(200); + expect(body).toMatchObject({ avatarColor: 'orange' }); + + const after = await getUserAdmin({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) }); + expect(after).toMatchObject({ avatarColor: 'orange' }); + }); }); describe('PUT /admin/users/:id/preferences', () => { @@ -240,19 +253,6 @@ describe('/admin/users', () => { expect(after).toMatchObject({ memories: { enabled: false } }); }); - it('should update the avatar color', async () => { - const { status, body } = await request(app) - .put(`/admin/users/${admin.userId}/preferences`) - .send({ avatar: { color: 'orange' } }) - .set('Authorization', `Bearer ${admin.accessToken}`); - - expect(status).toBe(200); - expect(body).toMatchObject({ avatar: { color: 'orange' } }); - - const after = await getUserPreferencesAdmin({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) }); - expect(after).toMatchObject({ avatar: { color: 'orange' } }); - }); - it('should update download archive size', async () => { const { status, body } = await request(app) .put(`/admin/users/${admin.userId}/preferences`) diff --git a/e2e/src/api/specs/user.e2e-spec.ts b/e2e/src/api/specs/user.e2e-spec.ts index 54d11e5049..b9eb140c56 100644 --- a/e2e/src/api/specs/user.e2e-spec.ts +++ b/e2e/src/api/specs/user.e2e-spec.ts @@ -139,6 +139,19 @@ describe('/users', () => { profileChangedAt: expect.anything(), }); }); + + it('should update avatar color', async () => { + const { status, body } = await request(app) + .put(`/users/me`) + .send({ avatarColor: 'blue' }) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(200); + expect(body).toMatchObject({ avatarColor: 'blue' }); + + const after = await getMyUser({ headers: asBearerAuth(admin.accessToken) }); + expect(after).toMatchObject({ avatarColor: 'blue' }); + }); }); describe('PUT /users/me/preferences', () => { @@ -158,19 +171,6 @@ describe('/users', () => { expect(after).toMatchObject({ memories: { enabled: false } }); }); - it('should update avatar color', async () => { - const { status, body } = await request(app) - .put(`/users/me/preferences`) - .send({ avatar: { color: 'blue' } }) - .set('Authorization', `Bearer ${admin.accessToken}`); - - expect(status).toBe(200); - expect(body).toMatchObject({ avatar: { color: 'blue' } }); - - const after = await getMyPreferences({ headers: asBearerAuth(admin.accessToken) }); - expect(after).toMatchObject({ avatar: { color: 'blue' } }); - }); - it('should require an integer for download archive size', async () => { const { status, body } = await request(app) .put(`/users/me/preferences`) diff --git a/e2e/src/web/specs/auth.e2e-spec.ts b/e2e/src/web/specs/auth.e2e-spec.ts index e89f17a4e9..74bee64e0a 100644 --- a/e2e/src/web/specs/auth.e2e-spec.ts +++ b/e2e/src/web/specs/auth.e2e-spec.ts @@ -25,7 +25,7 @@ test.describe('Registration', () => { // login await expect(page).toHaveTitle(/Login/); - await page.goto('/auth/login'); + await page.goto('/auth/login?autoLaunch=0'); await page.getByLabel('Email').fill('admin@immich.app'); await page.getByLabel('Password').fill('password'); await page.getByRole('button', { name: 'Login' }).click(); @@ -59,7 +59,7 @@ test.describe('Registration', () => { await context.clearCookies(); // login - await page.goto('/auth/login'); + await page.goto('/auth/login?autoLaunch=0'); await page.getByLabel('Email').fill('user@immich.cloud'); await page.getByLabel('Password').fill('password'); await page.getByRole('button', { name: 'Login' }).click(); @@ -72,7 +72,7 @@ test.describe('Registration', () => { await page.getByRole('button', { name: 'Change password' }).click(); // login with new password - await expect(page).toHaveURL('/auth/login'); + await expect(page).toHaveURL('/auth/login?autoLaunch=0'); await page.getByLabel('Email').fill('user@immich.cloud'); await page.getByLabel('Password').fill('new-password'); await page.getByRole('button', { name: 'Login' }).click(); diff --git a/e2e/src/web/specs/photo-viewer.e2e-spec.ts b/e2e/src/web/specs/photo-viewer.e2e-spec.ts index 4871e7522c..c8a9b42b2a 100644 --- a/e2e/src/web/specs/photo-viewer.e2e-spec.ts +++ b/e2e/src/web/specs/photo-viewer.e2e-spec.ts @@ -21,23 +21,9 @@ test.describe('Photo Viewer', () => { test.beforeEach(async ({ context, page }) => { // before each test, login as user await utils.setAuthCookies(context, admin.accessToken); - await page.goto('/photos'); await page.waitForLoadState('networkidle'); }); - test('initially shows a loading spinner', async ({ page }) => { - await page.route(`/api/assets/${asset.id}/thumbnail**`, async (route) => { - // slow down the request for thumbnail, so spinner has chance to show up - await new Promise((f) => setTimeout(f, 2000)); - await route.continue(); - }); - await page.goto(`/photos/${asset.id}`); - await page.waitForLoadState('load'); - // this is the spinner - await page.waitForSelector('svg[role=status]'); - await expect(page.getByTestId('loading-spinner')).toBeVisible(); - }); - test('loads original photo when zoomed', async ({ page }) => { await page.goto(`/photos/${asset.id}`); await expect.poll(async () => await imageLocator(page).getAttribute('src')).toContain('thumbnail'); diff --git a/e2e/src/web/specs/shared-link.e2e-spec.ts b/e2e/src/web/specs/shared-link.e2e-spec.ts index 562a0b4e8c..aeddb86322 100644 --- a/e2e/src/web/specs/shared-link.e2e-spec.ts +++ b/e2e/src/web/specs/shared-link.e2e-spec.ts @@ -55,7 +55,6 @@ test.describe('Shared Links', () => { await page.goto(`/share/${sharedLink.key}`); await page.getByRole('heading', { name: 'Test Album' }).waitFor(); await page.getByRole('button', { name: 'Download' }).click(); - await page.getByText('DOWNLOADING', { exact: true }).waitFor(); await page.waitForEvent('download'); }); diff --git a/i18n/be.json b/i18n/be.json index 8377ec5383..eea566df6a 100644 --- a/i18n/be.json +++ b/i18n/be.json @@ -4,6 +4,7 @@ "account_settings": "ĐаĐģĐ°Đ´Ņ ŅĐģŅĐēĐžĐ˛Đ°ĐŗĐ° СаĐŋŅŅŅ", "acknowledge": "ĐаŅвĐĩŅдСŅŅŅ", "action": "ĐСĐĩŅĐŊĐŊĐĩ", + "action_common_update": "ĐĐąĐŊавŅŅŅ", "actions": "ĐСĐĩŅĐŊĐŊŅ", "active": "ĐĐēŅŅŅĐŊŅ", "activity": "ĐĐēŅŅŅĐŊаŅŅŅ", @@ -20,8 +21,10 @@ "add_partner": "ĐадаŅŅ ĐŋаŅŅĐŊŅŅа", "add_path": "ĐадаŅŅ ŅĐģŅŅ ", "add_photos": "ĐадаŅŅ ŅĐžŅа", - "add_to": "ĐадаŅŅ Ņ...", + "add_to": "ĐадаŅŅ ŅâĻ", "add_to_album": "ĐадаŅŅ Ņ Đ°ĐģŅйОĐŧ", + "add_to_album_bottom_sheet_added": "ĐададСĐĩĐŊа да {album}", + "add_to_album_bottom_sheet_already_exists": "ĐŖĐļĐž СĐŊĐ°Ņ ĐžĐ´ĐˇŅŅŅа Ņ {album}", "add_to_shared_album": "ĐадаŅŅ Ņ Đ°ĐŗŅĐģŅĐŊŅ Đ°ĐģŅйОĐŧ", "add_url": "ĐадаŅŅ URL", "added_to_archive": "ĐададСĐĩĐŊа Ņ Đ°ŅŅ ŅŅ", @@ -41,6 +44,7 @@ "backup_settings": "ĐаĐģĐ°Đ´Ņ ŅŅСĐĩŅĐ˛ĐžĐ˛Đ°ĐŗĐ° ĐēаĐŋŅŅваĐŊĐŊŅ", "backup_settings_description": "ĐŅŅаваĐŊĐŊĐĩ ĐŊаĐģадĐēаĐŧŅ ŅŅСĐĩŅĐ˛ĐžĐ˛Đ°ĐŗĐ° ĐēаĐŋŅŅваĐŊĐŊŅ ĐąĐ°ĐˇŅ Đ´Đ°ĐŊŅŅ ", "check_all": "ĐŅавĐĩŅŅŅŅ ŅŅĐĩ", + "cleanup": "ĐŅŅŅŅĐēа", "cleared_jobs": "ĐŅŅŅŅаĐŊŅ ĐˇĐ°Đ´Đ°ĐŊĐŊŅ Đ´ĐģŅ: {job}", "config_set_by_file": "ĐаĐŊŅŅĐŗŅŅаŅŅŅ Ņ ĐˇĐ°ŅаС ŅŅŅаĐģŅваĐŊа ĐŋŅаС ŅаКĐģ ĐēаĐŊŅŅĐŗŅŅаŅŅŅ", "confirm_delete_library": "ĐŅ ŅĐŋŅŅĐŊĐĩĐŊŅ ŅŅĐž ĐļадаĐĩŅĐĩ вŅдаĐģŅŅŅ {library} ĐąŅĐąĐģŅŅŅŅĐēŅ?", diff --git a/i18n/ca.json b/i18n/ca.json index 52a47a83d5..c2482f3ddd 100644 --- a/i18n/ca.json +++ b/i18n/ca.json @@ -533,7 +533,7 @@ "backup_controller_page_backup_sub": "Fotografies i vÃdeos copiats", "backup_controller_page_created": "Creat el: {}", "backup_controller_page_desc_backup": "Activeu la cÃ˛pia de seguretat per pujar automà ticament els nous elements al servidor en obrir l'aplicaciÃŗ.", - "backup_controller_page_excluded": "Exclosos:", + "backup_controller_page_excluded": "Exclosos: ", "backup_controller_page_failed": "Fallats ({})", "backup_controller_page_filename": "Nom de l'arxiu: {} [{}]", "backup_controller_page_id": "ID: {}", diff --git a/i18n/cs.json b/i18n/cs.json index f3373f06a1..46cde0affd 100644 --- a/i18n/cs.json +++ b/i18n/cs.json @@ -39,11 +39,11 @@ "authentication_settings_disable_all": "Opravdu chcete zakÃĄzat vÅĄechny metody pÅihlÃĄÅĄenÃ? PÅihlaÅĄovÃĄnà bude ÃēplnÄ zakÃĄzÃĄno.", "authentication_settings_reenable": "Pro opÄtovnÊ povolenà pouÅžijte pÅÃkaz <link>PÅÃkaz serveru</link>.", "background_task_job": "Ãkoly na pozadÃ", - "backup_database": "ZÃĄlohovÃĄnà databÃĄze", - "backup_database_enable_description": "Povolit zÃĄlohovÃĄnà databÃĄze", - "backup_keep_last_amount": "PoÄet pÅedchozÃch zÃĄloh k uchovÃĄnÃ", - "backup_settings": "Nastavenà zÃĄlohovÃĄnÃ", - "backup_settings_description": "SprÃĄva nastavenà zÃĄlohovÃĄnà databÃĄze", + "backup_database": "VytvoÅit vÃŊpis databÃĄze", + "backup_database_enable_description": "Povolit vÃŊpisy z databÃĄze", + "backup_keep_last_amount": "PoÄet pÅedchozÃch vÃŊpisů, kterÊ se majà ponechat", + "backup_settings": "Nastavenà vÃŊpisu databÃĄze", + "backup_settings_description": "SprÃĄva nastavenà vÃŊpisu databÃĄze. PoznÃĄmka: Tyto Ãēlohy nejsou monitorovÃĄny a nebudete upozornÄni na jejich selhÃĄnÃ.", "check_all": "VÅĄe zkontrolovat", "cleanup": "VyÄiÅĄtÄnÃ", "cleared_jobs": "HotovÊ Ãēlohy pro: {job}", @@ -371,6 +371,8 @@ "admin_password": "Heslo sprÃĄvce", "administration": "Administrace", "advanced": "PokroÄilÊ", + "advanced_settings_enable_alternate_media_filter_subtitle": "Tuto moÅžnost pouÅžijte k filtrovÃĄnà mÊdià bÄhem synchronizace na zÃĄkladÄ alternativnÃch kritÊriÃ. Tuto moÅžnost vyzkouÅĄejte pouze v pÅÃpadÄ, Åže mÃĄte problÊmy s detekcà vÅĄech alb v aplikaci.", + "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTÃLNÃ] PouÅžÃt alternativnà filtr pro synchronizaci alb zaÅÃzenÃ", "advanced_settings_log_level_title": "ÃroveÅ protokolovÃĄnÃ: {}", "advanced_settings_prefer_remote_subtitle": "U nÄkterÃŊch zaÅÃzenà je naÄÃtÃĄnà miniatur z prostÅedků v zaÅÃzenà velmi pomalÊ. Aktivujte toto nastavenÃ, aby se mÃsto toho naÄÃtaly vzdÃĄlenÊ obrÃĄzky.", "advanced_settings_prefer_remote_title": "Preferovat vzdÃĄlenÊ obrÃĄzky", @@ -378,6 +380,8 @@ "advanced_settings_proxy_headers_title": "Proxy hlaviÄky", "advanced_settings_self_signed_ssl_subtitle": "VynechÃĄ ovÄÅenà SSL certifikÃĄtu serveru. VyÅžadovÃĄno pro self-signed certifikÃĄty.", "advanced_settings_self_signed_ssl_title": "Povolit self-signed SSL certifikÃĄty", + "advanced_settings_sync_remote_deletions_subtitle": "Automaticky odstranit nebo obnovit poloÅžku v tomto zaÅÃzenÃ, kdyÅž je tato akce provedena na webu", + "advanced_settings_sync_remote_deletions_title": "Synchronizace vzdÃĄlenÊho mazÃĄnà [EXPERIMENTÃLNÃ]", "advanced_settings_tile_subtitle": "PokroÄilÊ uÅživatelskÊ nastavenÃ", "advanced_settings_troubleshooting_subtitle": "Zobrazit dodateÄnÊ vlastnosti pro ÅeÅĄenà problÊmů", "advanced_settings_troubleshooting_title": "ÅeÅĄenà problÊmů", @@ -410,7 +414,7 @@ "album_viewer_appbar_delete_confirm": "Opravdu chcete toto album odstranit ze svÊho ÃēÄtu?", "album_viewer_appbar_share_err_delete": "NepodaÅilo se smazat album", "album_viewer_appbar_share_err_leave": "NepodaÅilo se opustit album", - "album_viewer_appbar_share_err_remove": "PÅi odstraÅovÃĄnà poloÅžek z alba se vyskytly problÊmy.", + "album_viewer_appbar_share_err_remove": "PÅi odstraÅovÃĄnà poloÅžek z alba se vyskytly problÊmy", "album_viewer_appbar_share_err_title": "NepodaÅilo se zmÄnit nÃĄzev alba", "album_viewer_appbar_share_leave": "Opustit album", "album_viewer_appbar_share_to": "SdÃlet na", @@ -504,16 +508,16 @@ "backup_album_selection_page_selection_info": "Informace o vÃŊbÄru", "backup_album_selection_page_total_assets": "CelkovÃŊ poÄet jedineÄnÃŊch poloÅžek", "backup_all": "VÅĄe", - "backup_background_service_backup_failed_message": "ZÃĄlohovÃĄnà mÊdià selhalo. ZkouÅĄÃm to znovu...", - "backup_background_service_connection_failed_message": "NepodaÅilo se pÅipojit k serveru. ZkouÅĄÃm to znovu...", + "backup_background_service_backup_failed_message": "ZÃĄlohovÃĄnà mÊdià selhalo. ZkouÅĄÃm to znovuâĻ", + "backup_background_service_connection_failed_message": "NepodaÅilo se pÅipojit k serveru. ZkouÅĄÃm to znovuâĻ", "backup_background_service_current_upload_notification": "NahrÃĄvÃĄnà {}", "backup_background_service_default_notification": "Kontrola novÃŊch mÊdiÃâĻ", "backup_background_service_error_title": "Chyba zÃĄlohovÃĄnÃ", - "backup_background_service_in_progress_notification": "ZÃĄlohovÃĄnà vaÅĄich mÊdiÃ...", + "backup_background_service_in_progress_notification": "ZÃĄlohovÃĄnà vaÅĄich mÊdiÃâĻ", "backup_background_service_upload_failure_notification": "NepodaÅilo se nahrÃĄt {}", "backup_controller_page_albums": "ZÃĄlohovanÃĄ alba", "backup_controller_page_background_app_refresh_disabled_content": "Povolte obnovenà aplikace na pozadà v Nastavenà > ObecnÊ > Obnovenà aplikace na pozadÃ, abyste mohli pouÅžÃvat zÃĄlohovÃĄnà na pozadÃ.", - "backup_controller_page_background_app_refresh_disabled_title": " ObnovovÃĄnà aplikacà na pozadà je vypnutÊ", + "backup_controller_page_background_app_refresh_disabled_title": "ObnovovÃĄnà aplikacà na pozadà je vypnutÊ", "backup_controller_page_background_app_refresh_enable_button_text": "PÅejÃt do nastavenÃ", "backup_controller_page_background_battery_info_link": "UkaÅž mi jak", "backup_controller_page_background_battery_info_message": "Chcete-li dosÃĄhnout nejlepÅĄÃch vÃŊsledků pÅi zÃĄlohovÃĄnà na pozadÃ, vypnÄte vÅĄechny optimalizace baterie, kterÊ omezujà aktivitu na pozadà pro Immich ve vaÅĄem zaÅÃzenÃ. \n\nJelikoÅž je to zÃĄvislÊ na typu zaÅÃzenÃ, vyhledejte poÅžadovanÊ informace pro vÃŊrobce vaÅĄeho zaÅÃzenÃ.", @@ -721,7 +725,7 @@ "delete_dialog_alert": "Tyto poloÅžky budou trvale smazÃĄny z aplikace Immich i z vaÅĄeho zaÅÃzenÃ", "delete_dialog_alert_local": "Tyto poloÅžky budou z vaÅĄeho zaÅÃzenà trvale smazÃĄny, ale budou stÃĄle k dispozici na Immich serveru", "delete_dialog_alert_local_non_backed_up": "NÄkterÊ poloÅžky nejsou zÃĄlohovÃĄny na Immich server a budou ze zaÅÃzenà trvale smazÃĄny", - "delete_dialog_alert_remote": "Tyto poloÅžky budou trvale smazÃĄny z Immich serveru ", + "delete_dialog_alert_remote": "Tyto poloÅžky budou trvale smazÃĄny z Immich serveru", "delete_dialog_ok_force": "PÅesto smazat", "delete_dialog_title": "Smazat trvale", "delete_duplicates_confirmation": "Opravdu chcete tyto duplicity trvale odstranit?", @@ -992,6 +996,7 @@ "filetype": "Typ souboru", "filter": "Filtr", "filter_people": "Filtrovat lidi", + "filter_places": "Filtrovat mÃsta", "find_them_fast": "NajdÄte je rychle vyhledÃĄnÃm jejich jmÊna", "fix_incorrect_match": "Opravit nesprÃĄvnou shodu", "folder": "SloÅžka", @@ -1040,7 +1045,7 @@ "home_page_delete_remote_err_local": "MÃstnà poloÅžky ve vzdÃĄlenÊm vÃŊbÄru pro smazÃĄnÃ, pÅeskakuji", "home_page_favorite_err_local": "ZatÃm nenà moÅžnÊ zaÅadit lokÃĄlnà mÊdia mezi oblÃbenÃĄ, pÅeskakuji", "home_page_favorite_err_partner": "PoloÅžky partnera nelze oznaÄit jako oblÃbenÊ, pÅeskakuji", - "home_page_first_time_notice": "Pokud aplikaci pouÅžÃvÃĄte poprvÊ, nezapomeÅte si vybrat zÃĄlohovanÃĄ alba, aby se na ÄasovÊ ose mohly nachÃĄzet fotografie a videa z vybranÃŊch alb.", + "home_page_first_time_notice": "Pokud aplikaci pouÅžÃvÃĄte poprvÊ, nezapomeÅte si vybrat zÃĄlohovanÃĄ alba, aby se na ÄasovÊ ose mohly nachÃĄzet fotografie a videa z vybranÃŊch alb", "home_page_share_err_local": "Nelze sdÃlet mÃstnà poloÅžky prostÅednictvÃm odkazu, pÅeskakuji", "home_page_upload_err_limit": "Lze nahrÃĄt nejvÃŊÅĄe 30 poloÅžek najednou, pÅeskakuji", "host": "Hostitel", @@ -1144,7 +1149,7 @@ "login_form_err_trailing_whitespace": "KoncovÃĄ mezera", "login_form_failed_get_oauth_server_config": "Chyba pÅihlÃĄÅĄenà pomocà OAuth, zkontrolujte adresu URL serveru", "login_form_failed_get_oauth_server_disable": "Funkce OAuth nenà na tomto serveru dostupnÃĄ", - "login_form_failed_login": "Chyba pÅihlÃĄÅĄenÃ, zkontrolujte URL adresu serveru, e-mail a heslo.", + "login_form_failed_login": "Chyba pÅihlÃĄÅĄenÃ, zkontrolujte URL adresu serveru, e-mail a heslo", "login_form_handshake_exception": "DoÅĄlo k vÃŊjimce Handshake se serverem. Pokud pouÅžÃvÃĄte self-signed certifikÃĄt, povolte v nastavenà podporu self-signed certifikÃĄtu.", "login_form_password_hint": "heslo", "login_form_save_login": "Zůstat pÅihlÃĄÅĄen", @@ -1282,6 +1287,7 @@ "onboarding_welcome_user": "VÃtej, {user}", "online": "Online", "only_favorites": "Pouze oblÃbenÊ", + "open": "OtevÅÃt", "open_in_map_view": "OtevÅÃt v zobrazenà mapy", "open_in_openstreetmap": "OtevÅÃt v OpenStreetMap", "open_the_search_filters": "OtevÅÃt vyhledÃĄvacà filtry", @@ -1759,7 +1765,7 @@ "theme_setting_system_primary_color_title": "PouÅžità systÊmovÊ barvy", "theme_setting_system_theme_switch": "Automaticky (podle systemovÊho nastavenÃ)", "theme_setting_theme_subtitle": "Vyberte nastavenà tÊmatu aplikace", - "theme_setting_three_stage_loading_subtitle": "TÅÃstupÅovÊ naÄÃtÃĄnà můŞe zvÃŊÅĄit vÃŊkonnost naÄÃtÃĄnÃ, ale vede k vÃŊraznÄ vyÅĄÅĄÃmu zatÃÅženà sÃtÄ.", + "theme_setting_three_stage_loading_subtitle": "TÅÃstupÅovÊ naÄÃtÃĄnà můŞe zvÃŊÅĄit vÃŊkonnost naÄÃtÃĄnÃ, ale vede k vÃŊraznÄ vyÅĄÅĄÃmu zatÃÅženà sÃtÄ", "theme_setting_three_stage_loading_title": "Povolenà tÅÃstupÅovÊho naÄÃtÃĄnÃ", "they_will_be_merged_together": "Budou slouÄeny dohromady", "third_party_resources": "Zdroje tÅetÃch stran", diff --git a/i18n/da.json b/i18n/da.json index 086b97f15a..a9aaef523c 100644 --- a/i18n/da.json +++ b/i18n/da.json @@ -70,8 +70,10 @@ "forcing_refresh_library_files": "Tvinger genopfriskning af alle biblioteksfiler", "image_format": "Format", "image_format_description": "WebP producerer mindre filer end JPEG, men er langsommere at komprimere.", + "image_fullsize_description": "Fuld størrelses billede uden metadata, brugt nÃĨr zoomet ind", + "image_fullsize_enabled": "Aktiver fuld størrelses billede generering", "image_prefer_embedded_preview": "ForetrÃĻk indlejret forhÃĨndsvisning", - "image_prefer_embedded_preview_setting_description": "Brug indlejrede forhÃĨndsvisninger i RAW fotos som input til billedbehandling, nÃĨr det er tilgÃĻngeligt. Dette kan give mere nøjagtige farver for nogle billeder, men kvaliteten af forhÃĨndsvisningen er kameraafhÃĻngig, og billedet kan have flere komprimeringsartefakter.", + "image_prefer_embedded_preview_setting_description": "Brug indlejrede forhÃĨndsvisninger i RAW fotos som input til billedbehandling og nÃĨr det er tilgÃĻngeligt. Dette kan give mere nøjagtige farver for nogle billeder, men kvaliteten af forhÃĨndsvisningen er kameraafhÃĻngig, og billedet kan have flere komprimeringsartefakter.", "image_prefer_wide_gamut": "ForetrÃĻkker bred farveskala", "image_prefer_wide_gamut_setting_description": "Brug Display P3 til miniaturebilleder. Dette bevarer billeder med brede farveskalaers dynamik bedre, men billeder kan komme til at se anderledes ud pÃĨ gamle enheder med en gammel browserversion. sRGB-billeder bliver beholdt som sRGB for at undgÃĨ farveskift.", "image_preview_description": "Mellemstørrelse billede med fjernet metadata, der bruges, nÃĨr du ser en enkelt mediefil og til machine learning", @@ -366,6 +368,7 @@ "admin_password": "Administratoradgangskode", "administration": "Administration", "advanced": "Avanceret", + "advanced_settings_enable_alternate_media_filter_subtitle": "Brug denne valgmulighed for at filtrere media under synkronisering baseret pÃĨ alternative kriterier. Prøv kun denne hvis du har problemer med at appen ikke opdager alle albums.", "advanced_settings_log_level_title": "Logniveau: {}", "advanced_settings_prefer_remote_subtitle": "Nogle enheder tager meget lang tid om at indlÃĻse miniaturebilleder af elementer pÃĨ enheden. Aktiver denne indstilling for i stedetat indlÃĻse elementer fra serveren.", "advanced_settings_prefer_remote_title": "ForetrÃĻk elementer pÃĨ serveren", diff --git a/i18n/de.json b/i18n/de.json index bc4bc28575..b0649474fd 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -39,11 +39,11 @@ "authentication_settings_disable_all": "Bist du sicher, dass du alle Anmeldemethoden deaktivieren willst? Die Anmeldung wird vollständig deaktiviert.", "authentication_settings_reenable": "Nutze einen <link>Server-Befehl</link> zur Reaktivierung.", "background_task_job": "Hintergrundaufgaben", - "backup_database": "Datenbank sichern", - "backup_database_enable_description": "Sicherung der Datenbank aktivieren", - "backup_keep_last_amount": "Anzahl der aufzubewahrenden frÃŧheren Sicherungen", - "backup_settings": "Datensicherungs-Einstellungen", - "backup_settings_description": "Datensicherungs-Einstellungen verwalten", + "backup_database": "Datenbankabbild erstellen", + "backup_database_enable_description": "Erstellen von Datenbankabbildern aktivieren", + "backup_keep_last_amount": "Anzahl der aufzubewahrenden frÃŧheren Abbilder", + "backup_settings": "Datenbankabbild-Einstellungen", + "backup_settings_description": "Einstellungen zum Datenbankabbild verwalten. Hinweis: Diese Jobs werden nicht Ãŧberwacht und du wirst nicht Ãŧber Fehlschläge informiert.", "check_all": "Alle ÃŧberprÃŧfen", "cleanup": "Aufräumen", "cleared_jobs": "Folgende Aufgaben zurÃŧckgesetzt: {job}", @@ -371,13 +371,17 @@ "admin_password": "Administrator Passwort", "administration": "Verwaltung", "advanced": "Erweitert", - "advanced_settings_log_level_title": "Log-Level: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "Verwende diese Option, um Medien während der Synchronisierung nach anderen Kriterien zu filtern. Versuchen dies nur, wenn Probleme mit der Erkennung aller Alben durch die App auftreten.", + "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTELL] Benutze alternativen Filter fÃŧr Synchronisierung der Gerätealben", + "advanced_settings_log_level_title": "Log-Level: {name}", "advanced_settings_prefer_remote_subtitle": "Einige Geräte sind sehr langsam beim Laden von Miniaturbildern direkt aus dem Gerät. Aktivieren Sie diese Einstellung, um stattdessen die Server-Bilder zu laden.", "advanced_settings_prefer_remote_title": "Server-Bilder bevorzugen", "advanced_settings_proxy_headers_subtitle": "Definiere einen Proxy-Header, den Immich bei jeder Netzwerkanfrage mitschicken soll", "advanced_settings_proxy_headers_title": "Proxy-Headers", "advanced_settings_self_signed_ssl_subtitle": "Verifizierung von SSL-Zertifikaten vom Server Ãŧberspringen. Notwendig bei selbstsignierten Zertifikaten.", "advanced_settings_self_signed_ssl_title": "Selbstsignierte SSL-Zertifikate erlauben", + "advanced_settings_sync_remote_deletions_subtitle": "Automatisches LÃļschen oder Wiederherstellen einer Datei auf diesem Gerät, wenn diese Aktion im Web durchgefÃŧhrt wird", + "advanced_settings_sync_remote_deletions_title": "Synchrone Remote-LÃļschungen [Experimentell]", "advanced_settings_tile_subtitle": "Erweiterte Benutzereinstellungen", "advanced_settings_troubleshooting_subtitle": "Erweiterte Funktionen zur Fehlersuche aktivieren", "advanced_settings_troubleshooting_title": "Fehlersuche", @@ -447,8 +451,8 @@ "archived_count": "{count, plural, other {# archiviert}}", "are_these_the_same_person": "Ist das dieselbe Person?", "are_you_sure_to_do_this": "Bist du sicher, dass du das tun willst?", - "asset_action_delete_err_read_only": "SchreibgeschÃŧtzte Inhalte kÃļnnen nicht gelÃļscht werden, Ãŧberspringen...", - "asset_action_share_err_offline": "Die Offline-Inhalte konnten nicht gelesen werden, Ãŧberspringen...", + "asset_action_delete_err_read_only": "SchreibgeschÃŧtzte Inhalte kÃļnnen nicht gelÃļscht werden, Ãŧberspringen", + "asset_action_share_err_offline": "Die Offline-Inhalte konnten nicht gelesen werden, Ãŧberspringen", "asset_added_to_album": "Zum Album hinzugefÃŧgt", "asset_adding_to_album": "HinzufÃŧgen zum AlbumâĻ", "asset_description_updated": "Die Beschreibung der Datei wurde aktualisiert", @@ -491,29 +495,29 @@ "assets_trashed_from_server": "{} Datei/en vom Immich-Server gelÃļscht", "assets_were_part_of_album_count": "{count, plural, one {# Datei ist} other {# Dateien sind}} bereits im Album vorhanden", "authorized_devices": "Verwendete Geräte", - "automatic_endpoint_switching_subtitle": "Verbinden Sie sich lokal Ãŧber ein bestimmtes WLAN, wenn es verfÃŧgbar ist, und verwenden Sie andere VerbindungsmÃļglichkeiten anderswo.", + "automatic_endpoint_switching_subtitle": "Verbinden Sie sich lokal Ãŧber ein bestimmtes WLAN, wenn es verfÃŧgbar ist, und verwenden Sie andere VerbindungsmÃļglichkeiten anderswo", "automatic_endpoint_switching_title": "Automatische URL-Umschaltung", "back": "ZurÃŧck", "back_close_deselect": "ZurÃŧck, SchlieÃen oder Abwählen", "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", "backup_album_selection_page_albums_device": "Alben auf dem Gerät ({})", - "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": "Einmalig das Album antippen um es zu sichern, doppelt antippen um es nicht mehr zu sichern", "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_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...", + "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âĻ", "backup_background_service_current_upload_notification": "Lädt {} hoch", "backup_background_service_default_notification": "Suche nach neuen ElementenâĻ", "backup_background_service_error_title": "Fehler bei der Sicherung", - "backup_background_service_in_progress_notification": "Elemente werden gesichert...", + "backup_background_service_in_progress_notification": "Elemente werden gesichertâĻ", "backup_background_service_upload_failure_notification": "Konnte {} nicht hochladen", "backup_controller_page_albums": "Gesicherte Alben", - "backup_controller_page_background_app_refresh_disabled_content": "Aktiviere Hintergrundaktualisierungen in Einstellungen -> Allgemein -> Hintergrundaktualisierungen um Sicherungen im Hintergrund zu ermÃļglichen. ", - "backup_controller_page_background_app_refresh_disabled_title": "Hintergrundaktualisierungen sind deaktiviert.", + "backup_controller_page_background_app_refresh_disabled_content": "Aktiviere Hintergrundaktualisierungen in Einstellungen -> Allgemein -> Hintergrundaktualisierungen um Sicherungen im Hintergrund zu ermÃļglichen.", + "backup_controller_page_background_app_refresh_disabled_title": "Hintergrundaktualisierungen sind deaktiviert", "backup_controller_page_background_app_refresh_enable_button_text": "Gehe zu Einstellungen", "backup_controller_page_background_battery_info_link": "Zeige mir wie", "backup_controller_page_background_battery_info_message": "FÃŧr die besten Ergebnisse fÃŧr Sicherungen im Hintergrund, deaktiviere alle Batterieoptimierungen und Einschränkungen fÃŧr die Hintergrundaktivitäten von Immich.\n\nDa dies gerätespezifisch ist, schlage diese Informationen fÃŧr deinen Gerätehersteller nach.", @@ -554,7 +558,7 @@ "backup_err_only_album": "Das einzige Album kann nicht entfernt werden", "backup_info_card_assets": "Elemente", "backup_manual_cancelled": "Abgebrochen", - "backup_manual_in_progress": "Sicherung läuft bereits. Bitte versuche es später erneut.", + "backup_manual_in_progress": "Sicherung läuft bereits. Bitte versuche es später erneut", "backup_manual_success": "Erfolgreich", "backup_manual_title": "Sicherungsstatus", "backup_options_page_title": "Sicherungsoptionen", @@ -630,8 +634,8 @@ "client_cert_import_success_msg": "Client Zertifikat wurde importiert", "client_cert_invalid_msg": "UngÃŧltige Zertifikatsdatei oder falsches Passwort", "client_cert_remove_msg": "Client Zertifikat wurde entfernt", - "client_cert_subtitle": "UnterstÃŧtzt nur das PKCS12 (.p12, .pfx) Format. Zertifikatsimporte oder -entfernungen sind nur vor dem Login mÃļglich.", - "client_cert_title": "SSL-Client-Zertifikat ", + "client_cert_subtitle": "UnterstÃŧtzt nur das PKCS12 (.p12, .pfx) Format. Zertifikatsimporte oder -entfernungen sind nur vor dem Login mÃļglich", + "client_cert_title": "SSL-Client-Zertifikat", "clockwise": "Im Uhrzeigersinn", "close": "SchlieÃen", "collapse": "Zusammenklappen", @@ -644,7 +648,7 @@ "comments_are_disabled": "Kommentare sind deaktiviert", "common_create_new_album": "Neues Album erstellen", "common_server_error": "Bitte ÃŧberprÃŧfe Deine Netzwerkverbindung und stelle sicher, dass die App und Server Versionen kompatibel sind.", - "completed": "Fertig\n", + "completed": "Fertig", "confirm": "Bestätigen", "confirm_admin_password": "Administrator Passwort bestätigen", "confirm_delete_face": "Bist du sicher dass du das Gesicht von {name} aus der Datei entfernen willst?", @@ -719,9 +723,9 @@ "delete_album": "Album lÃļschen", "delete_api_key_prompt": "Bist du sicher, dass du diesen API-SchlÃŧssel lÃļschen willst?", "delete_dialog_alert": "Diese Elemente werden unwiderruflich von Immich und dem Gerät entfernt", - "delete_dialog_alert_local": "Diese Inhalte werden vom Gerät gelÃļscht, bleiben aber auf dem Immich-Server.", - "delete_dialog_alert_local_non_backed_up": "Einige Inhalte sind nicht in Immich gesichert und werden dauerhaft vom Gerät gelÃļscht.", - "delete_dialog_alert_remote": "Diese Inhalte werden dauerhaft vom Immich-Server gelÃļscht.", + "delete_dialog_alert_local": "Diese Inhalte werden vom Gerät gelÃļscht, bleiben aber auf dem Immich-Server", + "delete_dialog_alert_local_non_backed_up": "Einige Inhalte sind nicht in Immich gesichert und werden dauerhaft vom Gerät gelÃļscht", + "delete_dialog_alert_remote": "Diese Inhalte werden dauerhaft vom Immich-Server gelÃļscht", "delete_dialog_ok_force": "Trotzdem lÃļschen", "delete_dialog_title": "EndgÃŧltig lÃļschen", "delete_duplicates_confirmation": "Bist du sicher, dass du diese Duplikate endgÃŧltig lÃļschen willst?", @@ -741,7 +745,7 @@ "deletes_missing_assets": "LÃļscht Dateien, die auf der Festplatte fehlen", "description": "Beschreibung", "description_input_hint_text": "Beschreibung hinzufÃŧgen...", - "description_input_submit_error": "Beschreibung konnte nicht geändert werden, bitte im Log fÃŧr mehr Details nachsehen.", + "description_input_submit_error": "Beschreibung konnte nicht geändert werden, bitte im Log fÃŧr mehr Details nachsehen", "details": "Details", "direction": "Richtung", "disabled": "Deaktiviert", @@ -758,23 +762,23 @@ "documentation": "Dokumentation", "done": "Fertig", "download": "Herunterladen", - "download_canceled": "Download abgebrochen!", - "download_complete": "Download vollständig!", - "download_enqueue": "Download in die Warteschlange gesetzt!", + "download_canceled": "Download abgebrochen", + "download_complete": "Download vollständig", + "download_enqueue": "Download in die Warteschlange gesetzt", "download_error": "Download fehlerhaft", - "download_failed": "Download fehlerhaft!", + "download_failed": "Download fehlerhaft", "download_filename": "Datei: {}", "download_finished": "Download abgeschlossen", "download_include_embedded_motion_videos": "Eingebettete Videos", "download_include_embedded_motion_videos_description": "Videos, die in Bewegungsfotos eingebettet sind, als separate Datei einfÃŧgen", - "download_notfound": "Download nicht gefunden!", - "download_paused": "Download pausiert!", + "download_notfound": "Download nicht gefunden", + "download_paused": "Download pausiert", "download_settings": "Download", "download_settings_description": "Einstellungen fÃŧr das Herunterladen von Dateien verwalten", "download_started": "Download gestartet", "download_sucess": "Download erfolgreich", "download_sucess_android": "Die Datei wurde nach DCIM/Immich heruntergeladen", - "download_waiting_to_retry": "Warte auf erneuten Versuch...", + "download_waiting_to_retry": "Warte auf erneuten Versuch", "downloading": "Herunterladen", "downloading_asset_filename": "Datei {filename} wird heruntergeladen", "downloading_media": "Medien werden heruntergeladen", @@ -954,9 +958,9 @@ "exif_bottom_sheet_people": "PERSONEN", "exif_bottom_sheet_person_add_person": "Namen hinzufÃŧgen", "exif_bottom_sheet_person_age": "Alter {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age_months": "{} Monate alt", + "exif_bottom_sheet_person_age_year_months": "1 Jahr, {} Monate alt", + "exif_bottom_sheet_person_age_years": "{} alt", "exit_slideshow": "Diashow beenden", "expand_all": "Alle aufklappen", "experimental_settings_new_asset_list_subtitle": "In Arbeit", @@ -992,6 +996,7 @@ "filetype": "Dateityp", "filter": "Filter", "filter_people": "Personen filtern", + "filter_places": "Orte filtern", "find_them_fast": "Finde sie schneller mit der Suche nach Namen", "fix_incorrect_match": "Fehlerhafte Ãbereinstimmung beheben", "folder": "Ordner", @@ -1001,7 +1006,7 @@ "forward": "Vorwärts", "general": "Allgemein", "get_help": "Hilfe erhalten", - "get_wifiname_error": "WLAN-Name konnte nicht ermittelt werden. Vergewissere dich, dass die erforderlichen Berechtigungen erteilt wurden und du mit einem WLAN-Netzwerk verbunden bist.\n", + "get_wifiname_error": "WLAN-Name konnte nicht ermittelt werden. Vergewissere dich, dass die erforderlichen Berechtigungen erteilt wurden und du mit einem WLAN-Netzwerk verbunden bist", "getting_started": "Erste Schritte", "go_back": "ZurÃŧck", "go_to_folder": "Gehe zu Ordner", @@ -1030,23 +1035,23 @@ "hide_person": "Person verbergen", "hide_unnamed_people": "Unbenannte Personen verbergen", "home_page_add_to_album_conflicts": "{added} Elemente zu {album} hinzugefÃŧgt. {failed} Elemente sind bereits vorhanden.", - "home_page_add_to_album_err_local": "Es kÃļnnen lokale Elemente noch nicht zu Alben hinzugefÃŧgt werden, Ãŧberspringen...", + "home_page_add_to_album_err_local": "Es kÃļnnen lokale Elemente noch nicht zu Alben hinzugefÃŧgt werden, Ãŧberspringen", "home_page_add_to_album_success": "{added} Elemente zu {album} hinzugefÃŧgt.", - "home_page_album_err_partner": "Inhalte von Partnern kÃļnnen derzeit nicht zu Alben hinzugefÃŧgt werden!", - "home_page_archive_err_local": "Kann lokale Elemente nicht archvieren, Ãŧberspringen...", - "home_page_archive_err_partner": "Inhalte von Partnern kÃļnnen nicht archiviert werden!", - "home_page_building_timeline": "Zeitachse wird erstellt.", - "home_page_delete_err_partner": "Inhalte von Partnern kÃļnnen nicht gelÃļscht werden!", - "home_page_delete_remote_err_local": "Lokale Inhalte in der Auswahl, Ãŧberspringen...", - "home_page_favorite_err_local": "Kann lokale Elemente noch nicht favorisieren, Ãŧberspringen...", - "home_page_favorite_err_partner": "Inhalte von Partnern kÃļnnen nicht favorisiert werden!", - "home_page_first_time_notice": "Wenn dies das erste Mal ist dass Du Immich nutzt, stelle bitte sicher, dass mindestens ein Album zur Sicherung ausgewählt ist, sodass die Zeitachse mit Fotos und Videos gefÃŧllt werden kann.", + "home_page_album_err_partner": "Inhalte von Partnern kÃļnnen derzeit nicht zu Alben hinzugefÃŧgt werden", + "home_page_archive_err_local": "Kann lokale Elemente nicht archvieren, Ãŧberspringen", + "home_page_archive_err_partner": "Inhalte von Partnern kÃļnnen nicht archiviert werden", + "home_page_building_timeline": "Zeitachse wird erstellt", + "home_page_delete_err_partner": "Inhalte von Partnern kÃļnnen nicht gelÃļscht werden, Ãŧberspringe", + "home_page_delete_remote_err_local": "Lokale Inhalte in der Auswahl, Ãŧberspringen", + "home_page_favorite_err_local": "Kann lokale Elemente noch nicht favorisieren, Ãŧberspringen", + "home_page_favorite_err_partner": "Inhalte von Partnern kÃļnnen nicht favorisiert werden, Ãŧberspringe", + "home_page_first_time_notice": "Wenn dies das erste Mal ist dass Du Immich nutzt, stelle bitte sicher, dass mindestens ein Album zur Sicherung ausgewählt ist, sodass die Zeitachse mit Fotos und Videos gefÃŧllt werden kann", "home_page_share_err_local": "Lokale Inhalte kÃļnnen nicht per Link geteilt werden, Ãŧberspringe", - "home_page_upload_err_limit": "Es kÃļnnen max. 30 Elemente gleichzeitig hochgeladen werden, Ãŧberspringen...", + "home_page_upload_err_limit": "Es kÃļnnen max. 30 Elemente gleichzeitig hochgeladen werden, Ãŧberspringen", "host": "Host", "hour": "Stunde", "ignore_icloud_photos": "iCloud Fotos ignorieren", - "ignore_icloud_photos_description": "Fotos, die in der iCloud gespeichert sind, werden nicht auf den immich Server hochgeladen", + "ignore_icloud_photos_description": "Fotos, die in der iCloud gespeichert sind, werden nicht auf den immich Server hochgeladen", "image": "Bild", "image_alt_text_date": "{isVideo, select, true {Video} other {Bild}} aufgenommen am {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Bild}} aufgenommen mit {person1} am {date}", @@ -1080,7 +1085,7 @@ "night_at_midnight": "Täglich um Mitternacht", "night_at_twoam": "Täglich nachts um 2:00 Uhr" }, - "invalid_date": "UngÃŧltiges Datum ", + "invalid_date": "UngÃŧltiges Datum", "invalid_date_format": "UngÃŧltiges Datumsformat", "invite_people": "Personen einladen", "invite_to_album": "Zum Album einladen", @@ -1143,7 +1148,7 @@ "login_form_err_leading_whitespace": "Leerzeichen am Anfang", "login_form_err_trailing_whitespace": "Leerzeichen am Ende", "login_form_failed_get_oauth_server_config": "Fehler beim Login per OAuth, bitte Server-URL ÃŧberprÃŧfen", - "login_form_failed_get_oauth_server_disable": "Die OAuth-Funktion ist auf diesem Server nicht verfÃŧgbar.", + "login_form_failed_get_oauth_server_disable": "Die OAuth-Funktion ist auf diesem Server nicht verfÃŧgbar", "login_form_failed_login": "Fehler beim Login, bitte ÃŧberprÃŧfe die Server-URL, deine E-Mail oder das Passwort", "login_form_handshake_exception": "Fehler beim Verbindungsaufbau mit dem Server. Falls du ein selbstsigniertes Zertifikat verwendest, aktiviere die UnterstÃŧtzung dafÃŧr in den Einstellungen.", "login_form_password_hint": "Passwort", @@ -1151,8 +1156,8 @@ "login_form_server_empty": "Serveradresse eingeben.", "login_form_server_error": "Es Konnte sich nicht mit dem Server verbunden werden.", "login_has_been_disabled": "Die Anmeldung wurde deaktiviert.", - "login_password_changed_error": "Fehler beim Passwort ändern!", - "login_password_changed_success": "Passwort erfolgreich geändert.", + "login_password_changed_error": "Fehler beim Ãndern deines Passwort", + "login_password_changed_success": "Passwort erfolgreich geändert", "logout_all_device_confirmation": "Bist du sicher, dass du alle Geräte abmelden willst?", "logout_this_device_confirmation": "Bist du sicher, dass du dieses Gerät abmelden willst?", "longitude": "Längengrad", @@ -1172,7 +1177,7 @@ "map": "Karte", "map_assets_in_bound": "{} Foto", "map_assets_in_bounds": "{} Fotos", - "map_cannot_get_user_location": "Standort konnte nicht ermittelt werden!", + "map_cannot_get_user_location": "Standort konnte nicht ermittelt werden", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Aufnahmeort verwenden", "map_location_service_disabled_content": "Ortungsdienste mÃŧssen aktiviert sein, um Inhalte am aktuellen Standort anzuzeigen. Willst du die Ortungsdienste jetzt aktivieren?", @@ -1181,7 +1186,7 @@ "map_marker_with_image": "Kartenmarkierung mit Bild", "map_no_assets_in_bounds": "Keine Fotos in dieser Gegend", "map_no_location_permission_content": "Ortungsdienste mÃŧssen aktiviert sein, um Inhalte am aktuellen Standort anzuzeigen. Willst du die Ortungsdienste jetzt aktivieren?", - "map_no_location_permission_title": "Kein Zugriff auf den Standort!", + "map_no_location_permission_title": "Kein Zugriff auf den Standort", "map_settings": "Karteneinstellungen", "map_settings_dark_mode": "Dunkler Modus", "map_settings_date_range_option_day": "Letzte 24 Stunden", @@ -1198,7 +1203,7 @@ "media_type": "Medientyp", "memories": "Erinnerungen", "memories_all_caught_up": "Alles aufgeholt", - "memories_check_back_tomorrow": "Schau morgen wieder vorbei fÃŧr weitere Erinnerungen!", + "memories_check_back_tomorrow": "Schau morgen wieder vorbei fÃŧr weitere Erinnerungen", "memories_setting_description": "Verwalte, was du in deinen Erinnerungen siehst", "memories_start_over": "Erneut beginnen", "memories_swipe_to_close": "Nach oben Wischen zum schlieÃen", @@ -1221,8 +1226,8 @@ "monthly_title_text_date_format": "MMMM y", "more": "Mehr", "moved_to_trash": "In den Papierkorb verschoben", - "multiselect_grid_edit_date_time_err_read_only": "Das Datum und die Uhrzeit von schreibgeschÃŧtzten Inhalten kann nicht verändert werden, Ãŧberspringen...", - "multiselect_grid_edit_gps_err_read_only": "Der Aufnahmeort von schreibgeschÃŧtzten Inhalten kann nicht verändert werden, Ãŧberspringen...", + "multiselect_grid_edit_date_time_err_read_only": "Das Datum und die Uhrzeit von schreibgeschÃŧtzten Inhalten kann nicht verändert werden, Ãŧberspringen", + "multiselect_grid_edit_gps_err_read_only": "Der Aufnahmeort von schreibgeschÃŧtzten Inhalten kann nicht verändert werden, Ãŧberspringen", "mute_memories": "Erinnerungen stumm schalten", "my_albums": "Meine Alben", "name": "Name", @@ -1260,8 +1265,8 @@ "not_selected": "Nicht ausgewählt", "note_apply_storage_label_to_previously_uploaded assets": "Hinweis: Um eine Speicherpfadbezeichnung anzuwenden, starte den", "notes": "Notizen", - "notification_permission_dialog_content": "Um Benachrichtigungen zu aktivieren, navigiere zu Einstellungen und klicke \"Erlauben\"", - "notification_permission_list_tile_content": "Erlaube Berechtigung fÃŧr Benachrichtigungen", + "notification_permission_dialog_content": "Um Benachrichtigungen zu aktivieren, navigiere zu Einstellungen und klicke \"Erlauben\".", + "notification_permission_list_tile_content": "Erlaube Berechtigung fÃŧr Benachrichtigungen.", "notification_permission_list_tile_enable_button": "Aktiviere Benachrichtigungen", "notification_permission_list_tile_title": "Benachrichtigungs-Berechtigung", "notification_toggle_setting_description": "E-Mail-Benachrichtigungen aktivieren", @@ -1282,6 +1287,7 @@ "onboarding_welcome_user": "Willkommen, {user}", "online": "Online", "only_favorites": "Nur Favoriten", + "open": "Ãffnen", "open_in_map_view": "In Kartenansicht Ãļffnen", "open_in_openstreetmap": "In OpenStreetMap Ãļffnen", "open_the_search_filters": "Die Suchfilter Ãļffnen", @@ -1371,7 +1377,7 @@ "profile_drawer_app_logs": "Logs", "profile_drawer_client_out_of_date_major": "Mobile-App ist veraltet. Bitte aktualisiere auf die neueste Major-Version.", "profile_drawer_client_out_of_date_minor": "Mobile-App ist veraltet. Bitte aktualisiere auf die neueste Minor-Version.", - "profile_drawer_client_server_up_to_date": "Die App-Version / Server-Version sind aktuell.", + "profile_drawer_client_server_up_to_date": "Die App-Version / Server-Version sind aktuell", "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Server-Version ist veraltet. Bitte aktualisiere auf die neueste Major-Version.", "profile_drawer_server_out_of_date_minor": "Server-Version ist veraltet. Bitte aktualisiere auf die neueste Minor-Version.", @@ -1504,7 +1510,7 @@ "search_city": "Suche nach Stadt...", "search_country": "Suche nach Land...", "search_filter_apply": "Filter anwenden", - "search_filter_camera_title": "Kameratyp auswählen ", + "search_filter_camera_title": "Kameratyp auswählen", "search_filter_date": "Datum", "search_filter_date_interval": "{start} bis {end}", "search_filter_date_title": "Wähle einen Zeitraum", @@ -1512,10 +1518,10 @@ "search_filter_display_options": "Anzeigeeinstellungen", "search_filter_filename": "Suche nach Dateiname", "search_filter_location": "Ort", - "search_filter_location_title": "Ort auswählen ", + "search_filter_location_title": "Ort auswählen", "search_filter_media_type": "Medientyp", - "search_filter_media_type_title": "Medientyp auswählen ", - "search_filter_people_title": "Personen auswählen ", + "search_filter_media_type_title": "Medientyp auswählen", + "search_filter_people_title": "Personen auswählen", "search_for": "Suche nach", "search_for_existing_person": "Suche nach vorhandener Person", "search_no_more_result": "Keine weiteren Ergebnisse", @@ -1596,7 +1602,7 @@ "setting_notifications_notify_minutes": "{} Minuten", "setting_notifications_notify_never": "niemals", "setting_notifications_notify_seconds": "{} Sekunden", - "setting_notifications_single_progress_subtitle": "Detaillierter Upload-Fortschritt fÃŧr jedes Element.", + "setting_notifications_single_progress_subtitle": "Detaillierter Upload-Fortschritt fÃŧr jedes Element", "setting_notifications_single_progress_title": "Zeige den detaillierten Fortschritt der Hintergrundsicherung", "setting_notifications_subtitle": "Benachrichtigungen anpassen", "setting_notifications_total_progress_subtitle": "Gesamter Upload-Fortschritt (abgeschlossen/Anzahl Elemente)", @@ -1605,14 +1611,14 @@ "setting_video_viewer_original_video_subtitle": "Beim Streaming eines Videos vom Server wird das Original abgespielt, auch wenn eine Transkodierung verfÃŧgbar ist. Kann zu Pufferung fÃŧhren. Lokal verfÃŧgbare Videos werden unabhängig von dieser Einstellung in Originalqualität wiedergegeben.", "setting_video_viewer_original_video_title": "Originalvideo erzwingen", "settings": "Einstellungen", - "settings_require_restart": "Bitte starte Immich neu, um diese Einstellung anzuwenden.", + "settings_require_restart": "Bitte starte Immich neu, um diese Einstellung anzuwenden", "settings_saved": "Einstellungen gespeichert", "share": "Teilen", "share_add_photos": "Fotos hinzufÃŧgen", "share_assets_selected": "{} ausgewählt", "share_dialog_preparing": "Vorbereiten...", "shared": "Geteilt", - "shared_album_activities_input_disable": "Kommentare sind deaktiviert.", + "shared_album_activities_input_disable": "Kommentare sind deaktiviert", "shared_album_activity_remove_content": "MÃļchtest du diese Aktivität entfernen?", "shared_album_activity_remove_title": "Aktivität entfernen", "shared_album_section_people_action_error": "Fehler beim Verlassen oder Entfernen aus dem Album", @@ -1635,20 +1641,20 @@ "shared_link_edit_expire_after_option_hours": "{} Stunden", "shared_link_edit_expire_after_option_minute": "1 Minute", "shared_link_edit_expire_after_option_minutes": "{} Minuten", - "shared_link_edit_expire_after_option_months": "{} Monat/en", - "shared_link_edit_expire_after_option_year": "{} Jahr/en", + "shared_link_edit_expire_after_option_months": "{} Monate", + "shared_link_edit_expire_after_option_year": "{} Jahr", "shared_link_edit_password_hint": "Passwort eingeben", "shared_link_edit_submit_button": "Link aktualisieren", "shared_link_error_server_url_fetch": "Fehler beim Ermitteln der Server-URL", "shared_link_expires_day": "Verfällt in {} Tag", - "shared_link_expires_days": "Verfällt in {} Tag/en", + "shared_link_expires_days": "Verfällt in {} Tagen", "shared_link_expires_hour": "Verfällt in {} Stunde", - "shared_link_expires_hours": "Verfällt in {} Stunde/n", + "shared_link_expires_hours": "Verfällt in {} Stunden", "shared_link_expires_minute": "Verfällt in {} Minute", - "shared_link_expires_minutes": "Verfällt in {} Minute/n", + "shared_link_expires_minutes": "Verfällt in {} Minuten", "shared_link_expires_never": "Läuft nie ab", "shared_link_expires_second": "Verfällt in {} Sekunde", - "shared_link_expires_seconds": "Verfällt in {} Sekunde/n", + "shared_link_expires_seconds": "Verfällt in {} Sekunden", "shared_link_individual_shared": "Individuell geteilt", "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Geteilte Links verwalten", @@ -1750,11 +1756,11 @@ "theme_selection_description": "Automatische Einstellung des Themes auf Hell oder Dunkel, je nach Systemeinstellung des Browsers", "theme_setting_asset_list_storage_indicator_title": "Forschrittsbalken der Sicherung auf dem Vorschaubild", "theme_setting_asset_list_tiles_per_row_title": "Anzahl der Elemente pro Reihe ({})", - "theme_setting_colorful_interface_subtitle": "Primärfarbe auf App-Hintergrund anwenden", + "theme_setting_colorful_interface_subtitle": "Primärfarbe auf App-Hintergrund anwenden.", "theme_setting_colorful_interface_title": "Farbige UI-Oberfläche", "theme_setting_image_viewer_quality_subtitle": "Einstellen der Qualität des Detailbildbetrachters", "theme_setting_image_viewer_quality_title": "Qualität des Bildbetrachters", - "theme_setting_primary_color_subtitle": "Farbauswahl fÃŧr primäre Aktionen und Akzente", + "theme_setting_primary_color_subtitle": "Farbauswahl fÃŧr primäre Aktionen und Akzente.", "theme_setting_primary_color_title": "Primärfarbe", "theme_setting_system_primary_color_title": "Systemfarbe verwenden", "theme_setting_system_theme_switch": "Automatisch (Systemeinstellung)", @@ -1784,7 +1790,7 @@ "trash_no_results_message": "GelÃļschte Fotos und Videos werden hier angezeigt.", "trash_page_delete_all": "Alle lÃļschen", "trash_page_empty_trash_dialog_content": "Elemente im Papierkorb lÃļschen? Diese Elemente werden dauerhaft aus Immich entfernt", - "trash_page_info": "Elemente im Papierkorb werden nach {} Tagen endgÃŧltig gelÃļscht.", + "trash_page_info": "Elemente im Papierkorb werden nach {} Tagen endgÃŧltig gelÃļscht", "trash_page_no_assets": "Es gibt keine Daten im Papierkorb", "trash_page_restore_all": "Alle wiederherstellen", "trash_page_select_assets_btn": "Elemente auswählen", diff --git a/i18n/el.json b/i18n/el.json index 52efcccd50..a8a56f5122 100644 --- a/i18n/el.json +++ b/i18n/el.json @@ -39,11 +39,11 @@ "authentication_settings_disable_all": "ÎίĪĪÎĩ βÎβιΚÎŋΚ ĪĪΚ θÎÎģÎĩĪÎĩ ÎŊÎą ÎąĪÎĩÎŊÎĩĪÎŗÎŋĪÎŋΚΎĪÎĩĪÎĩ ĪÎģÎĩĪ ĪÎšĪ ÎŧÎĩθĪδÎŋĪ Ī ĪĪÎŊδÎĩĪΡĪ; Î ĪĪÎŊδÎĩĪΡ θι ÎąĪÎĩÎŊÎĩĪÎŗÎŋĪÎŋΚΡθÎĩί ĪÎģÎŽĪĪĪ.", "authentication_settings_reenable": "ÎΚι ÎĩĪÎąÎŊÎĩÎŊÎĩĪÎŗÎŋĪÎŋÎ¯ÎˇĪΡ, ĪĪΡĪΚÎŧÎŋĪÎŋΚΎĪĪÎĩ ÎŧÎ¯Îą <link>ÎÎŊĪÎŋÎģÎŽ ÎΚιÎēÎŋÎŧΚĪĪÎŽ</link>.", "background_task_job": "ÎĪÎŗÎąĪίÎĩĪ Î ÎąĪÎąĪÎēΡÎŊίÎŋĪ ", - "backup_database": "ÎΡÎŧΚÎŋĪ ĪÎŗÎ¯Îą ÎÎŊĪÎšÎŗĪÎŦĪÎŋĪ ÎĪĪÎąÎģÎĩÎ¯ÎąĪ ĪÎˇĪ ÎÎŦĪÎˇĪ ÎÎĩδÎŋÎŧÎÎŊĪÎŊ", - "backup_database_enable_description": "ÎÎŊÎĩĪÎŗÎŋĪÎŋÎ¯ÎˇĪΡ ÎąÎŊĪÎšÎŗĪÎŦĪĪÎŊ ÎąĪĪÎąÎģÎĩÎ¯ÎąĪ ĪÎˇĪ Î˛ÎŦĪÎˇĪ Î´ÎĩδÎŋÎŧÎÎŊĪÎŊ", - "backup_keep_last_amount": "ÎĪΚθÎŧĪĪ ĪĪÎŋÎˇÎŗÎŋĪÎŧÎĩÎŊĪÎŊ ÎąÎŊĪÎšÎŗĪÎŦĪĪÎŊ ÎąĪĪÎąÎģÎĩÎ¯ÎąĪ ÎŗÎšÎą δΚιĪÎŽĪΡĪΡ", - "backup_settings": "ÎĄĪ Î¸ÎŧίĪÎĩÎšĪ ÎÎŊĪÎšÎŗĪÎŦĪĪÎŊ ÎĪĪÎąÎģÎĩÎ¯ÎąĪ", - "backup_settings_description": "ÎΚιĪÎĩίĪΡĪΡ ĪĪ Î¸ÎŧίĪÎĩĪÎŊ ĪĪÎŊ ÎąÎŊĪÎšÎŗĪÎŦĪĪÎŊ ÎąĪĪÎąÎģÎĩÎ¯ÎąĪ ĪÎˇĪ Î˛ÎŦĪÎˇĪ Î´ÎĩδÎŋÎŧÎÎŊĪÎŊ", + "backup_database": "ÎΡÎŧΚÎŋĪ ĪÎŗÎ¯Îą Dump βÎŦĪÎˇĪ Î´ÎĩδÎŋÎŧÎÎŊĪÎŊ", + "backup_database_enable_description": "ÎÎŊÎĩĪÎŗÎŋĪÎŋÎ¯ÎˇĪΡ dumps βÎŦĪÎˇĪ Î´ÎĩδÎŋÎŧÎÎŊĪÎŊ", + "backup_keep_last_amount": "Î ÎŋĪĪĪΡĪÎą ĪĪÎŋÎˇÎŗÎŋĪÎŧÎĩÎŊĪÎŊ dumps ĪÎŋĪ ĪĪÎĪÎĩΚ ÎŊÎą δΚιĪΡĪΡθÎŋĪÎŊ", + "backup_settings": "ÎĄĪ Î¸ÎŧίĪÎĩÎšĪ dump βÎŦĪÎˇĪ Î´ÎĩδÎŋÎŧÎÎŊĪÎŊ", + "backup_settings_description": "ÎΚιĪÎĩίĪΚĪΡ ĪĪ Î¸ÎŧίĪÎĩĪÎŊ dump ĪÎˇĪ Î˛ÎŦĪÎˇĪ Î´ÎĩδÎŋÎŧÎÎŊĪÎŊ. ÎŖÎˇÎŧÎĩίĪĪΡ: ÎĪ ĪÎĪ ÎŋΚ ÎĩĪÎŗÎąĪίÎĩĪ Î´ÎĩÎŊ ĪÎąĪÎąÎēÎŋÎģÎŋĪ Î¸ÎŋĪÎŊĪιΚ ÎēιΚ δÎĩÎŊ θι ÎĩΚδÎŋĪÎŋΚΡθÎĩίĪÎĩ ÎŗÎšÎą ÎąĪÎŋĪĪ ĪÎ¯Îą.", "check_all": "ÎÎģÎĩÎŗĪÎŋĪ ÎÎģĪÎŊ", "cleanup": "ÎÎēÎēιθÎŦĪΚĪΡ", "cleared_jobs": "ÎÎēÎēιθιĪίĪĪΡÎēÎąÎŊ ÎŋΚ ÎĩĪÎŗÎąĪίÎĩĪ ÎŗÎšÎą: {job}", @@ -371,13 +371,17 @@ "admin_password": "ÎĪδΚÎēĪĪ ĪĪĪĪβιĪÎˇĪ ÎΚιĪÎĩΚĪΚĪĪÎŽ", "administration": "ÎΚιĪÎĩίĪΚĪΡ", "advanced": "ÎΚι ĪĪÎŋĪĪĪΡÎŧÎÎŊÎŋĪ Ī", - "advanced_settings_log_level_title": "ÎĪίĪÎĩδÎŋ ÎēÎąĪÎąÎŗĪÎąĪÎŽĪ: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "ΧĪΡĪΚÎŧÎŋĪÎŋΚΎĪĪÎĩ ÎąĪ ĪÎŽÎŊ ĪΡÎŊ ÎĩĪΚÎģÎŋÎŗÎŽ ÎŗÎšÎą ÎŊÎą ĪΚÎģĪĪÎŦĪÎĩĪÎĩ ĪÎą ÎŧÎĪÎą ÎĩÎŊΡÎŧÎĪĪĪÎˇĪ ÎēÎąĪÎŦ ĪÎŋÎŊ ĪĪ ÎŗĪĪÎŋÎŊΚĪÎŧĪ ÎŧÎĩ βÎŦĪΡ ÎĩÎŊÎąÎģÎģÎąÎēĪΚÎēÎŦ ÎēĪΚĪÎŽĪΚι. ÎÎŋÎēΚÎŧÎŦĪĪÎĩ ÎąĪ ĪÎŽ ĪΡ Î´Ī ÎŊÎąĪĪĪΡĪÎą ÎŧĪÎŊÎŋ ÎąÎŊ ÎĪÎĩĪÎĩ ĪĪÎŋβÎģÎŽÎŧÎąĪÎą ÎŧÎĩ ĪΡÎŊ ÎĩĪÎąĪÎŧÎŋÎŗÎŽ ĪÎŋĪ ÎĩÎŊĪÎŋĪίÎļÎĩΚ ĪÎģÎą ĪÎą ÎŦÎģÎŧĪÎŋĪ Îŧ.", + "advanced_settings_enable_alternate_media_filter_title": "[Î ÎÎÎĄÎÎÎΤÎÎÎ] ΧĪÎŽĪΡ ÎĩÎŊÎąÎģÎģÎąÎēĪΚÎēÎŋĪ ĪίÎģĪĪÎŋĪ ĪĪ ÎŗĪĪÎŋÎŊΚĪÎŧÎŋĪ ÎŦÎģÎŧĪÎŋĪ Îŧ ĪĪ ĪÎēÎĩĪ ÎŽĪ", + "advanced_settings_log_level_title": "ÎĪίĪÎĩδÎŋ ĪĪÎŊδÎĩĪΡĪ: {}", "advanced_settings_prefer_remote_subtitle": "ÎÎĩĪΚÎēÎĪ ĪĪ ĪÎēÎĩĪ ÎĪ ÎąĪÎŗÎŋĪÎŊ ĪÎŋÎģĪ ÎŊÎą ĪÎŋĪĪĪĪÎŋĪ ÎŊ ÎŧΚÎēĪÎŋÎŗĪÎąĪίÎĩĪ ÎąĪĪ ÎąĪĪÎĩÎ¯Îą ĪĪΡ ĪĪ ĪÎēÎĩĪ ÎŽ. ÎÎŊÎĩĪÎŗÎŋĪÎŋΚΎĪĪÎĩ ÎąĪ ĪÎŽÎŊ ĪΡ ĪĪθÎŧΚĪΡ ÎŗÎšÎą ÎŊÎą ĪÎŋĪĪĪÎŊÎŋÎŊĪιΚ ÎąÎŊĪί ÎąĪ ĪÎŋĪ ÎąĪÎŋÎŧÎąÎēĪĪ ĪÎŧÎÎŊÎĩĪ ÎĩΚÎēĪÎŊÎĩĪ.", "advanced_settings_prefer_remote_title": "Î ĪÎŋĪίÎŧΡĪΡ ÎąĪÎŋÎŧÎąÎēĪĪ ĪÎŧÎÎŊĪÎŊ ÎĩΚÎēĪÎŊĪÎŊ.", "advanced_settings_proxy_headers_subtitle": "ÎιθÎŋĪΚĪÎŧĪĪ ÎēÎĩĪÎąÎģίδĪÎŊ δΚιÎēÎŋÎŧΚĪĪÎŽ ÎŧÎĩĪÎŋÎģÎŦβΡĪÎˇĪ ĪÎŋĪ ĪÎŋ Immich ĪĪÎĪÎĩΚ ÎŊÎą ĪĪÎÎģÎŊÎĩΚ ÎŧÎĩ ÎēÎŦθÎĩ ÎąÎ¯ĪΡÎŧÎą δΚÎēĪĪÎŋĪ ", "advanced_settings_proxy_headers_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": "ÎÎŊĪΚÎŧÎĩĪĪĪΚĪΡ ĪĪÎŋβÎģΡÎŧÎŦĪĪÎŊ", @@ -477,8 +481,8 @@ "assets_added_to_album_count": "Î ĪÎŋĪĪÎθΡÎēÎĩ {count, plural, one {# ÎąĪĪÎĩίÎŋ} other {# ÎąĪĪÎĩÎ¯Îą}} ĪĪÎŋ ÎŦÎģÎŧĪÎŋĪ Îŧ", "assets_added_to_name_count": "Î ĪÎŋĪĪÎθΡÎēÎĩ {count, plural, one {# ÎąĪĪÎĩίÎŋ} other {# ÎąĪĪÎĩÎ¯Îą}} ĪĪÎŋ {hasName, select, true {<b>{name}</b>} other {ÎŊÎÎŋ ÎŦÎģÎŧĪÎŋĪ Îŧ}}", "assets_count": "{count, plural, one {# ÎąĪĪÎĩίÎŋ} other {# ÎąĪĪÎĩÎ¯Îą}}", - "assets_deleted_permanently": "{} ĪĪÎŋΚĪÎĩίÎŋ(Îą) Î´ÎšÎąÎŗĪÎŦĪΡÎēÎąÎŊ ÎŋĪΚĪĪΚÎēÎŦ", - "assets_deleted_permanently_from_server": "{} ĪĪÎŋΚĪÎĩίÎŋ(Îą) Î´ÎšÎąÎŗĪÎŦĪΡÎēÎąÎŊ ÎŋĪΚĪĪΚÎēÎŦ ÎąĪĪ ĪÎŋÎŊ δΚιÎēÎŋÎŧΚĪĪÎŽ Immich", + "assets_deleted_permanently": "{} ĪĪÎŋΚĪÎĩίÎŋ(-Îą) Î´ÎšÎąÎŗĪÎŦĪΡÎēÎĩ(-ÎąÎŊ) ÎŋĪΚĪĪΚÎēÎŦ", + "assets_deleted_permanently_from_server": "{} ĪĪÎŋΚĪÎĩίÎŋ(Îą) Î´ÎšÎąÎŗĪÎŦĪΡÎēÎĩ(-ÎąÎŊ) ÎŋĪΚĪĪΚÎēÎŦ ÎąĪĪ ĪÎŋÎŊ δΚιÎēÎŋÎŧΚĪĪÎŽ Immich", "assets_moved_to_trash_count": "ÎÎĩĪÎąÎēΚÎŊΎθΡÎēÎĩ/ÎēÎąÎŊ {count, plural, one {# ÎąĪĪÎĩίÎŋ} other {# ÎąĪĪÎĩÎ¯Îą}} ĪĪÎŋÎŊ ÎēÎŦδÎŋ ÎąĪÎŋĪĪΚÎŧÎŧÎŦĪĪÎŊ", "assets_permanently_deleted_count": "ÎÎšÎąÎŗĪÎŦĪΡÎēÎĩ/ÎēÎąÎŊ ÎŧĪÎŊΚÎŧÎą {count, plural, one {# ÎąĪĪÎĩίÎŋ} other {# ÎąĪĪÎĩÎ¯Îą}}", "assets_removed_count": "ÎĪιΚĪÎθΡÎēÎąÎŊ {count, plural, one {# ÎąĪĪÎĩίÎŋ} other {# ÎąĪĪÎĩÎ¯Îą}}", @@ -529,11 +533,11 @@ "backup_controller_page_background_turn_on": "ÎÎŊÎĩĪÎŗÎŋĪÎŋÎ¯ÎˇĪΡ Ī ĪΡĪÎĩĪÎ¯ÎąĪ ĪÎąĪÎąĪÎēΡÎŊίÎŋĪ ", "backup_controller_page_background_wifi": "ÎĪÎŊÎŋ ĪÎĩ ĪĪÎŊδÎĩĪΡ WiFi", "backup_controller_page_backup": "ÎÎŊĪÎ¯ÎŗĪÎąĪÎą ÎąĪĪÎąÎģÎĩÎ¯ÎąĪ", - "backup_controller_page_backup_selected": "ÎĪΚÎģÎĩÎŗÎŧÎÎŊÎą:", + "backup_controller_page_backup_selected": "ÎĪΚÎģÎĩÎŗÎŧÎÎŊÎą: ", "backup_controller_page_backup_sub": "ÎĻĪĪÎŋÎŗĪÎąĪίÎĩĪ ÎēιΚ Î˛Î¯ÎŊĪÎĩÎŋ ÎŗÎšÎą ĪÎą ÎŋĪÎŋÎ¯Îą ÎĪÎŋĪ ÎŊ δΡÎŧΚÎŋĪ ĪÎŗÎˇÎ¸Îĩί ÎąÎŊĪÎ¯ÎŗĪÎąĪÎą ÎąĪĪÎąÎģÎĩÎ¯ÎąĪ", "backup_controller_page_created": "ÎΡÎŧΚÎŋĪ ĪÎŗÎŽÎ¸ÎˇÎēÎĩ ĪĪΚĪ: {}", "backup_controller_page_desc_backup": "ÎÎŊÎĩĪÎŗÎŋĪÎŋΚΎĪĪÎĩ ĪΡÎŊ δΡÎŧΚÎŋĪ ĪÎŗÎ¯Îą ÎąÎŊĪÎšÎŗĪÎŦĪĪÎŊ ÎąĪĪÎąÎģÎĩÎ¯ÎąĪ ĪĪÎŋ ĪĪÎŋĪÎēÎŽÎŊΚÎŋ ÎŗÎšÎą ÎąĪ ĪĪÎŧÎąĪΡ ÎŧÎĩĪÎąĪĪĪĪĪĪΡ ÎŊÎĪÎŊ ĪĪÎŋΚĪÎĩίĪÎŊ ĪĪÎŋÎŊ δΚιÎēÎŋÎŧΚĪĪÎŽ ĪĪÎąÎŊ ÎąÎŊÎŋÎ¯ÎŗÎĩĪÎĩ ĪΡÎŊ ÎĩĪÎąĪÎŧÎŋÎŗÎŽ.", - "backup_controller_page_excluded": "ÎΞιΚĪÎŋĪÎŧÎĩÎŊÎą:", + "backup_controller_page_excluded": "ÎΞιΚĪÎŋĪÎŧÎĩÎŊÎą: ", "backup_controller_page_failed": "ÎĪÎŋĪĪ ĪΡÎŧÎÎŊÎą ({})", "backup_controller_page_filename": "ÎÎŊÎŋÎŧÎą ÎąĪĪÎĩίÎŋĪ : {} [{}]", "backup_controller_page_id": "ID: {}", diff --git a/i18n/en.json b/i18n/en.json index c4b4746871..c01cd65712 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -39,11 +39,11 @@ "authentication_settings_disable_all": "Are you sure you want to disable all login methods? Login will be completely disabled.", "authentication_settings_reenable": "To re-enable, use a <link>Server Command</link>.", "background_task_job": "Background Tasks", - "backup_database": "Backup Database", - "backup_database_enable_description": "Enable database backups", - "backup_keep_last_amount": "Amount of previous backups to keep", - "backup_settings": "Backup Settings", - "backup_settings_description": "Manage database backup settings", + "backup_database": "Create Database Dump", + "backup_database_enable_description": "Enable database dumps", + "backup_keep_last_amount": "Amount of previous dumps to keep", + "backup_settings": "Database Dump Settings", + "backup_settings_description": "Manage database dump settings. Note: These jobs are not monitored and you will not be notified of failure.", "check_all": "Check All", "cleanup": "Cleanup", "cleared_jobs": "Cleared jobs for: {job}", @@ -192,26 +192,22 @@ "oauth_auto_register": "Auto register", "oauth_auto_register_description": "Automatically register new users after signing in with OAuth", "oauth_button_text": "Button text", - "oauth_client_id": "Client ID", - "oauth_client_secret": "Client Secret", + "oauth_client_secret_description": "Required if PKCE (Proof Key for Code Exchange) is not supported by the OAuth provider", "oauth_enable_description": "Login with OAuth", - "oauth_issuer_url": "Issuer URL", "oauth_mobile_redirect_uri": "Mobile redirect URI", "oauth_mobile_redirect_uri_override": "Mobile redirect URI override", "oauth_mobile_redirect_uri_override_description": "Enable when OAuth provider does not allow a mobile URI, like '{callback}'", - "oauth_profile_signing_algorithm": "Profile signing algorithm", - "oauth_profile_signing_algorithm_description": "Algorithm used to sign the user profile.", - "oauth_scope": "Scope", "oauth_settings": "OAuth", "oauth_settings_description": "Manage OAuth login settings", "oauth_settings_more_details": "For more details about this feature, refer to the <link>docs</link>.", - "oauth_signing_algorithm": "Signing algorithm", "oauth_storage_label_claim": "Storage label claim", "oauth_storage_label_claim_description": "Automatically set the user's storage label to the value of this claim.", "oauth_storage_quota_claim": "Storage quota claim", "oauth_storage_quota_claim_description": "Automatically set the user's storage quota to the value of this claim.", "oauth_storage_quota_default": "Default storage quota (GiB)", "oauth_storage_quota_default_description": "Quota in GiB to be used when no claim is provided (Enter 0 for unlimited quota).", + "oauth_timeout": "Request Timeout", + "oauth_timeout_description": "Timeout for requests in milliseconds", "offline_paths": "Offline Paths", "offline_paths_description": "These results may be due to manual deletion of files that are not part of an external library.", "password_enable_description": "Login with email and password", @@ -853,10 +849,12 @@ "failed_to_keep_this_delete_others": "Failed to keep this asset and delete the other assets", "failed_to_load_asset": "Failed to load asset", "failed_to_load_assets": "Failed to load assets", + "failed_to_load_notifications": "Failed to load notifications", "failed_to_load_people": "Failed to load people", "failed_to_remove_product_key": "Failed to remove product key", "failed_to_stack_assets": "Failed to stack assets", "failed_to_unstack_assets": "Failed to un-stack assets", + "failed_to_update_notification_status": "Failed to update notification status", "import_path_already_exists": "This import path already exists.", "incorrect_email_or_password": "Incorrect email or password", "paths_validation_failed": "{paths, plural, one {# path} other {# paths}} failed validation", @@ -978,7 +976,7 @@ "external": "External", "external_libraries": "External Libraries", "external_network": "External network", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network_sheet_info": "When not on the preferred Wi-Fi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", "face_unassigned": "Unassigned", "failed": "Failed", "failed_to_load_assets": "Failed to load assets", @@ -1125,7 +1123,7 @@ "local_network": "Local network", "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", "location_permission": "Location permission", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", + "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current Wi-Fi network's name", "location_picker_choose_on_map": "Choose on map", "location_picker_latitude_error": "Enter a valid latitude", "location_picker_latitude_hint": "Enter your latitude here", @@ -1199,6 +1197,9 @@ "map_settings_only_show_favorites": "Show Favorite Only", "map_settings_theme_settings": "Map Theme", "map_zoom_to_see_photos": "Zoom out to see photos", + "mark_as_read": "Mark as read", + "mark_all_as_read": "Mark all as read", + "marked_all_as_read": "Marked all as read", "matches": "Matches", "media_type": "Media type", "memories": "Memories", @@ -1225,6 +1226,8 @@ "month": "Month", "monthly_title_text_date_format": "MMMM y", "more": "More", + "moved_to_archive": "Moved {count, plural, one {# asset} other {# assets}} to archive", + "moved_to_library": "Moved {count, plural, one {# asset} other {# assets}} to library", "moved_to_trash": "Moved to trash", "multiselect_grid_edit_date_time_err_read_only": "Cannot edit date of read only asset(s), skipping", "multiselect_grid_edit_gps_err_read_only": "Cannot edit location of read only asset(s), skipping", @@ -1257,9 +1260,11 @@ "no_favorites_message": "Add favorites to quickly find your best pictures and videos", "no_libraries_message": "Create an external library to view your photos and videos", "no_name": "No Name", + "no_people_found": "No matching people found", "no_places": "No places", "no_results": "No results", "no_results_description": "Try a synonym or more general keyword", + "no_notifications": "No notifications", "no_shared_albums_message": "Create an album to share photos and videos with people in your network", "not_in_any_album": "Not in any album", "not_selected": "Not selected", @@ -1432,6 +1437,8 @@ "recent_searches": "Recent searches", "recently_added": "Recently added", "recently_added_page_title": "Recently Added", + "recently_taken": "Recently taken", + "recently_taken_page_title": "Recently Taken", "refresh": "Refresh", "refresh_encoded_videos": "Refresh encoded videos", "refresh_faces": "Refresh faces", @@ -1566,6 +1573,7 @@ "select_keep_all": "Select keep all", "select_library_owner": "Select library owner", "select_new_face": "Select new face", + "select_person_to_tag": "Select a person to tag", "select_photos": "Select photos", "select_trash_all": "Select trash all", "select_user_for_sharing_page_err_album": "Failed to create album", diff --git a/i18n/es.json b/i18n/es.json index 0fe78eb66f..64521c1aa8 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -14,7 +14,7 @@ "add_a_location": "Agregar ubicaciÃŗn", "add_a_name": "Agregar nombre", "add_a_title": "Agregar tÃtulo", - "add_endpoint": "Add endpoint", + "add_endpoint": "AÃąadir endpoint", "add_exclusion_pattern": "Agregar patrÃŗn de exclusiÃŗn", "add_import_path": "Agregar ruta de importaciÃŗn", "add_location": "Agregar ubicaciÃŗn", @@ -39,11 +39,11 @@ "authentication_settings_disable_all": "ÂŋEstÃĄs seguro de que deseas desactivar todos los mÊtodos de inicio de sesiÃŗn? Esto desactivarÃĄ por completo el inicio de sesiÃŗn.", "authentication_settings_reenable": "Para reactivarlo, utiliza un <link>Comando del servidor</link>.", "background_task_job": "Tareas en segundo plano", - "backup_database": "Respaldar base de datos", - "backup_database_enable_description": "Activar respaldo de base de datos", - "backup_keep_last_amount": "Cantidad de respaldos previos a mantener", - "backup_settings": "Ajustes de respaldo", - "backup_settings_description": "Administrar configuraciÃŗn de respaldo de base de datos", + "backup_database": "Crear volcado de base de datos", + "backup_database_enable_description": "Activar volcado de base de datos", + "backup_keep_last_amount": "Cantidad de volcados previos a mantener", + "backup_settings": "Ajustes de volcado de base de datos", + "backup_settings_description": "Administrar configuraciÃŗn de volcado de base de datos. Nota: estas tareas no estÃĄn monitorizadas y no se notificarÃĄn los fallos.", "check_all": "Verificar todo", "cleanup": "Limpieza", "cleared_jobs": "Trabajos borrados para: {job}", @@ -91,9 +91,9 @@ "image_thumbnail_quality_description": "Calidad de miniatura de 1 a 100. Es mejor cuanto mÃĄs alto es el valor pero genera archivos mÃĄs grandes y puede reducir la capacidad de respuesta de la aplicaciÃŗn.", "image_thumbnail_title": "Ajustes de las miniaturas", "job_concurrency": "{job}: Procesos simultÃĄneos", - "job_created": "Trabajo creado", + "job_created": "Tarea creada", "job_not_concurrency_safe": "Esta tarea no es segura para la simultaneidad.", - "job_settings": "ConfiguraciÃŗn tareas", + "job_settings": "ConfiguraciÃŗn de tareas", "job_settings_description": "Administrar tareas simultÃĄneas", "job_status": "Estado de la tarea", "jobs_delayed": "{jobCount, plural, one {# retrasado} other {# retrasados}}", @@ -169,7 +169,7 @@ "migration_job_description": "Migrar miniaturas de archivos y caras a la estructura de carpetas mÃĄs reciente", "no_paths_added": "No se han aÃąadido carpetas", "no_pattern_added": "No se han aÃąadido patrones", - "note_apply_storage_label_previous_assets": "Nota: para aplicar una Etiqueta de Almacenamient a un elemento anteriormente cargado, lanza el", + "note_apply_storage_label_previous_assets": "Nota: para aplicar una Etiqueta de Almacenamiento a un elemento anteriormente cargado, lanza el", "note_cannot_be_changed_later": "NOTA: ÂĄNo se puede cambiar posteriormente!", "notification_email_from_address": "Desde", "notification_email_from_address_description": "DirecciÃŗn de correo electrÃŗnico del remitente, por ejemplo: \"Immich Photo Server <noreply@example.com>\"", @@ -252,12 +252,12 @@ "storage_template_migration": "MigraciÃŗn de plantillas de almacenamiento", "storage_template_migration_description": "Aplicar la <link>{template}</link> actual a los elementos subidos previamente", "storage_template_migration_info": "La plantilla de almacenamiento convertirÃĄ todas las extensiones a minÃēscula. Los cambios en las plantillas solo se aplican a los elementos nuevos. Para aplicarlos retroactivamente a los elementos subidos previamente ejecute la <link>{job}</link>.", - "storage_template_migration_job": "MigraciÃŗn de la plantilla de almacenamiento", + "storage_template_migration_job": "Tarea de migraciÃŗn de la plantilla de almacenamiento", "storage_template_more_details": "Para obtener mÃĄs detalles sobre esta funciÃŗn, consulte la <template-link>Plantilla de almacenamiento</template-link> y sus <implications-link>implicaciones</implications-link>", "storage_template_onboarding_description": "Cuando estÃĄ habilitada, esta funciÃŗn organizarÃĄ automÃĄticamente los archivos segÃēn una plantilla definida por el usuario. Debido a problemas de estabilidad, la funciÃŗn se ha desactivado de forma predeterminada. Para obtener mÃĄs informaciÃŗn, consulte la <link>documentaciÃŗn</link>.", "storage_template_path_length": "LÃmite aproximado de la longitud de la ruta: <b>{length, number}</b>/{limit, number}", "storage_template_settings": "Plantilla de almacenamiento", - "storage_template_settings_description": "Administre la estructura de carpetas y el nombre de archivo del recurso cargado", + "storage_template_settings_description": "Administrar la estructura de carpetas y el nombre de archivo del recurso cargado", "storage_template_user_label": "<code>{label}</code> es la etiqueta de almacenamiento del usuario", "system_settings": "Ajustes del Sistema", "tag_cleanup_job": "Limpieza de etiquetas", @@ -345,7 +345,7 @@ "trash_settings": "ConfiguraciÃŗn papelera", "trash_settings_description": "Administrar la configuraciÃŗn de la papelera", "untracked_files": "Archivos sin seguimiento", - "untracked_files_description": "La aplicaciÃŗn no rastrea estos archivos. Puede ser el resultado de movimientos fallidos, cargas interrumpidas o sin procesar debido a un error", + "untracked_files_description": "La aplicaciÃŗn no rastrea estos archivos. Puede ser el resultado de movimientos fallidos, subidas interrumpidas o sin procesar debido a un error", "user_cleanup_job": "Limpieza de usuarios", "user_delete_delay": "La cuenta <b>{user}</b> y los archivos se programarÃĄn para su eliminaciÃŗn permanente en {delay, plural, one {# dÃa} other {# dÃas}}.", "user_delete_delay_settings": "Eliminar retardo", @@ -371,19 +371,23 @@ "admin_password": "ContraseÃąa del Administrador", "administration": "AdministraciÃŗn", "advanced": "Avanzada", + "advanced_settings_enable_alternate_media_filter_subtitle": "Usa esta opciÃŗn para filtrar medios durante la sincronizaciÃŗn segÃēn criterios alternativos. Intenta esto solo si tienes problemas con que la aplicaciÃŗn detecte todos los ÃĄlbumes.", + "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Usar filtro alternativo de sincronizaciÃŗn de ÃĄlbumes del dispositivo", "advanced_settings_log_level_title": "Nivel de registro: {}", "advanced_settings_prefer_remote_subtitle": "Algunos dispositivos tardan mucho en cargar las miniaturas de los elementos encontrados en el dispositivo. Activa esta opciÃŗn para cargar imÃĄgenes remotas en su lugar.", "advanced_settings_prefer_remote_title": "Preferir imÃĄgenes remotas", "advanced_settings_proxy_headers_subtitle": "Configura headers HTTP que Immich incluirÃĄ en cada peticiÃŗn de red", - "advanced_settings_proxy_headers_title": "Proxy Headers", - "advanced_settings_self_signed_ssl_subtitle": "Omitir verificaciÃŗn del certificado SSL del servidor. Requerido para certificados autofirmados", + "advanced_settings_proxy_headers_title": "Cabeceras Proxy", + "advanced_settings_self_signed_ssl_subtitle": "Omitir verificaciÃŗn del certificado SSL del servidor. Requerido para certificados autofirmados.", "advanced_settings_self_signed_ssl_title": "Permitir certificados autofirmados", + "advanced_settings_sync_remote_deletions_subtitle": "Eliminar o restaurar automÃĄticamente un recurso en este dispositivo cuando se realice esa acciÃŗn en la web", + "advanced_settings_sync_remote_deletions_title": "Sincronizar eliminaciones remotas [EXPERIMENTAL]", "advanced_settings_tile_subtitle": "Configuraciones avanzadas del usuario", "advanced_settings_troubleshooting_subtitle": "Habilitar funciones adicionales para soluciÃŗn de problemas", "advanced_settings_troubleshooting_title": "SoluciÃŗn de problemas", "age_months": "Tiempo {months, plural, one {# mes} other {# meses}}", "age_year_months": "1 aÃąo, {months, plural, one {# mes} other {# meses}}", - "age_years": "Edad {years, plural, one {# aÃąo} other {# aÃąos}}", + "age_years": "AntigÃŧedad {years, plural, one {# aÃąo} other {# aÃąos}}", "album_added": "Ãlbum aÃąadido", "album_added_notification_setting_description": "Reciba una notificaciÃŗn por correo electrÃŗnico cuando lo agreguen a un ÃĄlbum compartido", "album_cover_updated": "Portada del ÃĄlbum actualizada", @@ -401,7 +405,7 @@ "album_share_no_users": "Parece que has compartido este ÃĄlbum con todos los usuarios o no tienes ningÃēn usuario con quien compartirlo.", "album_thumbnail_card_item": "1 elemento", "album_thumbnail_card_items": "{} elementos", - "album_thumbnail_card_shared": "Compartido", + "album_thumbnail_card_shared": " ¡ Compartido", "album_thumbnail_shared_by": "Compartido por {}", "album_updated": "Album actualizado", "album_updated_setting_description": "Reciba una notificaciÃŗn por correo electrÃŗnico cuando un ÃĄlbum compartido tenga nuevos archivos", @@ -411,8 +415,8 @@ "album_viewer_appbar_share_err_delete": "No ha podido eliminar el ÃĄlbum", "album_viewer_appbar_share_err_leave": "No se ha podido abandonar el ÃĄlbum", "album_viewer_appbar_share_err_remove": "Hay problemas para eliminar los elementos del ÃĄlbum", - "album_viewer_appbar_share_err_title": "Error al cambiar el tÃtulo del ÃĄlbum ", - "album_viewer_appbar_share_leave": "Abandonar ÃĄlbum ", + "album_viewer_appbar_share_err_title": "Error al cambiar el tÃtulo del ÃĄlbum", + "album_viewer_appbar_share_leave": "Abandonar ÃĄlbum", "album_viewer_appbar_share_to": "Compartir Con", "album_viewer_page_share_add_users": "Agregar usuarios", "album_with_link_access": "Permita que cualquier persona con el enlace vea fotos y personas en este ÃĄlbum.", @@ -425,7 +429,7 @@ "allow_dark_mode": "Permitir modo oscuro", "allow_edits": "Permitir ediciÃŗn", "allow_public_user_to_download": "Permitir descargar al usuario pÃēblico", - "allow_public_user_to_upload": "Permitir cargar al usuario publico", + "allow_public_user_to_upload": "Permitir subir al usuario publico", "alt_text_qr_code": "CÃŗdigo QR", "anti_clockwise": "En sentido antihorario", "api_key": "Clave API", @@ -477,8 +481,8 @@ "assets_added_to_album_count": "AÃąadido {count, plural, one {# asset} other {# assets}} al ÃĄlbum", "assets_added_to_name_count": "AÃąadido {count, plural, one {# asset} other {# assets}} a {hasName, select, true {<b>{name}</b>} other {new album}}", "assets_count": "{count, plural, one {# activo} other {# activos}}", - "assets_deleted_permanently": "\n{} elementos(s) eliminado(s) permanentemente", - "assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server", + "assets_deleted_permanently": "{} elementos(s) eliminado(s) permanentemente", + "assets_deleted_permanently_from_server": "{} recurso(s) eliminado(s) de forma permanente del servidor de Immich", "assets_moved_to_trash_count": "{count, plural, one {# elemento movido} other {# elementos movidos}} a la papelera", "assets_permanently_deleted_count": "Eliminado permanentemente {count, plural, one {# elemento} other {# elementos}}", "assets_removed_count": "Eliminado {count, plural, one {# elemento} other {# elementos}}", @@ -488,15 +492,15 @@ "assets_restored_successfully": "{} elemento(s) restaurado(s) exitosamente", "assets_trashed": "{} elemento(s) eliminado(s)", "assets_trashed_count": "Borrado {count, plural, one {# elemento} other {# elementos}}", - "assets_trashed_from_server": "{} elemento(s) movido a la papelera en Immich", + "assets_trashed_from_server": "{} recurso(s) enviado(s) a la papelera desde el servidor de Immich", "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} ya forma parte del ÃĄlbum", "authorized_devices": "Dispositivos Autorizados", - "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", - "automatic_endpoint_switching_title": "Automatic URL switching", + "automatic_endpoint_switching_subtitle": "Conectarse localmente a travÊs de la Wi-Fi designada cuando estÊ disponible y usar conexiones alternativas en otros lugares", + "automatic_endpoint_switching_title": "Cambio automÃĄtico de URL", "back": "AtrÃĄs", "back_close_deselect": "AtrÃĄs, cerrar o anular la selecciÃŗn", - "background_location_permission": "Background location permission", - "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", + "background_location_permission": "Permiso de ubicaciÃŗn en segundo plano", + "background_location_permission_content": "Para poder cambiar de red mientras se ejecuta en segundo plano, Immich debe tener *siempre* acceso a la ubicaciÃŗn precisa para que la aplicaciÃŗn pueda leer el nombre de la red Wi-Fi", "backup_album_selection_page_albums_device": "Ãlbumes en el dispositivo ({})", "backup_album_selection_page_albums_tap": "Toque para incluir, doble toque para excluir", "backup_album_selection_page_assets_scatter": "Los elementos pueden dispersarse en varios ÃĄlbumes. De este modo, los ÃĄlbumes pueden ser incluidos o excluidos durante el proceso de copia de seguridad.", @@ -504,13 +508,13 @@ "backup_album_selection_page_selection_info": "InformaciÃŗn sobre la SelecciÃŗn", "backup_album_selection_page_total_assets": "Total de elementos Ãēnicos", "backup_all": "Todos", - "backup_background_service_backup_failed_message": "Error al copiar elementos. Reintentando...", - "backup_background_service_connection_failed_message": "Error al conectar con el servidor. Reintentando...", - "backup_background_service_current_upload_notification": "Cargando {}", - "backup_background_service_default_notification": "Verificando si hay nuevos elementos", + "backup_background_service_backup_failed_message": "Error al copiar elementos. ReintentandoâĻ", + "backup_background_service_connection_failed_message": "Error al conectar con el servidor. ReintentandoâĻ", + "backup_background_service_current_upload_notification": "Subiendo {}", + "backup_background_service_default_notification": "Comprobando nuevos elementosâĻ", "backup_background_service_error_title": "Error de copia de seguridad", - "backup_background_service_in_progress_notification": "Creando copia de seguridad de tus elementos...", - "backup_background_service_upload_failure_notification": "Error al cargar {}", + "backup_background_service_in_progress_notification": "Creando copia de seguridad de tus elementosâĻ", + "backup_background_service_upload_failure_notification": "Error al subir {}", "backup_controller_page_albums": "Ãlbumes de copia de seguridad", "backup_controller_page_background_app_refresh_disabled_content": "Activa la actualizaciÃŗn en segundo plano de la aplicaciÃŗn en ConfiguraciÃŗn > General > ActualizaciÃŗn en segundo plano para usar la copia de seguridad en segundo plano.", "backup_controller_page_background_app_refresh_disabled_title": "ActualizaciÃŗn en segundo plano desactivada", @@ -521,19 +525,19 @@ "backup_controller_page_background_battery_info_title": "Optimizaciones de baterÃa", "backup_controller_page_background_charging": "Solo mientras se carga", "backup_controller_page_background_configure_error": "Error al configurar el servicio en segundo plano", - "backup_controller_page_background_delay": "Retraso en la copia de seguridad de nuevos elementos: {}", - "backup_controller_page_background_description": "Activa el servicio en segundo plano para copiar automÃĄticamente cualquier nuevos elementos sin necesidad de abrir la aplicaciÃŗn.", + "backup_controller_page_background_delay": "Retrasar la copia de seguridad de los nuevos elementos: {}", + "backup_controller_page_background_description": "Activa el servicio en segundo plano para copiar automÃĄticamente cualquier nuevos elementos sin necesidad de abrir la aplicaciÃŗn", "backup_controller_page_background_is_off": "La copia de seguridad en segundo plano automÃĄtica estÃĄ desactivada", "backup_controller_page_background_is_on": "La copia de seguridad en segundo plano automÃĄtica estÃĄ activada", "backup_controller_page_background_turn_off": "Desactivar el servicio en segundo plano", "backup_controller_page_background_turn_on": "Activar el servicio en segundo plano", "backup_controller_page_background_wifi": "Solo en WiFi", "backup_controller_page_backup": "Copia de Seguridad", - "backup_controller_page_backup_selected": "Seleccionado:", + "backup_controller_page_backup_selected": "Seleccionado: ", "backup_controller_page_backup_sub": "Fotos y videos respaldados", "backup_controller_page_created": "Creado el: {}", - "backup_controller_page_desc_backup": "Active la copia de seguridad para cargar automÃĄticamente los nuevos elementos al servidor.", - "backup_controller_page_excluded": "Excluido:", + "backup_controller_page_desc_backup": "Active la copia de seguridad para subir automÃĄticamente los nuevos elementos al servidor cuando se abre la aplicaciÃŗn.", + "backup_controller_page_excluded": "Excluido: ", "backup_controller_page_failed": "Fallidos ({})", "backup_controller_page_filename": "Nombre del archivo: {} [{}]", "backup_controller_page_id": "ID: {}", @@ -550,11 +554,11 @@ "backup_controller_page_total_sub": "Todas las fotos y vÃdeos Ãēnicos de los ÃĄlbumes seleccionados", "backup_controller_page_turn_off": "Apagar la copia de seguridad", "backup_controller_page_turn_on": "Activar la copia de seguridad", - "backup_controller_page_uploading_file_info": "Cargando informaciÃŗn del archivo", + "backup_controller_page_uploading_file_info": "Subiendo informaciÃŗn del archivo", "backup_err_only_album": "No se puede eliminar el Ãēnico ÃĄlbum", "backup_info_card_assets": "elementos", "backup_manual_cancelled": "Cancelado", - "backup_manual_in_progress": "Subida en progreso. Espere", + "backup_manual_in_progress": "Subida ya en progreso. Vuelve a intentarlo mÃĄs tarde", "backup_manual_success": "Ãxito", "backup_manual_title": "Estado de la subida", "backup_options_page_title": "Opciones de Copia de Seguridad", @@ -593,12 +597,12 @@ "camera_model": "Modelo de cÃĄmara", "cancel": "Cancelar", "cancel_search": "Cancelar bÃēsqueda", - "canceled": "Canceled", + "canceled": "Cancelado", "cannot_merge_people": "No se pueden fusionar personas", "cannot_undo_this_action": "ÂĄNo puedes deshacer esta acciÃŗn!", "cannot_update_the_description": "No se puede actualizar la descripciÃŗn", "change_date": "Cambiar fecha", - "change_display_order": "Change display order", + "change_display_order": "Cambiar orden de visualizaciÃŗn", "change_expiration_time": "Cambiar fecha de caducidad", "change_location": "Cambiar ubicaciÃŗn", "change_name": "Cambiar nombre", @@ -613,9 +617,9 @@ "change_your_password": "Cambia tu contraseÃąa", "changed_visibility_successfully": "Visibilidad cambiada correctamente", "check_all": "Comprobar todo", - "check_corrupt_asset_backup": "Check for corrupt asset backups", - "check_corrupt_asset_backup_button": "Perform check", - "check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.", + "check_corrupt_asset_backup": "Comprobar copias de seguridad de archivos corruptos", + "check_corrupt_asset_backup_button": "Realizar comprobaciÃŗn", + "check_corrupt_asset_backup_description": "Ejecutar esta comprobaciÃŗn solo por Wi-Fi y una vez que todos los archivos hayan sido respaldados. El procedimiento puede tardar unos minutos.", "check_logs": "Comprobar Registros", "choose_matching_people_to_merge": "Elija personas similares para fusionar", "city": "Ciudad", @@ -627,11 +631,11 @@ "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "Introduzca contraseÃąa", "client_cert_import": "Importar", - "client_cert_import_success_msg": "Client certificate is imported", - "client_cert_invalid_msg": "Invalid certificate file or wrong password", - "client_cert_remove_msg": "Client certificate is removed", - "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login", - "client_cert_title": "SSL Client Certificate", + "client_cert_import_success_msg": "El certificado de cliente estÃĄ importado", + "client_cert_invalid_msg": "Archivo de certificado no vÃĄlido o contraseÃąa incorrecta", + "client_cert_remove_msg": "El certificado de cliente se ha eliminado", + "client_cert_subtitle": "Solo se admite el formato PKCS12 (.p12, .pfx). La importaciÃŗn/eliminaciÃŗn de certificados solo estÃĄ disponible antes de iniciar sesiÃŗn", + "client_cert_title": "Certificado de cliente SSL", "clockwise": "En el sentido de las agujas del reloj", "close": "Cerrar", "collapse": "Agrupar", @@ -644,7 +648,7 @@ "comments_are_disabled": "Los comentarios estÃĄn deshabilitados", "common_create_new_album": "Crear nuevo ÃĄlbum", "common_server_error": "Por favor, verifica tu conexiÃŗn de red, asegÃērate de que el servidor estÊ accesible y las versiones de la aplicaciÃŗn y del servidor sean compatibles.", - "completed": "Completed", + "completed": "Completado", "confirm": "Confirmar", "confirm_admin_password": "Confirmar ContraseÃąa de Administrador", "confirm_delete_face": "ÂŋEstÃĄs seguro que deseas eliminar la cara de {name} del archivo?", @@ -660,7 +664,7 @@ "control_bottom_app_bar_delete_from_local": "Borrar del dispositivo", "control_bottom_app_bar_edit_location": "Editar ubicaciÃŗn", "control_bottom_app_bar_edit_time": "Editar fecha y hora", - "control_bottom_app_bar_share_link": "Share Link", + "control_bottom_app_bar_share_link": "Enlace para compartir", "control_bottom_app_bar_share_to": "Enviar", "control_bottom_app_bar_trash_from_immich": "Mover a la papelera", "copied_image_to_clipboard": "Imagen copiada al portapapeles.", @@ -695,7 +699,7 @@ "crop": "Recortar", "curated_object_page_title": "Objetos", "current_device": "Dispositivo actual", - "current_server_address": "Current server address", + "current_server_address": "DirecciÃŗn actual del servidor", "custom_locale": "ConfiguraciÃŗn regional personalizada", "custom_locale_description": "Formatear fechas y nÃēmeros segÃēn el idioma y la regiÃŗn", "daily_title_text_date": "E dd, MMM", @@ -746,7 +750,7 @@ "direction": "DirecciÃŗn", "disabled": "Deshabilitado", "disallow_edits": "Bloquear ediciÃŗn", - "discord": "", + "discord": "Discord", "discover": "Descubrir", "dismiss_all_errors": "Descartar todos los errores", "dismiss_error": "Descartar error", @@ -763,7 +767,7 @@ "download_enqueue": "Descarga en cola", "download_error": "Error al descargar", "download_failed": "Descarga fallida", - "download_filename": "Archivo: {}", + "download_filename": "archivo: {}", "download_finished": "Descarga completada", "download_include_embedded_motion_videos": "VÃdeos incrustados", "download_include_embedded_motion_videos_description": "Incluir vÃdeos incrustados en fotografÃas en movimiento como un archivo separado", @@ -807,16 +811,16 @@ "editor_crop_tool_h2_aspect_ratios": "Proporciones del aspecto", "editor_crop_tool_h2_rotation": "RotaciÃŗn", "email": "Correo", - "empty_folder": "This folder is empty", + "empty_folder": "Esta carpeta estÃĄ vacÃa", "empty_trash": "Vaciar papelera", "empty_trash_confirmation": "ÂŋEstÃĄs seguro de que quieres vaciar la papelera? Esto eliminarÃĄ permanentemente todos los archivos de la basura de Immich.\nÂĄNo puedes deshacer esta acciÃŗn!", "enable": "Habilitar", "enabled": "Habilitado", "end_date": "Fecha final", - "enqueued": "Enqueued", + "enqueued": "AÃąadido a la cola", "enter_wifi_name": "Enter WiFi name", "error": "Error", - "error_change_sort_album": "Failed to change album sort order", + "error_change_sort_album": "No se pudo cambiar el orden de visualizaciÃŗn del ÃĄlbum", "error_delete_face": "Error al eliminar la cara del archivo", "error_loading_image": "Error al cargar la imagen", "error_saving_image": "Error: {}", @@ -953,15 +957,15 @@ "exif_bottom_sheet_location": "UBICACIÃN", "exif_bottom_sheet_people": "PERSONAS", "exif_bottom_sheet_person_add_person": "AÃąadir nombre", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "AntigÃŧedad {}", + "exif_bottom_sheet_person_age_months": "AntigÃŧedad {} meses", + "exif_bottom_sheet_person_age_year_months": "AntigÃŧedad 1 aÃąo, {} meses", + "exif_bottom_sheet_person_age_years": "AntigÃŧedad {}", "exit_slideshow": "Salir de la presentaciÃŗn", "expand_all": "Expandir todo", "experimental_settings_new_asset_list_subtitle": "Trabajo en progreso", "experimental_settings_new_asset_list_title": "Habilitar cuadrÃcula fotogrÃĄfica experimental", - "experimental_settings_subtitle": "Ãsalo bajo tu responsabilidad", + "experimental_settings_subtitle": "ÂĄÃsalo bajo tu propia responsabilidad!", "experimental_settings_title": "Experimental", "expire_after": "Expirar despuÊs de", "expired": "Caducado", @@ -973,12 +977,12 @@ "extension": "Extension", "external": "Externo", "external_libraries": "Bibliotecas Externas", - "external_network": "External network", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network": "Red externa", + "external_network_sheet_info": "Cuando no estÊs conectado a la red Wi-Fi preferida, la aplicaciÃŗn se conectarÃĄ al servidor utilizando la primera de las siguientes URLs a la que pueda acceder, comenzando desde la parte superior de la lista hacia abajo", "face_unassigned": "Sin asignar", - "failed": "Failed", + "failed": "Fallido", "failed_to_load_assets": "Error al cargar los activos", - "failed_to_load_folder": "Failed to load folder", + "failed_to_load_folder": "No se pudo cargar la carpeta", "favorite": "Favorito", "favorite_or_unfavorite_photo": "Foto favorita o no favorita", "favorites": "Favoritos", @@ -992,21 +996,22 @@ "filetype": "Tipo de archivo", "filter": "Filtrar", "filter_people": "Filtrar personas", + "filter_places": "Filtrar lugares", "find_them_fast": "EncuÊntrelos rÃĄpidamente por nombre con la bÃēsqueda", "fix_incorrect_match": "Corregir coincidencia incorrecta", - "folder": "Folder", - "folder_not_found": "Folder not found", + "folder": "Carpeta", + "folder_not_found": "Carpeta no encontrada", "folders": "Carpetas", "folders_feature_description": "Explorar la vista de carpetas para las fotos y los videos en el sistema de archivos", "forward": "Reenviar", "general": "General", "get_help": "Solicitar ayuda", - "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", + "get_wifiname_error": "No se pudo obtener el nombre de la red Wi-Fi. AsegÃērate de haber concedido los permisos necesarios y de estar conectado a una red Wi-Fi", "getting_started": "Comenzamos", "go_back": "Volver atrÃĄs", "go_to_folder": "Ir al directorio", "go_to_search": "Ir a bÃēsqueda", - "grant_permission": "Grant permission", + "grant_permission": "Conceder permiso", "group_albums_by": "Agrupar albums por...", "group_country": "Agrupar por paÃs", "group_no": "Sin agrupaciÃŗn", @@ -1031,7 +1036,7 @@ "hide_unnamed_people": "Ocultar personas anÃŗnimas", "home_page_add_to_album_conflicts": "{added} elementos agregados al ÃĄlbum {album}.{failed} elementos ya existen en el ÃĄlbum.", "home_page_add_to_album_err_local": "AÃēn no se pueden agregar elementos locales a ÃĄlbumes, omitiendo", - "home_page_add_to_album_success": "{added} elementos agregados al ÃĄlbum {album}. ", + "home_page_add_to_album_success": "Se aÃąadieron {added} elementos al ÃĄlbum {album}.", "home_page_album_err_partner": "AÃēn no se pueden agregar elementos a un ÃĄlbum de un compaÃąero, omitiendo", "home_page_archive_err_local": "Los elementos locales no pueden ser archivados, omitiendo", "home_page_archive_err_partner": "No se pueden archivar elementos de un compaÃąero, omitiendo", @@ -1040,7 +1045,7 @@ "home_page_delete_remote_err_local": "Elementos locales en la selecciÃŗn de eliminaciÃŗn remota, omitiendo", "home_page_favorite_err_local": "AÃēn no se pueden archivar elementos locales, omitiendo", "home_page_favorite_err_partner": "AÃēn no se pueden marcar elementos de compaÃąeros como favoritos, omitiendo", - "home_page_first_time_notice": "Si esta es la primera vez que usas la app, por favor, asegÃērate de elegir un ÃĄlbum de respaldo para que la lÃnea de tiempo pueda cargar fotos y videos en los ÃĄlbumes.", + "home_page_first_time_notice": "Si es la primera vez que usas la aplicaciÃŗn, asegÃērate de elegir un ÃĄlbum de copia de seguridad para que la lÃnea de tiempo pueda mostrar fotos y vÃdeos en Êl", "home_page_share_err_local": "No se pueden compartir elementos locales a travÊs de un enlace, omitiendo", "home_page_upload_err_limit": "Solo se pueden subir 30 elementos simultÃĄneamente, omitiendo", "host": "Host", @@ -1118,9 +1123,9 @@ "loading": "Cargando", "loading_search_results_failed": "Error al cargar los resultados de la bÃēsqueda", "local_network": "Local network", - "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", - "location_permission": "Location permission", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", + "local_network_sheet_info": "La aplicaciÃŗn se conectarÃĄ al servidor a travÊs de esta URL cuando utilice la red Wi-Fi especificada", + "location_permission": "Permiso de ubicaciÃŗn", + "location_permission_content": "Para usar la funciÃŗn de cambio automÃĄtico, Immich necesita permiso de ubicaciÃŗn precisa para poder leer el nombre de la red Wi-Fi actual", "location_picker_choose_on_map": "Elegir en el mapa", "location_picker_latitude_error": "Introduce una latitud vÃĄlida", "location_picker_latitude_hint": "Introduce tu latitud aquÃ", @@ -1145,7 +1150,7 @@ "login_form_failed_get_oauth_server_config": "Error al iniciar sesiÃŗn con OAuth, verifica la URL del servidor", "login_form_failed_get_oauth_server_disable": "La funciÃŗn de OAuth no estÃĄ disponible en este servidor", "login_form_failed_login": "Error al iniciar sesiÃŗn, comprueba la URL del servidor, el correo electrÃŗnico y la contraseÃąa", - "login_form_handshake_exception": "Hubo un error de verificaciÃŗn del certificado del servidor. Activa el soporte para certificados autofirmados en las preferencias si estÃĄs usando un certificado autofirmado", + "login_form_handshake_exception": "Hubo una excepciÃŗn de handshake con el servidor. Activa la compatibilidad con certificados autofirmados en la configuraciÃŗn si estÃĄs utilizando un certificado autofirmado.", "login_form_password_hint": "contraseÃąa", "login_form_save_login": "Mantener la sesiÃŗn iniciada", "login_form_server_empty": "Agrega la URL del servidor.", @@ -1222,7 +1227,7 @@ "more": "Mas", "moved_to_trash": "Movido a la papelera", "multiselect_grid_edit_date_time_err_read_only": "No se puede cambiar la fecha del archivo(s) de solo lectura, omitiendo", - "multiselect_grid_edit_gps_err_read_only": "No se puede cambiar la localizaciÃŗn de archivos de solo lectura. Saltando.", + "multiselect_grid_edit_gps_err_read_only": "No se puede editar la ubicaciÃŗn de activos de solo lectura, omitiendo", "mute_memories": "Silenciar Recuerdos", "my_albums": "Mis albums", "name": "Nombre", @@ -1257,8 +1262,8 @@ "no_results_description": "Pruebe con un sinÃŗnimo o una palabra clave mÃĄs general", "no_shared_albums_message": "Crea un ÃĄlbum para compartir fotos y vÃdeos con personas de tu red", "not_in_any_album": "Sin ÃĄlbum", - "not_selected": "Not selected", - "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar la etiqueta de almacenamiento a los archivos cargados previamente, ejecute el", + "not_selected": "No seleccionado", + "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar la etiqueta de almacenamiento a los archivos subidos previamente, ejecute el", "notes": "Notas", "notification_permission_dialog_content": "Para activar las notificaciones, ve a ConfiguraciÃŗn y selecciona permitir.", "notification_permission_list_tile_content": "Concede permiso para habilitar las notificaciones.", @@ -1282,6 +1287,7 @@ "onboarding_welcome_user": "Bienvenido, {user}", "online": "En lÃnea", "only_favorites": "Solo favoritos", + "open": "Abierto", "open_in_map_view": "Abrir en la vista del mapa", "open_in_openstreetmap": "Abrir en OpenStreetMap", "open_the_search_filters": "Abre los filtros de bÃēsqueda", @@ -1302,10 +1308,10 @@ "partner_list_view_all": "Ver todas", "partner_page_empty_message": "Tus fotos aÃēn no se han compartido con ningÃēn compaÃąero.", "partner_page_no_more_users": "No hay mÃĄs usuarios para agregar", - "partner_page_partner_add_failed": "CompaÃąero no pudo ser agregado ", + "partner_page_partner_add_failed": "No se pudo aÃąadir el socio", "partner_page_select_partner": "Seleccionar compaÃąero", "partner_page_shared_to_title": "Compartido con", - "partner_page_stop_sharing_content": "{} ya no podrÃĄ acceder a tus fotos", + "partner_page_stop_sharing_content": "{} ya no podrÃĄ acceder a tus fotos.", "partner_sharing": "Compartir con invitados", "partners": "Invitados", "password": "ContraseÃąa", @@ -1426,6 +1432,8 @@ "recent_searches": "BÃēsquedas recientes", "recently_added": "AÃąadidos recientemente", "recently_added_page_title": "ReciÊn Agregadas", + "recently_taken": "Recientemente tomado", + "recently_taken_page_title": "Recientemente Tomado", "refresh": "Actualizar", "refresh_encoded_videos": "Recargar los vÃdeos codificados", "refresh_faces": "Actualizar caras", @@ -1509,8 +1517,8 @@ "search_filter_date_interval": "{start} al {end}", "search_filter_date_title": "Selecciona un intervalo de fechas", "search_filter_display_option_not_in_album": "No en ÃĄlbum", - "search_filter_display_options": "Display Options", - "search_filter_filename": "Search by file name", + "search_filter_display_options": "Opciones de visualizaciÃŗn", + "search_filter_filename": "Buscar por nombre de archivo", "search_filter_location": "UbicaciÃŗn", "search_filter_location_title": "Seleccionar una ubicaciÃŗn", "search_filter_media_type": "Tipo de archivo", @@ -1518,10 +1526,10 @@ "search_filter_people_title": "Seleccionar personas", "search_for": "Buscar", "search_for_existing_person": "Buscar persona existente", - "search_no_more_result": "No more results", + "search_no_more_result": "No hay mÃĄs resultados", "search_no_people": "Ninguna persona", "search_no_people_named": "Ninguna persona llamada \"{name}\"", - "search_no_result": "No results found, try a different search term or combination", + "search_no_result": "No se encontraron resultados, prueba con un tÊrmino o combinaciÃŗn de bÃēsqueda diferente", "search_options": "Opciones de bÃēsqueda", "search_page_categories": "CategorÃas", "search_page_motion_photos": "Foto en Movimiento", @@ -1567,7 +1575,7 @@ "selected_count": "{count, plural, one {# seleccionado} other {# seleccionados}}", "send_message": "Enviar mensaje", "send_welcome_email": "Enviar correo de bienvenida", - "server_endpoint": "Server Endpoint", + "server_endpoint": "Punto final del servidor", "server_info_box_app_version": "VersiÃŗn de la AplicaciÃŗn", "server_info_box_server_url": "URL del servidor", "server_offline": "Servidor desconectado", @@ -1602,14 +1610,14 @@ "setting_notifications_total_progress_subtitle": "Progreso general de subida (elementos completados/total)", "setting_notifications_total_progress_title": "Mostrar progreso total de copia de seguridad en segundo plano", "setting_video_viewer_looping_title": "Bucle", - "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", + "setting_video_viewer_original_video_subtitle": "Al reproducir un video en streaming desde el servidor, reproducir el original incluso cuando haya una transcodificaciÃŗn disponible. Puede causar buffering. Los videos disponibles localmente se reproducen en calidad original independientemente de esta configuraciÃŗn.", + "setting_video_viewer_original_video_title": "Forzar vÃdeo original", "settings": "Ajustes", "settings_require_restart": "Por favor, reinicia Immich para aplicar este ajuste", "settings_saved": "Ajustes guardados", "share": "Compartir", "share_add_photos": "Agregar fotos", - "share_assets_selected": "{} seleccionados", + "share_assets_selected": "{} seleccionado(s)", "share_dialog_preparing": "Preparando...", "shared": "Compartido", "shared_album_activities_input_disable": "Los comentarios estÃĄn deshabilitados", @@ -1623,7 +1631,7 @@ "shared_by_user": "Compartido por {user}", "shared_by_you": "Compartido por ti", "shared_from_partner": "Fotos de {partner}", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", + "shared_intent_upload_button_progress_text": "{} / {} Cargado(s)", "shared_link_app_bar_title": "Enlaces compartidos", "shared_link_clipboard_copied_massage": "Copiado al portapapeles", "shared_link_clipboard_text": "Enlace: {}\nContraseÃąa: {}", @@ -1734,7 +1742,7 @@ "sync": "Sincronizar", "sync_albums": "Sincronizar ÃĄlbumes", "sync_albums_manual_subtitle": "Sincroniza todos los videos y fotos subidos con los ÃĄlbumes seleccionados a respaldar", - "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", + "sync_upload_album_setting_subtitle": "Crea y sube tus fotos y videos a los ÃĄlbumes seleccionados en Immich", "tag": "Etiqueta", "tag_assets": "Etiquetar activos", "tag_created": "Etiqueta creada: {tag}", @@ -1750,12 +1758,12 @@ "theme_selection_description": "Establece el tema automÃĄticamente como \"claro\" u \"oscuro\" segÃēn las preferencias del sistema/navegador", "theme_setting_asset_list_storage_indicator_title": "Mostrar indicador de almacenamiento en las miniaturas de los archivos", "theme_setting_asset_list_tiles_per_row_title": "NÃēmero de elementos por fila ({})", - "theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.", - "theme_setting_colorful_interface_title": "Colorful interface", + "theme_setting_colorful_interface_subtitle": "Aplicar el color primario a las superficies de fondo.", + "theme_setting_colorful_interface_title": "Color de Interfaz", "theme_setting_image_viewer_quality_subtitle": "Ajustar la calidad del visor de detalles de imÃĄgenes", "theme_setting_image_viewer_quality_title": "Calidad del visor de imÃĄgenes", - "theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.", - "theme_setting_primary_color_title": "Primary color", + "theme_setting_primary_color_subtitle": "Elige un color para las acciones principales y los acentos.", + "theme_setting_primary_color_title": "Color primario", "theme_setting_system_primary_color_title": "Usar color del sistema", "theme_setting_system_theme_switch": "AutomÃĄtico (seguir ajuste del sistema)", "theme_setting_theme_subtitle": "Elige la configuraciÃŗn del tema de la aplicaciÃŗn", @@ -1784,7 +1792,7 @@ "trash_no_results_message": "Las fotos y videos que se envÃen a la papelera aparecerÃĄn aquÃ.", "trash_page_delete_all": "Eliminar todos", "trash_page_empty_trash_dialog_content": "ÂŋEstÃĄ seguro que quiere eliminar los elementos? Estos elementos serÃĄn eliminados de Immich permanentemente", - "trash_page_info": "Los archivos en la papelera serÃĄn eliminados automÃĄticamente despuÊs de {} dÃas", + "trash_page_info": "Los archivos en la papelera serÃĄn eliminados automÃĄticamente de forma permanente despuÊs de {} dÃas", "trash_page_no_assets": "No hay elementos en la papelera", "trash_page_restore_all": "Restaurar todos", "trash_page_select_assets_btn": "Seleccionar elementos", @@ -1812,25 +1820,25 @@ "unstack": "Desapilar", "unstacked_assets_count": "Desapilado(s) {count, plural, one {# elemento} other {# elementos}}", "untracked_files": "Archivos no monitorizados", - "untracked_files_decription": "Estos archivos no estÃĄn siendo monitorizados por la aplicaciÃŗn. Es posible que sean resultado de errores al moverlos, cargas interrumpidas o por un fallo de la aplicaciÃŗn", + "untracked_files_decription": "Estos archivos no estÃĄn siendo monitorizados por la aplicaciÃŗn. Es posible que sean resultado de errores al moverlos, subidas interrumpidas o por un fallo de la aplicaciÃŗn", "up_next": "A continuaciÃŗn", "updated_password": "ContraseÃąa actualizada", "upload": "Subir", - "upload_concurrency": "Cargas simultÃĄneas", + "upload_concurrency": "Subidas simultÃĄneas", "upload_dialog_info": "ÂŋQuieres hacer una copia de seguridad al servidor de los elementos seleccionados?", "upload_dialog_title": "Subir elementos", - "upload_errors": "Carga completada con {count, plural, one {# error} other {# errores}}, actualice la pÃĄgina para ver los nuevos recursos de carga.", + "upload_errors": "Subida completada con {count, plural, one {# error} other {# errores}}, actualice la pÃĄgina para ver los nuevos recursos de la subida.", "upload_progress": "Restante {remaining, number} - Procesado {processed, number}/{total, number}", "upload_skipped_duplicates": "Saltado {count, plural, one {# duplicate asset} other {# duplicate assets}}", "upload_status_duplicates": "Duplicados", "upload_status_errors": "Errores", "upload_status_uploaded": "Subido", - "upload_success": "Carga realizada correctamente, actualice la pÃĄgina para ver los nuevos recursos de carga.", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", + "upload_success": "Subida realizada correctamente, actualice la pÃĄgina para ver los nuevos recursos de subida.", + "upload_to_immich": "Subir a Immich ({})", + "uploading": "Subiendo", "url": "URL", "usage": "Uso", - "use_current_connection": "use current connection", + "use_current_connection": "Usar conexiÃŗn actual", "use_custom_date_range": "Usa un intervalo de fechas personalizado", "user": "Usuario", "user_id": "ID de usuario", @@ -1845,7 +1853,7 @@ "users": "Usuarios", "utilities": "Utilidades", "validate": "Validar", - "validate_endpoint_error": "Please enter a valid URL", + "validate_endpoint_error": "Por favor, introduce una URL vÃĄlida", "variables": "Variables", "version": "VersiÃŗn", "version_announcement_closing": "Tu amigo, Alex", @@ -1888,6 +1896,6 @@ "years_ago": "Hace {years, plural, one {# aÃąo} other {# aÃąos}}", "yes": "SÃ", "you_dont_have_any_shared_links": "No tienes ningÃēn enlace compartido", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "El nombre de tu WiFi", "zoom_image": "Acercar Imagen" } diff --git a/i18n/et.json b/i18n/et.json index 673d2ea63a..98b829bc97 100644 --- a/i18n/et.json +++ b/i18n/et.json @@ -4,6 +4,7 @@ "account_settings": "Konto seaded", "acknowledge": "Sain aru", "action": "Tegevus", + "action_common_update": "Uuenda", "actions": "Tegevused", "active": "Aktiivne", "activity": "Aktiivsus", @@ -13,6 +14,7 @@ "add_a_location": "Lisa asukoht", "add_a_name": "Lisa nimi", "add_a_title": "Lisa pealkiri", + "add_endpoint": "Lisa lÃĩpp-punkt", "add_exclusion_pattern": "Lisa välistamismuster", "add_import_path": "Lisa imporditee", "add_location": "Lisa asukoht", @@ -22,6 +24,8 @@ "add_photos": "Lisa fotosid", "add_to": "Lisa kohtaâĻ", "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_shared_album": "Lisa jagatud albumisse", "add_url": "Lisa URL", "added_to_archive": "Lisatud arhiivi", @@ -35,11 +39,11 @@ "authentication_settings_disable_all": "Kas oled kindel, et soovid kÃĩik sisselogimismeetodid välja lÃŧlitada? Sisselogimine lÃŧlitatakse täielikult välja.", "authentication_settings_reenable": "Et taas lubada, kasuta <link>serveri käsku</link>.", "background_task_job": "Tausttegumid", - "backup_database": "Varunda andmebaas", - "backup_database_enable_description": "Luba andmebaasi varundamine", - "backup_keep_last_amount": "Varukoopiate arv, mida alles hoida", - "backup_settings": "Varundamise seaded", - "backup_settings_description": "Halda andmebaasi varundamise seadeid", + "backup_database": "Loo andmebaasi tÃĩmmis", + "backup_database_enable_description": "Luba andmebaasi tÃĩmmised", + "backup_keep_last_amount": "Eelmiste tÃĩmmiste arv, mida alles hoida", + "backup_settings": "Andmebaasi tÃĩmmiste seaded", + "backup_settings_description": "Halda andmebaasi tÃĩmmiste seadeid. Märkus: Neid tÃļÃļteid ei jälgita ning ebaÃĩnnestumisest ei hoiatata.", "check_all": "Märgi kÃĩik", "cleanup": "Koristus", "cleared_jobs": "TÃļÃļted eemaldatud: {job}", @@ -367,6 +371,10 @@ "admin_password": "Administraatori parool", "administration": "Administratsioon", "advanced": "Täpsemad valikud", + "advanced_settings_log_level_title": "Logimistase: {}", + "advanced_settings_proxy_headers_title": "Vaheserveri päised", + "advanced_settings_self_signed_ssl_title": "Luba endasigneeritud SSL-sertifikaadid", + "advanced_settings_troubleshooting_title": "TÃĩrkeotsing", "age_months": "Vanus {months, plural, one {# kuu} other {# kuud}}", "age_year_months": "Vanus 1 aasta, {months, plural, one {# kuu} other {# kuud}}", "age_years": "{years, plural, other {Vanus #}}", @@ -375,6 +383,8 @@ "album_cover_updated": "Albumi kaanepilt muudetud", "album_delete_confirmation": "Kas oled kindel, et soovid albumi {album} kustutada?", "album_delete_confirmation_description": "Kui see album on jagatud, ei pääse teised kasutajad sellele enam ligi.", + "album_info_card_backup_album_excluded": "VÃLJA JÃETUD", + "album_info_card_backup_album_included": "LISATUD", "album_info_updated": "Albumi info muudetud", "album_leave": "Lahku albumist?", "album_leave_confirmation": "Kas oled kindel, et soovid albumist {album} lahkuda?", @@ -383,10 +393,21 @@ "album_remove_user": "Eemalda kasutaja?", "album_remove_user_confirmation": "Kas oled kindel, et soovid kasutaja {user} eemaldada?", "album_share_no_users": "Paistab, et oled seda albumit kÃĩikide kasutajatega jaganud, vÃĩi pole Ãŧhtegi kasutajat, kellega jagada.", + "album_thumbnail_card_item": "1 Ãŧksus", + "album_thumbnail_card_items": "{} Ãŧksust", + "album_thumbnail_card_shared": " ¡ Jagatud", + "album_thumbnail_shared_by": "Jagas {}", "album_updated": "Album muudetud", "album_updated_setting_description": "Saa teavitus e-posti teel, kui jagatud albumis on uusi Ãŧksuseid", "album_user_left": "Lahkutud albumist {album}", "album_user_removed": "Kasutaja {user} eemaldatud", + "album_viewer_appbar_delete_confirm": "Kas oled kindel, et soovid selle albumi oma kontolt kustutada?", + "album_viewer_appbar_share_err_delete": "Albumi kustutamine ebaÃĩnnestus", + "album_viewer_appbar_share_err_leave": "Albumist lahkumine ebaÃĩnnestus", + "album_viewer_appbar_share_err_remove": "Ãksuste albumist eemaldamisel tekkis probleeme", + "album_viewer_appbar_share_err_title": "Albumi pealkirja muutmine ebaÃĩnnestus", + "album_viewer_appbar_share_leave": "Lahku albumist", + "album_viewer_page_share_add_users": "Lisa kasutajaid", "album_with_link_access": "Luba kÃĩigil, kellel on link, näha selle albumi fotosid ja isikuid.", "albums": "Albumid", "albums_count": "{count, plural, one {{count, number} album} other {{count, number} albumit}}", @@ -404,23 +425,36 @@ "api_key_description": "Seda väärtust kuvatakse ainult Ãŧks kord. Kopeeri see enne akna sulgemist.", "api_key_empty": "Su API vÃĩtme nimi ei tohiks olla tÃŧhi", "api_keys": "API vÃĩtmed", + "app_bar_signout_dialog_content": "Kas oled kindel, et soovid välja logida?", + "app_bar_signout_dialog_ok": "Jah", + "app_bar_signout_dialog_title": "Logi välja", "app_settings": "Rakenduse seaded", "appears_in": "Albumid", "archive": "Arhiiv", "archive_or_unarchive_photo": "Arhiveeri vÃĩi taasta foto", + "archive_page_no_archived_assets": "Arhiveeritud Ãŧksuseid ei leitud", "archive_size": "Arhiivi suurus", "archive_size_description": "Seadista arhiivi suurus allalaadimiseks (GiB)", + "archived": "Arhiveeritud", "archived_count": "{count, plural, other {# arhiveeritud}}", "are_these_the_same_person": "Kas need on sama isik?", "are_you_sure_to_do_this": "Kas oled kindel, et soovid seda teha?", + "asset_action_delete_err_read_only": "Kirjutuskaitstud Ãŧksuseid ei saa kustutada, jätan vahele", "asset_added_to_album": "Lisatud albumisse", "asset_adding_to_album": "Albumisse lisamineâĻ", "asset_description_updated": "Ãksuse kirjeldus on muudetud", "asset_filename_is_offline": "Ãksus {filename} ei ole kättesaadav", "asset_has_unassigned_faces": "Ãksusel on seostamata nägusid", "asset_hashing": "RäsimineâĻ", + "asset_list_group_by_sub_title": "Grupeeri", + "asset_list_layout_settings_dynamic_layout_title": "DÃŧnaamiline asetus", + "asset_list_layout_settings_group_automatically": "Automaatne", + "asset_list_layout_settings_group_by": "Grupeeri Ãŧksused", + "asset_list_layout_settings_group_by_month_day": "Kuu + päev", + "asset_list_layout_sub_title": "Asetus", "asset_offline": "Ãksus pole kättesaadav", "asset_offline_description": "Seda välise kogu Ãŧksust ei leitud kettalt. Abi saamiseks palun vÃĩta Ãŧhendust oma Immich'i administraatoriga.", + "asset_restored_successfully": "Ãksus edukalt taastatud", "asset_skipped": "Vahele jäetud", "asset_skipped_in_trash": "PrÃŧgikastis", "asset_uploaded": "Ãleslaaditud", @@ -438,8 +472,26 @@ "assets_trashed_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} liigutatud prÃŧgikasti", "assets_were_part_of_album_count": "{count, plural, one {Ãksus oli} other {Ãksused olid}} juba osa albumist", "authorized_devices": "Autoriseeritud seadmed", + "automatic_endpoint_switching_subtitle": "Ãhendu lokaalselt Ãŧle valitud WiFi-vÃĩrgu, kui see on saadaval, ja kasuta mujal alternatiivseid Ãŧhendusi", "back": "Tagasi", "back_close_deselect": "Tagasi, sulge vÃĩi tÃŧhista valik", + "backup_album_selection_page_select_albums": "Vali albumid", + "backup_album_selection_page_selection_info": "Valiku info", + "backup_album_selection_page_total_assets": "Unikaalseid Ãŧksuseid kokku", + "backup_all": "KÃĩik", + "backup_background_service_default_notification": "Uute Ãŧksuste kontrollimineâĻ", + "backup_background_service_error_title": "Varundamise viga", + "backup_controller_page_background_battery_info_ok": "OK", + "backup_controller_page_background_wifi": "Ainult WiFi-vÃĩrgus", + "backup_controller_page_backup_sub": "Varundatud fotod ja videod", + "backup_controller_page_desc_backup": "LÃŧlita sisse esiplaanil varundamine, et rakenduse avamisel uued Ãŧksused automaatselt serverisse Ãŧles laadida.", + "backup_controller_page_to_backup": "Albumid, mida varundada", + "backup_controller_page_total_sub": "KÃĩik unikaalsed fotod ja videod valitud albumitest", + "backup_err_only_album": "Ei saa ainsat albumit eemaldada", + "backup_info_card_assets": "Ãŧksused", + "backup_manual_cancelled": "TÃŧhistatud", + "backup_manual_title": "Ãleslaadimise staatus", + "backup_setting_subtitle": "Halda taustal ja esiplaanil Ãŧleslaadimise seadeid", "backward": "Tagasi", "birthdate_saved": "SÃŧnnikuupäev salvestatud", "birthdate_set_description": "SÃŧnnikuupäeva kasutatakse isiku vanuse arvutamiseks foto tegemise hetkel.", @@ -451,11 +503,14 @@ "bulk_keep_duplicates_confirmation": "Kas oled kindel, et soovid {count, plural, one {# dubleeritud Ãŧksuse} other {# dubleeritud Ãŧksust}} alles jätta? Sellega märgitakse kÃĩik duplikaadigrupid lahendatuks ilma midagi kustutamata.", "bulk_trash_duplicates_confirmation": "Kas oled kindel, et soovid {count, plural, one {# dubleeritud Ãŧksuse} other {# dubleeritud Ãŧksust}} masskustutada? Sellega jäetakse alles iga grupi suurim Ãŧksus ning duplikaadid liigutatakse prÃŧgikasti.", "buy": "Osta Immich", + "cache_settings_clear_cache_button": "TÃŧhjenda puhver", + "cache_settings_statistics_title": "Puhvri kasutus", "camera": "Kaamera", "camera_brand": "Kaamera mark", "camera_model": "Kaamera mudel", "cancel": "Katkesta", "cancel_search": "Katkesta otsing", + "canceled": "TÃŧhistatud", "cannot_merge_people": "Ei saa isikuid Ãŧhendada", "cannot_undo_this_action": "Sa ei saa seda tagasi vÃĩtta!", "cannot_update_the_description": "Kirjelduse muutmine ebaÃĩnnestus", @@ -466,6 +521,10 @@ "change_name_successfully": "Nimi edukalt muudetud", "change_password": "Parooli muutmine", "change_password_description": "See on su esimene kord sÃŧsteemi siseneda, vÃĩi on tehtud taotlus parooli muutmiseks. Palun sisesta allpool uus parool.", + "change_password_form_confirm_password": "Kinnita parool", + "change_password_form_new_password": "Uus parool", + "change_password_form_password_mismatch": "Paroolid ei klapi", + "change_password_form_reenter_new_password": "Korda uut parooli", "change_your_password": "Muuda oma parooli", "changed_visibility_successfully": "Nähtavus muudetud", "check_all": "Märgi kÃĩik", @@ -477,6 +536,14 @@ "clear_all_recent_searches": "TÃŧhjenda hiljutised otsingud", "clear_message": "TÃŧhjenda sÃĩnum", "clear_value": "TÃŧhjenda väärtus", + "client_cert_dialog_msg_confirm": "OK", + "client_cert_enter_password": "Sisesta parool", + "client_cert_import": "Impordi", + "client_cert_import_success_msg": "Klientsertifikaat on imporditud", + "client_cert_invalid_msg": "Vigane sertifikaadi fail vÃĩi vale parool", + "client_cert_remove_msg": "Klientsertifikaat on eemaldatud", + "client_cert_subtitle": "Toetab ainult PKCS12 (.p12, .pfx) formaati. Sertifikaadi importimine/eemaldamine on saadaval ainult enne sisselogimist", + "client_cert_title": "SSL klientsertifikaat", "clockwise": "Päripäeva", "close": "Sulge", "collapse": "Peida", @@ -487,6 +554,8 @@ "comment_options": "Kommentaari valikud", "comments_and_likes": "Kommentaarid ja meeldimised", "comments_are_disabled": "Kommentaarid on keelatud", + "common_create_new_album": "Lisa uus album", + "completed": "LÃĩpetatud", "confirm": "Kinnita", "confirm_admin_password": "Kinnita administraatori parool", "confirm_delete_face": "Kas oled kindel, et soovid isiku {name} näo Ãŧksuselt kustutada?", @@ -496,6 +565,10 @@ "contain": "Mahuta ära", "context": "Kontekst", "continue": "Jätka", + "control_bottom_app_bar_create_new_album": "Lisa uus album", + "control_bottom_app_bar_delete_from_local": "Kustuta seadmest", + "control_bottom_app_bar_edit_location": "Muuda asukohta", + "control_bottom_app_bar_edit_time": "Muuda kuupäeva ja aega", "copied_image_to_clipboard": "Pilt kopeeritud lÃĩikelauale.", "copied_to_clipboard": "Kopeeritud lÃĩikelauale!", "copy_error": "Kopeeri viga", @@ -517,6 +590,7 @@ "create_new_person": "Lisa uus isik", "create_new_person_hint": "Seosta valitud Ãŧksused uue isikuga", "create_new_user": "Lisa uus kasutaja", + "create_shared_album_page_share_select_photos": "Vali fotod", "create_tag": "Lisa silt", "create_tag_description": "Lisa uus silt. Pesastatud siltide jaoks sisesta täielik tee koos kaldkriipsudega.", "create_user": "Lisa kasutaja", @@ -541,19 +615,23 @@ "delete": "Kustuta", "delete_album": "Kustuta album", "delete_api_key_prompt": "Kas oled kindel, et soovid selle API vÃĩtme kustutada?", + "delete_dialog_title": "Kustuta jäädavalt", "delete_duplicates_confirmation": "Kas oled kindel, et soovid need duplikaadid jäädavalt kustutada?", "delete_face": "Kustuta nägu", "delete_key": "Kustuta vÃĩti", "delete_library": "Kustuta kogu", "delete_link": "Kustuta link", + "delete_local_dialog_ok_backed_up_only": "Kustuta ainult varundatud", "delete_others": "Kustuta teised", "delete_shared_link": "Kustuta jagatud link", + "delete_shared_link_dialog_title": "Kustuta jagatud link", "delete_tag": "Kustuta silt", "delete_tag_confirmation_prompt": "Kas oled kindel, et soovid sildi {tagName} kustutada?", "delete_user": "Kustuta kasutaja", "deleted_shared_link": "Jagatud link kustutatud", "deletes_missing_assets": "Kustutab Ãŧksused, mis on kettalt puudu", "description": "Kirjeldus", + "description_input_hint_text": "Lisa kirjeldus...", "details": "Ãksikasjad", "direction": "Suund", "disabled": "Välja lÃŧlitatud", @@ -570,10 +648,20 @@ "documentation": "Dokumentatsioon", "done": "Tehtud", "download": "Laadi alla", + "download_canceled": "Allalaadimine katkestatud", + "download_complete": "Allalaadimine lÃĩpetatud", + "download_enqueue": "Allalaadimine ootel", + "download_error": "Allalaadimise viga", + "download_failed": "Allalaadimine ebaÃĩnnestus", + "download_finished": "Allalaadimine lÃĩpetatud", "download_include_embedded_motion_videos": "Manustatud videod", "download_include_embedded_motion_videos_description": "Lisa liikuvatesse fotodesse manustatud videod eraldi failidena", + "download_paused": "Allalaadimine peatatud", "download_settings": "Allalaadimine", "download_settings_description": "Halda Ãŧksuste allalaadimise seadeid", + "download_started": "Allalaadimine alustatud", + "download_sucess": "Allalaadimine Ãĩnnestus", + "download_sucess_android": "Meediumid laaditi alla kataloogi DCIM/Immich", "downloading": "Allalaadimine", "downloading_asset_filename": "Ãksuse {filename} allalaadimine", "drop_files_to_upload": "Failide Ãŧleslaadimiseks sikuta need ÃŧkskÃĩik kuhu", @@ -592,6 +680,7 @@ "edit_key": "Muuda vÃĩtit", "edit_link": "Muuda linki", "edit_location": "Muuda asukohta", + "edit_location_dialog_title": "Asukoht", "edit_name": "Muuda nime", "edit_people": "Muuda isikuid", "edit_tag": "Muuda silti", @@ -604,12 +693,15 @@ "editor_crop_tool_h2_aspect_ratios": "Kuvasuhted", "editor_crop_tool_h2_rotation": "PÃļÃļre", "email": "E-post", + "empty_folder": "See kaust on tÃŧhi", "empty_trash": "TÃŧhjenda prÃŧgikast", "empty_trash_confirmation": "Kas oled kindel, et soovid prÃŧgikasti tÃŧhjendada? See eemaldab kÃĩik seal olevad Ãŧksused Immich'ist jäädavalt.\nSeda tegevust ei saa tagasi vÃĩtta!", "enable": "Luba", "enabled": "Lubatud", "end_date": "LÃĩppkuupäev", + "enter_wifi_name": "Sisesta WiFi-vÃĩrgu nimi", "error": "Viga", + "error_change_sort_album": "Albumi sorteerimisjärjestuse muutmine ebaÃĩnnestus", "error_delete_face": "Viga näo kustutamisel", "error_loading_image": "Viga pildi laadimisel", "error_title": "Viga - midagi läks valesti", @@ -740,8 +832,14 @@ "unable_to_upload_file": "Faili Ãŧleslaadimine ebaÃĩnnestus" }, "exif": "Exif", + "exif_bottom_sheet_description": "Lisa kirjeldus...", + "exif_bottom_sheet_details": "ÃKSIKASJAD", + "exif_bottom_sheet_location": "ASUKOHT", + "exif_bottom_sheet_people": "ISIKUD", + "exif_bottom_sheet_person_add_person": "Lisa nimi", "exit_slideshow": "Sulge slaidiesitlus", "expand_all": "Näita kÃĩik", + "experimental_settings_title": "Eksperimentaalne", "expire_after": "Aegub", "expired": "Aegunud", "expires_date": "Aegub {date}", @@ -752,6 +850,7 @@ "extension": "Laiend", "external": "Väline", "external_libraries": "Välised kogud", + "external_network_sheet_info": "Kui seade ei ole eelistatud WiFi-vÃĩrgus, Ãŧhendub rakendus serveriga allolevatest URL-idest esimese kättesaadava kaudu, alustades Ãŧlevalt", "face_unassigned": "Seostamata", "failed_to_load_assets": "Ãksuste laadimine ebaÃĩnnestus", "favorite": "Lemmik", @@ -767,6 +866,8 @@ "filter_people": "Filtreeri isikuid", "find_them_fast": "Leia teda kiiresti nime järgi otsides", "fix_incorrect_match": "Paranda ebaÃĩige vaste", + "folder": "Kaust", + "folder_not_found": "Kausta ei leitud", "folders": "Kaustad", "folders_feature_description": "Kaustavaate abil failisÃŧsteemis olevate fotode ja videote sirvimine", "forward": "Edasi", @@ -783,6 +884,10 @@ "group_places_by": "Grupeeri kohad...", "group_year": "Grupeeri aasta kaupa", "has_quota": "On kvoot", + "header_settings_add_header_tip": "Lisa päis", + "header_settings_field_validator_msg": "Väärtus ei saa olla tÃŧhi", + "header_settings_header_name_input": "Päise nimi", + "header_settings_header_value_input": "Päise väärtus", "hi_user": "Tere {name} ({email})", "hide_all_people": "Peida kÃĩik isikud", "hide_gallery": "Peida galerii", @@ -790,8 +895,20 @@ "hide_password": "Peida parool", "hide_person": "Peida isik", "hide_unnamed_people": "Peida nimetud isikud", + "home_page_add_to_album_conflicts": "{added} Ãŧksust lisati albumisse {album}. {failed} Ãŧksust oli juba albumis.", + "home_page_add_to_album_err_local": "Lokaalseid Ãŧksuseid ei saa veel albumisse lisada, jätan vahele", + "home_page_add_to_album_success": "{added} Ãŧksust lisati albumisse {album}.", + "home_page_album_err_partner": "Partneri Ãŧksuseid ei saa veel albumisse lisada, jätan vahele", + "home_page_archive_err_local": "Lokaalseid Ãŧksuseid ei saa veel arhiveerida, jätan vahele", + "home_page_archive_err_partner": "Partneri Ãŧksuseid ei saa arhiveerida, jätan vahele", + "home_page_building_timeline": "Ajajoone koostamine", + "home_page_delete_err_partner": "Partneri Ãŧksuseid ei saa kustutada, jätan vahele", + "home_page_favorite_err_local": "Lokaalseid Ãŧksuseid ei saa lemmikuks märkida, jätan vahele", + "home_page_favorite_err_partner": "Partneri Ãŧksuseid ei saa lemmikuks märkida, jätan vahele", + "home_page_share_err_local": "Lokaalseid Ãŧksuseid ei saa lingiga jagada, jätan vahele", "host": "Host", "hour": "Tund", + "ignore_icloud_photos": "Ignoreeri iCloud fotosid", "image": "Pilt", "image_alt_text_date": "{isVideo, select, true {Video} other {Pilt}} tehtud {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Pilt}} tehtud {date} koos isikuga {person1}", @@ -803,6 +920,8 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Pilt}} tehtud {date} kohas {city}, {country} koos isikutega {person1} ja {person2}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Pilt}} tehtud {date} kohas {city}, {country} koos isikutega {person1}, {person2} ja {person3}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Pilt}} tehtud {date} kohas {city}, {country} koos {person1}, {person2} ja veel {additionalCount, number} isikuga", + "image_viewer_page_state_provider_download_started": "Allalaadimine alustatud", + "image_viewer_page_state_provider_download_success": "Allalaadimine Ãĩnnestus", "immich_logo": "Immich'i logo", "immich_web_interface": "Immich'i veebiliides", "import_from_json": "Impordi JSON-formaadist", @@ -821,6 +940,8 @@ "night_at_midnight": "Iga päev keskÃļÃļl", "night_at_twoam": "Iga ÃļÃļ kell 2" }, + "invalid_date": "Vigane kuupäev", + "invalid_date_format": "Vigane kuupäevaformaat", "invite_people": "Kutsu inimesi", "invite_to_album": "Kutsu albumisse", "items_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}}", @@ -841,6 +962,9 @@ "level": "Tase", "library": "Kogu", "library_options": "Kogu seaded", + "library_page_new_album": "Uus album", + "library_page_sort_asset_count": "Ãksuste arv", + "library_page_sort_title": "Albumi pealkiri", "light": "Hele", "like_deleted": "Meeldimine kustutatud", "link_motion_video": "Lingi liikuv video", @@ -850,12 +974,24 @@ "list": "Loend", "loading": "Laadimine", "loading_search_results_failed": "Otsitulemuste laadimine ebaÃĩnnestus", + "local_network_sheet_info": "Rakendus Ãŧhendub valitud Wi-Fi vÃĩrgus olles serveriga selle URL-i kaudu", + "location_permission_content": "Automaatseks ÃŧmberlÃŧlitumiseks vajab Immich täpse asukoha luba, et saaks lugeda aktiivse WiFi-vÃĩrgu nime", + "location_picker_choose_on_map": "Vali kaardil", "log_out": "Logi välja", "log_out_all_devices": "Logi kÃĩigist seadmetest välja", "logged_out_all_devices": "KÃĩigist seadmetest välja logitud", "logged_out_device": "Seadmest välja logitud", "login": "Logi sisse", + "login_form_back_button_text": "Tagasi", + "login_form_email_hint": "sinunimi@email.com", + "login_form_endpoint_hint": "http://serveri-ip:port", + "login_form_endpoint_url": "Serveri lÃĩpp-punkti URL", + "login_form_err_http": "Palun täpsusta http:// vÃĩi https://", + "login_form_err_invalid_email": "Vigane e-posti aadress", + "login_form_err_invalid_url": "Vigane URL", + "login_form_password_hint": "parool", "login_has_been_disabled": "Sisselogimine on keelatud.", + "login_password_changed_success": "Parool edukalt uuendatud", "logout_all_device_confirmation": "Kas oled kindel, et soovid kÃĩigist seadmetest välja logida?", "logout_this_device_confirmation": "Kas oled kindel, et soovid sellest seadmest välja logida?", "longitude": "Pikkuskraad", @@ -876,10 +1012,14 @@ "map_marker_for_images": "Kaardimarker kohas {city}, {country} tehtud piltide jaoks", "map_marker_with_image": "Kaardimarker pildiga", "map_settings": "Kaardi seaded", + "map_settings_date_range_option_day": "Viimased 24 tundi", + "map_settings_date_range_option_year": "Viimane aasta", + "map_settings_dialog_title": "Kaardi seaded", "matches": "Ãhtivad failid", - "media_type": "Meedia tÃŧÃŧp", + "media_type": "Meediumi tÃŧÃŧp", "memories": "Mälestused", "memories_setting_description": "Halda, mida sa oma mälestustes näed", + "memories_year_ago": "Aasta tagasi", "memory": "Mälestus", "memory_lane_title": "Mälestus {title}", "menu": "MenÃŧÃŧ", @@ -896,10 +1036,14 @@ "month": "Kuu", "more": "Rohkem", "moved_to_trash": "Liigutatud prÃŧgikasti", + "multiselect_grid_edit_date_time_err_read_only": "Kirjutuskaitsega Ãŧksus(t)e kuupäeva ei saa muuta, jätan vahele", + "multiselect_grid_edit_gps_err_read_only": "Kirjutuskaitsega Ãŧksus(t)e asukohta ei saa muuta, jätan vahele", "mute_memories": "Vaigista mälestused", "my_albums": "Minu albumid", "name": "Nimi", "name_or_nickname": "Nimi vÃĩi hÃŧÃŧdnimi", + "networking_settings": "VÃĩrguÃŧhendus", + "networking_subtitle": "Halda serveri lÃĩpp-punkti seadeid", "never": "Mitte kunagi", "new_album": "Uus album", "new_api_key": "Uus API vÃĩti", @@ -962,6 +1106,9 @@ "partner_can_access": "{partner} pääseb ligi", "partner_can_access_assets": "KÃĩik su fotod ja videod, välja arvatud arhiveeritud ja kustutatud", "partner_can_access_location": "Asukohad, kus su fotod tehti", + "partner_list_view_all": "Vaata kÃĩiki", + "partner_page_partner_add_failed": "Partneri lisamine ebaÃĩnnestus", + "partner_page_select_partner": "Vali partner", "partner_sharing": "Partneriga jagamine", "partners": "Partnerid", "password": "Parool", @@ -990,6 +1137,7 @@ "permanently_delete_assets_prompt": "Kas oled kindel, et soovid {count, plural, one {selle Ãŧksuse} other {need <b>#</b> Ãŧksust}} jäädavalt kustutada? Sellega eemaldatakse {count, plural, one {see} other {need}} ka oma albumi(te)st.", "permanently_deleted_asset": "Ãksus jäädavalt kustutatud", "permanently_deleted_assets_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} jäädavalt kustutatud", + "permission_onboarding_back": "Tagasi", "person": "Isik", "person_birthdate": "SÃŧndinud {date}", "person_hidden": "{name}{hidden, select, true { (peidetud)} other {}}", @@ -1007,6 +1155,7 @@ "play_motion_photo": "Esita liikuv foto", "play_or_pause_video": "Esita vÃĩi peata video", "port": "Port", + "preferences_settings_title": "Eelistused", "preset": "Eelseadistus", "preview": "Eelvaade", "previous": "Eelmine", @@ -1014,6 +1163,8 @@ "previous_or_next_photo": "Eelmine vÃĩi järgmine foto", "primary": "Peamine", "privacy": "Privaatsus", + "profile_drawer_app_logs": "Logid", + "profile_drawer_github": "GitHub", "profile_image_of_user": "Kasutaja {user} profiilipilt", "profile_picture_set": "Profiilipilt määratud.", "public_album": "Avalik album", @@ -1063,6 +1214,8 @@ "recent": "Hiljutine", "recent-albums": "Hiljutised albumid", "recent_searches": "Hiljutised otsingud", + "recently_added": "Hiljuti lisatud", + "recently_added_page_title": "Hiljuti lisatud", "refresh": "Värskenda", "refresh_encoded_videos": "Värskenda kodeeritud videod", "refresh_faces": "Värskenda näod", @@ -1119,6 +1272,7 @@ "role_editor": "Muutja", "role_viewer": "Vaataja", "save": "Salvesta", + "save_to_gallery": "Salvesta galeriisse", "saved_api_key": "API vÃĩti salvestatud", "saved_profile": "Profiil salvestatud", "saved_settings": "Seaded salvestatud", @@ -1138,14 +1292,32 @@ "search_camera_model": "Otsi kaamera mudelit...", "search_city": "Otsi linna...", "search_country": "Otsi riiki...", + "search_filter_camera_title": "Vali kaamera tÃŧÃŧp", + "search_filter_date": "Kuupäev", + "search_filter_date_interval": "{start} kuni {end}", + "search_filter_date_title": "Vali kuupäevavahemik", + "search_filter_display_options": "Kuva valikud", + "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_people_title": "Vali isikud", "search_for": "Otsi", "search_for_existing_person": "Otsi olemasolevat isikut", "search_no_people": "Isikuid ei ole", "search_no_people_named": "Ei ole isikuid nimega \"{name}\"", "search_options": "Otsingu valikud", + "search_page_categories": "Kategooriad", + "search_page_screenshots": "Ekraanipildid", + "search_page_search_photos_videos": "Otsi oma fotosid ja videosid", + "search_page_selfies": "Selfid", + "search_page_things": "Asjad", + "search_page_view_all_button": "Vaata kÃĩiki", "search_people": "Otsi inimesi", "search_places": "Otsi kohti", "search_rating": "Otsi hinnangu järgi...", + "search_result_page_new_search_hint": "Uus otsing", "search_settings": "Otsi seadeid", "search_state": "Otsi osariiki...", "search_tags": "Otsi silte...", @@ -1168,10 +1340,14 @@ "select_new_face": "Vali uus nägu", "select_photos": "Vali fotod", "select_trash_all": "Vali kÃĩik prÃŧgikasti", + "select_user_for_sharing_page_err_album": "Albumi lisamine ebaÃĩnnestus", "selected": "Valitud", "selected_count": "{count, plural, other {# valitud}}", "send_message": "Saada sÃĩnum", "send_welcome_email": "Saada tervituskiri", + "server_endpoint": "Serveri lÃĩpp-punkt", + "server_info_box_app_version": "Rakenduse versioon", + "server_info_box_server_url": "Serveri URL", "server_offline": "Serveriga Ãŧhendus puudub", "server_online": "Server Ãŧhendatud", "server_stats": "Serveri statistika", @@ -1183,22 +1359,42 @@ "set_date_of_birth": "Määra sÃŧnnikuupäev", "set_profile_picture": "Sea profiilipilt", "set_slideshow_to_fullscreen": "Kuva slaidiesitlus täisekraanil", + "setting_languages_apply": "Rakenda", + "setting_languages_title": "Keeled", + "setting_notifications_notify_immediately": "kohe", + "setting_notifications_notify_never": "mitte kunagi", "settings": "Seaded", "settings_saved": "Seaded salvestatud", "share": "Jaga", "shared": "Jagatud", + "shared_album_section_people_action_error": "Viga albumist eemaldamisel/lahkumisel", + "shared_album_section_people_action_leave": "Eemalda kasutaja albumist", + "shared_album_section_people_action_remove_user": "Eemalda kasutaja albumist", + "shared_album_section_people_title": "ISIKUD", "shared_by": "Jagas", "shared_by_user": "Jagas {user}", "shared_by_you": "Jagasid sina", "shared_from_partner": "Fotod partnerilt {partner}", + "shared_link_app_bar_title": "Jagatud lingid", + "shared_link_clipboard_copied_massage": "Kopeeritud lÃĩikelauale", + "shared_link_create_error": "Viga jagatud lingi loomisel", + "shared_link_edit_expire_after_option_day": "1 päev", + "shared_link_edit_expire_after_option_hour": "1 tund", + "shared_link_edit_expire_after_option_minute": "1 minut", + "shared_link_info_chip_metadata": "EXIF", + "shared_link_manage_links": "Halda jagatud linke", "shared_link_options": "Jagatud lingi valikud", "shared_links": "Jagatud lingid", "shared_links_description": "Jaga fotosid ja videosid lingiga", "shared_photos_and_videos_count": "{assetCount, plural, other {# jagatud fotot ja videot.}}", + "shared_with_me": "Minuga jagatud", "shared_with_partner": "Jagatud partneriga {partner}", "sharing": "Jagamine", "sharing_enter_password": "Palun sisesta selle lehe vaatamiseks salasÃĩna.", + "sharing_page_album": "Jagatud albumid", "sharing_sidebar_description": "Kuva kÃŧlgmenÃŧÃŧs Jagamise linki", + "sharing_silver_appbar_create_shared_album": "Uus jagatud album", + "sharing_silver_appbar_share_partner": "Jaga partneriga", "shift_to_permanent_delete": "vajuta â§, et Ãŧksus jäädavalt kustutada", "show_album_options": "Näita albumi valikuid", "show_albums": "Näita albumeid", @@ -1265,6 +1461,7 @@ "support_third_party_description": "Sinu Immich'i install on kolmanda osapoole pakendatud. Probleemid, mida täheldad, vÃĩivad olla pÃĩhjustatud selle pakendamise poolt, seega vÃĩta esmajärjekorras nendega Ãŧhendust, kasutades allolevaid linke.", "swap_merge_direction": "Muuda Ãŧhendamise suunda", "sync": "SÃŧnkrooni", + "sync_albums": "SÃŧnkrooni albumid", "tag": "Silt", "tag_assets": "Sildista Ãŧksuseid", "tag_created": "Lisatud silt: {tag}", @@ -1278,6 +1475,9 @@ "theme": "Teema", "theme_selection": "Teema valik", "theme_selection_description": "Sea automaatselt hele vÃĩi tume teema vastavalt veebilehitseja eelistustele", + "theme_setting_primary_color_title": "PÃĩhivärv", + "theme_setting_system_primary_color_title": "Kasuta sÃŧsteemset värvi", + "theme_setting_system_theme_switch": "Automaatne (järgi sÃŧsteemi seadet)", "they_will_be_merged_together": "Nad Ãŧhendatakse kokku", "third_party_resources": "Kolmanda osapoole ressursid", "time_based_memories": "AjapÃĩhised mälestused", @@ -1298,6 +1498,9 @@ "trash_count": "Liiguta {count, number} prÃŧgikasti", "trash_delete_asset": "Kustuta Ãŧksus", "trash_no_results_message": "Siia ilmuvad prÃŧgikasti liigutatud fotod ja videod.", + "trash_page_delete_all": "Kustuta kÃĩik", + "trash_page_restore_all": "Taasta kÃĩik", + "trash_page_select_assets_btn": "Vali Ãŧksused", "trashed_items_will_be_permanently_deleted_after": "PrÃŧgikasti tÃĩstetud Ãŧksused kustutatakse jäädavalt {days, plural, one {# päeva} other {# päeva}} pärast.", "type": "TÃŧÃŧp", "unarchive": "Taasta arhiivist", @@ -1333,6 +1536,7 @@ "upload_status_errors": "Vead", "upload_status_uploaded": "Ãleslaaditud", "upload_success": "Ãleslaadimine Ãĩnnestus, uute Ãŧksuste nägemiseks värskenda lehte.", + "uploading": "Ãleslaadimine", "url": "URL", "usage": "Kasutus", "use_custom_date_range": "Kasuta kohandatud kuupäevavahemikku", @@ -1372,15 +1576,19 @@ "view_previous_asset": "Vaata eelmist Ãŧksust", "view_qr_code": "Vaata QR-koodi", "view_stack": "Vaata virna", + "viewer_remove_from_stack": "Eemalda virnast", + "viewer_unstack": "Eralda", "visibility_changed": "{count, plural, one {# isiku} other {# isiku}} nähtavus muudetud", "waiting": "Ootel", "warning": "Hoiatus", "week": "Nädal", "welcome": "Tere tulemast", "welcome_to_immich": "Tere tulemast Immich'isse", + "wifi_name": "WiFi-vÃĩrgu nimi", "year": "Aasta", "years_ago": "{years, plural, one {# aasta} other {# aastat}} tagasi", "yes": "Jah", "you_dont_have_any_shared_links": "Sul pole Ãŧhtegi jagatud linki", + "your_wifi_name": "Sinu WiFi-vÃĩrgu nimi", "zoom_image": "Suumi pilti" } diff --git a/i18n/fi.json b/i18n/fi.json index 8ca468fbab..8e64c372de 100644 --- a/i18n/fi.json +++ b/i18n/fi.json @@ -520,11 +520,11 @@ "backup_controller_page_background_turn_on": "Kytke taustapalvelu päälle", "backup_controller_page_background_wifi": "Vain WiFi-verkossa", "backup_controller_page_backup": "Varmuuskopiointi", - "backup_controller_page_backup_selected": "Valittu:", + "backup_controller_page_backup_selected": "Valittu: ", "backup_controller_page_backup_sub": "Varmuuskopioidut kuvat ja videot", "backup_controller_page_created": "Luotu: {}", "backup_controller_page_desc_backup": "Kytke varmuuskopiointi päälle lähettääksesi uudet kohteet palvelimelle automaattisesti.", - "backup_controller_page_excluded": "Jätetty pois:", + "backup_controller_page_excluded": "Jätetty pois: ", "backup_controller_page_failed": "Epäonnistui ({})", "backup_controller_page_filename": "Tiedoston nimi: {} [{}]", "backup_controller_page_id": "ID: {}", diff --git a/i18n/fr.json b/i18n/fr.json index c116ee261a..00a4a281b5 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -87,9 +87,9 @@ "image_resolution_description": "Les rÊsolutions plus ÊlevÊes permettent de prÊserver davantage de dÊtails, mais l'encodage est plus long, les fichiers sont plus volumineux et la rÊactivitÊ de l'application peut s'en trouver rÊduite.", "image_settings": "Paramètres d'image", "image_settings_description": "Gestion de la qualitÊ et rÊsolution des images gÊnÊrÊes", - "image_thumbnail_description": "Petite vignette avec mÊtadonnÊes retirÊes, utilisÊe lors de la visualisation de groupes de photos comme sur la vue chronologique principale", - "image_thumbnail_quality_description": "QualitÊ des vignettes : de 1 à 100. Une valeur ÊlevÊe produit de meilleurs rÊsultats, mais elle produit des fichiers plus volumineux et peut rÊduire la rÊactivitÊ de l'application.", - "image_thumbnail_title": "Paramètres des vignettes", + "image_thumbnail_description": "Petite miniature avec les mÊtadonnÊes retirÊes, utilisÊe lors de la visualisation de groupes de photos comme sur la vue chronologique principale", + "image_thumbnail_quality_description": "QualitÊ des miniatures : de 1 à 100. Une valeur ÊlevÊe produit de meilleurs rÊsultats, mais elle produit des fichiers plus volumineux et peut rÊduire la rÊactivitÊ de l'application.", + "image_thumbnail_title": "Paramètres des miniatures", "job_concurrency": "{job}â¯: nombre de tÃĸches simultanÊes", "job_created": "TÃĸche crÊÊe", "job_not_concurrency_safe": "Cette tÃĸche ne peut pas ÃĒtre exÊcutÊe en multitÃĸche de façon sÃģre.", @@ -345,7 +345,7 @@ "trash_settings": "Corbeille", "trash_settings_description": "GÊrer les paramètres de la corbeille", "untracked_files": "Fichiers non suivis", - "untracked_files_description": "Ces fichiers ne sont pas suivis par l'application. Ils peuvent ÃĒtre le rÊsultat d'erreurs de dÊplacement, d'envois interrompus, ou d'abandons en raison d'un bug", + "untracked_files_description": "Ces fichiers ne sont pas suivis par l'application. Ils peuvent ÃĒtre le rÊsultat d'Êchecs de dÊplacement, d'envois interrompus, ou d'abandons en raison d'un bug", "user_cleanup_job": "Nettoyage des utilisateurs", "user_delete_delay": "La suppression dÊfinitive du compte et des mÊdias de <b>{user}</b> sera programmÊe dans {delay, plural, one {# jour} other {# jours}}.", "user_delete_delay_settings": "DÊlai de suppression", @@ -371,13 +371,17 @@ "admin_password": "Mot de passe Admin", "administration": "Administration", "advanced": "AvancÊ", - "advanced_settings_log_level_title": "Niveau de log : {}", - "advanced_settings_prefer_remote_subtitle": "Certains appareils sont très lents à charger des vignettes à partir de ressources prÊsentes sur l'appareil. Activez ce paramètre pour charger des images externes à la place.", + "advanced_settings_enable_alternate_media_filter_subtitle": "Utilisez cette option pour filtrer les mÊdia durant la synchronisation avec des critères alternatifs. N'utilisez cela que lorsque l'application n'arrive pas à dÊtecter tout les albums.", + "advanced_settings_enable_alternate_media_filter_title": "[EXEPRIMENTAL] Utiliser le filtre de synchronisation d'album alternatif", + "advanced_settings_log_level_title": "Niveau de journalisation : {}", + "advanced_settings_prefer_remote_subtitle": "Certains appareils sont très lents à charger des miniatures à partir de ressources prÊsentes sur l'appareil. Activez ce paramètre pour charger des images externes à la place.", "advanced_settings_prefer_remote_title": "PrÊfÊrer les images externes", "advanced_settings_proxy_headers_subtitle": "Ajoutez des en-tÃĒtes personnalisÊs à chaque requÃĒte rÊseau", "advanced_settings_proxy_headers_title": "En-tÃĒtes de proxy", "advanced_settings_self_signed_ssl_subtitle": "Permet d'ignorer la vÊrification du certificat SSL pour le point d'accès du serveur. Requis pour les certificats auto-signÊs.", "advanced_settings_self_signed_ssl_title": "Autoriser les certificats SSL auto-signÊs", + "advanced_settings_sync_remote_deletions_subtitle": "Supprimer ou restaurer automatiquement un mÊdia sur cet appareil lorsqu'une action a ÊtÊ faite sur le web", + "advanced_settings_sync_remote_deletions_title": "Synchroniser les suppressions depuis le serveur [EXPERIMENTAL]", "advanced_settings_tile_subtitle": "Paramètres d'utilisateur avancÊs", "advanced_settings_troubleshooting_subtitle": "Activer des fonctions supplÊmentaires pour le dÊpannage", "advanced_settings_troubleshooting_title": "DÊpannage", @@ -506,11 +510,11 @@ "backup_all": "Tout", "backup_background_service_backup_failed_message": "Ãchec de la sauvegarde des ÊlÊments. Nouvelle tentative...", "backup_background_service_connection_failed_message": "Impossible de se connecter au serveur. Nouvelle tentative...", - "backup_background_service_current_upload_notification": "Transfert {}", + "backup_background_service_current_upload_notification": "Envoi {}", "backup_background_service_default_notification": "Recherche de nouveaux ÊlÊments...", "backup_background_service_error_title": "Erreur de sauvegarde", "backup_background_service_in_progress_notification": "Sauvegarde de vos ÊlÊments...", - "backup_background_service_upload_failure_notification": "Impossible de transfÊrer {}", + "backup_background_service_upload_failure_notification": "Ãchec lors de l'envoi {}", "backup_controller_page_albums": "Sauvegarder les albums", "backup_controller_page_background_app_refresh_disabled_content": "Activez le rafraÃŽchissement de l'application en arrière-plan dans Paramètres > GÊnÊral > RafraÃŽchissement de l'application en arrière-plan afin d'utiliser la sauvegarde en arrière-plan.", "backup_controller_page_background_app_refresh_disabled_title": "RafraÃŽchissement de l'application en arrière-plan dÊsactivÊ", @@ -521,7 +525,7 @@ "backup_controller_page_background_battery_info_title": "Optimisation de la batterie", "backup_controller_page_background_charging": "Seulement pendant la charge", "backup_controller_page_background_configure_error": "Ãchec de la configuration du service d'arrière-plan", - "backup_controller_page_background_delay": "Retarder la sauvegarde des nouveaux ÊlÊments d'actif: {}", + "backup_controller_page_background_delay": "Retarder la sauvegarde des nouveaux ÊlÊments : {}", "backup_controller_page_background_description": "Activez le service d'arrière-plan pour sauvegarder automatiquement tous les nouveaux ÊlÊments sans avoir à ouvrir l'application.", "backup_controller_page_background_is_off": "La sauvegarde automatique en arrière-plan est dÊsactivÊe", "backup_controller_page_background_is_on": "La sauvegarde automatique en arrière-plan est activÊe", @@ -531,12 +535,12 @@ "backup_controller_page_backup": "SauvegardÊ", "backup_controller_page_backup_selected": "SÊlectionnÊ: ", "backup_controller_page_backup_sub": "Photos et vidÊos sauvegardÊes", - "backup_controller_page_created": "CrÊÊ le: {}", + "backup_controller_page_created": "CrÊÊ le : {}", "backup_controller_page_desc_backup": "Activez la sauvegarde pour envoyer automatiquement les nouveaux ÊlÊments sur le serveur.", "backup_controller_page_excluded": "Exclus: ", "backup_controller_page_failed": "Ãchec de l'opÊration ({})", - "backup_controller_page_filename": "Nom du fichier: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_filename": "Nom du fichier : {} [{}]", + "backup_controller_page_id": "ID : {}", "backup_controller_page_info": "Informations de sauvegarde", "backup_controller_page_none_selected": "Aucune sÊlection", "backup_controller_page_remainder": "Restant", @@ -545,7 +549,7 @@ "backup_controller_page_start_backup": "DÊmarrer la sauvegarde", "backup_controller_page_status_off": "La sauvegarde est dÊsactivÊe", "backup_controller_page_status_on": "La sauvegarde est activÊe", - "backup_controller_page_storage_format": "{} de {} utilisÊ", + "backup_controller_page_storage_format": "{} sur {} utilisÊs", "backup_controller_page_to_backup": "Albums à sauvegarder", "backup_controller_page_total_sub": "Toutes les photos et vidÊos uniques des albums sÊlectionnÊs", "backup_controller_page_turn_off": "DÊsactiver la sauvegarde", @@ -570,21 +574,21 @@ "bulk_keep_duplicates_confirmation": "Ãtes-vous sÃģr de vouloir conserver {count, plural, one {# doublon} other {# doublons}}â¯? Cela rÊsoudra tous les groupes de doublons sans rien supprimer.", "bulk_trash_duplicates_confirmation": "Ãtes-vous sÃģr de vouloir mettre à la corbeille {count, plural, one {# doublon} other {# doublons}}â¯? Cette opÊration permet de conserver le plus grand mÊdia de chaque groupe et de mettre à la corbeille tous les autres doublons.", "buy": "Acheter Immich", - "cache_settings_album_thumbnails": "vignettes de la page bibliothèque ({} ÊlÊments)", + "cache_settings_album_thumbnails": "Page des miniatures de la bibliothèque ({} ÊlÊments)", "cache_settings_clear_cache_button": "Effacer le cache", "cache_settings_clear_cache_button_title": "Efface le cache de l'application. Cela aura un impact significatif sur les performances de l'application jusqu'à ce que le cache soit reconstruit.", "cache_settings_duplicated_assets_clear_button": "EFFACER", "cache_settings_duplicated_assets_subtitle": "Photos et vidÊos qui sont exclues par l'application", "cache_settings_duplicated_assets_title": "ÃlÊments dupliquÊs ({})", "cache_settings_image_cache_size": "Taille du cache des images ({} ÊlÊments)", - "cache_settings_statistics_album": "vignettes de la bibliothèque", + "cache_settings_statistics_album": "Miniatures de la bibliothèque", "cache_settings_statistics_assets": "{} ÊlÊments ({})", "cache_settings_statistics_full": "Images complètes", - "cache_settings_statistics_shared": "vignettes d'albums partagÊs", - "cache_settings_statistics_thumbnail": "vignettes", + "cache_settings_statistics_shared": "Miniatures de l'album partagÊ", + "cache_settings_statistics_thumbnail": "Miniatures", "cache_settings_statistics_title": "Utilisation du cache", "cache_settings_subtitle": "Contrôler le comportement de mise en cache de l'application mobile Immich", - "cache_settings_thumbnail_size": "Taille du cache des vignettes ({} ÊlÊments)", + "cache_settings_thumbnail_size": "Taille du cache des miniatures ({} ÊlÊments)", "cache_settings_tile_subtitle": "Contrôler le comportement du stockage local", "cache_settings_tile_title": "Stockage local", "cache_settings_title": "Paramètres de mise en cache", @@ -654,7 +658,7 @@ "contain": "Contenu", "context": "Contexte", "continue": "Continuer", - "control_bottom_app_bar_album_info_shared": "{} ÊlÊments - PartagÊs", + "control_bottom_app_bar_album_info_shared": "{} ÊlÊments ¡ PartagÊs", "control_bottom_app_bar_create_new_album": "CrÊer un nouvel album", "control_bottom_app_bar_delete_from_immich": "Supprimer de Immich", "control_bottom_app_bar_delete_from_local": "Supprimer de l'appareil", @@ -763,7 +767,7 @@ "download_enqueue": "TÊlÊchargement en attente", "download_error": "Erreur de tÊlÊchargement", "download_failed": "TÊlÊchargement ÊchouÊ", - "download_filename": "fichier : {}", + "download_filename": "fichier : {}", "download_finished": "TÊlÊchargement terminÊ", "download_include_embedded_motion_videos": "VidÊos intÊgrÊes", "download_include_embedded_motion_videos_description": "Inclure des vidÊos intÊgrÊes dans les photos de mouvement comme un fichier sÊparÊ", @@ -819,7 +823,7 @@ "error_change_sort_album": "Impossible de modifier l'ordre de tri des albums", "error_delete_face": "Erreur lors de la suppression du visage pour le mÊdia", "error_loading_image": "Erreur de chargement de l'image", - "error_saving_image": "Erreur : {}", + "error_saving_image": "Erreur : {}", "error_title": "Erreur - Quelque chose s'est mal passÊ", "errors": { "cannot_navigate_next_asset": "Impossible de naviguer jusqu'au prochain mÊdia", @@ -907,7 +911,7 @@ "unable_to_log_out_all_devices": "Incapable de dÊconnecter tous les appareils", "unable_to_log_out_device": "Impossible de dÊconnecter l'appareil", "unable_to_login_with_oauth": "Impossible de se connecter avec OAuth", - "unable_to_play_video": "Impossible de jouer la vidÊo", + "unable_to_play_video": "Impossible de lancer la vidÊo", "unable_to_reassign_assets_existing_person": "Impossible de rÊattribuer les mÊdias à {name, select, null {une personne existante} other {{name}}}", "unable_to_reassign_assets_new_person": "Impossible de rÊattribuer les mÊdias à une nouvelle personne", "unable_to_refresh_user": "Impossible d'actualiser l'utilisateur", @@ -954,9 +958,9 @@ "exif_bottom_sheet_people": "PERSONNES", "exif_bottom_sheet_person_add_person": "Ajouter un nom", "exif_bottom_sheet_person_age": "Ãge {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age_months": "Ãge {} mois", + "exif_bottom_sheet_person_age_year_months": "Ãge 1 an, {} mois", + "exif_bottom_sheet_person_age_years": "Ãge {}", "exit_slideshow": "Quitter le diaporama", "expand_all": "Tout dÊvelopper", "experimental_settings_new_asset_list_subtitle": "En cours de dÊveloppement", @@ -978,7 +982,7 @@ "face_unassigned": "Non attribuÊ", "failed": "Ãchec", "failed_to_load_assets": "Ãchec du chargement des ressources", - "failed_to_load_folder": "Impossible d'ouvrir le dossier", + "failed_to_load_folder": "Ãchec de chargement du dossier", "favorite": "Favori", "favorite_or_unfavorite_photo": "Ajouter ou supprimer des favoris", "favorites": "Favoris", @@ -1282,6 +1286,7 @@ "onboarding_welcome_user": "Bienvenue {user}", "online": "En ligne", "only_favorites": "Uniquement les favoris", + "open": "Ouvert", "open_in_map_view": "Montrer sur la carte", "open_in_openstreetmap": "Ouvrir dans OpenStreetMap", "open_the_search_filters": "Ouvrir les filtres de recherche", @@ -1354,10 +1359,10 @@ "place": "Lieu", "places": "Lieux", "places_count": "{count, plural, one {{count, number} Lieu} other {{count, number} Lieux}}", - "play": "Jouer", + "play": "Lancer", "play_memories": "Lancer les souvenirs", "play_motion_photo": "Jouer la photo animÊe", - "play_or_pause_video": "Jouer ou mettre en pause la vidÊo", + "play_or_pause_video": "Lancer ou mettre en pause la vidÊo", "port": "Port", "preferences_settings_subtitle": "GÊrer les prÊfÊrences de l'application", "preferences_settings_title": "PrÊfÊrences", @@ -1390,7 +1395,7 @@ "purchase_button_reminder": "Me le rappeler dans 30 jours", "purchase_button_remove_key": "Supprimer la clÊ", "purchase_button_select": "SÊlectionner", - "purchase_failed_activation": "Erreur à l'activation. Veuillez vÊrifier votre courriel pour obtenir la clÊ du produit correcteâ¯!", + "purchase_failed_activation": "Ãchec de l'activation. Veuillez vÊrifier votre courriel pour obtenir la clÊ correcte du produitâ¯!", "purchase_individual_description_1": "Pour un utilisateur", "purchase_individual_description_2": "Statut de contributeur", "purchase_individual_title": "Utilisateur", @@ -1430,13 +1435,13 @@ "refresh_encoded_videos": "Actualiser les vidÊos encodÊes", "refresh_faces": "Actualiser les visages", "refresh_metadata": "Actualiser les mÊtadonnÊes", - "refresh_thumbnails": "Actualiser les vignettes", + "refresh_thumbnails": "Actualiser les miniatures", "refreshed": "ActualisÊ", "refreshes_every_file": "Actualise tous les fichiers (existants et nouveaux)", "refreshing_encoded_video": "Actualisation de la vidÊo encodÊe", "refreshing_faces": "Actualisation des visages", "refreshing_metadata": "Actualisation des mÊtadonnÊes", - "regenerating_thumbnails": "RegÊnÊration des vignettes", + "regenerating_thumbnails": "RegÊnÊration des miniatures", "remove": "Supprimer", "remove_assets_album_confirmation": "Ãtes-vous sÃģr de vouloir supprimer {count, plural, one {# mÊdia} other {# mÊdias}} de l'albumâ¯?", "remove_assets_shared_link_confirmation": "Ãtes-vous sÃģr de vouloir supprimer {count, plural, one {# mÊdia} other {# mÊdias}} de ce lien partagÊâ¯?", @@ -1581,16 +1586,16 @@ "set_date_of_birth": "Changer la date de naissance", "set_profile_picture": "DÊfinir la photo de profil", "set_slideshow_to_fullscreen": "Afficher le diaporama en plein Êcran", - "setting_image_viewer_help": "Le visualiseur de dÊtails charge d'abord la petite vignette, puis l'aperçu de taille moyenne (s'il est activÊ), enfin l'original (s'il est activÊ).", + "setting_image_viewer_help": "Le visualiseur de dÊtails charge d'abord la petite miniature, puis l'aperçu de taille moyenne (s'il est activÊ), enfin l'original (s'il est activÊ).", "setting_image_viewer_original_subtitle": "Activez cette option pour charger l'image en rÊsolution originale (volumineux!). DÊsactiver pour rÊduire l'utilisation des donnÊes (rÊseau et cache de l'appareil).", "setting_image_viewer_original_title": "Charger l'image originale", - "setting_image_viewer_preview_subtitle": "Activer pour charger une image de rÊsolution moyenne. DÊsactiver pour charger directement l'original ou utiliser uniquement la vignette.", + "setting_image_viewer_preview_subtitle": "Activer pour charger une image de rÊsolution moyenne. DÊsactiver pour charger directement l'original ou utiliser uniquement la miniature.", "setting_image_viewer_preview_title": "Charger l'image d'aperçu", "setting_image_viewer_title": "Images", "setting_languages_apply": "Appliquer", "setting_languages_subtitle": "Changer la langue de l'application", "setting_languages_title": "Langues", - "setting_notifications_notify_failures_grace_period": "Notifier les Êchecs de la sauvegarde en arrière-plan: {}", + "setting_notifications_notify_failures_grace_period": "Notifier les Êchecs de sauvegarde en arrière-plan : {}", "setting_notifications_notify_hours": "{} heures", "setting_notifications_notify_immediately": "immÊdiatement", "setting_notifications_notify_minutes": "{} minutes", @@ -1609,7 +1614,7 @@ "settings_saved": "Paramètres sauvegardÊs", "share": "Partager", "share_add_photos": "Ajouter des photos", - "share_assets_selected": "{} sÊlÊctionnÊ(s)", + "share_assets_selected": "{} sÊlectionnÊ(s)", "share_dialog_preparing": "PrÊparation...", "shared": "PartagÊ", "shared_album_activities_input_disable": "Les commentaires sont dÊsactivÊs", @@ -1623,10 +1628,10 @@ "shared_by_user": "PartagÊ par {user}", "shared_by_you": "PartagÊ par vous", "shared_from_partner": "Photos de {partner}", - "shared_intent_upload_button_progress_text": "{} / {} TÊlÊversÊ", + "shared_intent_upload_button_progress_text": "{} / {} EnvoyÊs", "shared_link_app_bar_title": "Liens partagÊs", "shared_link_clipboard_copied_massage": "CopiÊ dans le presse-papier\n", - "shared_link_clipboard_text": "\nLien : {}\nMot de passe : {}", + "shared_link_clipboard_text": "Lien : {}\nMot de passe : {}", "shared_link_create_error": "Erreur pendant la crÊation du lien partagÊ", "shared_link_edit_description_hint": "Saisir la description du partage", "shared_link_edit_expire_after_option_day": "1 jour", @@ -1636,7 +1641,7 @@ "shared_link_edit_expire_after_option_minute": "1 minute", "shared_link_edit_expire_after_option_minutes": "{} minutes", "shared_link_edit_expire_after_option_months": "{} mois", - "shared_link_edit_expire_after_option_year": "{} ans", + "shared_link_edit_expire_after_option_year": "{} annÊes", "shared_link_edit_password_hint": "Saisir le mot de passe de partage", "shared_link_edit_submit_button": "Mettre à jour le lien", "shared_link_error_server_url_fetch": "Impossible de rÊcupÊrer l'url du serveur", @@ -1826,7 +1831,7 @@ "upload_status_errors": "Erreurs", "upload_status_uploaded": "EnvoyÊ", "upload_success": "Envoi rÊussi. RafraÃŽchir la page pour voir les nouveaux mÊdias envoyÊs.", - "upload_to_immich": "TÊlÊverser vers Immich ({})", + "upload_to_immich": "Envoyer vers Immich ({})", "uploading": "TÊlÊversement en cours", "url": "URL", "usage": "Utilisation", @@ -1859,7 +1864,7 @@ "version_history_item": "Version {version} installÊe le {date}", "video": "VidÊo", "video_hover_setting": "Lire la miniature des vidÊos au survol", - "video_hover_setting_description": "Jouer la prÊvisualisation vidÊo au survol. Si dÊsactivÊ, la lecture peut quand mÃĒme ÃĒtre dÊmarrÊe en survolant le bouton Play.", + "video_hover_setting_description": "Lancer la prÊvisualisation vidÊo au survol. Si dÊsactivÊ, la lecture peut quand mÃĒme ÃĒtre dÊmarrÊe en survolant le bouton Play.", "videos": "VidÊos", "videos_count": "{count, plural, one {# VidÊo} other {# VidÊos}}", "view": "Voir", diff --git a/i18n/gl.json b/i18n/gl.json index 72cf8d540a..d03294c778 100644 --- a/i18n/gl.json +++ b/i18n/gl.json @@ -4,576 +4,1896 @@ "account_settings": "ConfiguraciÃŗn da conta", "acknowledge": "De acordo", "action": "AcciÃŗn", - "action_common_update": "Update", + "action_common_update": "Actualizar", "actions": "AcciÃŗns", "active": "Activo", "activity": "Actividade", - "activity_changed": "A actividade estÃĄ {enabled, select, true {habilitada} other {deshabilitada}}", + "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 localizaciÃŗn", + "add_a_location": "Engadir unha ubicaciÃŗn", "add_a_name": "Engadir un nome", "add_a_title": "Engadir un tÃtulo", - "add_endpoint": "Add endpoint", + "add_endpoint": "Engadir endpoint", "add_exclusion_pattern": "Engadir patrÃŗn de exclusiÃŗn", "add_import_path": "Engadir ruta de importaciÃŗn", - "add_location": "Engadir localizaciÃŗn", + "add_location": "Engadir ubicaciÃŗn", "add_more_users": "Engadir mÃĄis usuarios", - "add_partner": "Engadir compaÃąeiro", + "add_partner": "Engadir compaÃąeiro/a", "add_path": "Engadir ruta", "add_photos": "Engadir fotos", "add_to": "Engadir aâĻ", "add_to_album": "Engadir ao ÃĄlbum", - "add_to_album_bottom_sheet_added": "Added to {album}", - "add_to_album_bottom_sheet_already_exists": "Already in {album}", + "add_to_album_bottom_sheet_added": "Engadido a {album}", + "add_to_album_bottom_sheet_already_exists": "Xa estÃĄ en {album}", "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": "Engadidos {count, number} a favoritos", + "added_to_favorites_count": "Engadido {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/**\".", + "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.", "authentication_settings": "ConfiguraciÃŗn de autenticaciÃŗn", - "authentication_settings_description": "Xestionar contrasinal, OAuth e outros parÃĄmetros de autenticaciÃŗn", - "authentication_settings_disable_all": "EstÃĄs seguro de deshabilitar todos os mÊtodos de inicio de sesiÃŗn? Iniciar a sesiÃŗn quedarÃĄ completamente deshabilitado.", - "authentication_settings_reenable": "Para rehabilitala, usa un <link>Comando do servidor</link>.", + "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_reenable": "Para reactivalo, use un <link>Comando de servidor</link>.", "background_task_job": "Tarefas en segundo plano", - "backup_database": "Respaldo da base de datos", - "backup_database_enable_description": "Habilitar as copias de seguridade da base de datos", - "backup_keep_last_amount": "Cantidade de copias de seguridade previas a manter", - "backup_settings": "ConfiguraciÃŗn de copias de seguridade", - "backup_settings_description": "Xestionar a configuraciÃŗn das copias de seguridade da base de datos", - "check_all": "Comprobar todo", + "backup_database": "Copia de seguridade da base de datos", + "backup_database_enable_description": "Activar copias de seguridade da base de datos", + "backup_keep_last_amount": "Cantidade de copias de seguridade anteriores a conservar", + "backup_settings": "ConfiguraciÃŗn da copia de seguridade", + "backup_settings_description": "Xestionar a configuraciÃŗn da copia de seguridade da base de datos", + "check_all": "Marcar todo", + "cleanup": "Limpeza", "cleared_jobs": "Traballos borrados para: {job}", - "config_set_by_file": "As configuraciÃŗns estÃĄn actualmente seleccionadas por un ficheiro de configuraciÃŗns", + "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}?", - "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, coma ficheiros RAW.", + "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_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}?", + "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 <link>Crontab Guru</link>", + "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.", "external_library_created_at": "Biblioteca externa (creada o {date})", - "external_library_management": "XestiÃŗn de bibliotecas externas", + "external_library_management": "XestiÃŗn da biblioteca externa", "face_detection": "DetecciÃŗn de caras", - "job_settings": "ConfiguraciÃŗn de tarefas", - "job_settings_description": "Administrar tarefas simultÃĄneas", - "job_status": "Estado da tarefa", - "jobs_failed": "{jobCount, one {# errado}, plural, other {# errados}}", - "note_cannot_be_changed_later": "NOTA: Non editable posteriormente!", - "notification_email_host_description": "Host do servidor de correo electrÃŗnico (p. ex.: smtp.immich.app)", + "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.", + "forcing_refresh_library_files": "Forzando a actualizaciÃŗn de todos os ficheiros da biblioteca", + "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_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_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_title": "ConfiguraciÃŗn da vista previa", + "image_quality": "Calidade", + "image_resolution": "ResoluciÃŗn", + "image_resolution_description": "ResoluciÃŗns mÃĄis altas poden preservar mÃĄis detalles pero tardan mÃĄis en codificarse, teÃąen tamaÃąos de ficheiro mÃĄis grandes e poden reducir a capacidade de resposta da aplicaciÃŗn.", + "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_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_settings": "ConfiguraciÃŗn de traballos", + "job_settings_description": "Xestionar a concorrencia de traballos", + "job_status": "Estado do traballo", + "jobs_delayed": "{jobCount, plural, other {# atrasados}}", + "jobs_failed": "{jobCount, plural, other {# fallados}}", + "library_created": "Biblioteca creada: {library}", + "library_deleted": "Biblioteca eliminada", + "library_import_path_description": "Especifique un cartafol para importar. Este cartafol, incluÃdos os subcartafoles, escanearase en busca de imaxes e vÃdeos.", + "library_scanning": "Escaneo periÃŗdico", + "library_scanning_description": "Configurar o escaneo periÃŗdico da biblioteca", + "library_scanning_enable_description": "Activar o escaneo periÃŗdico da biblioteca", + "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_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_clip_model": "Modelo CLIP", + "machine_learning_clip_model_description": "O nome dun modelo CLIP listado <link>aquÃ</link>. Ten 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_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_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_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", + "machine_learning_max_detection_distance_description": "Distancia mÃĄxima entre dÃēas imaxes para consideralas duplicadas, variando de 0.001 a 0.1. Valores mÃĄis altos detectarÃĄn mÃĄis duplicados, pero poden resultar en falsos positivos.", + "machine_learning_max_recognition_distance": "Distancia mÃĄxima de recoÃąecemento", + "machine_learning_max_recognition_distance_description": "Distancia mÃĄxima entre dÃēas caras para ser consideradas a mesma persoa, variando de 0 a 2. Baixar isto pode evitar etiquetar dÃēas persoas como a mesma persoa, mentres que subilo pode evitar etiquetar a mesma persoa como dÃēas persoas diferentes. TeÃąa en conta que Ê mÃĄis fÃĄcil fusionar dÃēas persoas que dividir unha persoa en dÃēas, asà que erre polo lado dun limiar mÃĄis baixo cando sexa posible.", + "machine_learning_min_detection_score": "PuntuaciÃŗn mÃnima de detecciÃŗn", + "machine_learning_min_detection_score_description": "PuntuaciÃŗn mÃnima de confianza para que unha cara sexa detectada, de 0 a 1. Valores mÃĄis baixos detectarÃĄn mÃĄis caras pero poden resultar en falsos positivos.", + "machine_learning_min_recognized_faces": "MÃnimo de caras recoÃąecidas", + "machine_learning_min_recognized_faces_description": "O nÃēmero mÃnimo de caras recoÃąecidas para que se cree unha persoa. Aumentar isto fai que o RecoÃąecemento Facial sexa mÃĄis preciso a costa de aumentar a posibilidade de que unha cara non se asigne a unha persoa.", + "machine_learning_settings": "ConfiguraciÃŗn da Aprendizaxe AutomÃĄtica", + "machine_learning_settings_description": "Xestionar funciÃŗns e configuraciÃŗns da aprendizaxe automÃĄtica", + "machine_learning_smart_search": "Busca Intelixente", + "machine_learning_smart_search_description": "Buscar imaxes semanticamente usando incrustaciÃŗns CLIP", + "machine_learning_smart_search_enabled": "Activar busca intelixente", + "machine_learning_smart_search_enabled_description": "Se estÃĄ desactivado, as imaxes non se codificarÃĄn para a busca intelixente.", + "machine_learning_url_description": "A URL do servidor de aprendizaxe automÃĄtica. Se se proporciona mÃĄis dunha URL, intentarase con cada servidor un por un ata que un responda correctamente, en orde do primeiro ao Ãēltimo. Os servidores que non respondan ignoraranse temporalmente ata que volvan estar en liÃąa.", + "manage_concurrency": "Xestionar Concorrencia", + "manage_log_settings": "Xestionar configuraciÃŗn de rexistro", + "map_dark_style": "Estilo escuro", + "map_enable_description": "Activar funciÃŗns do mapa", + "map_gps_settings": "ConfiguraciÃŗn do Mapa e GPS", + "map_gps_settings_description": "Xestionar a configuraciÃŗn do Mapa e GPS (XeocodificaciÃŗn Inversa)", + "map_implications": "A funciÃŗn do mapa depende dun servizo de teselas externo (tiles.immich.cloud)", + "map_light_style": "Estilo claro", + "map_manage_reverse_geocoding_settings": "Xestionar configuraciÃŗn de <link>XeocodificaciÃŗn Inversa</link>", + "map_reverse_geocoding": "XeocodificaciÃŗn Inversa", + "map_reverse_geocoding_enable_description": "Activar xeocodificaciÃŗn inversa", + "map_reverse_geocoding_settings": "ConfiguraciÃŗn da XeocodificaciÃŗn Inversa", + "map_settings": "Mapa", + "map_settings_description": "Xestionar a configuraciÃŗn do mapa", + "map_style_description": "URL a un tema de mapa style.json", + "memory_cleanup_job": "Limpeza de recordos", + "memory_generate_job": "XeraciÃŗn de recordos", + "metadata_extraction_job": "Extraer metadatos", + "metadata_extraction_job_description": "Extraer informaciÃŗn de metadatos de cada activo, como GPS, caras e resoluciÃŗn", + "metadata_faces_import_setting": "Activar importaciÃŗn de caras", + "metadata_faces_import_setting_description": "Importar caras dos datos EXIF da imaxe e ficheiros sidecar", + "metadata_settings": "ConfiguraciÃŗn de Metadatos", + "metadata_settings_description": "Xestionar a configuraciÃŗn de metadatos", + "migration_job": "MigraciÃŗn", + "migration_job_description": "Migrar miniaturas de activos e caras ÃĄ Ãēltima estrutura de cartafoles", + "no_paths_added": "Non se engadiron rutas", + "no_pattern_added": "Non se engadiu ningÃēn padrÃŗ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 <noreply@example.com>\"", + "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 de certificados TLS (non recomendado)", - "notification_settings": "ConfiguraciÃŗn de notificaciÃŗns" + "notification_email_ignore_certificate_errors_description": "Ignorar erros de validaciÃŗn do certificado TLS (non recomendado)", + "notification_email_password_description": "Contrasinal a usar ao autenticarse co servidor de correo electrÃŗnico", + "notification_email_port_description": "Porto do servidor de correo electrÃŗnico (p. ex. 25, 465 ou 587)", + "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_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", + "notification_settings_description": "Xestionar a configuraciÃŗn de notificaciÃŗns, incluÃdo o correo electrÃŗnico", + "oauth_auto_launch": "Lanzamento automÃĄtico", + "oauth_auto_launch_description": "Iniciar o fluxo de inicio de sesiÃŗn OAuth automaticamente ao navegar ÃĄ pÃĄxina de inicio de sesiÃŗn", + "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_id": "ID de cliente", + "oauth_client_secret": "Segredo do cliente", + "oauth_enable_description": "Iniciar sesiÃŗn con OAuth", + "oauth_issuer_url": "URL do emisor", + "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_profile_signing_algorithm": "Algoritmo de sinatura do perfil", + "oauth_profile_signing_algorithm_description": "Algoritmo usado para asinar o perfil do usuario.", + "oauth_scope": "Ãmbito", + "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 <link>documentaciÃŗn</link>.", + "oauth_signing_algorithm": "Algoritmo de sinatura", + "oauth_storage_label_claim": "DeclaraciÃŗn de etiqueta de almacenamento", + "oauth_storage_label_claim_description": "Establecer automaticamente a etiqueta de almacenamento do usuario ao valor desta declaraciÃŗn.", + "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).", + "offline_paths": "Rutas fÃŗra de liÃąa", + "offline_paths_description": "Estes resultados poden deberse ÃĄ eliminaciÃŗn manual de ficheiros que non forman parte dunha biblioteca externa.", + "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", + "paths_validated_successfully": "Todas as rutas validadas correctamente", + "person_cleanup_job": "Limpeza de persoas", + "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.", + "repair_all": "Reparar todo", + "repair_matched_items": "Coincidiron {count, plural, one {# elemento} other {# elementos}}", + "repaired_items": "ReparÃĄronse {count, plural, one {# elemento} other {# elementos}}", + "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", + "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_settings": "ConfiguraciÃŗn do servidor", + "server_settings_description": "Xestionar a configuraciÃŗn do servidor", + "server_welcome_message": "Mensaxe de benvida", + "server_welcome_message_description": "Unha mensaxe que se mostra na pÃĄxina de inicio de sesiÃŗn.", + "sidecar_job": "Metadatos Sidecar", + "sidecar_job_description": "Descubrir ou sincronizar metadatos sidecar desde o sistema de ficheiros", + "slideshow_duration_description": "NÃēmero de segundos para mostrar cada imaxe", + "smart_search_job_description": "Executar aprendizaxe automÃĄtica nos activos para soportar a busca intelixente", + "storage_template_date_time_description": "A marca de tempo de creaciÃŗn do activo Ãēsase para a informaciÃŗn de data e hora", + "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_migration": "MigraciÃŗn do modelo de almacenamento", + "storage_template_migration_description": "Aplicar o <link>{template}</link> 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 <link>{job}</link>.", + "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 <template-link>Modelo de Almacenamento</template-link> e as sÃēas <implications-link>implicaciÃŗns</implications-link>", + "storage_template_onboarding_description": "Cando estea activada, esta funciÃŗn autoorganizarÃĄ os ficheiros baseÃĄndose nun modelo definido polo usuario. Debido a problemas de estabilidade, a funciÃŗn desactivouse por defecto. Para obter mÃĄis informaciÃŗn, consulte a <link>documentaciÃŗn</link>.", + "storage_template_path_length": "LÃmite aproximado da lonxitude da ruta: <b>{length, number}</b>/{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": "<code>{label}</code> Ê 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_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", + "template_email_settings": "Modelos de Correo ElectrÃŗnico", + "template_email_settings_description": "Xestionar modelos personalizados de notificaciÃŗn por correo electrÃŗnico", + "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.", + "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_settings": "ConfiguraciÃŗn do Tema", + "theme_settings_description": "Xestionar a personalizaciÃŗn da interface web de Immich", + "these_files_matched_by_checksum": "Estes ficheiros coinciden polas sÃēas sumas de verificaciÃŗn", + "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_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)", + "transcoding_acceleration_vaapi": "VAAPI", + "transcoding_accepted_audio_codecs": "CÃŗdecs de audio aceptados", + "transcoding_accepted_audio_codecs_description": "Seleccione que cÃŗdecs de audio non necesitan ser transcodificados. SÃŗ se usa para certas polÃticas de transcodificaciÃŗn.", + "transcoding_accepted_containers": "Contedores aceptados", + "transcoding_accepted_containers_description": "Seleccione que formatos de contedor non necesitan ser remuxados a MP4. SÃŗ se usa para certas polÃticas de transcodificaciÃŗn.", + "transcoding_accepted_video_codecs": "CÃŗdecs de vÃdeo aceptados", + "transcoding_accepted_video_codecs_description": "Seleccione que cÃŗdecs de vÃdeo non necesitan ser transcodificados. SÃŗ se usa para certas polÃticas de transcodificaciÃŗn.", + "transcoding_advanced_options_description": "OpciÃŗns que a maiorÃa dos usuarios non deberÃan necesitar cambiar", + "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 <h264-link>cÃŗdec H.264</h264-link>, <hevc-link>cÃŗdec HEVC</hevc-link> e <vp9-link>cÃŗdec VP9</vp9-link>.", + "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_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_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_hevc_codec": "CÃŗdec HEVC", + "transcoding_max_b_frames": "MÃĄximo de B-frames", + "transcoding_max_b_frames_description": "Valores mÃĄis altos melloran a eficiencia da compresiÃŗn, pero ralentizan a codificaciÃŗn. Pode non ser compatible coa aceleraciÃŗn por hardware en dispositivos mÃĄis antigos. 0 desactiva os B-frames, mentres que -1 establece este valor automaticamente.", + "transcoding_max_bitrate": "Bitrate mÃĄximo", + "transcoding_max_bitrate_description": "Establecer un bitrate mÃĄximo pode facer que os tamaÃąos dos ficheiros sexan mÃĄis predicibles a un custo menor para a calidade. A 720p, os valores tÃpicos son 2600 kbit/s para VP9 ou HEVC, ou 4500 kbit/s para H.264. Desactivado se se establece en 0.", + "transcoding_max_keyframe_interval": "Intervalo mÃĄximo de fotogramas clave", + "transcoding_max_keyframe_interval_description": "Establece a distancia mÃĄxima de fotogramas entre fotogramas clave. Valores mÃĄis baixos empeoran a eficiencia da compresiÃŗn, pero melloran os tempos de busca e poden mellorar a calidade en escenas con movemento rÃĄpido. 0 establece este valor automaticamente.", + "transcoding_optimal_description": "VÃdeos cunha resoluciÃŗn superior ÃĄ obxectivo ou que non estean nun formato aceptado", + "transcoding_policy": "PolÃtica de TranscodificaciÃŗn", + "transcoding_policy_description": "Establecer cando se transcodificarÃĄ un vÃdeo", + "transcoding_preferred_hardware_device": "Dispositivo de hardware preferido", + "transcoding_preferred_hardware_device_description": "AplÃcase sÃŗ a VAAPI e QSV. Establece o nodo dri usado para a transcodificaciÃŗn por hardware.", + "transcoding_preset_preset": "Preaxuste (-preset)", + "transcoding_preset_preset_description": "Velocidade de compresiÃŗn. Preaxustes mÃĄis lentos producen ficheiros mÃĄis pequenos e aumentan a calidade ao apuntar a un certo bitrate. VP9 ignora velocidades superiores a 'faster'.", + "transcoding_reference_frames": "Fotogramas de referencia", + "transcoding_reference_frames_description": "O nÃēmero de fotogramas aos que facer referencia ao comprimir un fotograma dado. Valores mÃĄis altos melloran a eficiencia da compresiÃŗn, pero ralentizan a codificaciÃŗn. 0 establece este valor automaticamente.", + "transcoding_required_description": "SÃŗ vÃdeos que non estean nun formato aceptado", + "transcoding_settings": "ConfiguraciÃŗn da TranscodificaciÃŗn de VÃdeo", + "transcoding_settings_description": "Xestionar que vÃdeos transcodificar e como procesalos", + "transcoding_target_resolution": "ResoluciÃŗn obxectivo", + "transcoding_target_resolution_description": "ResoluciÃŗns mÃĄis altas poden preservar mÃĄis detalles pero tardan mÃĄis en codificarse, teÃąen tamaÃąos de ficheiro mÃĄis grandes e poden reducir a capacidade de resposta da aplicaciÃŗn.", + "transcoding_temporal_aq": "AQ Temporal", + "transcoding_temporal_aq_description": "AplÃcase sÃŗ a NVENC. Aumenta a calidade de escenas de alto detalle e baixo movemento. Pode non ser compatible con dispositivos mÃĄis antigos.", + "transcoding_threads": "FÃos", + "transcoding_threads_description": "Valores mÃĄis altos levan a unha codificaciÃŗn mÃĄis rÃĄpida, pero deixan menos marxe para que o servidor procese outras tarefas mentres estÃĄ activo. Este valor non deberÃa ser maior que o nÃēmero de nÃēcleos da CPU. Maximiza a utilizaciÃŗn se se establece en 0.", + "transcoding_tone_mapping": "Mapeo de tons", + "transcoding_tone_mapping_description": "Intenta preservar a aparencia dos vÃdeos HDR cando se converten a SDR. Cada algoritmo fai diferentes compromisos para cor, detalle e brillo. Hable preserva o detalle, Mobius preserva a cor e Reinhard preserva o brillo.", + "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_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", + "trash_number_of_days": "NÃēmero de dÃas", + "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", + "untracked_files": "Ficheiros non rastrexados", + "untracked_files_description": "Estes ficheiros non son rastrexados pola aplicaciÃŗn. Poden ser o resultado de movementos fallidos, cargas interrompidas ou deixados atrÃĄs debido a un erro", + "user_cleanup_job": "Limpeza de usuarios", + "user_delete_delay": "A conta e os activos de <b>{user}</b> 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", + "user_delete_delay_settings_description": "NÃēmero de dÃas despois da eliminaciÃŗn para eliminar permanentemente a conta e os activos dun usuario. O traballo de eliminaciÃŗn de usuarios execÃētase ÃĄ medianoite para comprobar os usuarios que estÃĄn listos para a eliminaciÃŗn. Os cambios nesta configuraciÃŗn avaliaranse na prÃŗxima execuciÃŗn.", + "user_delete_immediately": "A conta e os activos de <b>{user}</b> poranse en cola para a sÃēa eliminaciÃŗn permanente <b>inmediatamente</b>.", + "user_delete_immediately_checkbox": "PoÃąer en cola o usuario e os activos para a sÃēa eliminaciÃŗn inmediata", + "user_management": "XestiÃŗn de Usuarios", + "user_password_has_been_reset": "Restableceuse o contrasinal do usuario:", + "user_password_reset_description": "Proporcione o contrasinal temporal ao usuario e infÃŗrmelle de que necesitarÃĄ cambiar o contrasinal no seu prÃŗximo inicio de sesiÃŗn.", + "user_restore_description": "Restaurarase a conta de <b>{user}</b>.", + "user_restore_scheduled_removal": "Restaurar usuario - eliminaciÃŗn programada o {date, date, long}", + "user_settings": "ConfiguraciÃŗn do Usuario", + "user_settings_description": "Xestionar a configuraciÃŗn do usuario", + "user_successfully_removed": "Usuario {email} eliminado correctamente.", + "version_check_enabled_description": "Activar comprobaciÃŗn de versiÃŗn", + "version_check_implications": "A funciÃŗn de comprobaciÃŗn de versiÃŗn depende da comunicaciÃŗn periÃŗdica con github.com", + "version_check_settings": "ComprobaciÃŗn de VersiÃŗn", + "version_check_settings_description": "Activar/desactivar a notificaciÃŗn de nova versiÃŗn", + "video_conversion_job": "Transcodificar vÃdeos", + "video_conversion_job_description": "Transcodificar vÃdeos para unha maior compatibilidade con navegadores e dispositivos" }, - "advanced_settings_log_level_title": "Log level: {}", - "advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from assets on the device. Activate this setting to load remote images instead.", - "advanced_settings_prefer_remote_title": "Prefer remote images", - "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", - "advanced_settings_proxy_headers_title": "Proxy Headers", - "advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.", - "advanced_settings_self_signed_ssl_title": "Allow self-signed SSL certificates (EXPERIMENTAL)", - "advanced_settings_tile_subtitle": "Advanced user's settings", - "advanced_settings_troubleshooting_subtitle": "Enable additional features for troubleshooting", - "advanced_settings_troubleshooting_title": "Troubleshooting", - "album_info_card_backup_album_excluded": "EXCLUDED", - "album_info_card_backup_album_included": "INCLUDED", - "album_thumbnail_card_item": "1 item", - "album_thumbnail_card_items": "{} items", - "album_thumbnail_card_shared": " ¡ Shared", - "album_thumbnail_shared_by": "Shared by {}", - "album_viewer_appbar_delete_confirm": "Are you sure you want to delete this album from your account?", - "album_viewer_appbar_share_err_delete": "Failed to delete album", - "album_viewer_appbar_share_err_leave": "Failed to leave album", - "album_viewer_appbar_share_err_remove": "There are problems in removing assets from album", - "album_viewer_appbar_share_err_title": "Failed to change album title", - "album_viewer_appbar_share_leave": "Leave album", - "album_viewer_appbar_share_to": "Share To", - "album_viewer_page_share_add_users": "Add users", - "albums": "Albums", - "all": "All", - "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", - "archive_page_no_archived_assets": "No archived assets found", - "archive_page_title": "Archive ({})", - "archived": "Archived", - "asset_action_delete_err_read_only": "Cannot delete read only asset(s), skipping", - "asset_action_share_err_offline": "Cannot fetch offline asset(s), skipping", - "asset_list_group_by_sub_title": "Group by", - "asset_list_layout_settings_dynamic_layout_title": "Dynamic layout", - "asset_list_layout_settings_group_automatically": "Automatic", - "asset_list_layout_settings_group_by": "Group assets by", - "asset_list_layout_settings_group_by_month_day": "Month + day", - "asset_list_layout_sub_title": "Layout", - "asset_list_settings_subtitle": "Photo grid layout settings", - "asset_list_settings_title": "Photo Grid", - "asset_restored_successfully": "Asset restored successfully", - "asset_viewer_settings_subtitle": "Manage your gallery viewer settings", - "asset_viewer_settings_title": "Asset Viewer", - "assets_deleted_permanently": "{} asset(s) deleted permanently", - "assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server", - "assets_removed_permanently_from_device": "{} asset(s) removed permanently from your device", - "assets_restored_successfully": "{} asset(s) restored successfully", - "assets_trashed": "{} asset(s) trashed", - "assets_trashed_from_server": "{} asset(s) trashed from the Immich server", - "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", - "automatic_endpoint_switching_title": "Automatic URL switching", - "background_location_permission": "Background location permission", - "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", - "backup_album_selection_page_albums_device": "Albums on device ({})", - "backup_album_selection_page_albums_tap": "Tap to include, double tap to exclude", - "backup_album_selection_page_assets_scatter": "Assets can scatter across multiple albums. Thus, albums can be included or excluded during the backup process.", - "backup_album_selection_page_select_albums": "Select albums", - "backup_album_selection_page_selection_info": "Selection Info", - "backup_album_selection_page_total_assets": "Total unique assets", - "backup_all": "All", - "backup_background_service_backup_failed_message": "Failed to backup assets. RetryingâĻ", - "backup_background_service_connection_failed_message": "Failed to connect to the server. RetryingâĻ", - "backup_background_service_current_upload_notification": "Uploading {}", - "backup_background_service_default_notification": "Checking for new assetsâĻ", - "backup_background_service_error_title": "Backup error", - "backup_background_service_in_progress_notification": "Backing up your assetsâĻ", - "backup_background_service_upload_failure_notification": "Failed to upload {}", - "backup_controller_page_albums": "Backup Albums", - "backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.", - "backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled", - "backup_controller_page_background_app_refresh_enable_button_text": "Go to settings", - "backup_controller_page_background_battery_info_link": "Show me how", - "backup_controller_page_background_battery_info_message": "For the best background backup experience, please disable any battery optimizations restricting background activity for Immich.\n\nSince this is device-specific, please lookup the required information for your device manufacturer.", - "backup_controller_page_background_battery_info_ok": "OK", - "backup_controller_page_background_battery_info_title": "Battery optimizations", - "backup_controller_page_background_charging": "Only while charging", - "backup_controller_page_background_configure_error": "Failed to configure the background service", - "backup_controller_page_background_delay": "Delay new assets backup: {}", - "backup_controller_page_background_description": "Turn on the background service to automatically backup any new assets without needing to open the app", - "backup_controller_page_background_is_off": "Automatic background backup is off", - "backup_controller_page_background_is_on": "Automatic background backup is on", - "backup_controller_page_background_turn_off": "Turn off background service", - "backup_controller_page_background_turn_on": "Turn on background service", - "backup_controller_page_background_wifi": "Only on WiFi", - "backup_controller_page_backup": "Backup", - "backup_controller_page_backup_selected": "Selected: ", - "backup_controller_page_backup_sub": "Backed up photos and videos", - "backup_controller_page_created": "Created on: {}", - "backup_controller_page_desc_backup": "Turn on foreground backup to automatically upload new assets to the server when opening the app.", - "backup_controller_page_excluded": "Excluded: ", - "backup_controller_page_failed": "Failed ({})", - "backup_controller_page_filename": "File name: {} [{}]", + "admin_email": "Correo electrÃŗnico do administrador", + "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_title": "[EXPERIMENTAL] Usar filtro alternativo de sincronizaciÃŗn de ÃĄlbums do dispositivo", + "advanced_settings_log_level_title": "Nivel de rexistro: {}", + "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_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_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", + "advanced_settings_sync_remote_deletions_title": "Sincronizar eliminaciÃŗns remotas [EXPERIMENTAL]", + "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 #}}", + "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_cover_updated": "Portada do ÃĄlbum actualizada", + "album_delete_confirmation": "EstÃĄs seguro de que queres eliminar o ÃĄlbum {album}?", + "album_delete_confirmation_description": "Se este ÃĄlbum estÃĄ compartido, outros usuarios non poderÃĄn acceder a el.", + "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_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_thumbnail_card_item": "1 elemento", + "album_thumbnail_card_items": "{} elementos", + "album_thumbnail_card_shared": " ¡ Compartido", + "album_thumbnail_shared_by": "Compartido por {}", + "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_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_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", + "album_viewer_page_share_add_users": "Engadir usuarios", + "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}}", + "all": "Todo", + "all_albums": "Todos os ÃĄlbums", + "all_people": "Todas as persoas", + "all_videos": "Todos os vÃdeos", + "allow_dark_mode": "Permitir modo escuro", + "allow_edits": "Permitir ediciÃŗns", + "allow_public_user_to_download": "Permitir que o usuario pÃēblico descargue", + "allow_public_user_to_upload": "Permitir que o usuario pÃēblico cargue", + "alt_text_qr_code": "Imaxe de cÃŗdigo QR", + "anti_clockwise": "Sentido antihorario", + "api_key": "Chave API", + "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_ok": "Si", + "app_bar_signout_dialog_title": "Pechar sesiÃŗn", + "app_settings": "ConfiguraciÃŗn da AplicaciÃŗn", + "appears_in": "Aparece en", + "archive": "Arquivo", + "archive_or_unarchive_photo": "Arquivar ou desarquivar foto", + "archive_page_no_archived_assets": "Non se atoparon activos arquivados", + "archive_page_title": "Arquivo ({})", + "archive_size": "TamaÃąo do arquivo", + "archive_size_description": "Configurar o tamaÃąo do arquivo para descargas (en GiB)", + "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?", + "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_hashing": "Calculando hashâĻ", + "asset_list_group_by_sub_title": "Agrupar por", + "asset_list_layout_settings_dynamic_layout_title": "DeseÃąo dinÃĄmico", + "asset_list_layout_settings_group_automatically": "AutomÃĄtico", + "asset_list_layout_settings_group_by": "Agrupar activos por", + "asset_list_layout_settings_group_by_month_day": "Mes + dÃa", + "asset_list_layout_sub_title": "DeseÃąo", + "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_restored_successfully": "Activo restaurado correctamente", + "asset_skipped": "Omitido", + "asset_skipped_in_trash": "No lixo", + "asset_uploaded": "Subido", + "asset_uploading": "SubindoâĻ", + "asset_viewer_settings_subtitle": "Xestionar a tÃē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_name_count": "Engadido {count, plural, one {# activo} other {# activos}} a {hasName, select, true {<b>{name}</b>} other {novo ÃĄlbum}}", + "assets_count": "{count, plural, one {# activo} other {# activos}}", + "assets_deleted_permanently": "{} activo(s) eliminado(s) permanentemente", + "assets_deleted_permanently_from_server": "{} activo(s) eliminado(s) permanentemente do servidor Immich", + "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": "{} 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_restored_count": "Restaurados {count, plural, one {# activo} other {# activos}}", + "assets_restored_successfully": "{} activo(s) restaurado(s) correctamente", + "assets_trashed": "{} activo(s) movido(s) ao lixo", + "assets_trashed_count": "Movido {count, plural, one {# activo} other {# activos}} ao lixo", + "assets_trashed_from_server": "{} 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", + "authorized_devices": "Dispositivos Autorizados", + "automatic_endpoint_switching_subtitle": "Conectar localmente a travÊs de Wi-Fi designada cando estea dispoÃąible e usar conexiÃŗns alternativas noutros lugares", + "automatic_endpoint_switching_title": "Cambio automÃĄtico de URL", + "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 Wi-Fi", + "backup_album_selection_page_albums_device": "Ãlbums no dispositivo ({})", + "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_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_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 {}", + "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_upload_failure_notification": "Erro ao subir {}", + "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_ok": "Aceptar", + "backup_controller_page_background_battery_info_title": "OptimizaciÃŗns da baterÃa", + "backup_controller_page_background_charging": "SÃŗ mentres se carga", + "backup_controller_page_background_configure_error": "Erro ao configurar o servizo en segundo plano", + "backup_controller_page_background_delay": "Atrasar copia de seguridade de novos activos: {}", + "backup_controller_page_background_description": "Active o servizo en segundo plano para facer copia de seguridade automaticamente de calquera activo novo sen necesidade de abrir a aplicaciÃŗn", + "backup_controller_page_background_is_off": "A copia de seguridade automÃĄtica en segundo plano estÃĄ desactivada", + "backup_controller_page_background_is_on": "A copia de seguridade automÃĄtica en segundo plano estÃĄ activada", + "backup_controller_page_background_turn_off": "Desactivar servizo en segundo plano", + "backup_controller_page_background_turn_on": "Activar servizo en segundo plano", + "backup_controller_page_background_wifi": "SÃŗ con WiFi", + "backup_controller_page_backup": "Copia de Seguridade", + "backup_controller_page_backup_selected": "Seleccionado: ", + "backup_controller_page_backup_sub": "Fotos e vÃdeos con copia de seguridade", + "backup_controller_page_created": "Creado o: {}", + "backup_controller_page_desc_backup": "Active a copia de seguridade en primeiro plano para cargar automaticamente novos activos ao servidor ao abrir a aplicaciÃŗn.", + "backup_controller_page_excluded": "ExcluÃdo: ", + "backup_controller_page_failed": "Fallado ({})", + "backup_controller_page_filename": "Nome do ficheiro: {} [{}]", "backup_controller_page_id": "ID: {}", - "backup_controller_page_info": "Backup Information", - "backup_controller_page_none_selected": "None selected", - "backup_controller_page_remainder": "Remainder", - "backup_controller_page_remainder_sub": "Remaining photos and videos to back up from selection", - "backup_controller_page_server_storage": "Server Storage", - "backup_controller_page_start_backup": "Start Backup", - "backup_controller_page_status_off": "Automatic foreground backup is off", - "backup_controller_page_status_on": "Automatic foreground backup is on", - "backup_controller_page_storage_format": "{} of {} used", - "backup_controller_page_to_backup": "Albums to be backed up", - "backup_controller_page_total_sub": "All unique photos and videos from selected albums", - "backup_controller_page_turn_off": "Turn off foreground backup", - "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_info_card_assets": "assets", - "backup_manual_cancelled": "Cancelled", - "backup_manual_in_progress": "Upload already in progress. Try after some time", - "backup_manual_success": "Success", - "backup_manual_title": "Upload status", - "backup_options_page_title": "Backup options", - "backup_setting_subtitle": "Manage background and foreground upload settings", - "cache_settings_album_thumbnails": "Library page thumbnails ({} assets)", - "cache_settings_clear_cache_button": "Clear cache", - "cache_settings_clear_cache_button_title": "Clears the app's cache. This will significantly impact the app's performance until the cache has rebuilt.", - "cache_settings_duplicated_assets_clear_button": "CLEAR", - "cache_settings_duplicated_assets_subtitle": "Photos and videos that are black listed by the app", - "cache_settings_duplicated_assets_title": "Duplicated Assets ({})", - "cache_settings_image_cache_size": "Image cache size ({} assets)", - "cache_settings_statistics_album": "Library thumbnails", - "cache_settings_statistics_assets": "{} assets ({})", - "cache_settings_statistics_full": "Full images", - "cache_settings_statistics_shared": "Shared album thumbnails", - "cache_settings_statistics_thumbnail": "Thumbnails", - "cache_settings_statistics_title": "Cache usage", - "cache_settings_subtitle": "Control the caching behaviour of the Immich mobile application", - "cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)", - "cache_settings_tile_subtitle": "Control the local storage behaviour", - "cache_settings_tile_title": "Local Storage", - "cache_settings_title": "Caching Settings", - "cancel": "Cancel", - "canceled": "Canceled", - "change_display_order": "Change display order", - "change_password_form_confirm_password": "Confirm Password", - "change_password_form_description": "Hi {name},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.", - "change_password_form_new_password": "New Password", - "change_password_form_password_mismatch": "Passwords do not match", - "change_password_form_reenter_new_password": "Re-enter New Password", - "check_corrupt_asset_backup": "Check for corrupt asset backups", - "check_corrupt_asset_backup_button": "Perform check", - "check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.", - "client_cert_dialog_msg_confirm": "OK", - "client_cert_enter_password": "Enter Password", - "client_cert_import": "Import", - "client_cert_import_success_msg": "Client certificate is imported", - "client_cert_invalid_msg": "Invalid certificate file or wrong password", - "client_cert_remove_msg": "Client certificate is removed", - "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login", - "client_cert_title": "SSL Client Certificate (EXPERIMENTAL)", - "common_create_new_album": "Create new album", - "common_server_error": "Please check your network connection, make sure the server is reachable and app/server versions are compatible.", - "completed": "Completed", - "control_bottom_app_bar_album_info_shared": "{} items ¡ Shared", - "control_bottom_app_bar_create_new_album": "Create new album", - "control_bottom_app_bar_delete_from_immich": "Delete from Immich", - "control_bottom_app_bar_delete_from_local": "Delete from device", - "control_bottom_app_bar_edit_location": "Edit Location", - "control_bottom_app_bar_edit_time": "Edit Date & Time", - "control_bottom_app_bar_share_to": "Share To", - "control_bottom_app_bar_trash_from_immich": "Move to Trash", - "create_album": "Create album", - "create_album_page_untitled": "Untitled", - "create_new": "CREATE NEW", - "create_shared_album_page_share_add_assets": "ADD ASSETS", - "create_shared_album_page_share_select_photos": "Select Photos", - "crop": "Crop", - "curated_object_page_title": "Things", - "current_server_address": "Current server address", - "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "E, MMM dd, yyyy", - "date_format": "E, LLL d, y âĸ h:mm a", - "delete_dialog_alert": "These items will be permanently deleted from Immich and from your device", - "delete_dialog_alert_local": "These items will be permanently removed from your device but still be available on the Immich server", - "delete_dialog_alert_local_non_backed_up": "Some of the items aren't backed up to Immich and will be permanently removed from your device", - "delete_dialog_alert_remote": "These items will be permanently deleted from the Immich server", - "delete_dialog_ok_force": "Delete Anyway", - "delete_dialog_title": "Delete Permanently", - "delete_local_dialog_ok_backed_up_only": "Delete Backed Up Only", - "delete_local_dialog_ok_force": "Delete Anyway", - "delete_shared_link_dialog_title": "Delete Shared Link", - "description_input_hint_text": "Add description...", - "description_input_submit_error": "Error updating description, check the log for more details", - "download_canceled": "Download canceled", - "download_complete": "Download complete", - "download_enqueue": "Download enqueued", - "download_error": "Download Error", - "download_failed": "Download failed", - "download_filename": "file: {}", - "download_finished": "Download finished", - "download_notfound": "Download not found", - "download_paused": "Download paused", - "download_started": "Download started", - "download_sucess": "Download success", - "download_sucess_android": "The media has been downloaded to DCIM/Immich", - "download_waiting_to_retry": "Waiting to retry", - "downloading": "Downloading...", - "downloading_media": "Downloading media", - "edit_location_dialog_title": "Location", - "end_date": "End date", - "enqueued": "Enqueued", - "enter_wifi_name": "Enter WiFi name", - "error_change_sort_album": "Failed to change album sort order", - "error_saving_image": "Error: {}", - "exif_bottom_sheet_description": "Add Description...", - "exif_bottom_sheet_details": "DETAILS", - "exif_bottom_sheet_location": "LOCATION", - "exif_bottom_sheet_people": "PEOPLE", - "exif_bottom_sheet_person_add_person": "Add name", - "experimental_settings_new_asset_list_subtitle": "Work in progress", - "experimental_settings_new_asset_list_title": "Enable experimental photo grid", - "experimental_settings_subtitle": "Use at your own risk!", + "backup_controller_page_info": "InformaciÃŗn da Copia de Seguridade", + "backup_controller_page_none_selected": "NingÃēn seleccionado", + "backup_controller_page_remainder": "Restante", + "backup_controller_page_remainder_sub": "Fotos e vÃdeos restantes para facer copia de seguridade da selecciÃŗn", + "backup_controller_page_server_storage": "Almacenamento do Servidor", + "backup_controller_page_start_backup": "Iniciar Copia de Seguridade", + "backup_controller_page_status_off": "A copia de seguridade automÃĄtica en primeiro plano estÃĄ desactivada", + "backup_controller_page_status_on": "A copia de seguridade automÃĄtica en primeiro plano estÃĄ activada", + "backup_controller_page_storage_format": "{} de {} usado", + "backup_controller_page_to_backup": "Ãlbums para facer copia de seguridade", + "backup_controller_page_total_sub": "Todas as fotos e vÃdeos Ãēnicos dos ÃĄlbums seleccionados", + "backup_controller_page_turn_off": "Desactivar copia de seguridade en primeiro plano", + "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_info_card_assets": "activos", + "backup_manual_cancelled": "Cancelado", + "backup_manual_in_progress": "Subida xa en progreso. Intenta despois dun tempo", + "backup_manual_success": "Ãxito", + "backup_manual_title": "Estado da subida", + "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", + "backward": "AtrÃĄs", + "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", + "cache_settings_album_thumbnails": "Miniaturas da pÃĄxina da biblioteca ({} activos)", + "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_title": "Activos Duplicados ({})", + "cache_settings_image_cache_size": "TamaÃąo da cachÊ de imaxes ({} activos)", + "cache_settings_statistics_album": "Miniaturas da biblioteca", + "cache_settings_statistics_assets": "{} activos ({})", + "cache_settings_statistics_full": "Imaxes completas", + "cache_settings_statistics_shared": "Miniaturas de ÃĄlbums compartidos", + "cache_settings_statistics_thumbnail": "Miniaturas", + "cache_settings_statistics_title": "Uso da cachÊ", + "cache_settings_subtitle": "Controlar o comportamento da cachÊ da aplicaciÃŗn mÃŗbil Immich", + "cache_settings_thumbnail_size": "TamaÃąo da cachÊ de miniaturas ({} activos)", + "cache_settings_tile_subtitle": "Controlar o comportamento do almacenamento local", + "cache_settings_tile_title": "Almacenamento Local", + "cache_settings_title": "ConfiguraciÃŗn da CachÊ", + "camera": "CÃĄmara", + "camera_brand": "Marca da cÃĄmara", + "camera_model": "Modelo da cÃĄmara", + "cancel": "Cancelar", + "cancel_search": "Cancelar busca", + "canceled": "Cancelado", + "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", + "change_date": "Cambiar data", + "change_display_order": "Cambiar orde de visualizaciÃŗn", + "change_expiration_time": "Cambiar hora de caducidade", + "change_location": "Cambiar ubicaciÃŗ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_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_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", + "changed_visibility_successfully": "Visibilidade cambiada correctamente", + "check_all": "Marcar todo", + "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 de Wi-Fi e unha vez que todos os activos teÃąan copia de seguridade. O procedemento pode tardar uns minutos.", + "check_logs": "Comprobar Rexistros", + "choose_matching_people_to_merge": "Elixir persoas coincidentes para fusionar", + "city": "Cidade", + "clear": "Limpar", + "clear_all": "Limpar todo", + "clear_all_recent_searches": "Limpar todas as buscas recentes", + "clear_message": "Limpar mensaxe", + "clear_value": "Limpar valor", + "client_cert_dialog_msg_confirm": "Aceptar", + "client_cert_enter_password": "Introducir Contrasinal", + "client_cert_import": "Importar", + "client_cert_import_success_msg": "Certificado de cliente importado", + "client_cert_invalid_msg": "Ficheiro de certificado invÃĄlido ou contrasinal incorrecto", + "client_cert_remove_msg": "Certificado de cliente eliminado", + "client_cert_subtitle": "SÃŗ admite o formato PKCS12 (.p12, .pfx). A importaciÃŗn/eliminaciÃŗn de certificados sÃŗ estÃĄ dispoÃąible antes de iniciar sesiÃŗn", + "client_cert_title": "Certificado de Cliente SSL", + "clockwise": "Sentido horario", + "close": "Pechar", + "collapse": "Contraer", + "collapse_all": "Contraer todo", + "color": "Cor", + "color_theme": "Tema de cor", + "comment_deleted": "Comentario eliminado", + "comment_options": "OpciÃŗns de comentario", + "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.", + "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_password": "Confirmar contrasinal", + "contain": "Conter", + "context": "Contexto", + "continue": "Continuar", + "control_bottom_app_bar_album_info_shared": "{} elementos ¡ Compartidos", + "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_time": "Editar Data e Hora", + "control_bottom_app_bar_share_link": "Compartir LigazÃŗn", + "control_bottom_app_bar_share_to": "Compartir Con", + "control_bottom_app_bar_trash_from_immich": "Mover ao Lixo", + "copied_image_to_clipboard": "Imaxe copiada ao portapapeis.", + "copied_to_clipboard": "Copiado ao portapapeis!", + "copy_error": "Erro ao copiar", + "copy_file_path": "Copiar ruta do ficheiro", + "copy_image": "Copiar Imaxe", + "copy_link": "Copiar ligazÃŗn", + "copy_link_to_clipboard": "Copiar ligazÃŗn ao portapapeis", + "copy_password": "Copiar contrasinal", + "copy_to_clipboard": "Copiar ao Portapapeis", + "country": "PaÃs", + "cover": "Portada", + "covers": "Portadas", + "create": "Crear", + "create_album": "Crear ÃĄlbum", + "create_album_page_untitled": "Sen tÃtulo", + "create_library": "Crear Biblioteca", + "create_link": "Crear ligazÃŗn", + "create_link_to_share": "Crear ligazÃŗn para compartir", + "create_link_to_share_description": "Permitir que calquera persoa coa ligazÃŗn vexa a(s) foto(s) seleccionada(s)", + "create_new": "CREAR NOVO", + "create_new_person": "Crear nova persoa", + "create_new_person_hint": "Asignar activos seleccionados a unha nova persoa", + "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_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", + "crop": "Recortar", + "curated_object_page_title": "Cousas", + "current_device": "Dispositivo 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", + "daily_title_text_date": "E, dd MMM", + "daily_title_text_date_year": "E, dd MMM, yyyy", + "dark": "Escuro", + "date_after": "Data posterior a", + "date_and_time": "Data e Hora", + "date_before": "Data anterior a", + "date_format": "E, d LLL, y âĸ H:mm", + "date_of_birth_saved": "Data de nacemento gardada correctamente", + "date_range": "Rango de datas", + "day": "DÃa", + "deduplicate_all": "Eliminar Duplicados Todos", + "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", + "delete": "Eliminar", + "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_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_face": "Eliminar cara", + "delete_key": "Eliminar chave", + "delete_library": "Eliminar Biblioteca", + "delete_link": "Eliminar ligazÃŗn", + "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_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_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", + "details": "Detalles", + "direction": "DirecciÃŗn", + "disabled": "Desactivado", + "disallow_edits": "Non permitir ediciÃŗns", + "discord": "Discord", + "discover": "Descubrir", + "dismiss_all_errors": "Descartar todos os erros", + "dismiss_error": "Descartar erro", + "display_options": "OpciÃŗns de visualizaciÃŗn", + "display_order": "Orde de visualizaciÃŗn", + "display_original_photos": "Mostrar fotos orixinais", + "display_original_photos_setting_description": "Preferir mostrar a foto orixinal ao ver un activo en lugar de miniaturas cando o activo orixinal Ê compatible coa web. Isto pode resultar en velocidades de visualizaciÃŗn de fotos mÃĄis lentas.", + "do_not_show_again": "Non mostrar esta mensaxe de novo", + "documentation": "DocumentaciÃŗn", + "done": "Feito", + "download": "Descargar", + "download_canceled": "Descarga cancelada", + "download_complete": "Descarga completada", + "download_enqueue": "Descarga en cola", + "download_error": "Erro na Descarga", + "download_failed": "Descarga fallada", + "download_filename": "ficheiro: {}", + "download_finished": "Descarga finalizada", + "download_include_embedded_motion_videos": "VÃdeos incrustados", + "download_include_embedded_motion_videos_description": "IncluÃr vÃdeos incrustados en fotos en movemento como un ficheiro separado", + "download_notfound": "Descarga non atopada", + "download_paused": "Descarga pausada", + "download_settings": "Descarga", + "download_settings_description": "Xestionar configuraciÃŗns relacionadas coa descarga de activos", + "download_started": "Descarga iniciada", + "download_sucess": "Descarga exitosa", + "download_sucess_android": "Os medios descargÃĄronse en DCIM/Immich", + "download_waiting_to_retry": "Agardando para reintentar", + "downloading": "Descargando", + "downloading_asset_filename": "Descargando activo {filename}", + "downloading_media": "Descargando medios", + "drop_files_to_upload": "Solte ficheiros en calquera lugar para cargar", + "duplicates": "Duplicados", + "duplicates_description": "Resolve cada grupo indicando cales, se os houber, son duplicados", + "duration": "DuraciÃŗn", + "edit": "Editar", + "edit_album": "Editar ÃĄlbum", + "edit_avatar": "Editar avatar", + "edit_date": "Editar data", + "edit_date_and_time": "Editar data e hora", + "edit_exclusion_pattern": "Editar padrÃŗ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_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", + "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!", + "enable": "Activar", + "enabled": "Activado", + "end_date": "Data de fin", + "enqueued": "En cola", + "enter_wifi_name": "Introducir nome da WiFi", + "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_loading_image": "Erro ao cargar a imaxe", + "error_saving_image": "Erro: {}", + "error_title": "Erro - Algo saÃu mal", + "errors": { + "cannot_navigate_next_asset": "Non se pode navegar ao seguinte activo", + "cannot_navigate_previous_asset": "Non se pode navegar ao activo anterior", + "cant_apply_changes": "Non se poden aplicar os cambios", + "cant_change_activity": "Non se pode {enabled, select, true {desactivar} other {activar}} a actividade", + "cant_change_asset_favorite": "Non se pode cambiar o favorito do activo", + "cant_change_metadata_assets_count": "Non se poden cambiar os metadatos de {count, plural, one {# activo} other {# activos}}", + "cant_get_faces": "Non se poden obter caras", + "cant_get_number_of_comments": "Non se pode obter o nÃēmero de comentarios", + "cant_search_people": "Non se poden buscar persoas", + "cant_search_places": "Non se poden buscar lugares", + "cleared_jobs": "Traballos borrados para: {job}", + "error_adding_assets_to_album": "Erro ao engadir activos ao ÃĄlbum", + "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_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.", + "failed_job_command": "O comando {command} fallou para o traballo: {job}", + "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", + "failed_to_get_people": "Erro ao obter persoas", + "failed_to_keep_this_delete_others": "Erro ao conservar este activo e eliminar os outros activos", + "failed_to_load_asset": "Erro ao cargar o activo", + "failed_to_load_assets": "Erro ao cargar activos", + "failed_to_load_people": "Erro ao cargar persoas", + "failed_to_remove_product_key": "Erro ao eliminar a chave do produto", + "failed_to_stack_assets": "Erro ao apilar activos", + "failed_to_unstack_assets": "Erro ao desapilar activos", + "import_path_already_exists": "Esta ruta de importaciÃŗn xa existe.", + "incorrect_email_or_password": "Correo electrÃŗnico ou contrasinal incorrectos", + "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", + "repair_unable_to_check_items": "Non se puideron comprobar {count, select, one {elemento} other {elementos}}", + "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_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", + "unable_to_add_remove_favorites": "Non se puido {favorite, select, true {engadir activo a} other {eliminar activo de}} favoritos", + "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_favorite": "Non se puido cambiar o favorito do activo", + "unable_to_change_location": "Non se puido cambiar a ubicaciÃŗ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_connect_to_server": "Non se puido conectar ao servidor", + "unable_to_copy_to_clipboard": "Non se puido copiar ao portapapeis, asegÃērate 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", + "unable_to_create_user": "Non se puido crear o usuario", + "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_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_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", + "unable_to_exit_fullscreen": "Non se puido saÃr da pantalla completa", + "unable_to_get_comments_number": "Non se puido obter o nÃēmero de comentarios", + "unable_to_get_shared_link": "Erro ao obter a ligazÃŗn compartida", + "unable_to_hide_person": "Non se puido ocultar a persoa", + "unable_to_link_motion_video": "Non se puido ligar o vÃdeo en movemento", + "unable_to_link_oauth_account": "Non se puido ligar a conta OAuth", + "unable_to_load_album": "Non se puido cargar o ÃĄlbum", + "unable_to_load_asset_activity": "Non se puido cargar a actividade do activo", + "unable_to_load_items": "Non se puideron cargar os elementos", + "unable_to_load_liked_status": "Non se puido cargar o estado de gustar", + "unable_to_log_out_all_devices": "Non se puido pechar sesiÃŗn en todos os dispositivos", + "unable_to_log_out_device": "Non se puido pechar sesiÃŗn no dispositivo", + "unable_to_login_with_oauth": "Non se puido iniciar sesiÃŗn con OAuth", + "unable_to_play_video": "Non se puido reproducir o vÃdeo", + "unable_to_reassign_assets_existing_person": "Non se puideron reasignar activos a {name, select, null {unha persoa existente} other {{name}}}", + "unable_to_reassign_assets_new_person": "Non se puideron reasignar activos a unha nova persoa", + "unable_to_refresh_user": "Non se puido actualizar o usuario", + "unable_to_remove_album_users": "Non se puideron eliminar usuarios do ÃĄlbum", + "unable_to_remove_api_key": "Non se puido eliminar a Chave API", + "unable_to_remove_assets_from_shared_link": "Non se puideron eliminar activos da ligazÃŗn compartida", + "unable_to_remove_deleted_assets": "Non se puideron eliminar ficheiros fÃŗra de liÃąa", + "unable_to_remove_library": "Non se puido eliminar a biblioteca", + "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_repair_items": "Non se puideron reparar os elementos", + "unable_to_reset_password": "Non se puido restablecer o contrasinal", + "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", + "unable_to_restore_user": "Non se puido restaurar o usuario", + "unable_to_save_album": "Non se puido gardar o ÃĄlbum", + "unable_to_save_api_key": "Non se puido gardar a Chave API", + "unable_to_save_date_of_birth": "Non se puido gardar a data de nacemento", + "unable_to_save_name": "Non se puido gardar o nome", + "unable_to_save_profile": "Non se puido gardar o perfil", + "unable_to_save_settings": "Non se puido gardar a configuraciÃŗn", + "unable_to_scan_libraries": "Non se puideron escanear as bibliotecas", + "unable_to_scan_library": "Non se puido escanear a biblioteca", + "unable_to_set_feature_photo": "Non se puido establecer a foto destacada", + "unable_to_set_profile_picture": "Non se puido establecer a imaxe de perfil", + "unable_to_submit_job": "Non se puido enviar o traballo", + "unable_to_trash_asset": "Non se puido mover o activo ao lixo", + "unable_to_unlink_account": "Non se puido desvincular a conta", + "unable_to_unlink_motion_video": "Non se puido desvincular o vÃdeo en movemento", + "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_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_details": "DETALLES", + "exif_bottom_sheet_location": "UBICACIÃN", + "exif_bottom_sheet_people": "PERSOAS", + "exif_bottom_sheet_person_add_person": "Engadir nome", + "exif_bottom_sheet_person_age": "Idade {}", + "exif_bottom_sheet_person_age_months": "Idade {} meses", + "exif_bottom_sheet_person_age_year_months": "Idade 1 ano, {} meses", + "exif_bottom_sheet_person_age_years": "Idade {}", + "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_title": "Experimental", - "external_network": "External network", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", - "failed": "Failed", - "favorites": "Favorites", - "favorites_page_no_favorites": "No favorite assets found", - "filter": "Filter", - "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", - "grant_permission": "Grant permission", - "haptic_feedback_switch": "Enable haptic feedback", - "haptic_feedback_title": "Haptic Feedback", - "header_settings_add_header_tip": "Add Header", - "header_settings_field_validator_msg": "Value cannot be empty", - "header_settings_header_name_input": "Header name", - "header_settings_header_value_input": "Header value", - "headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request", - "headers_settings_tile_title": "Custom proxy headers", - "home_page_add_to_album_conflicts": "Added {added} assets to album {album}. {failed} assets are already in the album.", - "home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping", - "home_page_add_to_album_success": "Added {added} assets to album {album}.", - "home_page_album_err_partner": "Can not add partner assets to an album yet, skipping", - "home_page_archive_err_local": "Can not archive local assets yet, skipping", - "home_page_archive_err_partner": "Can not archive partner assets, skipping", - "home_page_building_timeline": "Building the timeline", - "home_page_delete_err_partner": "Can not delete partner assets, skipping", - "home_page_delete_remote_err_local": "Local assets in delete remote selection, skipping", - "home_page_favorite_err_local": "Can not favorite local assets yet, skipping", - "home_page_favorite_err_partner": "Can not favorite partner assets yet, skipping", - "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).", - "home_page_share_err_local": "Can not share local assets via link, skipping", - "home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping", - "ignore_icloud_photos": "Ignore iCloud photos", - "ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server", - "image_saved_successfully": "Image saved", - "image_viewer_page_state_provider_download_started": "Download Started", - "image_viewer_page_state_provider_download_success": "Download Success", - "image_viewer_page_state_provider_share_error": "Share Error", - "invalid_date": "Invalid date", - "invalid_date_format": "Invalid date format", - "library": "Library", - "library_page_device_albums": "Albums on Device", - "library_page_new_album": "New album", - "library_page_sort_asset_count": "Number of assets", - "library_page_sort_created": "Created date", - "library_page_sort_last_modified": "Last modified", - "library_page_sort_title": "Album title", - "local_network": "Local network", - "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", - "location_permission": "Location permission", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", - "location_picker_choose_on_map": "Choose on map", - "location_picker_latitude_error": "Enter a valid latitude", - "location_picker_latitude_hint": "Enter your latitude here", - "location_picker_longitude_error": "Enter a valid longitude", - "location_picker_longitude_hint": "Enter your longitude here", - "login_disabled": "Login has been disabled", - "login_form_api_exception": "API exception. Please check the server URL and try again.", - "login_form_back_button_text": "Back", - "login_form_email_hint": "youremail@email.com", - "login_form_endpoint_hint": "http://your-server-ip:port", - "login_form_endpoint_url": "Server Endpoint URL", - "login_form_err_http": "Please specify http:// or https://", - "login_form_err_invalid_email": "Invalid Email", - "login_form_err_invalid_url": "Invalid URL", - "login_form_err_leading_whitespace": "Leading whitespace", - "login_form_err_trailing_whitespace": "Trailing whitespace", - "login_form_failed_get_oauth_server_config": "Error logging using OAuth, check server URL", - "login_form_failed_get_oauth_server_disable": "OAuth feature is not available on this server", - "login_form_failed_login": "Error logging you in, check server URL, email and password", - "login_form_handshake_exception": "There was an Handshake Exception with the server. Enable self-signed certificate support in the settings if you are using a self-signed certificate.", - "login_form_password_hint": "password", - "login_form_save_login": "Stay logged in", - "login_form_server_empty": "Enter a server URL.", - "login_form_server_error": "Could not connect to server.", - "login_password_changed_error": "There was an error updating your password", - "login_password_changed_success": "Password updated successfully", - "map_assets_in_bound": "{} photo", - "map_assets_in_bounds": "{} photos", - "map_cannot_get_user_location": "Cannot get user's location", - "map_location_dialog_yes": "Yes", - "map_location_picker_page_use_location": "Use this location", - "map_location_service_disabled_content": "Location service needs to be enabled to display assets from your current location. Do you want to enable it now?", - "map_location_service_disabled_title": "Location Service disabled", - "map_no_assets_in_bounds": "No photos in this area", - "map_no_location_permission_content": "Location permission is needed to display assets from your current location. Do you want to allow it now?", - "map_no_location_permission_title": "Location Permission denied", - "map_settings_dark_mode": "Dark mode", - "map_settings_date_range_option_day": "Past 24 hours", - "map_settings_date_range_option_days": "Past {} days", - "map_settings_date_range_option_year": "Past year", - "map_settings_date_range_option_years": "Past {} years", - "map_settings_dialog_title": "Map Settings", - "map_settings_include_show_archived": "Include Archived", - "map_settings_include_show_partners": "Include Partners", - "map_settings_only_show_favorites": "Show Favorite Only", - "map_settings_theme_settings": "Map Theme", - "map_zoom_to_see_photos": "Zoom out to see photos", - "memories_all_caught_up": "All caught up", - "memories_check_back_tomorrow": "Check back tomorrow for more memories", - "memories_start_over": "Start Over", - "memories_swipe_to_close": "Swipe up to close", - "memories_year_ago": "A year ago", - "memories_years_ago": "{} years ago", + "expire_after": "Caduca despois de", + "expired": "Caducado", + "expires_date": "Caduca o {date}", + "explore": "Explorar", + "explorer": "Explorador", + "export": "Exportar", + "export_as_json": "Exportar como JSON", + "extension": "ExtensiÃŗn", + "external": "Externo", + "external_libraries": "Bibliotecas Externas", + "external_network": "Rede externa", + "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_load_assets": "Erro ao cargar activos", + "failed_to_load_folder": "Erro ao cargar o cartafol", + "favorite": "Favorito", + "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_setting_description": "Xestionar as funciÃŗns da aplicaciÃŗn", + "file_name": "Nome do ficheiro", + "file_name_or_extension": "Nome do ficheiro ou extensiÃŗn", + "filename": "Nome do ficheiro", + "filetype": "Tipo de ficheiro", + "filter": "Filtro", + "filter_people": "Filtrar persoas", + "filter_places": "Filtrar lugares", + "find_them_fast": "AtÃŗpaos rÃĄpido por nome coa busca", + "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", + "forward": "Adiante", + "general": "Xeral", + "get_help": "Obter Axuda", + "get_wifiname_error": "Non se puido obter o nome da Wi-Fi. AsegÃērate de que concedeu os permisos necesarios e estÃĄ conectado a unha rede Wi-Fi", + "getting_started": "Primeiros Pasos", + "go_back": "Volver", + "go_to_folder": "Ir ao cartafol", + "go_to_search": "Ir ÃĄ busca", + "grant_permission": "Conceder permiso", + "group_albums_by": "Agrupar ÃĄlbums por...", + "group_country": "Agrupar por paÃs", + "group_no": "Sen agrupaciÃŗn", + "group_owner": "Agrupar por propietario", + "group_places_by": "Agrupar lugares por...", + "group_year": "Agrupar por ano", + "haptic_feedback_switch": "Activar resposta hÃĄptica", + "haptic_feedback_title": "Resposta HÃĄptica", + "has_quota": "Ten cota", + "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", + "header_settings_header_value_input": "Valor da cabeceira", + "headers_settings_tile_subtitle": "Definir cabeceiras de proxy que a aplicaciÃŗn deberÃa enviar con cada solicitude de rede", + "headers_settings_tile_title": "Cabeceiras de proxy personalizadas", + "hi_user": "Ola {name} ({email})", + "hide_all_people": "Ocultar todas as persoas", + "hide_gallery": "Ocultar galerÃa", + "hide_named_person": "Ocultar persoa {name}", + "hide_password": "Ocultar contrasinal", + "hide_person": "Ocultar persoa", + "hide_unnamed_people": "Ocultar persoas sen nome", + "home_page_add_to_album_conflicts": "Engadidos {added} activos ao ÃĄlbum {album}. {failed} activos xa estÃĄn no ÃĄlbum.", + "home_page_add_to_album_err_local": "Non se poden engadir activos locais a ÃĄlbums aÃnda, omitindo", + "home_page_add_to_album_success": "Engadidos {added} activos ao ÃĄlbum {album}.", + "home_page_album_err_partner": "Non se poden engadir activos de compaÃąeiro/a a un ÃĄlbum aÃnda, omitindo", + "home_page_archive_err_local": "Non se poden arquivar activos locais aÃnda, omitindo", + "home_page_archive_err_partner": "Non se poden arquivar activos de compaÃąeiro/a, omitindo", + "home_page_building_timeline": "ConstruÃndo a liÃąa de tempo", + "home_page_delete_err_partner": "Non se poden eliminar activos de compaÃąeiro/a, omitindo", + "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_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": "Host", + "hour": "Hora", + "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", + "image_alt_text_date": "{isVideo, select, true {VÃdeo} other {Imaxe}} tomado/a o {date}", + "image_alt_text_date_1_person": "{isVideo, select, true {VÃdeo} other {Imaxe}} tomado/a con {person1} o {date}", + "image_alt_text_date_2_people": "{isVideo, select, true {VÃdeo} other {Imaxe}} tomado/a con {person1} e {person2} o {date}", + "image_alt_text_date_3_people": "{isVideo, select, true {VÃdeo} other {Imaxe}} tomado/a con {person1}, {person2} e {person3} o {date}", + "image_alt_text_date_4_or_more_people": "{isVideo, select, true {VÃdeo} other {Imaxe}} tomado/a con {person1}, {person2} e {additionalCount, number} outros/as o {date}", + "image_alt_text_date_place": "{isVideo, select, true {VÃdeo} other {Imaxe}} tomado/a en {city}, {country} o {date}", + "image_alt_text_date_place_1_person": "{isVideo, select, true {VÃdeo} other {Imaxe}} tomado/a en {city}, {country} con {person1} o {date}", + "image_alt_text_date_place_2_people": "{isVideo, select, true {VÃdeo} other {Imaxe}} tomado/a en {city}, {country} con {person1} e {person2} o {date}", + "image_alt_text_date_place_3_people": "{isVideo, select, true {VÃdeo} other {Imaxe}} tomado/a en {city}, {country} con {person1}, {person2} e {person3} o {date}", + "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {VÃdeo} other {Imaxe}} tomado/a en {city}, {country} con {person1}, {person2} e {additionalCount, number} outros/as o {date}", + "image_saved_successfully": "Imaxe gardada", + "image_viewer_page_state_provider_download_started": "Descarga Iniciada", + "image_viewer_page_state_provider_download_success": "Descarga Exitosa", + "image_viewer_page_state_provider_share_error": "Erro ao Compartir", + "immich_logo": "Logo de Immich", + "immich_web_interface": "Interface Web de Immich", + "import_from_json": "Importar desde JSON", + "import_path": "Ruta de importaciÃŗn", + "in_albums": "En {count, plural, one {# ÃĄlbum} other {# ÃĄlbums}}", + "in_archive": "No arquivo", + "include_archived": "IncluÃr arquivados", + "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", + "info": "InformaciÃŗn", + "interval": { + "day_at_onepm": "Todos os dÃas ÃĄs 13:00", + "hours": "Cada {hours, plural, one {hora} other {{hours, number} horas}}", + "night_at_midnight": "Todas as noites ÃĄ medianoite", + "night_at_twoam": "Todas as noites ÃĄs 2:00" + }, + "invalid_date": "Data invÃĄlida", + "invalid_date_format": "Formato de data invÃĄlido", + "invite_people": "Invitar Persoas", + "invite_to_album": "Invitar ao ÃĄlbum", + "items_count": "{count, plural, one {# elemento} other {# elementos}}", + "jobs": "Traballos", + "keep": "Conservar", + "keep_all": "Conservar Todo", + "keep_this_delete_others": "Conservar este, eliminar outros", + "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", + "last_seen": "Visto por Ãēltima vez", + "latest_version": "Ãltima VersiÃŗn", + "latitude": "Latitude", + "leave": "SaÃr", + "lens_model": "Modelo da lente", + "let_others_respond": "Permitir que outros respondan", + "level": "Nivel", + "library": "Biblioteca", + "library_options": "OpciÃŗns da biblioteca", + "library_page_device_albums": "Ãlbums no Dispositivo", + "library_page_new_album": "Novo ÃĄlbum", + "library_page_sort_asset_count": "NÃēmero de activos", + "library_page_sort_created": "Data de creaciÃŗn", + "library_page_sort_last_modified": "Ãltima modificaciÃŗn", + "library_page_sort_title": "TÃtulo do ÃĄlbum", + "light": "Claro", + "like_deleted": "GÃēstame eliminado", + "link_motion_video": "Ligar vÃdeo en movemento", + "link_options": "OpciÃŗns da ligazÃŗn", + "link_to_oauth": "Ligar a OAuth", + "linked_oauth_account": "Conta OAuth ligada", + "list": "Lista", + "loading": "Cargando", + "loading_search_results_failed": "Erro ao cargar os resultados da busca", + "local_network": "Rede local", + "local_network_sheet_info": "A aplicaciÃŗn conectarase ao servidor a travÊs desta URL cando use a rede Wi-Fi 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_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_longitude_error": "Introducir unha lonxitude vÃĄlida", + "location_picker_longitude_hint": "Introduza a tÃēa lonxitude aquÃ", + "log_out": "Pechar sesiÃŗn", + "log_out_all_devices": "Pechar SesiÃŗn en Todos os Dispositivos", + "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_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", + "login_form_err_invalid_url": "URL invÃĄlida", + "login_form_err_leading_whitespace": "Espazo en branco inicial", + "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_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_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?", + "longitude": "Lonxitude", + "look": "Ollar", + "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_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", + "map": "Mapa", + "map_assets_in_bound": "{} foto", + "map_assets_in_bounds": "{} fotos", + "map_cannot_get_user_location": "Non se pode obter a ubicaciÃŗ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_marker_for_images": "Marcador de mapa para imaxes tomadas en {city}, {country}", + "map_marker_with_image": "Marcador de mapa con imaxe", + "map_no_assets_in_bounds": "Non hai fotos nesta ÃĄrea", + "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_settings": "ConfiguraciÃŗn do mapa", + "map_settings_dark_mode": "Modo escuro", + "map_settings_date_range_option_day": "Ãltimas 24 horas", + "map_settings_date_range_option_days": "Ãltimos {} dÃas", + "map_settings_date_range_option_year": "Ãltimo ano", + "map_settings_date_range_option_years": "Ãltimos {} anos", + "map_settings_dialog_title": "ConfiguraciÃŗn do Mapa", + "map_settings_include_show_archived": "IncluÃr Arquivados", + "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", + "matches": "Coincidencias", + "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_start_over": "Comezar de novo", + "memories_swipe_to_close": "Deslizar cara arriba para pechar", + "memories_year_ago": "Hai un ano", + "memories_years_ago": "Hai {} anos", + "memory": "Recordo", + "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_successfully": "Persoas fusionadas correctamente", + "merged_people_count": "Fusionadas {count, plural, one {# persoa} other {# persoas}}", + "minimize": "Minimizar", + "minute": "Minuto", + "missing": "Faltantes", + "model": "Modelo", + "month": "Mes", "monthly_title_text_date_format": "MMMM y", - "multiselect_grid_edit_date_time_err_read_only": "Cannot edit date of read only asset(s), skipping", - "multiselect_grid_edit_gps_err_read_only": "Cannot edit location of read only asset(s), skipping", - "my_albums": "My albums", - "networking_settings": "Networking", - "networking_subtitle": "Manage the server endpoint settings", - "no_assets_to_show": "No assets to show", - "no_name": "No name", - "not_selected": "Not selected", - "notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.", - "notification_permission_list_tile_content": "Grant permission to enable notifications.", - "notification_permission_list_tile_enable_button": "Enable Notifications", - "notification_permission_list_tile_title": "Notification Permission", - "on_this_device": "On this device", - "partner_list_user_photos": "{user}'s photos", - "partner_list_view_all": "View all", - "partner_page_empty_message": "Your photos are not yet shared with any partner.", - "partner_page_no_more_users": "No more users to add", - "partner_page_partner_add_failed": "Failed to add partner", - "partner_page_select_partner": "Select partner", - "partner_page_shared_to_title": "Shared to", - "partner_page_stop_sharing_content": "{} will no longer be able to access your photos.", - "partners": "Partners", - "paused": "Paused", - "people": "People", - "permission_onboarding_back": "Back", - "permission_onboarding_continue_anyway": "Continue anyway", - "permission_onboarding_get_started": "Get started", - "permission_onboarding_go_to_settings": "Go to settings", - "permission_onboarding_permission_denied": "Permission denied. To use Immich, grant photo and video permissions in Settings.", - "permission_onboarding_permission_granted": "Permission granted! You are all set.", - "permission_onboarding_permission_limited": "Permission limited. To let Immich backup and manage your entire gallery collection, grant photo and video permissions in Settings.", - "permission_onboarding_request": "Immich requires permission to view your photos and videos.", - "places": "Places", - "preferences_settings_subtitle": "Manage the app's preferences", - "preferences_settings_title": "Preferences", - "profile_drawer_app_logs": "Logs", - "profile_drawer_client_out_of_date_major": "Mobile App is out of date. Please update to the latest major version.", - "profile_drawer_client_out_of_date_minor": "Mobile App is out of date. Please update to the latest minor version.", - "profile_drawer_client_server_up_to_date": "Client and Server are up-to-date", + "more": "MÃĄis", + "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", + "mute_memories": "Silenciar Recordos", + "my_albums": "Os meus ÃĄlbums", + "name": "Nome", + "name_or_nickname": "Nome ou alcume", + "networking_settings": "Rede", + "networking_subtitle": "Xestionar a configuraciÃŗn do punto final do servidor", + "never": "Nunca", + "new_album": "Novo Ãlbum", + "new_api_key": "Nova Chave API", + "new_password": "Novo contrasinal", + "new_person": "Nova persoa", + "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_assets_message": "PREMA PARA CARGAR A SÃA PRIMEIRA FOTO", + "no_assets_to_show": "Non hai activos para mostrar", + "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_name": "Sen Nome", + "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", + "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", + "notes": "Notas", + "notification_permission_dialog_content": "Para activar as notificaciÃŗns, vaia a Axustes e seleccione permitir.", + "notification_permission_list_tile_content": "Conceda permiso para activar as notificaciÃŗns.", + "notification_permission_list_tile_enable_button": "Activar NotificaciÃŗns", + "notification_permission_list_tile_title": "Permiso de NotificaciÃŗn", + "notification_toggle_setting_description": "Activar notificaciÃŗns por correo electrÃŗnico", + "notifications": "NotificaciÃŗns", + "notifications_setting_description": "Xestionar notificaciÃŗns", + "oauth": "OAuth", + "official_immich_resources": "Recursos Oficiais de Immich", + "offline": "FÃŗra de liÃąa", + "offline_paths": "Rutas fÃŗra de liÃąa", + "offline_paths_description": "Estes resultados poden deberse ÃĄ eliminaciÃŗn manual de ficheiros que non forman parte dunha biblioteca externa.", + "ok": "Aceptar", + "oldest_first": "MÃĄis antigos primeiro", + "on_this_device": "Neste dispositivo", + "onboarding": "IncorporaciÃŗn", + "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_welcome_description": "Imos configurar a tÃēa instancia con algunhas configuraciÃŗns comÃēns.", + "onboarding_welcome_user": "Benvido/a, {user}", + "online": "En liÃąa", + "only_favorites": "SÃŗ favoritos", + "open": "Abrir", + "open_in_map_view": "Abrir na vista de mapa", + "open_in_openstreetmap": "Abrir en OpenStreetMap", + "open_the_search_filters": "Abrir os filtros de busca", + "options": "OpciÃŗns", + "or": "ou", + "organize_your_library": "Organizar a tÃēa biblioteca", + "original": "orixinal", + "other": "Outro", + "other_devices": "Outros dispositivos", + "other_variables": "Outras variables", + "owned": "Propio", + "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_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.", + "partner_page_no_more_users": "Non hai mÃĄis usuarios para engadir", + "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": "{} xa non poderÃĄs acceder ÃĄs tÃēas fotos.", + "partner_sharing": "ComparticiÃŗn con CompaÃąeiro/a", + "partners": "CompaÃąeiros/as", + "password": "Contrasinal", + "password_does_not_match": "O contrasinal non coincide", + "password_required": "RequÃrese Contrasinal", + "password_reset_success": "Contrasinal restablecido correctamente", + "past_durations": { + "days": "Ãltimos {days, plural, one {dÃa} other {# dÃas}}", + "hours": "Ãltimas {hours, plural, one {hora} other {# horas}}", + "years": "Ãltimos {years, plural, one {ano} other {# anos}}" + }, + "path": "Ruta", + "pattern": "PadrÃŗn", + "pause": "Pausa", + "pause_memories": "Pausar recordos", + "paused": "Pausado", + "pending": "Pendente", + "people": "Persoas", + "people_edits_count": "Editadas {count, plural, one {# persoa} other {# persoas}}", + "people_feature_description": "Navegar por fotos e vÃdeos agrupados por persoas", + "people_sidebar_description": "Mostrar unha ligazÃŗn a Persoas na barra lateral", + "permanent_deletion_warning": "Aviso de eliminaciÃŗn permanente", + "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 <b>#</b> activos?}} Isto tamÊn {count, plural, one {o eliminarÃĄ do teu} other {os eliminarÃĄ dos teus}} ÃĄ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", + "permission_onboarding_continue_anyway": "Continuar de todos os xeitos", + "permission_onboarding_get_started": "Comezar", + "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.", + "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.", + "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", + "place": "Lugar", + "places": "Lugares", + "places_count": "{count, plural, one {{count, number} Lugar} other {{count, number} Lugares}}", + "play": "Reproducir", + "play_memories": "Reproducir recordos", + "play_motion_photo": "Reproducir Foto en Movemento", + "play_or_pause_video": "Reproducir ou pausar vÃdeo", + "port": "Porto", + "preferences_settings_subtitle": "Xestionar as preferencias da aplicaciÃŗn", + "preferences_settings_title": "Preferencias", + "preset": "Preaxuste", + "preview": "Vista previa", + "previous": "Anterior", + "previous_memory": "Recordo anterior", + "previous_or_next_photo": "Foto anterior ou seguinte", + "primary": "Principal", + "privacy": "Privacidade", + "profile_drawer_app_logs": "Rexistros", + "profile_drawer_client_out_of_date_major": "A aplicaciÃŗn mÃŗbil estÃĄ desactualizada. Por favor, actualice ÃĄ Ãēltima versiÃŗn maior.", + "profile_drawer_client_out_of_date_minor": "A aplicaciÃŗn mÃŗbil estÃĄ desactualizada. Por favor, actualice ÃĄ Ãēltima versiÃŗn menor.", + "profile_drawer_client_server_up_to_date": "Cliente e Servidor estÃĄn actualizados", "profile_drawer_github": "GitHub", - "profile_drawer_server_out_of_date_major": "Server is out of date. Please update to the latest major version.", - "profile_drawer_server_out_of_date_minor": "Server is out of date. Please update to the latest minor version.", - "recently_added": "Recently added", - "recently_added_page_title": "Recently Added", - "save": "Save", - "save_to_gallery": "Save to gallery", - "scaffold_body_error_occurred": "Error occurred", - "search_albums": "Search albums", - "search_filter_apply": "Apply filter", - "search_filter_camera_title": "Select camera type", - "search_filter_date": "Date", - "search_filter_date_interval": "{start} to {end}", - "search_filter_date_title": "Select a date range", - "search_filter_display_option_not_in_album": "Not in album", - "search_filter_display_options": "Display Options", - "search_filter_location": "Location", - "search_filter_location_title": "Select location", - "search_filter_media_type": "Media Type", - "search_filter_media_type_title": "Select media type", - "search_filter_people_title": "Select people", - "search_page_categories": "Categories", - "search_page_motion_photos": "Motion Photos", - "search_page_no_objects": "No Objects Info Available", - "search_page_no_places": "No Places Info Available", - "search_page_screenshots": "Screenshots", - "search_page_search_photos_videos": "Search for your photos and videos", + "profile_drawer_server_out_of_date_major": "O servidor estÃĄ desactualizado. Por favor, actualice ÃĄ Ãēltima versiÃŗn maior.", + "profile_drawer_server_out_of_date_minor": "O servidor estÃĄ desactualizado. Por favor, actualice ÃĄ Ãēltima versiÃŗn menor.", + "profile_image_of_user": "Imaxe de perfil de {user}", + "profile_picture_set": "Imaxe de perfil establecida.", + "public_album": "Ãlbum pÃēblico", + "public_share": "Compartir PÃēblico", + "purchase_account_info": "Seguidor/a", + "purchase_activated_subtitle": "Grazas por apoiar Immich e o software de cÃŗdigo aberto", + "purchase_activated_time": "Activado o {date, 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_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_individual_description_1": "Para un individuo", + "purchase_individual_description_2": "Estado de seguidor/a", + "purchase_individual_title": "Individual", + "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_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_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_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_server_description_1": "Para todo o servidor", + "purchase_server_description_2": "Estado de seguidor/a", + "purchase_server_title": "Servidor", + "purchase_settings_server_activated": "A chave do produto do servidor Ê xestionada polo administrador", + "rating": "ClasificaciÃŗn por estrelas", + "rating_clear": "Borrar clasificaciÃŗn", + "rating_count": "{count, plural, one {# estrela} other {# estrelas}}", + "rating_description": "Mostrar a clasificaciÃŗn EXIF no panel de informaciÃŗn", + "reaction_options": "OpciÃŗns de reacciÃŗn", + "read_changelog": "Ler Rexistro de Cambios", + "reassign": "Reasignar", + "reassigned_assets_to_existing_person": "Reasignados {count, plural, one {# activo} other {# activos}} a {name, select, null {unha persoa existente} other {{name}}}", + "reassigned_assets_to_new_person": "Reasignados {count, plural, one {# activo} other {# activos}} a unha nova persoa", + "reassing_hint": "Asignar activos seleccionados a unha persoa existente", + "recent": "Recente", + "recent-albums": "Ãlbums recentes", + "recent_searches": "Buscas recentes", + "recently_added": "Engadido recentemente", + "recently_added_page_title": "Engadido Recentemente", + "refresh": "Actualizar", + "refresh_encoded_videos": "Actualizar vÃdeos codificados", + "refresh_faces": "Actualizar caras", + "refresh_metadata": "Actualizar metadatos", + "refresh_thumbnails": "Actualizar miniaturas", + "refreshed": "Actualizado", + "refreshes_every_file": "Volve ler todos os ficheiros existentes e novos", + "refreshing_encoded_video": "Actualizando vÃdeo codificado", + "refreshing_faces": "Actualizando caras", + "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_title": "Eliminar activos?", + "remove_custom_date_range": "Eliminar rango de datas personalizado", + "remove_deleted_assets": "Eliminar Activos Eliminados", + "remove_from_album": "Eliminar do ÃĄlbum", + "remove_from_favorites": "Eliminar de favoritos", + "remove_from_shared_link": "Eliminar da ligazÃŗn compartida", + "remove_memory": "Eliminar recordo", + "remove_photo_from_memory": "Eliminar foto deste recordo", + "remove_url": "Eliminar URL", + "remove_user": "Eliminar usuario", + "removed_api_key": "Chave API eliminada: {name}", + "removed_from_archive": "Eliminado do arquivo", + "removed_from_favorites": "Eliminado de favoritos", + "removed_from_favorites_count": "{count, plural, other {Eliminados #}} de favoritos", + "removed_memory": "Recordo eliminado", + "removed_photo_from_memory": "Foto eliminada do recordo", + "removed_tagged_assets": "Etiqueta eliminada de {count, plural, one {# activo} other {# activos}}", + "rename": "Renomear", + "repair": "Reparar", + "repair_no_results_message": "Os ficheiros non rastrexados e faltantes aparecerÃĄn aquÃ", + "replace_with_upload": "SubstituÃr con carga", + "repository": "Repositorio", + "require_password": "Requirir contrasinal", + "require_user_to_change_password_on_first_login": "Requirir que o usuario cambie o contrasinal no primeiro inicio de sesiÃŗn", + "rescan": "Volver escanear", + "reset": "Restablecer", + "reset_password": "Restablecer contrasinal", + "reset_people_visibility": "Restablecer visibilidade das persoas", + "reset_to_default": "Restablecer ao predeterminado", + "resolve_duplicates": "Resolver duplicados", + "resolved_all_duplicates": "ResolvÊronse todos os duplicados", + "restore": "Restaurar", + "restore_all": "Restaurar todo", + "restore_user": "Restaurar usuario", + "restored_asset": "Activo restaurado", + "resume": "Reanudar", + "retry_upload": "Reintentar carga", + "review_duplicates": "Revisar duplicados", + "role": "Rol", + "role_editor": "Editor", + "role_viewer": "Visor", + "save": "Gardar", + "save_to_gallery": "Gardar na galerÃa", + "saved_api_key": "Chave API gardada", + "saved_profile": "Perfil gardado", + "saved_settings": "ConfiguraciÃŗn gardada", + "say_something": "Dicir algo", + "scaffold_body_error_occurred": "Ocorreu un erro", + "scan_all_libraries": "Escanear Todas as Bibliotecas", + "scan_library": "Escanear", + "scan_settings": "ConfiguraciÃŗn de Escaneo", + "scanning_for_album": "Escaneando ÃĄlbum...", + "search": "Buscar", + "search_albums": "Buscar ÃĄlbums", + "search_by_context": "Buscar por contexto", + "search_by_description": "Buscar por descriciÃŗn", + "search_by_description_example": "DÃa de sendeirismo en Sapa", + "search_by_filename": "Buscar por nome de ficheiro ou extensiÃŗn", + "search_by_filename_example": "p. ex. IMG_1234.JPG ou PNG", + "search_camera_make": "Buscar marca de cÃĄmara...", + "search_camera_model": "Buscar modelo de cÃĄmara...", + "search_city": "Buscar cidade...", + "search_country": "Buscar paÃs...", + "search_filter_apply": "Aplicar filtro", + "search_filter_camera_title": "Seleccionar tipo de cÃĄmara", + "search_filter_date": "Data", + "search_filter_date_interval": "{start} a {end}", + "search_filter_date_title": "Seleccionar un rango de datas", + "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_media_type": "Tipo de Medio", + "search_filter_media_type_title": "Seleccionar tipo de medio", + "search_filter_people_title": "Seleccionar persoas", + "search_for": "Buscar por", + "search_for_existing_person": "Buscar persoa existente", + "search_no_more_result": "Non hai mÃĄis resultados", + "search_no_people": "Sen persoas", + "search_no_people_named": "Sen persoas chamadas \"{name}\"", + "search_no_result": "Non se atoparon resultados, probe cun termo de busca ou combinaciÃŗn diferente", + "search_options": "OpciÃŗns de busca", + "search_page_categories": "CategorÃas", + "search_page_motion_photos": "Fotos en Movemento", + "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_selfies": "Selfies", - "search_page_things": "Things", - "search_page_view_all_button": "View all", - "search_page_your_activity": "Your activity", - "search_page_your_map": "Your Map", - "search_result_page_new_search_hint": "New Search", - "search_suggestion_list_smart_search_hint_1": "Smart search is enabled by default, to search for metadata use the syntax ", - "search_suggestion_list_smart_search_hint_2": "m:your-search-term", - "select_user_for_sharing_page_err_album": "Failed to create album", - "server_endpoint": "Server Endpoint", - "server_info_box_app_version": "App Version", - "server_info_box_server_url": "Server URL", - "setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).", - "setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).", - "setting_image_viewer_original_title": "Load original image", - "setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.", - "setting_image_viewer_preview_title": "Load preview image", - "setting_image_viewer_title": "Images", - "setting_languages_apply": "Apply", - "setting_languages_subtitle": "Change the app's language", - "setting_languages_title": "Languages", - "setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}", - "setting_notifications_notify_hours": "{} hours", - "setting_notifications_notify_immediately": "immediately", - "setting_notifications_notify_minutes": "{} minutes", - "setting_notifications_notify_never": "never", - "setting_notifications_notify_seconds": "{} seconds", - "setting_notifications_single_progress_subtitle": "Detailed upload progress information per asset", - "setting_notifications_single_progress_title": "Show background backup detail progress", - "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_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", - "settings_require_restart": "Please restart Immich to apply this setting", - "share_add_photos": "Add photos", - "share_assets_selected": "{} selected", - "share_dialog_preparing": "Preparing...", - "shared_album_activities_input_disable": "Comment is disabled", - "shared_album_activity_remove_content": "Do you want to delete this activity?", - "shared_album_activity_remove_title": "Delete Activity", - "shared_album_section_people_action_error": "Error leaving/removing from album", - "shared_album_section_people_action_leave": "Remove user from album", - "shared_album_section_people_action_remove_user": "Remove user from album", - "shared_album_section_people_title": "PEOPLE", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", - "shared_link_app_bar_title": "Shared Links", - "shared_link_clipboard_copied_massage": "Copied to clipboard", - "shared_link_clipboard_text": "Link: {}\nPassword: {}", - "shared_link_create_error": "Error while creating shared link", - "shared_link_edit_description_hint": "Enter the share description", - "shared_link_edit_expire_after_option_day": "1 day", - "shared_link_edit_expire_after_option_days": "{} days", - "shared_link_edit_expire_after_option_hour": "1 hour", - "shared_link_edit_expire_after_option_hours": "{} hours", - "shared_link_edit_expire_after_option_minute": "1 minute", - "shared_link_edit_expire_after_option_minutes": "{} minutes", - "shared_link_edit_expire_after_option_months": "{} months", - "shared_link_edit_expire_after_option_year": "{} year", - "shared_link_edit_password_hint": "Enter the share password", - "shared_link_edit_submit_button": "Update link", - "shared_link_error_server_url_fetch": "Cannot fetch the server url", - "shared_link_expires_day": "Expires in {} day", - "shared_link_expires_days": "Expires in {} days", - "shared_link_expires_hour": "Expires in {} hour", - "shared_link_expires_hours": "Expires in {} hours", - "shared_link_expires_minute": "Expires in {} minute", - "shared_link_expires_minutes": "Expires in {} minutes", - "shared_link_expires_never": "Expires â", - "shared_link_expires_second": "Expires in {} second", - "shared_link_expires_seconds": "Expires in {} seconds", - "shared_link_individual_shared": "Individual shared", + "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_people": "Buscar persoas", + "search_places": "Buscar lugares", + "search_rating": "Buscar por clasificaciÃŗn...", + "search_result_page_new_search_hint": "Nova Busca", + "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_tags": "Buscar etiquetas...", + "search_timezone": "Buscar fuso horario...", + "search_type": "Tipo de busca", + "search_your_photos": "Buscar as tÃēas fotos", + "searching_locales": "Buscando configuraciÃŗns rexionais...", + "second": "Segundo", + "see_all_people": "Ver todas as persoas", + "select": "Seleccionar", + "select_album_cover": "Seleccionar portada do ÃĄlbum", + "select_all": "Seleccionar todo", + "select_all_duplicates": "Seleccionar todos os duplicados", + "select_avatar_color": "Seleccionar cor do avatar", + "select_face": "Seleccionar cara", + "select_featured_photo": "Seleccionar foto destacada", + "select_from_computer": "Seleccionar do ordenador", + "select_keep_all": "Seleccionar conservar todo", + "select_library_owner": "Seleccionar propietario da biblioteca", + "select_new_face": "Seleccionar nova cara", + "select_photos": "Seleccionar fotos", + "select_trash_all": "Seleccionar mover todo ao lixo", + "select_user_for_sharing_page_err_album": "Erro ao crear o ÃĄlbum", + "selected": "Seleccionado", + "selected_count": "{count, plural, other {# seleccionados}}", + "send_message": "Enviar mensaxe", + "send_welcome_email": "Enviar correo electrÃŗnico de benvida", + "server_endpoint": "Punto Final do Servidor", + "server_info_box_app_version": "VersiÃŗn da AplicaciÃŗn", + "server_info_box_server_url": "URL do Servidor", + "server_offline": "Servidor FÃŗra de LiÃąa", + "server_online": "Servidor En LiÃąa", + "server_stats": "EstatÃsticas do Servidor", + "server_version": "VersiÃŗn do Servidor", + "set": "Establecer", + "set_as_album_cover": "Establecer como portada do ÃĄlbum", + "set_as_featured_photo": "Establecer como foto destacada", + "set_as_profile_picture": "Establecer como imaxe de perfil", + "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_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.", + "setting_image_viewer_preview_title": "Cargar imaxe de vista previa", + "setting_image_viewer_title": "Imaxes", + "setting_languages_apply": "Aplicar", + "setting_languages_subtitle": "Cambiar a lingua da aplicaciÃŗn", + "setting_languages_title": "Linguas", + "setting_notifications_notify_failures_grace_period": "Notificar fallos da copia de seguridade en segundo plano: {}", + "setting_notifications_notify_hours": "{} horas", + "setting_notifications_notify_immediately": "inmediatamente", + "setting_notifications_notify_minutes": "{} minutos", + "setting_notifications_notify_never": "nunca", + "setting_notifications_notify_seconds": "{} 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_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_title": "Forzar vÃdeo orixinal", + "settings": "ConfiguraciÃŗn", + "settings_require_restart": "Por favor, reinicie Immich para aplicar esta configuraciÃŗn", + "settings_saved": "ConfiguraciÃŗn gardada", + "share": "Compartir", + "share_add_photos": "Engadir fotos", + "share_assets_selected": "{} seleccionados", + "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_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", + "shared_album_section_people_action_remove_user": "Eliminar usuario do ÃĄlbum", + "shared_album_section_people_title": "PERSOAS", + "shared_by": "Compartido por", + "shared_by_user": "Compartido por {user}", + "shared_by_you": "Compartido por ti", + "shared_from_partner": "Fotos de {partner}", + "shared_intent_upload_button_progress_text": "{} / {} Subidos", + "shared_link_app_bar_title": "LigazÃŗns Compartidas", + "shared_link_clipboard_copied_massage": "Copiado ao portapapeis", + "shared_link_clipboard_text": "LigazÃŗn: {}\nContrasinal: {}", + "shared_link_create_error": "Erro ao crear ligazÃŗn compartida", + "shared_link_edit_description_hint": "Introduza a descriciÃŗn da comparticiÃŗn", + "shared_link_edit_expire_after_option_day": "1 dÃa", + "shared_link_edit_expire_after_option_days": "{} dÃas", + "shared_link_edit_expire_after_option_hour": "1 hora", + "shared_link_edit_expire_after_option_hours": "{} horas", + "shared_link_edit_expire_after_option_minute": "1 minuto", + "shared_link_edit_expire_after_option_minutes": "{} minutos", + "shared_link_edit_expire_after_option_months": "{} meses", + "shared_link_edit_expire_after_option_year": "{} 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_expires_day": "Caduca en {} dÃa", + "shared_link_expires_days": "Caduca en {} dÃas", + "shared_link_expires_hour": "Caduca en {} hora", + "shared_link_expires_hours": "Caduca en {} horas", + "shared_link_expires_minute": "Caduca en {} minuto", + "shared_link_expires_minutes": "Caduca en {} minutos", + "shared_link_expires_never": "Caduca â", + "shared_link_expires_second": "Caduca en {} segundo", + "shared_link_expires_seconds": "Caduca en {} segundos", + "shared_link_individual_shared": "Compartido individualmente", "shared_link_info_chip_metadata": "EXIF", - "shared_link_manage_links": "Manage Shared links", - "shared_links": "Shared links", - "shared_with_me": "Shared with me", - "sharing_page_album": "Shared albums", - "sharing_page_description": "Create shared albums to share photos and videos with people in your network.", - "sharing_page_empty_list": "EMPTY LIST", - "sharing_silver_appbar_create_shared_album": "New shared album", - "sharing_silver_appbar_share_partner": "Share with partner", - "start_date": "Start date", - "sync": "Sync", - "sync_albums": "Sync albums", - "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", - "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", - "theme_setting_asset_list_storage_indicator_title": "Show storage indicator on asset tiles", - "theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({})", - "theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.", - "theme_setting_colorful_interface_title": "Colorful interface", - "theme_setting_image_viewer_quality_subtitle": "Adjust the quality of the detail image viewer", - "theme_setting_image_viewer_quality_title": "Image viewer quality", - "theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.", - "theme_setting_primary_color_title": "Primary color", - "theme_setting_system_primary_color_title": "Use system color", - "theme_setting_system_theme_switch": "Automatic (Follow system setting)", - "theme_setting_theme_subtitle": "Choose the app's theme setting", - "theme_setting_three_stage_loading_subtitle": "Three-stage loading might increase the loading performance but causes significantly higher network load", - "theme_setting_three_stage_loading_title": "Enable three-stage loading", - "trash": "Trash", - "trash_emptied": "Emptied trash", - "trash_page_delete_all": "Delete All", - "trash_page_empty_trash_dialog_content": "Do you want to empty your trashed assets? These items will be permanently removed from Immich", - "trash_page_info": "Trashed items will be permanently deleted after {} days", - "trash_page_no_assets": "No trashed assets", - "trash_page_restore_all": "Restore All", - "trash_page_select_assets_btn": "Select assets", - "trash_page_title": "Trash ({})", - "upload": "Upload", - "upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?", - "upload_dialog_title": "Upload Asset", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", - "use_current_connection": "use current connection", - "validate_endpoint_error": "Please enter a valid URL", - "version_announcement_overlay_release_notes": "release notes", - "version_announcement_overlay_text_1": "Hi friend, there is a new release of", - "version_announcement_overlay_text_2": "please take your time to visit the ", - "version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.", - "version_announcement_overlay_title": "New Server Version Available đ", - "videos": "Videos", - "viewer_remove_from_stack": "Remove from Stack", - "viewer_stack_use_as_main_asset": "Use as Main Asset", - "viewer_unstack": "Un-Stack", - "wifi_name": "WiFi Name", + "shared_link_manage_links": "Xestionar ligazÃŗns Compartidas", + "shared_link_options": "OpciÃŗns da ligazÃŗn compartida", + "shared_links": "LigazÃŗns compartidas", + "shared_links_description": "Compartir fotos e vÃdeos cunha ligazÃŗn", + "shared_photos_and_videos_count": "{assetCount, plural, other {# fotos e vÃdeos compartidos.}}", + "shared_with_me": "Compartido comigo", + "shared_with_partner": "Compartido con {partner}", + "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_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", + "sharing_silver_appbar_share_partner": "Compartir con compaÃąeiro/a", + "shift_to_permanent_delete": "prema â§ para eliminar permanentemente o activo", + "show_album_options": "Mostrar opciÃŗns do ÃĄlbum", + "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_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_keyboard_shortcuts": "Mostrar atallos de teclado", + "show_metadata": "Mostrar metadatos", + "show_or_hide_info": "Mostrar ou ocultar informaciÃŗn", + "show_password": "Mostrar contrasinal", + "show_person_options": "Mostrar opciÃŗns da persoa", + "show_progress_bar": "Mostrar Barra de Progreso", + "show_search_options": "Mostrar opciÃŗns de busca", + "show_shared_links": "Mostrar ligazÃŗns compartidas", + "show_slideshow_transition": "Mostrar transiciÃŗn da presentaciÃŗn", + "show_supporter_badge": "Insignia de seguidor/a", + "show_supporter_badge_description": "Mostrar unha insignia de seguidor/a", + "shuffle": "Aleatorio", + "sidebar": "Barra lateral", + "sidebar_display_description": "Mostrar unha ligazÃŗn ÃĄ vista na barra lateral", + "sign_out": "Pechar SesiÃŗn", + "sign_up": "Rexistrarse", + "size": "TamaÃąo", + "skip_to_content": "Saltar ao contido", + "skip_to_folders": "Saltar a cartafoles", + "skip_to_tags": "Saltar a etiquetas", + "slideshow": "PresentaciÃŗn", + "slideshow_settings": "ConfiguraciÃŗn da presentaciÃŗn", + "sort_albums_by": "Ordenar ÃĄlbums por...", + "sort_created": "Data de creaciÃŗn", + "sort_items": "NÃēmero de elementos", + "sort_modified": "Data de modificaciÃŗn", + "sort_oldest": "Foto mÃĄis antiga", + "sort_people_by_similarity": "Ordenar persoas por similitude", + "sort_recent": "Foto mÃĄis recente", + "sort_title": "TÃtulo", + "source": "Fonte", + "stack": "Apilar", + "stack_duplicates": "Apilar duplicados", + "stack_select_one_photo": "Seleccionar unha foto principal para a pila", + "stack_selected_photos": "Apilar fotos seleccionadas", + "stacked_assets_count": "Apilados {count, plural, one {# activo} other {# activos}}", + "stacktrace": "Rastro da Pila", + "start": "Iniciar", + "start_date": "Data de inicio", + "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", + "storage_label": "Etiqueta de almacenamento", + "storage_usage": "{used} de {available} usado", + "submit": "Enviar", + "suggestions": "SuxestiÃŗns", + "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.", + "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", + "tag": "Etiqueta", + "tag_assets": "Etiquetar activos", + "tag_created": "Etiqueta creada: {tag}", + "tag_feature_description": "Navegar por fotos e vÃdeos agrupados por temas de etiquetas lÃŗxicas", + "tag_not_found_question": "Non atopa unha etiqueta? <link>Crear unha nova etiqueta.</link>", + "tag_people": "Etiquetar Persoas", + "tag_updated": "Etiqueta actualizada: {tag}", + "tagged_assets": "Etiquetados {count, plural, one {# activo} other {# activos}}", + "tags": "Etiquetas", + "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_setting_asset_list_tiles_per_row_title": "NÃēmero de activos por fila ({})", + "theme_setting_colorful_interface_subtitle": "Aplicar cor primaria ÃĄs superficies de fondo.", + "theme_setting_colorful_interface_title": "Interface colorida", + "theme_setting_image_viewer_quality_subtitle": "Axustar a calidade do visor de imaxes de detalle", + "theme_setting_image_viewer_quality_title": "Calidade do visor de imaxes", + "theme_setting_primary_color_subtitle": "Elixa unha cor para acciÃŗns primarias e acentos.", + "theme_setting_primary_color_title": "Cor primaria", + "theme_setting_system_primary_color_title": "Usar cor do sistema", + "theme_setting_system_theme_switch": "AutomÃĄtico (Seguir configuraciÃŗn do sistema)", + "theme_setting_theme_subtitle": "Elixir a configuraciÃŗn do tema da aplicaciÃŗn", + "theme_setting_three_stage_loading_subtitle": "A carga en tres etapas pode aumentar o rendemento da carga pero causa unha carga de rede significativamente maior", + "theme_setting_three_stage_loading_title": "Activar carga en tres etapas", + "they_will_be_merged_together": "Fusionaranse xuntos", + "third_party_resources": "Recursos de Terceiros", + "time_based_memories": "Recordos baseados no tempo", + "timeline": "LiÃąa de tempo", + "timezone": "Fuso horario", + "to_archive": "Arquivar", + "to_change_password": "Cambiar contrasinal", + "to_favorite": "Favorito", + "to_login": "Iniciar sesiÃŗn", + "to_parent": "Ir ao pai", + "to_trash": "Lixo", + "toggle_settings": "Alternar configuraciÃŗn", + "toggle_theme": "Alternar tema escuro", + "total": "Total", + "total_usage": "Uso total", + "trash": "Lixo", + "trash_all": "Mover Todo ao Lixo", + "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_info": "Os elementos no lixo eliminaranse permanentemente despois de {} dÃas", + "trash_page_no_assets": "Non hai activos no lixo", + "trash_page_restore_all": "Restaurar Todo", + "trash_page_select_assets_btn": "Seleccionar activos", + "trash_page_title": "Lixo ({})", + "trashed_items_will_be_permanently_deleted_after": "Os elementos no lixo eliminaranse permanentemente despois de {days, plural, one {# dÃa} other {# dÃas}}.", + "type": "Tipo", + "unarchive": "Desarquivar", + "unarchived_count": "{count, plural, other {Desarquivados #}}", + "unfavorite": "Desmarcar como favorito", + "unhide_person": "Mostrar persoa", + "unknown": "DescoÃąecido", + "unknown_country": "PaÃs DescoÃąecido", + "unknown_year": "Ano DescoÃąecido", + "unlimited": "Ilimitado", + "unlink_motion_video": "Desvincular vÃdeo en movemento", + "unlink_oauth": "Desvincular OAuth", + "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", + "unsaved_change": "Cambio sen gardar", + "unselect_all": "Deseleccionar todo", + "unselect_all_duplicates": "Deseleccionar todos os duplicados", + "unstack": "Desapilar", + "unstacked_assets_count": "Desapilados {count, plural, one {# activo} other {# activos}}", + "untracked_files": "Ficheiros non rastrexados", + "untracked_files_decription": "Estes ficheiros non son rastrexados pola aplicaciÃŗn. Poden ser o resultado de movementos fallidos, cargas interrompidas ou deixados atrÃĄs debido a un erro", + "up_next": "A continuaciÃŗn", + "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_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_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_to_immich": "Subir a Immich ({})", + "uploading": "Subindo", + "url": "URL", + "usage": "Uso", + "use_current_connection": "usar conexiÃŗn actual", + "use_custom_date_range": "Usar rango de datas personalizado no seu lugar", + "user": "Usuario", + "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_role_set": "Establecer {user} como {role}", + "user_usage_detail": "Detalle de uso do usuario", + "user_usage_stats": "EstatÃsticas de uso da conta", + "user_usage_stats_description": "Ver estatÃsticas de uso da conta", + "username": "Nome de usuario", + "users": "Usuarios", + "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 <link>notas de lanzamento</link> 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_overlay_release_notes": "notas de lanzamento", + "version_announcement_overlay_text_1": "Ola amigo/a, hai unha nova versiÃŗn de", + "version_announcement_overlay_text_2": "por favor, toma o teu tempo para visitar as ", + "version_announcement_overlay_text_3": " e asegÃērate de que a tÃēa configuraciÃŗn de docker-compose e .env estÃĄ actualizada para evitar calquera configuraciÃŗn incorrecta, especialmente se usa WatchTower ou calquera mecanismo que xestione a actualizaciÃŗn automÃĄtica da tÃēa aplicaciÃŗn de servidor.", + "version_announcement_overlay_title": "Nova VersiÃŗn do Servidor DispoÃąible đ", + "version_history": "Historial de VersiÃŗns", + "version_history_item": "Instalado {version} o {date}", + "video": "VÃdeo", + "video_hover_setting": "Reproducir miniatura do vÃdeo ao pasar o rato por riba", + "video_hover_setting_description": "Reproducir miniatura do vÃdeo cando o rato estÃĄ sobre o elemento. Mesmo cando estÃĄ desactivado, a reproduciÃŗn pode iniciarse pasando o rato sobre a icona de reproduciÃŗn.", + "videos": "VÃdeos", + "videos_count": "{count, plural, one {# VÃdeo} other {# VÃdeos}}", + "view": "Ver", + "view_album": "Ver Ãlbum", + "view_all": "Ver Todo", + "view_all_users": "Ver todos os usuarios", + "view_in_timeline": "Ver na liÃąa de tempo", + "view_link": "Ver ligazÃŗn", + "view_links": "Ver ligazÃŗns", + "view_name": "Vista", + "view_next_asset": "Ver seguinte activo", + "view_previous_asset": "Ver activo anterior", + "view_qr_code": "Ver cÃŗdigo QR", + "view_stack": "Ver Pila", + "viewer_remove_from_stack": "Eliminar da Pila", + "viewer_stack_use_as_main_asset": "Usar como Activo Principal", + "viewer_unstack": "Desapilar", + "visibility_changed": "Visibilidade cambiada para {count, plural, one {# persoa} other {# persoas}}", + "waiting": "Agardando", + "warning": "Aviso", + "week": "Semana", + "welcome": "Benvido/a", + "welcome_to_immich": "Benvido/a a Immich", + "wifi_name": "Nome da WiFi", "year": "Ano", + "years_ago": "Hai {years, plural, one {# ano} other {# anos}}", "yes": "Si", - "your_wifi_name": "Your WiFi name", - "zoom_image": "Acercar imaxe" + "you_dont_have_any_shared_links": "Non tes ningunha ligazÃŗn compartida", + "your_wifi_name": "O nome da tÃēa WiFi", + "zoom_image": "Ampliar Imaxe" } diff --git a/i18n/he.json b/i18n/he.json index 6a743ad6f7..927a43f020 100644 --- a/i18n/he.json +++ b/i18n/he.json @@ -33,7 +33,7 @@ "added_to_favorites_count": "{count, number} × ××Ą×¤× ××××ĸ×פ××", "admin": { "add_exclusion_pattern_description": "××ץפ×Ē ×פ××Ą× ××ר××. × ×Ē×××Ē ××Ē×××Ē ×פ×ץ×× ××××Ļ×ĸ××Ē *, ** ×-?. ××× ×××Ē×ĸ×× ××× ××§××Ļ×× ××Ē××§×× ××׊×× ××Š× \"Raw\", ×׊ ××׊×Ē×׊ × \"**/Raw/**\". ××× ×××Ē×ĸ×× ××× ××§××Ļ×× ××ץ×Ē××××× × \"tif.\", ×׊ ××׊×Ē×׊ × \"tif.*/**\". ××× ×××Ē×ĸ×× ×× ×Ē×× ×××××, ×׊ ××׊×Ē×׊ × \"**/× ×Ē××/×××Ē×ĸ××××Ē\".", - "asset_offline_description": "× ×ץ ץפר××× ×××Ļ×× ××Ē ×× ×× × ××Ļ× ×××Ēר ×××ץק ××××ĸ×ר ××׊פ×. ×× ××§×××Ĩ ×××ĸ×ר ××Ē×× ×ץפר×××, × × ×××××§ ××Ē ×Ļ×ר ×××× ×Š×× ×ĸ××ר ×× ×ץ ×××§××× ×××׊. ××× ×׊××ר × ×ץ ××, × × ××××× ×Š-Immich ×××× ××׊×Ē ×× × ×Ē×× ××§×××Ĩ ×××× ××ץר××§ ×××׊ ××Ē ×ץפר×××.", + "asset_offline_description": "×Ē××× × ×ץפר××× ×××Ļ×× ××Ē ×× ×× × ××Ļ××Ē ×××Ēר ×××ץק ××××ĸ××¨× ××׊פ×. ×× ××§×××Ĩ ×××ĸ×ר ××Ē×× ×ץפר×××, × × ×××××§ ××Ē ×Ļ×ר ×××× ×Š×× ×ĸ××ר ××Ē××× × ×××§×××× ×××׊. ××× ×׊××ר ×Ē××× × ××, × × ××××× ×Š-Immich ×××× ××׊×Ē ×× × ×Ē×× ××§×××Ĩ ×××× ××ץר××§ ×××׊ ××Ē ×ץפר×××.", "authentication_settings": "×××ר××Ē ××Ē××ר××Ē", "authentication_settings_description": "× ×××× ×Ą×ץ××, OAuth, ××××ר××Ē ××Ē××ר××Ē ××ר××Ē", "authentication_settings_disable_all": "××× ×ר×Ļ×× × ××׊×××Ē ××Ē ×× ×Š××××Ē ×××Ē××ר××Ē? ×× ××Ą× ×××ĸר××Ē ×Ē××× ××׊××Ē×Ē ×××××××.", @@ -49,7 +49,7 @@ "cleared_jobs": "× ××§× ×׊××××Ē ×ĸ××ר: {job}", "config_set_by_file": "××Ē×Ļ××¨× ××××ר×Ē ××ĸ×Ē ×ĸ× ××× ×§×××Ĩ ×Ē×Ļ×ר×", "confirm_delete_library": "××× ××××Ē ×ר×Ļ×× × ×××××§ ××Ē ×ץפר××× {library}?", - "confirm_delete_library_assets": "××× ××××Ē ×ר×Ļ×× × ×××××§ ××Ē ×ץפר××× ×××? ×× ××××§ ××Ē {count, plural, one {× ×ץ # ×××××} other {×× # ×× ×ץ×× ×××××××}} ×× ×-Immich ×××× × × ××Ē× ××××××. ×§××Ļ×× ××׊××¨× ×××ץק.", + "confirm_delete_library_assets": "××× ××××Ē ×ר×Ļ×× × ×××××§ ××Ē ×ץפר××× ×××? ×× ××××§ ××Ē {count, plural, one {×Ē××× × # ××××××Ē} other {×× # ×Ē××× ××Ē ×××××××}} ×× ×-Immich ×××× × × ××Ē× ××××××. ×§××Ļ×× ××׊××¨× ×××ץק.", "confirm_email_below": "××× ××׊ר, ×׊ ×××§××× \"{email}\" ××××", "confirm_reprocess_all_faces": "××× ××××Ē ×ר×Ļ×× × ××ĸ×× ×××׊ ××Ē ×× ××¤× ××? ×× ×× ×× ×§× ×× ×Š×× ××ĸ×× ×Š×.", "confirm_user_password_reset": "××× ××××Ē ×ר×Ļ×× × ××פץ ××Ē ×ץ×ץ×× ×Š× ××׊×Ē×׊ {user}?", @@ -58,15 +58,15 @@ "cron_expression_description": "×××ר ××Ē ×ר××× ×ץר××§× ××××Ļ×ĸ××Ē ×Ē×× ××Ē ×- cron. ×××××ĸ × ××Ą×Ŗ × × ××¤× ××Ē ×××Š× ×× <link>Crontab Guru</link>", "cron_expression_presets": "×××ר××Ē ×§×××ĸ××Ē ×ר×׊ ×Š× ××××× cron", "disable_login": "×׊××Ē ×× ×ץ×", - "duplicate_detection_job_description": "×פ×ĸ× ×××××Ē ×××× × ×ĸ× × ×ץ×× ××× ×××××Ē ×Ē××× ××Ē ×××××Ē. × ×Š×ĸ× ×ĸ× ××פ×׊ ×××", + "duplicate_detection_job_description": "×פ×ĸ× ×××××Ē ×××× × ×ĸ× ×Ē××× ××Ē ××× ×××××Ē ×Ē××× ××Ē ×××××Ē. × ×Š×ĸ× ×ĸ× ××פ×׊ ×××", "exclusion_pattern_description": "×פ××Ą× ××ר×× ××פ׊ר×× ×× ×××Ē×ĸ×× ××§××Ļ×× ×××Ē××§×××Ē ××ĸ×Ē ×Ą×¨××§×Ē ×ץפר××× ×Š××. ×× ×Š××××Š× ×× ×׊ ×× ×Ē××§×××Ē ×××××××Ē ×§××Ļ×× ×Š××× × ×¨××Ļ× ×××××, ×××× ×§×××Ļ× RAW.", "external_library_created_at": "ץפר××× ×××Ļ×× ××Ē (× ××Ļ×¨× ×-{date})", "external_library_management": "× ×××× ×Ą×¤×¨××× ×××Ļ×× ××Ē", "face_detection": "×××Ē×ר ×¤× ××", - "face_detection_description": "××Ēר ××Ē ××¤× ×× ×× ×ץ×× ××××Ļ×ĸ××Ē ×××××Ē ×××× ×. ×ĸ××ר ץר××× ××, רק ××Ē××× × ××××××ĸר×Ē × ××§××Ē ××׊×××. \"ר×ĸ× ××\" ××ĸ×× (×××׊) ××Ē ×× ×× ×ץ××. \"××פ×ץ\" ×× ×§× ×× ××Ą×Ŗ ××Ē ×× × ×Ē×× × ××¤× ×× ×× ××××××. \"×ץר××\" ××ץ××Ŗ ××Ē×ר × ×ץ×× ×Š×× ×ĸ×××× ×ĸ××××. ×××ר ׊×××Ē×ר ××¤× ×× ××׊××, ×¤× ×× ×Š×××Ē×¨× ××ĸ××× ××Ē×ר ×××××× ×¤× ×× ××׊××× ×××Ē× ××× ×Š×× ×§××××× ×× ××׊××.", + "face_detection_description": "××Ēר ××Ē ××¤× ×× ××Ē××× ××Ē ××××Ļ×ĸ××Ē ×××××Ē ×××× ×. ×ĸ××ר ץר××× ××, רק ××Ē××× × ××××××ĸר×Ē × ××§××Ē ××׊×××. \"ר×ĸ× ××\" ××ĸ×× (×××׊) ××Ē ×× ××Ē××× ××Ē. \"××פ×ץ\" ×× ×§× ×× ××Ą×Ŗ ××Ē ×× × ×Ē×× × ××¤× ×× ×× ××××××. \"×ץר××\" ××ץ××Ŗ ××Ē×ר ×Ē××× ××Ē ×Š×× ×ĸ×××× ×ĸ××××. ×××ר ׊×××Ē×ר ××¤× ×× ××׊××, ×¤× ×× ×Š×××Ē×¨× ××ĸ××× ××Ē×ר ×××××× ×¤× ×× ××׊××× ×××Ē× ××× ×Š×× ×§××××× ×× ××׊××.", "facial_recognition_job_description": "×§××Ĩ ×¤× ×× ×Š×××Ē×¨× ××Ē×× ×× ×Š××. ׊×× ×× ××ר×Ĩ ×××ר ×׊×××Ē ×××Ē×ר ×¤× ××. \"××פ×ץ\" ××§××Ĩ (×××׊) ××Ē ×× ×פר×Ļ×פ××. \"×ץר××\" ××ץ××Ŗ ××Ē×ר ×¤× ×× ×Š×× ×××§×Ļ× ××× ×××.", "failed_job_command": "×פק××× {command} × ×׊×× ×ĸ××ר ××׊×××: {job}", - "force_delete_user_warning": "×××ר×: פ×ĸ××× ×× ×Ēץ×ר ××× ××Ē ××׊×Ē×׊ ×××Ē ×× ×× ×ץ××. ×× × ××Ē× ×××× ×¤×ĸ××× ×× ×××§××Ļ×× ×× × ××Ē× ×× ×׊×××ר.", + "force_delete_user_warning": "×××ר×: פ×ĸ××× ×× ×Ēץ×ר ××× ××Ē ××׊×Ē×׊ ×××Ē ×× ××Ē××× ××Ē. ×× × ××Ē× ×××× ×¤×ĸ××× ×× ×××§××Ļ×× ×× × ××Ē× ×× ×׊×××ר.", "forcing_refresh_library_files": "×פ×××Ē ×¨×ĸ× ×× ×Š× ×× ×§××Ļ× ×ץפר×××", "image_format": "פ×ר××", "image_format_description": "WebP ×פ××§ ×§××Ļ×× ×§×× ×× ×××Ēר × JPEG, ×× ××× ×××× ×××Ēר ××§××××.", @@ -79,7 +79,7 @@ "image_prefer_embedded_preview_setting_description": "×׊×Ē×׊ ××Ē×Ļ××××Ē ××§×××××Ē ×××××ĸ××Ē ××Ē××× ××Ē RAW ××§×× ××ĸ×××× ×Ē××× × ×××׊ר ×××× ××Ē. ×× ×××× ××פ××§ ×Ļ××ĸ×× ×××××§×× ×××Ēר ×ĸ××ר ×Ē××× ××Ē ×ץ×××××Ē, ××× ××××××Ē ×Š× ××Ē×Ļ××× ×××§×××× ××× ×Ē××××Ē ××Ļ××× ×××Ē××× × ×ĸ׊×××× ×××××Ē ×××Ēר פ××× ×××ץ×.", "image_prefer_wide_gamut": "××ĸ××Ŗ ץ××× ×Ļ××ĸ×× ×¨××", "image_prefer_wide_gamut_setting_description": "×׊×Ē×׊ ×-Display P3 ××Ē××× ××Ē ×××××ĸר××Ē. ×× ×׊×ר ××× ×××Ēר ××Ē ××××× ×××Ē ×Š× ×Ē××× ××Ē ×ĸ× ×ר××× ×Ļ××ĸ ר××××, ××× ×Ē××× ××Ē ×ĸ׊××××Ē ×××פ××ĸ ××ר×Ē ×××׊×ר×× ××Š× ×× ×ĸ× ×רץ×Ē ×פ××¤× ××Š× ×. ×Ē××× ××Ē sRGB × ×Š×ר××Ē ×-sRGB ××× ××× ××ĸ ׊×× ××× ×Ļ××ĸ.", - "image_preview_description": "×Ē××× × ××××× ××× ×× × ×ĸ× ×××-× ×Ē×× ×× ×Š××ץר×, ×׊×׊×Ē ××ĸ×Ē ×Ļפ××× ×× ×ץ ×××× ××ĸ××ר ×××××Ē ×××× ×", + "image_preview_description": "×Ē××× × ××××× ××× ×× × ×ĸ× ×××-× ×Ē×× ×× ×Š××ץר×, ×׊×׊×Ē ××ĸ×Ē ×Ļפ××× ××Ē××× × ×××××Ē ××ĸ××ר ×××××Ē ×××× ×", "image_preview_quality_description": "×××××Ē ×Ē×Ļ××× ××§×××× ×-1 ×ĸ× 100. ×××××Ē ××××× ×××Ēר ××× ×××× ×××Ēר, ××× ××××Ļר×Ē ×§××Ļ×× ×××××× ×××Ēר ×××××× ××פ×××Ē ××Ē ×Ē××××Ē×××Ē ×××׊××. ×××ר×Ē ×ĸ×¨× × ××× ×ĸ׊××× ××׊פ××ĸ ×ĸ× ×××××Ē ×Ē××Ļ×××Ē ×Š× ×××××Ē ×××× ×.", "image_preview_title": "×××ר××Ē ×Ē×Ļ××× ××§××××", "image_quality": "×××××Ē", @@ -106,7 +106,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": "×ĸ×§×× ××××××××Ē ××ר ׊×× ××× ×§××Ļ××", @@ -117,7 +117,7 @@ "machine_learning_clip_model_description": "׊×× ×Š× ×××× CLIP ר׊×× <link>×××</link>. ׊×× ×× ×Š×ĸ××× ××פ×ĸ×× ×××׊ ××Ē ××׊××× '××פ×׊ ×××' ×ĸ××ר ×× ××Ē××× ××Ē ××ĸ×Ē ×Š×× ×× ××××.", "machine_learning_duplicate_detection": "×××Ē×ר ×פ××××××Ē", "machine_learning_duplicate_detection_enabled": "×פ׊ר ×××Ē×ר ×פ××××××Ē", - "machine_learning_duplicate_detection_enabled_description": "×× ××׊××Ē, × ×ץ×× ×××× ×××××§ ×ĸ×××× ××ĸ××¨× ××××× ×פ××××××Ē.", + "machine_learning_duplicate_detection_enabled_description": "×× ××׊××Ē, ×Ē××× ××Ē ××××Ē ×××××§ ×ĸ×××× ××ĸ××¨× ××××× ×פ××××××Ē.", "machine_learning_duplicate_detection_setting_description": "×׊×Ē×׊ ×××××ĸ××Ē ×Š× CLIP ××× ×××Ļ×× ×פ××××××Ē ×פ׊ר×××Ē", "machine_learning_enabled": "×פ׊ר ×××××Ē ×××× ×", "machine_learning_enabled_description": "×× ××׊××Ē, ×× ×Ē××× ××Ē ×××××Ē ×××× × ×××× ××׊××Ē××Ē ××× ×§×Š×¨ ××××ר××Ē ×Š××××.", @@ -160,16 +160,16 @@ "memory_cleanup_job": "× ××§×× ×××ר××", "memory_generate_job": "××Ļ×ר×Ē ×××ר××", "metadata_extraction_job": "×××Ĩ ×××-× ×Ē×× ××", - "metadata_extraction_job_description": "×××Ĩ ××××ĸ ×××-× ×Ē×× ×× ××× × ×ץ, ×××× GPS, ×¤× ×× ×ר×××××Ļ××", + "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": "××ĸ×ר ×Ē××× ××Ē ×××××ĸר××Ē ×Š× × ×ץ×× ××¤× ×× ×××× × ××Ē××§×××Ē ××ĸ××× × ××××Ēר", + "migration_job_description": "××ĸ×ר ×Ē××× ××Ē ×××××ĸר××Ē ×Š× ×Ē××× ××Ē ××¤× ×× ×××× × ××Ē××§×××Ē ××ĸ××× × ××××Ēר", "no_paths_added": "×× × ××Ą×¤× × ×Ē××××", "no_pattern_added": "×× × ××Ą×¤× ×Ē×× ××Ē", - "note_apply_storage_label_previous_assets": "××ĸר×: ××× ××××× ××Ē ×Ē××××Ē ×××ץ×× ×ĸ× × ×ץ×× ×Š×××ĸ×× ××ĸ×ר, ×פ×ĸ× ××Ē", + "note_apply_storage_label_previous_assets": "××ĸר×: ××× ××××× ××Ē ×Ē××××Ē ×××ץ×× ×ĸ× ×Ē××× ××Ē ×Š×××ĸ×× ××ĸ×ר, ×פ×ĸ× ××Ē", "note_cannot_be_changed_later": "××ĸר×: ×× ×פ׊ר ××Š× ××Ē ×××Ē ××××ר ×××Ēר!", "notification_email_from_address": "×××Ē×××Ē", "notification_email_from_address_description": "××Ē×××Ē ×××\"× ×Š× ×׊×××, ××××××: \"Immich ׊ר×Ē ×Ē××× ××Ē <noreply@example.com>\"", @@ -243,21 +243,21 @@ "sidecar_job": "×××-× ×Ē×× ×× × ×××××", "sidecar_job_description": "××× ×× ×Ą× ××¨× ×××-× ×Ē×× ×× × ××××× ×××ĸר××Ē ××§××Ļ××", "slideshow_duration_description": "×ץפר ×Š× ×××Ē ×××Ļ××Ē ×× ×Ē××× ×", - "smart_search_job_description": "×פ×ĸ× ×××××Ē ×××× × ×ĸ× × ×ץ×× ××× ××Ē××× ×××פ×׊ ×××", - "storage_template_date_time_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": "××× ××Ē ×<link>{template}</link> ×× ×××××Ē ×ĸ× × ×ץ×× ×Š×××ĸ×× ××ĸ×ר", - "storage_template_migration_info": "×Ē×× ××Ē ×××ץ×× ×Ē××ר ××Ē ×× ××ר××××Ē ××××Ē×××Ē ×§×× ××Ē. ׊×× ×××× ××Ē×× ××Ē ××××× ×¨×§ ×ĸ× × ×ץ×× ××׊××. ××× ××××× ××××¤× ×¨×ר×××§×××× ××Ē ××Ē×× ××Ē ×ĸ× × ×ץ×× ×Š×××ĸ×× ××ĸ×ר, ×פ×ĸ× ××Ē <link>{job}</link>.", + "storage_template_migration_description": "××× ××Ē ×<link>{template}</link> ×× ×××××Ē ×ĸ× ×Ē××× ××Ē ×Š×××ĸ×× ××ĸ×ר", + "storage_template_migration_info": "×Ē×× ××Ē ×××ץ×× ×Ē××ר ××Ē ×× ××ר××××Ē ××××Ē×××Ē ×§×× ××Ē. ׊×× ×××× ××Ē×× ××Ē ××××× ×¨×§ ×ĸ× ×Ē××× ××Ē ××׊××Ē. ××× ××××× ××××¤× ×¨×ר×××§×××× ××Ē ××Ē×× ××Ē ×ĸ× ×Ē××× ××Ē ×Š×××ĸ×× ××ĸ×ר, ×פ×ĸ× ××Ē <link>{job}</link>.", "storage_template_migration_job": "×׊×××Ē ××ĸ×ר×Ē ×Ē×× ××Ē ××ץ××", "storage_template_more_details": "×פר××× × ×ץפ×× ×××××Ē ×Ē××× × ××, ×ĸ××× ×<template-link>×Ē×× ××Ē ×××ץ××</template-link> ××<implications-link>×׊××××Ē××</implications-link>", "storage_template_onboarding_description": "××׊ר ××פ×ĸ××Ē, ×Ē××× × ×× ×Ē×ר×× ××××××××Ē ×§××Ļ×× ×××Ē×ץץ ×ĸ× ×Ē×× ××Ē ×Š××׊×Ē×׊ ××××ר. ×ĸ×§× ××ĸ×××Ē ××Ļ××××Ē ××Ē××× × ××××× ××ר×ר×Ē ××××. ×××××ĸ × ××Ą×Ŗ, × × ×ר×××Ē ××Ē ×<link>×Ē××ĸ××</link>.", "storage_template_path_length": "×××××Ē ×××¨× × ×Ē×× ×׊××ĸר×Ē: <b>{length, number}</b>/{limit, number}", "storage_template_settings": "×Ē×× ××Ē ××ץ××", - "storage_template_settings_description": "× ×××× ××× × ××Ē××§×××Ē ×××Ē ×Š× ××§×××Ĩ ×Š× × ×ץ ×××ĸ×××", + "storage_template_settings_description": "× ×××× ××× × ××Ē××§×××Ē ×××Ē ×Š× ××§×××Ĩ ×Š× ××Ē××× × ×Š×××ĸ××Ē×", "storage_template_user_label": "<code>{label}</code> ××× ×Ē××××Ē ×××ץ×× ×Š× ××׊×Ē×׊", "system_settings": "×××ר××Ē ××ĸר××Ē", "tag_cleanup_job": "× ××§×× ×Ē×××", @@ -277,7 +277,7 @@ "theme_settings_description": "× ×××× ××Ē××× ××׊××Ē ×Š× ××׊ק ×××× ××¨× × ×Š× Immich", "these_files_matched_by_checksum": "×§××Ļ×× ××× ×Ē××××× ××¤× ×Ą××××× ××××§×ר×Ē ×Š×××", "thumbnail_generation_job": "×Ļ×ר ×Ē××× ××Ē ×××××ĸר××Ē", - "thumbnail_generation_job_description": "×××Ļר ×Ē××× ××Ē ×××××ĸר××Ē ××××××Ē, ×§×× ××Ē ××××׊×׊××Ē ×ĸ××ר ×× × ×ץ, ××× ×× ×Ē××× ××Ē ×××××ĸר××Ē ×ĸ××ר ×× ×××", + "thumbnail_generation_job_description": "×××Ļר ×Ē××× ××Ē ×××××ĸר××Ē ××××××Ē, ×§×× ××Ē ××××׊×׊××Ē ×ĸ××ר ×× ×Ē××× ×, ××× ×× ×Ē××× ××Ē ×××××ĸר××Ē ×ĸ××ר ×× ×××", "transcoding_acceleration_api": "API ×××Ļ×", "transcoding_acceleration_api_description": "×-API ׊×××Ļ×ר ××× ×ר××§×Ļ×× ×ĸ× ×××׊×ר ׊×× ××× ×××××Ĩ ××Ē ××ר×Ē ××§××××. ××××¨× ×× ××× '×××××Ĩ ×××× ××××Ēר': ××× ×Ē×××ר ××§×××× ×Ē××× × ×××§×¨× ×Š× ×׊×. VP9 ×ĸ׊×× ××ĸ××× ×× ××, ×Ē××× ×××××¨× ×Š××.", "transcoding_acceleration_nvenc": "NVENC (××ר׊ ×ר××ץ ××Ą× ×Š× NVIDIA)", @@ -341,17 +341,17 @@ "transcoding_video_codec_description": "×-VP9 ×׊ ××ĸ××××Ē ××××× ××Ē×××××Ē ×¨×Š×Ē, ××× ×××§× ×××Ēר ××× ××××ר ××Ē ××§×××× ×ĸ××ר×. HEVC ××Ē×¤×§× ××××¤× ××××, ×× ××ĸ× ×Ē×××××Ē ×¨×Š×Ē × ×××× ×××Ēר. H.264 ×Ē××× ××××¤× × ×¨×× ××××ר ××××ר ××Ē ×§×××××, ××× ××× ××××Ļר ×§××Ļ×× ×××××× ××ר××. AV1 ××× ××§×××× ×××ĸ×× ××××Ēר ×× ×××§× ××Ē×××× ×××׊×ר×× ××Š× ×× ×××Ēר.", "trash_enabled_description": "×פ×ĸ× ××Ē ×Ē××× ××Ē ××׊פ×", "trash_number_of_days": "×ץפר ×××××", - "trash_number_of_days_description": "×ץפר ××××× ×׊×××¨× ×ĸ× ×× ×ץ×× ×××Š×¤× ××¤× × ×ץר×Ē× ××Ļ×××Ē××Ē", + "trash_number_of_days_description": "×ץפר ××××× ×׊×××¨× ×Š× ×Ē××× ××Ē ×××Š×¤× ××¤× × ×ץר×Ē× ××Ļ×××Ē××Ē", "trash_settings": "×××ר××Ē ××׊פ×", "trash_settings_description": "× ×××× ×××ר××Ē ××׊פ×", "untracked_files": "×§××Ļ×× ××× ××ĸ×§×", "untracked_files_description": "×§××Ļ×× ××× ××× × × ××Ļ××× ×××ĸ×§× ×Š× ×××׊××. ×× ×××××× ×××××Ē ×Ē××Ļ×××Ē ×Š× ××ĸ×ר××Ē ××׊×××Ē, ××ĸ××××Ē ×Š× ×§××ĸ×, ×× ×Š× ××Ē×¨× ××××ר ×××× ×Š×××׊ ××Ē××× ×", "user_cleanup_job": "× ××§×× ×׊×Ē×׊××", - "user_delete_delay": "××׊××× ××× ×ץ×× ×Š× <b>{user}</b> ××Ē×××× × ×××××§× ××Ļ×××Ē××Ē ××ĸ×× {delay, plural, one {××× #} other {# ××××}}.", + "user_delete_delay": "××׊××× ×××Ē××× ××Ē ×Š× <b>{user}</b> ××Ē×××× × ×××××§× ××Ļ×××Ē××Ē ××ĸ×× {delay, plural, one {××× #} other {# ××××}}.", "user_delete_delay_settings": "×ĸ×××× ××××§×", - "user_delete_delay_settings_description": "×ץפר ××××× ×××ר ×××Ą×¨× ×ĸ× ××××§× ××Ļ×××Ē××Ē ×Š× ××׊××× ××× ×ץ×× ×Š× ××׊×Ē×׊. ×׊×××Ē ××××§×Ē ××׊×Ē×׊ פ××ĸ××Ē ×××Ļ××Ē ××× ×××××§ ×× ×׊ ×׊×Ē×׊×× ×Š×××× ×× ×××××§×. ׊×× ×××× ×××××¨× ×× ×××ĸר×× ××××Ļ××ĸ ×××.", - "user_delete_immediately": "××׊××× ××× ×ץ×× ×Š× <b>{user}</b> ××ĸ××× ××Ē×ר ×××××§× ××Ļ×××Ē××Ē <b>××××¤× ×××××</b>.", - "user_delete_immediately_checkbox": "××ĸ×× ×׊×Ē×׊ ×× ×ץ×× ××Ē×ר ×××××§× ××××××Ē", + "user_delete_delay_settings_description": "×ץפר ××××× ×××ר ×××Ą×¨× ×ĸ× ××××§× ××Ļ×××Ē××Ē ×Š× ××׊××× ×××Ē××× ××Ē ×Š× ××׊×Ē×׊. ×׊×××Ē ××××§×Ē ××׊×Ē×׊ פ××ĸ××Ē ×××Ļ××Ē ××× ×××××§ ×× ×׊ ×׊×Ē×׊×× ×Š×××× ×× ×××××§×. ׊×× ×××× ×××××¨× ×× ×××ĸר×× ××××Ļ××ĸ ×××.", + "user_delete_immediately": "××׊××× ×××Ē××× ××Ē ×Š× <b>{user}</b> ××ĸ××× ××Ē×ר ×××××§× ××Ļ×××Ē××Ē <b>××××¤× ×××××</b>.", + "user_delete_immediately_checkbox": "××Ļ× ×׊×Ē×׊ ××Ē××× ××Ē ××Ē×ר ×××××§× ××××××Ē", "user_management": "× ×××× ×׊×Ē×׊××", "user_password_has_been_reset": "ץ×ץ××Ē ××׊×Ē×׊ ××פץ×:", "user_password_reset_description": "×× × ×Ą×¤×§ ××Ē ×ץ×ץ×× ×××× ××Ē ××׊×Ē×׊ ×××××ĸ ×× ×Š×׊ ×Ļ××¨× ××Š× ××Ē ××Ē ×ץ×ץ×× ××× ××Ą× ×××× ×Š××.", @@ -371,13 +371,17 @@ "admin_password": "ץ×ץ××Ē ×× ××", "administration": "× ××××", "advanced": "××Ē×§××", - "advanced_settings_log_level_title": "ר××Ē ×Ē××ĸ×× ××ר××ĸ××: {}", - "advanced_settings_prefer_remote_subtitle": "×××§ ××××׊×ר×× ×× ×××××× ××× ×××ĸ×× ×Ē××× ××Ē ×××××ĸר××Ē ×× ×ץ×× ×Š×××׊×ר. ×פ×ĸ× ××××¨× ×× ××× ×××ĸ×× ×Ē××× ××Ē ×ר×××§××Ē ×××§××", + "advanced_settings_enable_alternate_media_filter_subtitle": "×׊×Ē×׊ ××פ׊ר××Ē ×× ××× ××Ą× × ×××× ××××× ××Ą× ×ר×× ××¤× ×§×¨××ר××× ×× ×××פ×××. ×××××Ĩ ××׊×Ē×׊ ××× ×¨×§ ×× ×׊ ××ĸ×× ×××××× ×× ×××××××× ××פ×××§×Ļ××.", + "advanced_settings_enable_alternate_media_filter_title": "[× ×ץ××× ×] ×׊×Ē×׊ ×××Ą× × ×Ą× ×ר×× ××××× ××××¤× ×Š×××׊×ר", + "advanced_settings_log_level_title": "ר××Ē ×¨×׊×× ×××××: {}", + "advanced_settings_prefer_remote_subtitle": "×××§ ××××׊×ר×× ×× ×××××× ××× ×××ĸ×× × ×Š× ×Ē××× ××Ē ×××××ĸר××Ē ××Ē××× ××Ē ×Š×××׊×ר. ×פ×ĸ× ××××¨× ×× ××× ×××ĸ×× ×Ē××× ××Ē ×ר×××§××Ē ×××§××.", "advanced_settings_prefer_remote_title": "××ĸ××Ŗ ×Ē××× ××Ē ×ר×××§××Ē", - "advanced_settings_proxy_headers_subtitle": "×××ר ×××Ēר××Ē ×¤×¨××§×Ą× ×Š×××׊×× ×Ļר×× ×׊××× ×ĸ× ×× ×ק׊×Ē ×¨×Š×Ē", + "advanced_settings_proxy_headers_subtitle": "×××ר proxy headers ׊×××׊×× ×Ļר×× ×׊××× ×ĸ× ×× ×ק׊×Ē ×¨×Š×Ē", "advanced_settings_proxy_headers_title": "×××Ēר××Ē ×¤×¨×קץ×", - "advanced_settings_self_signed_ssl_subtitle": "×××× ×ĸ× ×××××Ē ×Ē×ĸ×××Ē SSL ×ĸ××ר × ×§×××Ē ××§×Ļ× ×Š× ×׊ר×Ē. ×ר×׊ ×ĸ××ר ×Ē×ĸ××××Ē ×××Ē××× ×ĸ×Ļ×××Ē", + "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": "פ×Ēר×× ××ĸ×××Ē", @@ -402,15 +406,15 @@ "album_thumbnail_card_item": "פר×× 1", "album_thumbnail_card_items": "{} פר××××", "album_thumbnail_card_shared": " ¡ ×׊××Ē×Ŗ", - "album_thumbnail_shared_by": "×׊××Ē×Ŗ ×ĸ× ××× {}", + "album_thumbnail_shared_by": "׊××Ē×Ŗ ×ĸ× ××× {}", "album_updated": "××××× ×ĸ××××", - "album_updated_setting_description": "×§×× ××××ĸ×Ē ×××\"× ××׊ר ×××××× ×׊××Ē×Ŗ ×׊ × ×ץ×× ××׊××", + "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_remove": "×׊ ××ĸ×××Ē ××ץר×Ē ××Ē××× ××Ē ×××××××", "album_viewer_appbar_share_err_title": "× ××Š× ×׊×× ×× ×××Ēר×Ē ××××××", "album_viewer_appbar_share_leave": "×ĸ××× ×××××", "album_viewer_appbar_share_to": "׊×Ē×Ŗ ×ĸ×", @@ -439,57 +443,57 @@ "appears_in": "××פ××ĸ ×", "archive": "×ר××××", "archive_or_unarchive_photo": "××ĸ×ר ×Ē××× × ××ר×××× ×× ×××Ļ× ×××Ē× ×׊×", - "archive_page_no_archived_assets": "×× × ××Ļ×× × ×ץ×× ××ר××××", - "archive_page_title": "×ר×××× ({})", + "archive_page_no_archived_assets": "×× × ××Ļ×× ×Ē××× ××Ē ××ר××××", + "archive_page_title": "××ר×××× ({})", "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_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_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": "×§××Ĩ ×Ē××× ××Ē ×פ×", "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_offline": "×Ē××× × ×× ××§××× ×Ē", + "asset_offline_description": "××Ē××× × ××××Ļ×× ××Ē ××××Ē ××ר ×× × ××Ļ××Ē ×××ץק. × × ×××Ļ×ר ק׊ר ×ĸ× ×× ×× Immich ׊×× ××§×××Ē ×ĸ×ר×.", + "asset_restored_successfully": "×Ē××× × ×Š××××¨× ×××Ļ×××", "asset_skipped": "××××", "asset_skipped_in_trash": "××׊פ×", "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_name_count": "{count, plural, one {× ×ץ # × ××Ą×Ŗ} other {# × ×ץ×× × ×ץפ×}} ×× {hasName, select, true {<b>{name}</b>} other {××××× ××׊}}", - "assets_count": "{count, plural, one {× ×ץ #} other {# × ×ץ××}}", - "assets_deleted_permanently": "{} × ×ץ(××) × ×××§× ××Ļ×××Ē××Ē", - "assets_deleted_permanently_from_server": "{} × ×ץ(××) × ×××§× ××Ļ×××Ē××Ē ×׊ר×Ē ×-Immich", - "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": "{} × ×ץ(××) × ×××§× ××Ļ×××Ē××Ē ××××׊×ר ׊××", - "assets_restore_confirmation": "××× ××××Ē ×ר×Ļ×× × ×׊××ר ××Ē ×× ×× ×ץ×× ×Š××׊פ×? ××× ××פ׊ר××Ē× ×××× ××Ē ×פ×ĸ××× ×××! ×׊ ×׊×× ×× ×Š×× × ××Ē× ×׊××ר × ×ץ×× ×× ××§××× ×× ×××¨× ××.", - "assets_restored_count": "{count, plural, one {× ×ץ # ׊×××ר} other {# × ×ץ×× ×Š×××ר×}}", - "assets_restored_successfully": "{} × ×ץ(××) ׊××××¨× ×××Ļ×××", - "assets_trashed": "{} × ×ץ(××) ×××ĸ××¨× ××׊פ×", - "assets_trashed_count": "{count, plural, one {× ×ץ # ××׊××} other {# × ×ץ×× ××׊×××}} ××׊פ×", - "assets_trashed_from_server": "{} × ×ץ(××) ×××ĸ××¨× ×××Š×¤× ×׊ר×Ē ×-Immich", - "assets_were_part_of_album_count": "{count, plural, one {× ×ץ ×××} other {× ×ץ×× ×××}} ××ר ×××§ ×××××××", + "asset_viewer_settings_title": "××Ļ×× ××Ē××× ××Ē", + "assets": "×Ē××× ××Ē", + "assets_added_count": "{count, plural, one {× ××Ą×¤× ×Ē××× × #} other {× ××Ą×¤× # ×Ē××× ××Ē}}", + "assets_added_to_album_count": "{count, plural, one {× ××Ą×¤× ×Ē××× × #} other {× ××Ą×¤× # ×Ē××× ××Ē}} ××××××", + "assets_added_to_name_count": "{count, plural, one {×Ē××× × # × ×ץפ×} other {# ×Ē××× ××Ē × ×ץפ×}} ×× {hasName, select, true {<b>{name}</b>} other {××××× ××׊}}", + "assets_count": "{count, plural, one {×Ē××× × #} other {# ×Ē××× ××Ē}}", + "assets_deleted_permanently": "{} ×Ē××× ××Ē × ×××§× ××Ļ×××Ē××Ē", + "assets_deleted_permanently_from_server": "{} ×Ē××× ××Ē × ×××§× ××Ļ×××Ē××Ē ×׊ר×Ē ×-Immich", + "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": "{} ×Ē××× ××Ē × ×××§× ××Ļ×××Ē××Ē ××××׊×ר ׊××", + "assets_restore_confirmation": "××× ××××Ē ×ר×Ļ×× × ×׊××ר ××Ē ×× ××Ē××× ××Ē ×Š××׊פ×? ××× ××פ׊ר××Ē× ×××× ××Ē ×פ×ĸ××× ×××! ×׊ ×׊×× ×× ×Š×× × ××Ē× ×׊××ר ×Ē××× ××Ē ×× ××§××× ××Ē ×××¨× ××.", + "assets_restored_count": "{count, plural, one {×Ē××× × # ׊×××ר×} other {# ×Ē××× ××Ē ×Š×××ר×}}", + "assets_restored_successfully": "{} ×Ē××× ××Ē ×Š××××¨× ×××Ļ×××", + "assets_trashed": "{} ×Ē××× ××Ē ×××ĸ××¨× ××׊פ×", + "assets_trashed_count": "{count, plural, one {×Ē××× × # ××׊×××} other {# ×Ē××× ××Ē ××׊×××}} ××׊פ×", + "assets_trashed_from_server": "{} ×Ē××× ××Ē ×××ĸ××¨× ×××Š×¤× ××׊ר×Ē", + "assets_were_part_of_album_count": "{count, plural, one {×Ē××× × ××××Ē×} other {×Ē××× ××Ē ×××}} ××ר ×××§ ×××××××", "authorized_devices": "××׊×ר×× ××ר׊××", "automatic_endpoint_switching_subtitle": "××Ē××ר ××§××××Ē ××¨× ××× ××¨× × ×××××× ×××ĸ××× ××׊ר ×××× ××׊×Ē×׊ ×××××ר×× ×××פ××× ×××§××××Ē ××ר××", "automatic_endpoint_switching_title": "×××פ×Ē ××Ē×××Ē ××××××××Ē", @@ -497,22 +501,22 @@ "back_close_deselect": "×××ר, ץ××ר, ×× ××× ×××ר×", "background_location_permission": "×ר׊××Ē ×××§×× ×רק×ĸ", "background_location_permission_content": "××× ××××××Ŗ ר׊×Ē××Ē ××ĸ×Ē ×¨××Ļ× ×רק×ĸ, ×××׊×× ×Ļר×× *×Ē×××* ×××Š× ××××§×× ×××××§ ×ĸ× ×× ×Ē ×קר×× ××Ē ××Š× ×Š× ×¨×Š×Ē ×××× ××¨× × ×××××××", - "backup_album_selection_page_albums_device": "××××××× ×××׊×ר ({})", + "backup_album_selection_page_albums_device": "({}) ××××××× ×××׊×ר", "backup_album_selection_page_albums_tap": "×ק׊ ××× ×××××, ×ק׊ פ×ĸ×××× ××× ×××ר××", - "backup_album_selection_page_assets_scatter": "× ×ץ×× ×××××× ×××Ēפ×ר ×ĸ× ×¤× × ××××××× ×ר××××. ×פ×××, × ××Ē× ××××× ×× ×××ר×× ××××××× ××××× ×Ē×××× ××××××", + "backup_album_selection_page_assets_scatter": "×Ē××× ××Ē ××××××Ē ×××Ēפ×ר ×ĸ× ×¤× × ××××××× ×ר××××. ×פ×××, × ××Ē× ××××× ×× ×××ר×× ××××××× ××××× ×Ē×××× ××××××.", "backup_album_selection_page_select_albums": "×××ר×Ē ×××××××", "backup_album_selection_page_selection_info": "פר×× ×××ר×", - "backup_album_selection_page_total_assets": "ץ××´× × ×ץ×× ××××××××", + "backup_album_selection_page_total_assets": "ץ××´× ×Ē××× ××Ē ××××××××Ē", "backup_all": "×××", - "backup_background_service_backup_failed_message": "× ××Š× ×××××× × ×ץ××. ×× ×Ą× ×Š××...", - "backup_background_service_connection_failed_message": "× ××Š× ×××Ē××ר××Ē ×׊ר×Ē. ×× ×Ą× ×Š××...", + "backup_background_service_backup_failed_message": "× ××Š× ×××××× ×Ē××× ××Ē. ×× ×Ą× ×Š××âĻ", + "backup_background_service_connection_failed_message": "× ××Š× ×××Ē××ר××Ē ×׊ר×Ē. ×× ×Ą× ×Š××âĻ", "backup_background_service_current_upload_notification": "××ĸ×× {}", - "backup_background_service_default_notification": "××פ׊ × ×ץ×× ××׊××...", + "backup_background_service_default_notification": "××פ׊ ×Ē××× ××Ē ××׊××ĒâĻ", "backup_background_service_error_title": "׊××××Ē ×××××", - "backup_background_service_in_progress_notification": "×××× ××Ē ×× ×ץ×× ×Š××...", - "backup_background_service_upload_failure_notification": "× ××Š× ×××ĸ×××Ē {}", + "backup_background_service_in_progress_notification": "×××× ××Ē ××Ē××× ××Ē ×Š××âĻ", + "backup_background_service_upload_failure_notification": "{} × ××Š× ×××ĸ×××", "backup_controller_page_albums": "××××××× ××××××", - "backup_controller_page_background_app_refresh_disabled_content": "×פ׊ר ר×ĸ× ×× ×פ×××§×Ļ×× ×רק×ĸ ××××ר××Ē > ×××× > ר×ĸ× ×× ×פ×××§×Ļ×× ×רק×ĸ ××× ××׊×Ē×׊ ×××××× ×רק×ĸ", + "backup_controller_page_background_app_refresh_disabled_content": "×פ׊ר ר×ĸ× ×× ×פ×××§×Ļ×× ×רק×ĸ ××××ר××Ē > ×××× > ר×ĸ× ×× ×פ×××§×Ļ×× ×רק×ĸ ××× ××׊×Ē×׊ ×××××× ×רק×ĸ.", "backup_controller_page_background_app_refresh_disabled_title": "ר×ĸ× ×× ×פ×××§×Ļ×× ×רק×ĸ ××׊××Ē", "backup_controller_page_background_app_refresh_enable_button_text": "×× ××××ר××Ē", "backup_controller_page_background_battery_info_link": "×ר×× ×× ×××", @@ -521,8 +525,8 @@ "backup_controller_page_background_battery_info_title": "×××××× ×Ą××××", "backup_controller_page_background_charging": "רק ×××ĸ×× ×", "backup_controller_page_background_configure_error": "× ××Š× ××××ר×Ē ×Ē×Ļ×ר×Ē ×Š×ר××Ē ×רק×ĸ", - "backup_controller_page_background_delay": "××× ××××× × ×ץ×× ××׊××: {}", - "backup_controller_page_background_description": "×פ×ĸ× ××Ē ×׊×ר××Ē ×¨×§×ĸ ××× ×××××Ē ××××¤× ××××××× ×× × ×ץ ××׊ ×××× ×××Ļ××¨× ×פ×Ē×× ××Ē ×××׊××", + "backup_controller_page_background_delay": "×׊×× ××××× ×Š× ×Ē××× ××Ē ××׊××Ē: {}", + "backup_controller_page_background_description": "×פ×ĸ× ××Ē ×׊×ר××Ē ×¨×§×ĸ ××× ×××××Ē ××××¤× ××××××× ×× ×Ē××× × ×××Š× ×××× ×××Ļ××¨× ×פ×Ē×× ××Ē ×××׊××", "backup_controller_page_background_is_off": "××××× ××××××× ×רק×ĸ ××××", "backup_controller_page_background_is_on": "××××× ××××××× ×רק×ĸ ××פ×ĸ×", "backup_controller_page_background_turn_off": "××× ×Š×ר××Ē ××××× ×רק×ĸ", @@ -532,10 +536,10 @@ "backup_controller_page_backup_selected": "× ××ר×: ", "backup_controller_page_backup_sub": "×Ē××× ××Ē ×ץר××× ×× ××××××", "backup_controller_page_created": "× ××Ļר ×: {}", - "backup_controller_page_desc_backup": "×פ×ĸ× ××××× ××××Ē ××× ×××ĸ×××Ē ××××¤× ××××××× × ×ץ×× ××׊×× ×׊ר×Ē ×׊פ××Ē××× ××Ē ×××׊××", + "backup_controller_page_desc_backup": "×פ×ĸ× ××××× ××××Ē ××× ×××ĸ×××Ē ××××¤× ××××××× ×Ē××× ××Ē ××׊××Ē ×׊ר×Ē ×׊פ××Ē××× ××Ē ×××׊××.", "backup_controller_page_excluded": "×××ר××: ", - "backup_controller_page_failed": "× ××Š× ({})", - "backup_controller_page_filename": "×Š× ×§×××Ĩ: {} [{}]", + "backup_controller_page_failed": "({}) × ×׊××", + "backup_controller_page_filename": "×Š× ××§×××Ĩ: {} [{}]", "backup_controller_page_id": "××××: {}", "backup_controller_page_info": "פר×× ×××××", "backup_controller_page_none_selected": "××× ×××ר×", @@ -545,14 +549,14 @@ "backup_controller_page_start_backup": "××Ē×× ×××××", "backup_controller_page_status_off": "××××× ××××Ē ××××××× ××××", "backup_controller_page_status_on": "××××× ××××Ē ××××××× ××פ×ĸ×", - "backup_controller_page_storage_format": "{} ××Ē×× {} × ××Ļ××", + "backup_controller_page_storage_format": "{} ××Ē×× {} ×׊×××׊", "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_info_card_assets": "× ×ץ××", + "backup_info_card_assets": "×Ē××× ××Ē", "backup_manual_cancelled": "××××", "backup_manual_in_progress": "××ĸ××× ××ר ××Ē××××. × ×Ą× ×××¨× ××× ××", "backup_manual_success": "××Ļ×××", @@ -566,25 +570,25 @@ "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 {# × ×ץ×× ×פ××××}}? ×× ×׊××ר ×ĸ× ×× ×ץ ××××× ××××Ēר ×Š× ×× ×§×××Ļ× ×××ĸ××ר ×××Š×¤× ××Ē ×× ×Š×ר ××פ××××××Ē.", + "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_album_thumbnails": "×Ē××× ××Ē ×××××ĸר××Ē ×Š× ××Ŗ ץפר××× ({} × ×ץ××)", + "cache_settings_album_thumbnails": "×Ē××× ××Ē ×××××ĸר××Ē ×Š× ××Ŗ ץפר××× ({} ×Ē××× ××Ē)", "cache_settings_clear_cache_button": "× ××§×× ×××××", - "cache_settings_clear_cache_button_title": "×× ×§× ××Ē ×××××× ×Š× ×××׊××. ×× ×׊פ××ĸ ××××¤× ×׊××ĸ××Ē× ×ĸ× ××××Ļ××ĸ×× ×Š× ×××׊×× ×ĸ× ×Š×××××× × ×× × ×××׊", + "cache_settings_clear_cache_button_title": "×× ×§× ××Ē ×××××× ×Š× ×××׊××. ×× ×׊פ××ĸ ××××¤× ×׊××ĸ××Ē× ×ĸ× ××××Ļ××ĸ×× ×Š× ×××׊×× ×ĸ× ×Š×××××× ××Ē××× ×××׊.", "cache_settings_duplicated_assets_clear_button": "× ×§×", "cache_settings_duplicated_assets_subtitle": "×Ē××× ××Ē ×ץר××× ×× ×Š× ××Ļ××× ×ר׊××× ×׊×××¨× ×Š× ×××׊××", - "cache_settings_duplicated_assets_title": "× ×ץ×× ×׊××פ××× ({})", - "cache_settings_image_cache_size": "×××× ××××× ×Ē××× × ({} × ×ץ××)", + "cache_settings_duplicated_assets_title": "({}) ×Ē××× ××Ē ×׊××פ×××Ē", + "cache_settings_image_cache_size": "×××× ××××× ××Ē××× × ({} ×Ē××× ××Ē)", "cache_settings_statistics_album": "×Ē××× ××Ē ×××××ĸר××Ē ×Š× ×Ą×¤×¨×××", - "cache_settings_statistics_assets": "{} × ×ץ×× ({})", + "cache_settings_statistics_assets": "{} ×Ē××× ××Ē ({})", "cache_settings_statistics_full": "×Ē××× ××Ē ×××××Ē", "cache_settings_statistics_shared": "×Ē××× ××Ē ×××××ĸר××Ē ×Š× ××××× ×׊××Ē×Ŗ", "cache_settings_statistics_thumbnail": "×Ē××× ××Ē ×××××ĸר××Ē", "cache_settings_statistics_title": "׊×××׊ ××××××", - "cache_settings_subtitle": "׊××× ×××Ē× ××××Ē ×Š××ר×Ē ×××××× ×Š× ×××׊×× ×× ×××", - "cache_settings_thumbnail_size": "×××× ××××× ×Ē××× × ×××××ĸר×Ē ({} × ×ץ××)", + "cache_settings_subtitle": "×××ר ×××Ļ× ×פ×××§×Ļ×××Ē Immich ׊××ר×Ē × ×Ē×× ×× ××××¤× ××× ×", + "cache_settings_thumbnail_size": "×××× ××××× ×Ē××× × ×××××ĸר×Ē ({} ×Ē××× ××Ē)", "cache_settings_tile_subtitle": "׊××× ×××Ē× ××××Ē ×××ץ×× ×××§×××", "cache_settings_tile_title": "××ץ×× ××§×××", "cache_settings_title": "×××ר××Ē ×Š××ר×Ē ×××××", @@ -613,9 +617,9 @@ "change_your_password": "××××Ŗ ××Ē ×ץ×ץ×× ×Š××", "changed_visibility_successfully": "×× ×¨×××Ē ×Š×× ×Ē× ×××Ļ×××", "check_all": "×ץ×× ×××", - "check_corrupt_asset_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": "×ĸ×ר", @@ -643,13 +647,13 @@ "comments_and_likes": "×Ē×××××Ē & ××××§××", "comments_are_disabled": "×Ē×××××Ē ××׊××Ē××Ē", "common_create_new_album": "×Ļ×ר ××××× ××׊", - "common_server_error": "× × ×××××§ ××Ē ××××ר ×ר׊×Ē ×Š××, ×Ē××××/× ×Š×׊ר×Ē × ××׊ ×׊×רץ×××Ē ×פ×××§×Ļ××/׊ר×Ē ×Ē×××××Ē", + "common_server_error": "× × ×××××§ ××Ē ××××ר ×ר׊×Ē ×Š××, ×Ē××××/× ×Š×׊ר×Ē × ××׊ ×׊×רץ×××Ē ×פ×××§×Ļ××/׊ר×Ē ×Ē×××××Ē.", "completed": "××׊×××", "confirm": "××׊×ר", "confirm_admin_password": "××׊×ר ץ×ץ××Ē ×× ××", - "confirm_delete_face": "××× ××××Ē ×ר×Ļ×× × ×××××§ ××Ē ××¤× ×× ×Š× {name} ××× ×ץ?", + "confirm_delete_face": "××× ××××Ē ×ר×Ļ×× × ×××××§ ××Ē ××¤× ×× ×Š× {name} ×××Ē××× ×?", "confirm_delete_shared_link": "××× ××××Ē ×ר×Ļ×× × ×××××§ ××Ē ××§×׊×ר ××׊××Ē×Ŗ ×××?", - "confirm_keep_this_delete_others": "×× ×Š×ר ×× ×ץ×× ××ĸר××× ×××××§× ×××ĸ× × ×ץ ××. ××× ××××Ē ×ר×Ļ×× × ×××׊××?", + "confirm_keep_this_delete_others": "×× ×Š×ר ×Ē××× ××Ē ×Š××ĸר××× ×××××§× ×××ĸ× ×Ē××× × ×××Ē. ××× ××××Ē ×ר×Ļ×× × ×××׊××?", "confirm_password": "×׊ר ץ×ץ××", "contain": "××××", "context": "×ק׊ר", @@ -684,9 +688,9 @@ "create_link_to_share_description": "×פ׊ר ××× ××× ×ĸ× ××§×׊×ר ×ר×××Ē ××Ē ××Ē××× ××Ē ×Š× ××ר×", "create_new": "×Ļ×ר ××׊", "create_new_person": "×Ļ×ר ××× ××׊", - "create_new_person_hint": "××§×Ļ× ××Ē ×× ×ץ×× ×Š× ×××¨× ×××× ××׊", + "create_new_person_hint": "××§×Ļ× ××Ē ××Ē××× ××Ē ×Š× ×××¨× ×××× ××׊", "create_new_user": "×Ļ×ר ×׊×Ē×׊ ××׊", - "create_shared_album_page_share_add_assets": "×××Ą×Ŗ × ×ץ××", + "create_shared_album_page_share_add_assets": "×××Ą×Ŗ ×Ē××× ××Ē", "create_shared_album_page_share_select_photos": "×××ר×Ē ×Ē××× ××Ē", "create_tag": "×Ļ×ר ×Ē×", "create_tag_description": "×Ļ×ר ×Ē× ××׊. ×ĸ××ר ×Ē××× ××§×× × ××, × × ××××× ××Ē ×× ×Ē×× ×××× ×Š× ××Ē× ×××× ×§×××× × ×××××.", @@ -712,12 +716,12 @@ "deduplication_criteria_1": "×××× ×Ē××× × ×××Ē××", "deduplication_criteria_2": "ץפ×ר×Ē × ×Ē×× × EXIF", "deduplication_info": "××××ĸ ×ĸ× ××××× ×פ××××××Ē", - "deduplication_info_description": "××× ××××ר ×ר×׊ × ×ץ×× ××××¤× ××××××× ×××ץ×ר ×פ××××××Ē ×××××Ē ×××××, ×× × ×ץ×Ē×××× ×ĸ×:", + "deduplication_info_description": "××× ××××ר ×ר×׊ ×Ē××× ××Ē ××××¤× ××××××× ×××ץ×ר ×פ××××××Ē ×××××Ē ×××××, ×× × ×ץ×Ē×××× ×ĸ×:", "default_locale": "׊פ×Ē ×ר×ר×Ē ××××", "default_locale_description": "פ×ר×× ×Ē×ר×××× ××ץפר×× ×××ץץ ׊פ×Ē ××פ××¤× ×Š××", "delete": "×××§", "delete_album": "×××§ ×××××", - "delete_api_key_prompt": "××× ××××Ē ×ר×Ļ×× × ×××××§ ×פ×Ē× ×-API ×××?", + "delete_api_key_prompt": "××× ××Ē× ×××× ×Š×ר×Ļ×× × ×××××§ ×פ×Ē× ×-API ×××?", "delete_dialog_alert": "×פר×××× ×××× ××××§× ××Ļ×××Ē××Ē ××׊ר×Ē ×××××׊×ר ׊××", "delete_dialog_alert_local": "×פר×××× ×××× ×××Ą×¨× ××Ļ×××Ē××Ē ××××׊×ר ׊×× ××× ×ĸ×××× ×××× ×××× ×× ×׊ר×Ē", "delete_dialog_alert_local_non_backed_up": "×××§ ××פר×××× ×× ×××××× ×׊ר×Ē ××××Ą×¨× ××Ļ×××Ē××Ē ××××׊×ר ׊××", @@ -738,7 +742,7 @@ "delete_tag_confirmation_prompt": "××× ××××Ē ×ר×Ļ×× × ×××××§ ×Ē× {tagName}?", "delete_user": "×××§ ×׊×Ē×׊", "deleted_shared_link": "×§×׊×ר ×׊××Ē×Ŗ × ×××§", - "deletes_missing_assets": "××××§ × ×ץ×× ×Š×ץר×× ×××ץק", + "deletes_missing_assets": "××××§ ×Ē××× ××Ē ×Š×ץר××Ē ×××ץק", "description": "×Ē×××ר", "description_input_hint_text": "×××Ą×Ŗ ×Ē×××ר...", "description_input_submit_error": "׊×××× ××ĸ×××× ×Ē×××ר, ××××§ ××Ē ××××× ×פר××× × ×ץפ××", @@ -753,7 +757,7 @@ "display_options": "××Ļ××Ē ×פ׊ר××××Ē", "display_order": "ץ×ר ×Ē×Ļ×××", "display_original_photos": "××Ļ××Ē ×Ē××× ××Ē ××§×ר×××Ē", - "display_original_photos_setting_description": "××ĸ××Ŗ ×××Ļ×× ××Ē ××Ē××× × ×××§×ר××Ē ××ĸ×Ē ×Ļפ×××Ē × ×ץ ×××§×× ×Ē××× ××Ē ×××××ĸר××Ē ××׊ר ×× ×ץ ×××§××¨× ×Ē××× ××Ē×Ļ××× ××פ×פ×. ×× ×ĸ××× ××ר×× ××Ē××× ××Ē ×××××Ē ×××Ļ×××Ē ×××××××Ē.", + "display_original_photos_setting_description": "××Ļ× ×Ē××× × ××§×ר××Ē ××ĸ×Ē ×Ļפ××× ××Ē××× × ×××§×× ×Ē××× ××Ē ×××××ĸר××Ē ××׊ר ××Ē××× × ×××§×ר××Ē ×Ē××××Ē ××Ē×Ļ××× ××פ×פ×. ×× ×ĸ××× ××ר×× ××Ē××× ××Ē ×××××Ē ×××Ļ×××Ē ×××××××Ē.", "do_not_show_again": "×× ×Ē×Ļ×× ××Ē ×××××ĸ× ××××Ē ×Š××", "documentation": "×Ē××ĸ××", "done": "ץ×××", @@ -770,13 +774,13 @@ "download_notfound": "××ר×× ×× × ××Ļ×", "download_paused": "××ר×× ××פץק×", "download_settings": "××ר××", - "download_settings_description": "× ×××× ×××ר××Ē ×ק׊×ר××Ē ×××ר××Ē × ×ץ××", + "download_settings_description": "× ×××× ×××ר××Ē ×ק׊×ר××Ē ×××ר××Ē ×Ē××× ××Ē", "download_started": "××ר×× ××××", "download_sucess": "××Ļ×××Ē ××ר××", "download_sucess_android": "××××× ××ר×× ×× DCIM/Immich", "download_waiting_to_retry": "×××× ××× ×× ×Ą××Ē ×Š××", "downloading": "××ר××", - "downloading_asset_filename": "××ר×× × ×ץ {filename}", + "downloading_asset_filename": "××ר×× ×Ē××× × {filename}", "downloading_media": "××ר×× ××××", "drop_files_to_upload": "׊×רר ×§××Ļ×× ××× ××§×× ××× ×××ĸ×××Ē", "duplicates": "×פ××××××Ē", @@ -809,7 +813,7 @@ "email": "×××\"×", "empty_folder": "×Ē××§×× ×× ×¨××§×", "empty_trash": "ר××§× ×׊פ×", - "empty_trash_confirmation": "××× ××××Ē ×ר×Ļ×× × ×ר××§× ××Ē ××׊פ×? ×× ×ץ×ר ××Ļ×××Ē××Ē ××Ē ×× ×× ×ץ×× ×××Š×¤× ×Immich.\n××× ××פ׊ר××Ē× ×××× ×¤×ĸ××× ××!", + "empty_trash_confirmation": "××× ××××Ē ×ר×Ļ×× × ×ר××§× ××Ē ××׊פ×? ×× ×ץ×ר ××Ļ×××Ē××Ē ××Ē ×× ××Ē××× ××Ē ××××Š×¤× ×Š× ×׊ר×Ē.\n××× ××פ׊ר××Ē× ×××× ×¤×ĸ××× ××!", "enable": "×פ׊ר", "enabled": "××פ×ĸ×", "end_date": "×Ē×ר×× ×Ą×××", @@ -817,42 +821,42 @@ "enter_wifi_name": "××× ×Š× ××× ××¨× × ××××××", "error": "׊××××", "error_change_sort_album": "׊×× ×× ×Ą×ר ×××× ××××× × ×׊×", - "error_delete_face": "׊×××× ×××××§×Ē ×¤× ×× ×× ×ץ", + "error_delete_face": "׊×××× ×××××§×Ē ×¤× ×× ××Ē××× ×", "error_loading_image": "׊×××× ×××ĸ×× ×Ē ××Ē××× ×", "error_saving_image": "׊××××: {}", "error_title": "׊×××× - ×׊×× ×׊×Ē×׊", "errors": { - "cannot_navigate_next_asset": "×× × ××Ē× ×× ××× ×× ×ץ ×××", - "cannot_navigate_previous_asset": "×× × ××Ē× ×× ××× ×× ×ץ ××§×××", + "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_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": "×× × ××Ē× ××פ׊ ××§××××Ē", "cleared_jobs": "×׊××××Ē × ××§× ×ĸ××ר: {job}", - "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_job_command": "×פק××× {command} × ×׊×× ×ĸ××ר ××׊×××: âĒâĒ{job}", "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_keep_this_delete_others": "×פ×ĸ××× × ×׊×× ×× × ××Ē× ××× ×׊××ר ××Ē ××Ē××× × ××× ××××××§ ××Ē ×Š×ר ××Ē××× ××Ē", + "failed_to_load_asset": "××ĸ×× ×Ē ××Ē××× × × ×׊××", + "failed_to_load_assets": "××ĸ×× ×Ē ××Ē××× ××Ē × ×׊××", "failed_to_load_people": "× ××Š× ×××××ר ×× ×Š××", "failed_to_remove_product_key": "×ץר×Ē ×פ×Ē× ×××Ļר × ×׊××", - "failed_to_stack_assets": "××Ļ×ר×Ē ×ĸר×××Ē × ×ץ×× × ×׊××", - "failed_to_unstack_assets": "××××× ×ĸר×××Ē × ×ץ×× × ×׊×", + "failed_to_stack_assets": "××Ļ×ר×Ē ×ĸר×××Ē ×Ē××× ××Ē × ×׊××", + "failed_to_unstack_assets": "××××× ×ĸר×××Ē ×Ē××× ××Ē × ×׊××", "import_path_already_exists": "× ×Ē×× ×××××× ××× ××ר ×§×××.", "incorrect_email_or_password": "×××\"× ×× ×Ą×ץ×× ×Š×××××", "paths_validation_failed": "{paths, plural, one {× ×Ē×× # × ×׊×} other {# × ×Ē×××× × ×׊××}} ×××××Ē", @@ -860,17 +864,17 @@ "quota_higher_than_disk_size": "×××ר×Ē ×××Ą× ××××× ×××Ēר ××××× ×××ץק", "repair_unable_to_check_items": "×× × ××Ē× ×ץ×× {count, select, one {פר××} other {פר××××}}", "unable_to_add_album_users": "×× × ××Ē× ×××ץ××Ŗ ×׊×Ē×׊×× ××××××", - "unable_to_add_assets_to_shared_link": "×× × ××Ē× ×××ץ××Ŗ × ×ץ×× ××§×׊×ר ×׊××Ē×Ŗ", + "unable_to_add_assets_to_shared_link": "×× × ××Ē× ×××ץ××Ŗ ×Ē××× ××Ē ××§×׊×ר ×׊××Ē×Ŗ", "unable_to_add_comment": "×× × ××Ē× ×××ץ××Ŗ ×Ē××××", "unable_to_add_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_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_favorite": "×× × ××Ē× ××Š× ××Ē ××Ļ× ×××ĸ××Ŗ ×ĸ××ר × ×ץ", + "unable_to_change_favorite": "×× × ××Ē× ××Š× ××Ē ××Ļ× ×××ĸ××Ŗ ×ĸ××ר ××Ē××× ×", "unable_to_change_location": "×× × ××Ē× ××Š× ××Ē ×××§××", "unable_to_change_password": "×× × ××Ē× ××Š× ××Ē ×Ą×ץ××", "unable_to_change_visibility": "×× × ××Ē× ××Š× ××Ē ××Ē ×× ×¨×××Ē ×ĸ××ר {count, plural, one {××× #} other {# ×× ×Š××}}", @@ -883,8 +887,8 @@ "unable_to_create_library": "×× × ××Ē× ×××Ļ×ר ץפר×××", "unable_to_create_user": "×× × ××Ē× ×××Ļ×ר ×׊×Ē×׊", "unable_to_delete_album": "×× × ××Ē× ×××××§ ×××××", - "unable_to_delete_asset": "×× × ××Ē× ×××××§ × ×ץ", - "unable_to_delete_assets": "׊×××× ×××××§×Ē × ×ץ××", + "unable_to_delete_asset": "×× × ××Ē× ×××××§ ××Ē ××Ē××× ×", + "unable_to_delete_assets": "׊×××× ×××××§×Ē ××Ē××× ××Ē", "unable_to_delete_exclusion_pattern": "×× × ××Ē× ×××××§ ×פ×ץ ××ר××", "unable_to_delete_import_path": "×× × ××Ē× ×××××§ ××Ē × ×Ē×× ××××××", "unable_to_delete_shared_link": "×× × ××Ē× ×××××§ ×§×׊×ר ×׊××Ē×Ŗ", @@ -901,19 +905,19 @@ "unable_to_link_motion_video": "×× × ××Ē× ×ק׊ר ץר××× ×Ē× ××ĸ×", "unable_to_link_oauth_account": "×× × ××Ē× ×ק׊ר ×׊××× OAuth", "unable_to_load_album": "×× × ××Ē× ×××ĸ×× ×××××", - "unable_to_load_asset_activity": "×× × ××Ē× ×××ĸ×× ××Ē ×¤×ĸ××××Ē ×× ×ץ", + "unable_to_load_asset_activity": "×× × ××Ē× ×××ĸ×× ××Ē ×פ×ĸ××××Ē ××Ē××× ×", "unable_to_load_items": "×× × ××Ē× ×××ĸ×× ×¤×¨××××", "unable_to_load_liked_status": "×× × ××Ē× ×××ĸ×× ××Ļ× '××××Ē×'", "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_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_assets_from_shared_link": "×× × ××Ē× ××ץ×ר ×Ē××× ××Ē ××§×׊×ר ×׊××Ē×Ŗ", "unable_to_remove_deleted_assets": "×× × ××Ē× ××ץ×ר ×§××Ļ×× ×× ××§××× ××", "unable_to_remove_library": "×× × ××Ē× ××ץ×ר ץפר×××", "unable_to_remove_partner": "×× × ××Ē× ××ץ×ר ׊××Ē×Ŗ", @@ -921,7 +925,7 @@ "unable_to_repair_items": "×× × ××Ē× ××Ē×§× ×¤×¨××××", "unable_to_reset_password": "×× × ××Ē× ××פץ ץ×ץ××", "unable_to_resolve_duplicate": "×× × ××Ē× ×פ×Ē×ר ×פ××××Ē", - "unable_to_restore_assets": "×× × ××Ē× ×׊××ר × ×ץ××", + "unable_to_restore_assets": "×× × ××Ē× ×׊××ר ×Ē××× ××Ē", "unable_to_restore_trash": "×× × ××Ē× ×׊××ר ×׊פ×", "unable_to_restore_user": "×× × ××Ē× ×׊××ר ×׊×Ē×׊", "unable_to_save_album": "×× × ××Ē× ×׊××ר ×××××", @@ -935,7 +939,7 @@ "unable_to_set_feature_photo": "×× × ××Ē× ×××××ר ×Ē××× × ××××Ļ××Ē", "unable_to_set_profile_picture": "×× × ××Ē× ×××××ר ×Ē××× ×Ē ×¤×¨×פ××", "unable_to_submit_job": "×× × ××Ē× ×׊××× ×׊×××", - "unable_to_trash_asset": "×× × ××Ē× ×××ĸ××ר × ×ץ ××׊פ×", + "unable_to_trash_asset": "×× × ××Ē× ×××ĸ××ר ×Ē××× × ××׊פ×", "unable_to_unlink_account": "×× × ××Ē× ×××× ×§×׊×ר ×׊×××", "unable_to_unlink_motion_video": "×× × ××Ē× ×××× ×§×׊×ר ץר××× ×Ē× ××ĸ×", "unable_to_update_album_cover": "×× × ××Ē× ××ĸ××× ×ĸ××פ×Ē ×××××", @@ -955,7 +959,7 @@ "exif_bottom_sheet_person_add_person": "×××Ą×Ŗ ׊×", "exif_bottom_sheet_person_age": "××× {}", "exif_bottom_sheet_person_age_months": "××× {} ×××׊××", - "exif_bottom_sheet_person_age_year_months": "××× ×Š× ×, {} ×××׊××", + "exif_bottom_sheet_person_age_year_months": "××× ×Š× × ×-{} ×××׊××", "exif_bottom_sheet_person_age_years": "××× {}", "exit_slideshow": "×Ļ× ×××Ļ××Ē ×Š×§×פ×××Ē", "expand_all": "×ר×× ×××", @@ -977,12 +981,12 @@ "external_network_sheet_info": "××׊ר ×× ×ĸ× ×¨×Š×Ē ×××× ××¨× × ××××××× ××××ĸ×פ×Ē, ×××׊×× ××Ē××ר ×׊ר×Ē ××¨× ×××Ē×××Ē ×ר×׊×× × ×Š× ××Ē× ××׊×× ××××Ē××××Ē ×Š××××, ××× ××××ĸ×× ××××", "face_unassigned": "×× ×××§×Ļ×", "failed": "× ×׊××", - "failed_to_load_assets": "××ĸ×× ×Ē × ×ץ×× × ×׊××", + "failed_to_load_assets": "××ĸ×× ×Ē ×Ē××× ××Ē × ×׊××", "failed_to_load_folder": "××ĸ×× ×Ē ×Ē××§×× × ×׊××", "favorite": "×××ĸ××Ŗ", "favorite_or_unfavorite_photo": "×××Ą×Ŗ ×× ×ץר ×Ē××× × ×××××ĸ×פ××", "favorites": "×××ĸ×פ××", - "favorites_page_no_favorites": "×× × ××Ļ×× × ×ץ×× ×××ĸ×פ××", + "favorites_page_no_favorites": "×× × ××Ļ×× ×Ē××× ××Ē ×××ĸ×פ××", "feature_photo_updated": "×Ē××× × ××××Ļ××Ē ×ĸ×××× ×", "features": "×Ē××× ××Ē", "features_setting_description": "× ×××× ×Ē××× ××Ē ×××׊××", @@ -992,6 +996,7 @@ "filetype": "ץ×× ×§×××Ĩ", "filter": "×Ą× ×", "filter_people": "×Ą× × ×× ×Š××", + "filter_places": "ץ×× ×× ××§××××Ē", "find_them_fast": "××Ļ× ×××Ē× ××ר ××¤× ×Š× ×ĸ× ××פ×׊", "fix_incorrect_match": "×Ē×§× ××Ē××× ×Š××××", "folder": "×Ē××§××", @@ -1029,24 +1034,24 @@ "hide_password": "×ץ×Ēר ץ×ץ××", "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_success": "{added} × ×ץ×× × ××Ą×¤× ×××××× {album}", - "home_page_album_err_partner": "×× × ××Ē× ×××ץ××Ŗ × ××Ą× ×Š××Ē×Ŗ ×××××× ×ĸ××××, ××××", - "home_page_archive_err_local": "×× × ××Ē× ×××ĸ××ר ××ר×××× × ×ץ×× ××§××××× ×ĸ××××, ××××", - "home_page_archive_err_partner": "×× × ××Ē× ×××ĸ××ר ××ר×××× × ××Ą× ×Š××Ē×Ŗ, ××××", + "home_page_add_to_album_conflicts": "{added} ×Ē××× ××Ē × ××Ą×¤× ×××××× {album}. {failed} ×Ē××× ××Ē ××ר × ××Ļ×××Ē ××××××.", + "home_page_add_to_album_err_local": "×× × ××Ē× ×××ץ××Ŗ ×Ē××× ××Ē ××§×××××Ē ×××××× ×ĸ××××, ××××", + "home_page_add_to_album_success": "{added} ×Ē××× ××Ē × ××Ą×¤× ×××××× {album}.", + "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_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_share_err_local": "×× × ××Ē× ×׊×Ē×Ŗ × ×ץ×× ××§××××× ×ĸ× ××× ×§×׊×ר, ××××", - "home_page_upload_err_limit": "× ××Ē× ×××ĸ×××Ē ×¨×§ ×קץ×××× ×Š× 30 × ×ץ×× ××× ×¤×ĸ×, ××××", + "home_page_share_err_local": "×× × ××Ē× ×׊×Ē×Ŗ ×Ē××× ××Ē ××§×××××Ē ×ĸ× ××× ×§×׊×ר, ××××", + "home_page_upload_err_limit": "× ××Ē× ×××ĸ×××Ē ×¨×§ ×קץ×××× ×Š× 30 ×Ē××× ××Ē ××× ×¤×ĸ×, ××××", "host": "××ר×", "hour": "׊×ĸ×", "ignore_icloud_photos": "××Ē×ĸ×× ××Ē××× ××Ē iCloud", - "ignore_icloud_photos_description": "×Ē××× ××Ē ×Š×××××Ą× ××Ē ×-iCloud ×× ×××ĸ×× ×׊ר×Ē ×-Immich", + "ignore_icloud_photos_description": "×Ē××× ××Ē ×Š×××××Ą× ××Ē ×-iCloud ×× ×××ĸ×× ×׊ר×Ē", "image": "×Ē××× ×", "image_alt_text_date": "{isVideo, select, true {ץר××× ×Š×Ļ×××} other {×Ē××× × ×Š×Ļ××××}} ×-{date}", "image_alt_text_date_1_person": "{isVideo, select, true {ץר××× ×Š×Ļ×××} other {×Ē××× × ×Š×Ļ××××}} ×ĸ× {person1} ×-{date}", @@ -1070,7 +1075,7 @@ "in_archive": "××ר××××", "include_archived": "×××× ×ר××××", "include_shared_albums": "×××× ××××××× ×׊××Ēפ××", - "include_shared_partner_assets": "×××× × ××Ą× ×Š××Ē×Ŗ ×׊××Ēפ××", + "include_shared_partner_assets": "×××× ×Ē××× ××Ē ×Š×Š××Ē×¤× ×ĸ\"× ×׊××Ē×Ŗ", "individual_share": "׊××Ē××Ŗ ××××", "individual_shares": "׊××Ē×פ×× ××××××", "info": "××××ĸ", @@ -1089,7 +1094,7 @@ "keep": "׊××ר", "keep_all": "׊××ר ×××", "keep_this_delete_others": "׊××ר ×ĸ× ××, ×××§ ××ר××", - "kept_this_deleted_others": "× ×ץ ×× × ×Š×ר ×× ×××§× {count, plural, one {× ×ץ #} other {# × ×ץ××}}", + "kept_this_deleted_others": "×Ē××× × ×× × ×Š××¨× ×× ×××§× {count, plural, one {×Ē××× × #} other {# ×Ē××× ××Ē}}", "keyboard_shortcuts": "×§××Ļ××¨× ××§×××Ē", "language": "׊פ×", "language_setting_description": "××ר ××Ē ××Š×¤× ××××ĸ×פ×Ē ×ĸ×××", @@ -1104,7 +1109,7 @@ "library_options": "×פ׊ר××××Ē ×Ą×¤×¨×××", "library_page_device_albums": "××××××× ×××׊×ר", "library_page_new_album": "××××× ××׊", - "library_page_sort_asset_count": "×ץפר × ×ץ××", + "library_page_sort_asset_count": "×ץפר ×Ē××× ××Ē", "library_page_sort_created": "×Ē×ר×× ××Ļ×ר×", "library_page_sort_last_modified": "׊×× × ×××ר×× ×", "library_page_sort_title": "×××Ēר×Ē ×××××", @@ -1132,7 +1137,7 @@ "logged_out_device": "××׊×ר ×× ××Ē×§", "login": "×× ×ץ×", "login_disabled": "×× ××Ą× ×××ĸר××Ē ××׊××Ē×", - "login_form_api_exception": "×ר×××Ē API. × × ×××××§ ××Ē ××Ē×××Ē ×׊ר×Ē ××× ×Ą××Ē ×Š××", + "login_form_api_exception": "×ר×××Ē API. × × ×××××§ ××Ē ××Ē×××Ē ×׊ר×Ē ××× ×Ą××Ē ×Š××.", "login_form_back_button_text": "××ר×", "login_form_email_hint": "yourmail@email.com", "login_form_endpoint_hint": "http://your-server-ip:port", @@ -1145,11 +1150,11 @@ "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_handshake_exception": "××ר×ĸ× ×ר××× ××ĸ×Ē ×××Ļ××ĸ Handshake ×ĸ× ×׊ר×Ē. ×פ׊ר ×Ē×××× ××Ē×ĸ××× ×××Ē××× ×ĸ×Ļ×××Ē ××××ר××Ē ×× ××Ē/× ×׊×Ē×׊/×Ē ××Ē×ĸ××× ×××Ē××× ×ĸ×Ļ×××Ē.", "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": "ץ×ץ×× ×ĸ×××× × ×××Ļ×××", @@ -1170,24 +1175,24 @@ "manage_your_devices": "× ×××× ×××׊×ר×× ×××××ר×× ×Š××", "manage_your_oauth_connection": "× ×××× ××××ר ×-OAuth ׊××", "map": "×פ×", - "map_assets_in_bound": "{} ×Ē××× ×", + "map_assets_in_bound": "×Ē××× × {}", "map_assets_in_bounds": "{} ×Ē××× ××Ē", "map_cannot_get_user_location": "×× × ××Ē× ××§×× ××Ē ×××§×× ××׊×Ē×׊", "map_location_dialog_yes": "××", "map_location_picker_page_use_location": "×׊×Ē×׊ ××××§×× ×××", - "map_location_service_disabled_content": "׊×ר××Ē ×××§×× ×Ļר×× ×××××Ē ××פ×ĸ× ××× ×××Ļ×× × ×ץ×× ×××××§×× ×× ×××× ×Š××. ××× ×ר×Ļ×× × ××פ×ĸ×× ×××Ē× ×ĸ×׊××?", + "map_location_service_disabled_content": "׊×ר××Ē ××××§×× ×Ļר×× ×××××Ē ××פ×ĸ× ××× ×××Ļ×× ×Ē××× ××Ē ×××××§×× ×× ×××× ×Š××. ××× ×ר×Ļ×× × ××פ×ĸ×× ×××Ē× ×ĸ×׊××?", "map_location_service_disabled_title": "׊×ר××Ē ×××§×× ×××××", "map_marker_for_images": "ץ×× ××¤× ××Ē××× ××Ē ×Š×Ļ×××× ×{city}, {country}", "map_marker_with_image": "ץ×× ××¤× ×ĸ× ×Ē××× ×", "map_no_assets_in_bounds": "××× ×Ē××× ××Ē ××××ר ××", - "map_no_location_permission_content": "×׊ ×Ļ××¨× ××ר׊×× ××××§×× ××× ×××Ļ×× × ×ץ×× ×××××§×× ×× ×××× ×Š××. ××× ×ר×Ļ×× × ××פ׊ר ×××Ē ×ĸ×׊××?", + "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": "{} ×××× ××ר×× ××", + "map_settings_date_range_option_days": "×-{} ×××× ××ר×× ××", "map_settings_date_range_option_year": "×Š× × ××ר×× ×", - "map_settings_date_range_option_years": "{} ×Š× ×× ××ר×× ××Ē", + "map_settings_date_range_option_years": "×-{} ×Š× ×× ××ר×× ××Ē", "map_settings_dialog_title": "×××ר××Ē ×פ×", "map_settings_include_show_archived": "×××× ×ר××××", "map_settings_include_show_partners": "×××× ×Š××Ēפ××", @@ -1221,8 +1226,8 @@ "monthly_title_text_date_format": "MMMM y", "more": "×ĸ××", "moved_to_trash": "×××ĸ×ר ××׊פ×", - "multiselect_grid_edit_date_time_err_read_only": "×× × ××Ē× ××ĸר×× ×Ē×ר×× ×Š× × ×ץ(××) ×קר××× ××××, ××××", - "multiselect_grid_edit_gps_err_read_only": "×× × ××Ē× ××ĸר×× ×××§×× ×Š× × ×ץ(××) ×קר××× ××××, ××××", + "multiselect_grid_edit_date_time_err_read_only": "×× × ××Ē× ××ĸר×× ×Ē×ר×× ×Š× ×Ē××× ××Ē ×קר××× ××××, ××××", + "multiselect_grid_edit_gps_err_read_only": "×× × ××Ē× ××ĸר×× ×××§×× ×Š× ×Ē××× ××Ē ×קר××× ××××, ××××", "mute_memories": "×׊×Ē×§×Ē ×××ר×× ××Ē", "my_albums": "×××××××× ×Š××", "name": "׊×", @@ -1245,7 +1250,7 @@ "no_albums_yet": "×× × ×¨×× ×Š××× ×× ×ĸ×××× ×××××××.", "no_archived_assets_message": "××ĸ×ר ×Ē××× ××Ē ×ץר××× ×× ××ר×××× ××× ××ץ×Ē×ר ×××Ē× ××Ē×Ļ×××Ē ××Ē××× ××Ē ×Š××", "no_assets_message": "×××Ĩ ××× ×××ĸ×××Ē ××Ē ××Ē××× × ×ר×׊×× × ×Š××", - "no_assets_to_show": "××× × ×ץ×× ×××Ļ××", + "no_assets_to_show": "××× ×Ē××× ××Ē ×××Ļ××", "no_duplicates_found": "×× × ××Ļ×× ×פ××××××Ē.", "no_exif_info_available": "××× ××××ĸ ×××× ×ĸ× ×××-× ×Ē×× ×× (exif)", "no_explore_results_message": "××ĸ×× ×Ē××× ××Ē × ×ץפ××Ē ××× ×××§×ר ××Ē ××××Ą×Ŗ ׊××.", @@ -1258,17 +1263,17 @@ "no_shared_albums_message": "×Ļ×ר ××××× ××× ×׊×Ē×Ŗ ×Ē××× ××Ē ×ץר××× ×× ×ĸ× ×× ×Š×× ×ר׊×Ē ×Š××", "not_in_any_album": "×× ×׊×× ×××××", "not_selected": "×× × ××ר×", - "note_apply_storage_label_to_previously_uploaded assets": "××ĸר×: ××× ××××× ××Ē ×Ē××××Ē ×××ץ×× ×ĸ× × ×ץ×× ×Š×××ĸ×× ××ĸ×ר, ×פ×ĸ× ××Ē", + "note_apply_storage_label_to_previously_uploaded assets": "××ĸר×: ××× ××××× ××Ē ×Ē××××Ē ×××ץ×× ×ĸ× ×Ē××× ××Ē ×Š×××ĸ×× ××ĸ×ר, ×פ×ĸ× ××Ē", "notes": "××ĸר××Ē", - "notification_permission_dialog_content": "××× ××פ׊ר ××Ēר×××Ē, ×× ××××ר××Ē ×××ר ××Ēר", - "notification_permission_list_tile_content": "××ĸ× ×§ ×ר׊×× ××× ××פ׊ר ××Ēר×××Ē", + "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 ר׊××××", + "official_immich_resources": "××§×ר××Ē ×¨×Š×××× ×Š× Immich", "offline": "×× ××§×××", "offline_paths": "× ×Ē×××× ×× ××§××× ××", "offline_paths_description": "×Ē××Ļ×××Ē ××× ×ĸ׊××××Ē ×××××Ē ×ĸ×§× ××××§× ××× ××Ē ×Š× ×§××Ļ×× ×Š××× × ×××§ ×ץפר××× ×××Ļ×× ××Ē.", @@ -1282,6 +1287,7 @@ "onboarding_welcome_user": "×ר×× ××××, {user}", "online": "××§×××", "only_favorites": "רק ×××ĸ×פ××", + "open": "פ×Ē×", "open_in_map_view": "פ×Ē× ××Ē×Ļ×××Ē ×פ×", "open_in_openstreetmap": "פ×Ē× ×-OpenStreetMap", "open_the_search_filters": "פ×Ē× ××Ē ××Ą× × × ×××פ×׊", @@ -1300,12 +1306,12 @@ "partner_can_access_location": "××××§×× ×Š×× ×Ļ×××× ××Ē××× ××Ē ×Š××", "partner_list_user_photos": "×Ē××× ××Ē ×Š× {user}", "partner_list_view_all": "××Ļ× ×××", - "partner_page_empty_message": "××Ē××× ××Ē ×Š×× ×ĸ×××× ×× ×׊××Ēפ××Ē ×ĸ× ××Ŗ ׊××Ē×Ŗ", + "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_page_stop_sharing_content": "{} ×× ×××× ×××Ēר ××׊×Ē ××Ē××× ××Ē ×Š××.", "partner_sharing": "׊××Ē××Ŗ ׊××Ēפ××", "partners": "׊××Ēפ××", "password": "ץ×ץ××", @@ -1328,20 +1334,20 @@ "people_feature_description": "×ĸ××× ××Ē××× ××Ē ×ץר××× ×× ×Š×§×××Ļ× ×ĸ× ××× ×× ×Š××", "people_sidebar_description": "××Ļ× ×§×׊×ר ×× ×× ×Š×× ×ץר×× ××Ļ×", "permanent_deletion_warning": "×××ר×Ē ××××§× ××Ļ×××Ē××Ē", - "permanent_deletion_warning_setting_description": "××Ļ× ××××¨× ××ĸ×Ē ××××§×Ē × ×ץ×× ××Ļ×××Ē××Ē", + "permanent_deletion_warning_setting_description": "××Ļ× ××××¨× ××ĸ×Ē ××××§×Ē ×Ē××× ××Ē ××Ļ×××Ē××Ē", "permanently_delete": "×××§ ××Ļ×××Ē××Ē", - "permanently_delete_assets_count": "×××§ ××Ļ×××Ē××Ē {count, plural, one {× ×ץ} other {× ×ץ××}}", - "permanently_delete_assets_prompt": "××× ××××Ē ×ר×Ļ×× × ×××××§ ××Ļ×××Ē××Ē {count, plural, one {× ×ץ ××?} other {<b>#</b> × ×ץ×× ×××?}}×× ×× ×ץ×ר {count, plural, one {×××Ē× ×××××××} other {×××Ē× ×××××××}}.", - "permanently_deleted_asset": "× ×ץ × ×××§ ××Ļ×××Ē××Ē", - "permanently_deleted_assets_count": "{count, plural, one {× ×ץ # × ×××§} other {# × ×ץ×× × ×××§×}} ××Ļ×××Ē××Ē", + "permanently_delete_assets_count": "×××§ ××Ļ×××Ē××Ē {count, plural, one {×Ē××× ×} other {×Ē××× ××Ē}}", + "permanently_delete_assets_prompt": "××× ××××Ē ×ר×Ļ×× × ×××××§ ××Ļ×××Ē××Ē {count, plural, one {×Ē××× × ×××Ē?} other {<b>#</b> ×Ē××× ××Ē ×××?}}×× ×× ×ץ×ר {count, plural, one {×××Ē× ×××××××} other {×××Ē× ××××××××}}.", + "permanently_deleted_asset": "××Ē××× × × ×××§× ××Ļ×××Ē××Ē", + "permanently_deleted_assets_count": "{count, plural, one {×Ē××× × # × ×××§×} other {# ×Ē××× ××Ē × ×××§×}} ××Ļ×××Ē××Ē", "permission_onboarding_back": "××ר×", "permission_onboarding_continue_anyway": "×××Š× ××× ×××Ē", "permission_onboarding_get_started": "×××Ē×××", "permission_onboarding_go_to_settings": "×× ××××ר××Ē", - "permission_onboarding_permission_denied": "×ר׊×× × ×××Ē×. ××× ××׊×Ē×׊ ×××׊××, ××ĸ× ×§ ×ר׊×× ××Ē××× ××Ē ×ץר××× ×× ××××ר××Ē", - "permission_onboarding_permission_granted": "××ר׊×× × ××Ē× ×! ××Ē/× ×××× /×", - "permission_onboarding_permission_limited": "×ר׊×× ××××××Ē. ××× ××Ē×Ē ×××׊×× ×××××Ē ××× ×× ××Ē ×× ×××Ą×Ŗ ×××ר×× ×Š××, ××ĸ× ×§ ×ר׊×× ××Ē××× ××Ē ×ץר××× ×× ××××ר××Ē", - "permission_onboarding_request": "×××׊×× ××ר׊ ×ר׊×× ××× ×ר×××Ē ××Ē ××Ē××× ××Ē ××ץר××× ×× ×Š××", + "permission_onboarding_permission_denied": "×ר׊×× × ×××Ē×. ××× ××׊×Ē×׊ ×××׊××, ××ĸ× ×§ ×ר׊×× ××Ē××× ××Ē ×ץר××× ×× ××××ר××Ē.", + "permission_onboarding_permission_granted": "××ר׊×× × ××Ē× ×! ××× ××××.", + "permission_onboarding_permission_limited": "×ר׊×× ××××××Ē. ××× ××Ē×Ē ×××׊×× ×××××Ē ××× ×× ××Ē ×× ×××Ą×Ŗ ×××ר×× ×Š××, ××ĸ× ×§ ×ר׊×× ××Ē××× ××Ē ×ץר××× ×× ××××ר××Ē.", + "permission_onboarding_request": "×××׊×× ××ר׊ ×ר׊×× ××× ×ר×××Ē ××Ē ××Ē××× ××Ē ××ץר××× ×× ×Š××.", "person": "×××", "person_birthdate": "× ××× ××Ē×ר×× {date}", "person_hidden": "{name}{hidden, select, true { (××ץ×Ēר)} other {}}", @@ -1369,12 +1375,12 @@ "primary": "ר×׊×", "privacy": "פר××××Ē", "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_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": "××××× ×Ļ×××ר×", @@ -1418,9 +1424,9 @@ "reaction_options": "×פ׊ר××××Ē ××××", "read_changelog": "×§×¨× ××Ē ×××× ×׊×× ××××", "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": "××§×Ļ× × ×ץ×× ×Š× ×××¨× ×××× ×§×××", + "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": "××פ×׊×× ××ר×× ××", @@ -1438,9 +1444,9 @@ "refreshing_metadata": "×ר×ĸ× × ×××-× ×Ē×× ××", "regenerating_thumbnails": "×××׊ ×Ē××× ××Ē ×××××ĸר××Ē", "remove": "×ץר", - "remove_assets_album_confirmation": "××× ××××Ē ×ר×Ļ×× × ××ץ×ר {count, plural, one {× ×ץ #} other {# × ×ץ××}} ×××××××?", - "remove_assets_shared_link_confirmation": "××× ××××Ē ×ר×Ļ×× × ××ץ×ר {count, plural, one {× ×ץ #} other {# × ×ץ××}} ×××§×׊×ר ××׊××Ē×Ŗ ×××?", - "remove_assets_title": "×ץר × ×ץ××?", + "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": "×ץר ××××××", @@ -1456,7 +1462,7 @@ "removed_from_favorites_count": "{count, plural, other {×××Ą×¨× #}} ×××××ĸ×פ××", "removed_memory": "×××ר×× ××ץר", "removed_photo_from_memory": "××Ē××× × ×××Ą×¨× ×××××ר××", - "removed_tagged_assets": "×Ē× ××ץר ×{count, plural, one {× ×ץ #} other {# × ×ץ××}}", + "removed_tagged_assets": "×Ē× ××ץר ×{count, plural, one {×Ē××× × #} other {# ×Ē××× ××Ē}}", "rename": "×Š× × ×Š×", "repair": "×Ē××§××", "repair_no_results_message": "×§××Ļ×× ××Ą×¨× ××ĸ×§× ××ץר×× ××פ××ĸ× ×××", @@ -1474,7 +1480,7 @@ "restore": "׊××ר", "restore_all": "׊××ר ×××", "restore_user": "׊××ר ×׊×Ē×׊", - "restored_asset": "× ×ץ ×׊×××ר", + "restored_asset": "××Ē××× × ×Š×××ר×", "resume": "××׊×", "retry_upload": "× ×Ą× ×Š×× ×××ĸ×××Ē", "review_duplicates": "××××§ ×פ××××××Ē", @@ -1540,7 +1546,7 @@ "search_result_page_new_search_hint": "××פ×׊ ××׊", "search_settings": "×××ר××Ē ××פ×׊", "search_state": "××פ×׊ ×××× ×...", - "search_suggestion_list_smart_search_hint_1": "××פ×׊ ××× ××פ×ĸ× ××ר×ר×Ē ××××, ××× ××פ׊ ×××-× ×Ē×× ×× ×׊×Ē×׊ ××Ē×××ר", + "search_suggestion_list_smart_search_hint_1": "××פ×׊ ××× ××פ×ĸ× ××ר×ר×Ē ××××, ××× ××פ׊ ×××-× ×Ē×× ×× ×׊×Ē×׊ ××Ē×××ר ", "search_suggestion_list_smart_search_hint_2": "×Ē× ××-×××פ×׊-׊××:m", "search_tags": "××פ×׊ ×Ē×××...", "search_timezone": "××פ×׊ ×××ר ×××...", @@ -1581,10 +1587,10 @@ "set_date_of_birth": "×××ר ×Ē×ר×× ××××", "set_profile_picture": "×××ר ×Ē××× ×Ē ×¤×¨×פ××", "set_slideshow_to_fullscreen": "×××ר ××Ļ××Ē ×Š×§×פ×××Ē ×××Ą× ×××", - "setting_image_viewer_help": "××Ļ×× ×פר××× ×××ĸ× ××Ē ××Ē××× × ××××××ĸר×Ē ××§×× × ×§×××, ×××ר ××× ×××ĸ× ××Ē ××Ē×Ļ××× ×××§×××× ××××× ××× ×× × (×× ××פ×ĸ××Ē), ××ץ××Ŗ ×××ĸ× ××Ē ×××§×ר××Ē (×× ××פ×ĸ××Ē)", - "setting_image_viewer_original_subtitle": "×פ׊ר ×××ĸ×× ××Ē ××Ē××× × ×××§×ר××Ē ×ר××××Ļ×× ×××× (×××××!). ×׊××Ē ××× ×××§××× ×Š×××׊ ×× ×Ē×× ×× (×× ×׊ר×Ē ××× ××××ר×× ×××××× ×Š×××׊×ר)", + "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": "×××", @@ -1596,10 +1602,10 @@ "setting_notifications_notify_minutes": "{} ××§××Ē", "setting_notifications_notify_never": "××Ŗ פ×ĸ×", "setting_notifications_notify_seconds": "{} ×Š× ×××Ē", - "setting_notifications_single_progress_subtitle": "××××ĸ ×פ××¨× ×ĸ× ××Ē×§××××Ē ××ĸ××× ××× × ×ץ", + "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_original_video_subtitle": "××׊ר ××ר×××× ×Ą×¨××× ××׊ר×Ē, × ×× ××Ē ×××§××¨× ×פ××× ×׊××ר×Ē ×§×××× ×××× ×. ×ĸ××× ×××××× ××Ē×§××ĸ××Ē. ץר××× ×× ×××× ×× ××§××××Ē ×× ××× ×× ××××××Ē ××§×ר××Ē ××× ×§×Š×¨ ×××××¨× ××.", @@ -1623,7 +1629,7 @@ "shared_by_user": "×׊××Ē×Ŗ ×ĸ× ××× {user}", "shared_by_you": "×׊××Ē×Ŗ ×ĸ× ×××", "shared_from_partner": "×Ē××× ××Ē ×××Ē {partner}", - "shared_intent_upload_button_progress_text": "×××ĸ×× {} / {}", + "shared_intent_upload_button_progress_text": "{} / {} ×××ĸ××", "shared_link_app_bar_title": "×§×׊×ר×× ×׊××Ēפ××", "shared_link_clipboard_copied_massage": "×××ĸ×Ē×§ ××××", "shared_link_clipboard_text": "×§×׊×ר: {}\nץ×ץ××: {}", @@ -1640,14 +1646,14 @@ "shared_link_edit_password_hint": "××× ×Ą ××Ē ×Ą×ץ××Ē ×׊××Ē××Ŗ", "shared_link_edit_submit_button": "×ĸ××× ×§×׊×ר", "shared_link_error_server_url_fetch": "×× × ××Ē× ××׊×× ××Ē ××Ē×××Ē ×××× ××¨× × ×Š× ×׊ר×Ē", - "shared_link_expires_day": "×פ×× ××ĸ×× {} ×××", + "shared_link_expires_day": "×פ×× ××ĸ×× ××× {}", "shared_link_expires_days": "×פ×× ××ĸ×× {} ××××", - "shared_link_expires_hour": "×פ×× ××ĸ×× {} ׊×ĸ×", + "shared_link_expires_hour": "×פ×× ××ĸ×× ×Š×ĸ× {}", "shared_link_expires_hours": "×פ×× ××ĸ×× {} ׊×ĸ××Ē", - "shared_link_expires_minute": "×פ×× ××ĸ×× {} ××§×", + "shared_link_expires_minute": "×פ×× ××ĸ×× ××§× {}", "shared_link_expires_minutes": "×פ×× ××ĸ×× {} ××§××Ē", "shared_link_expires_never": "×פ×× â", - "shared_link_expires_second": "×פ×× ××ĸ×× {} ×Š× ××", + "shared_link_expires_second": "×פ×× ××ĸ×× ×Š× ××× {}", "shared_link_expires_seconds": "×פ×× ××ĸ×× {} ×Š× ×××Ē", "shared_link_individual_shared": "×׊××Ē×Ŗ ×××××", "shared_link_info_chip_metadata": "EXIF", @@ -1661,12 +1667,12 @@ "sharing": "׊××Ē××Ŗ", "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": "××××× ×׊××Ē×Ŗ ××׊", "sharing_silver_appbar_share_partner": "׊××Ē××Ŗ ×ĸ× ×Š××Ē×Ŗ", - "shift_to_permanent_delete": "×××Ĩ â§ ××× ×××××§ ××Ļ×××Ē××Ē × ×ץ", + "shift_to_permanent_delete": "×××Ĩ â§ ××× ×××××§ ×Ē××× × ××Ļ×××Ē××Ē", "show_album_options": "××Ļ× ×פ׊ר××××Ē ×××××", "show_albums": "××Ļ× ×××××××", "show_all_people": "××Ļ× ××Ē ×× ××× ×Š××", @@ -1711,7 +1717,7 @@ "stack_duplicates": "×Ļ×ר ×ĸר×××Ē ×פ××××××Ē", "stack_select_one_photo": "××ר ×Ē××× × ×¨×׊××Ē ×××Ē ×ĸ××ר ××ĸר×××", "stack_selected_photos": "×Ļ×ר ×ĸר×××Ē ×Ē××× ××Ē × ××ר××Ē", - "stacked_assets_count": "{count, plural, one {× ×ץ # × ×ĸר×} other {# × ×ץ×× × ×ĸר××}}", + "stacked_assets_count": "{count, plural, one {×Ē××× × # × ×ĸר××} other {# ×Ē××× ××Ē × ×ĸר××}}", "stacktrace": "Stack trace", "start": "××Ē××", "start_date": "×Ē×ר×× ××Ē×××", @@ -1736,25 +1742,25 @@ "sync_albums_manual_subtitle": "×Ą× ××¨× ××Ē ×× ×ץר××× ×× ×××Ē××× ××Ē ×Š×××ĸ×× ××××××× ×××××× ×Š× ××ר×", "sync_upload_album_setting_subtitle": "×Ļ×ר ×××ĸ×× ×Ē××× ××Ē ×ץר××× ×× ×Š×× ×××××××× ×Š× ×××¨× ×××׊××", "tag": "×Ē×", - "tag_assets": "×Ē××× × ×ץ××", + "tag_assets": "×Ē××× ×Ē××× ××Ē", "tag_created": "× ××Ļר ×Ē×: {tag}", "tag_feature_description": "×ĸ××× ××Ē××× ××Ē ×ץר××× ×× ×Š×§×××Ļ× ×ĸ× ××× × ×׊×× ×Ē× ××××××", "tag_not_found_question": "×× ××Ļ××× ×××Ļ×× ×Ē×? <link>×Ļ×ר ×Ē× ××׊</link>", "tag_people": "×Ē××× ×× ×Š××", "tag_updated": "×Ē× ××ĸ××××: {tag}", - "tagged_assets": "×Ē×××× {count, plural, one {× ×ץ #} other {# × ×ץ××}}", + "tagged_assets": "×Ē×××× {count, plural, one {×Ē××× × #} other {# ×Ē××× ××Ē}}", "tags": "×Ē×××", "template": "×Ē×× ××Ē", "theme": "×ĸר××Ē × ×׊×", "theme_selection": "×××ר×Ē ×ĸר××Ē × ×׊×", "theme_selection_description": "×××ר ××××××××Ē ××Ē ×ĸר××Ē ×× ××Š× ××××ר ×× ××× ×××Ē×ץץ ×ĸ× ××ĸ×פ×Ē ×××ĸר××Ē ×Š× ××פ××¤× ×Š××", - "theme_setting_asset_list_storage_indicator_title": "×ר×× ××××× ××ץ×× ×ĸ× ×ר××× × ×ץ××", - "theme_setting_asset_list_tiles_per_row_title": "×ץפר × ×ץ×× ××× ×Š××¨× ({})", - "theme_setting_colorful_interface_subtitle": "××× ××Ē ××Ļ××ĸ ××ĸ××§×¨× ××׊××× ×¨×§×ĸ", + "theme_setting_asset_list_storage_indicator_title": "××Ļ× ×Ą×××ץ ××ץ×× ×ĸ× ××× ××Ē××× ××Ē", + "theme_setting_asset_list_tiles_per_row_title": "×ץפר ×Ē××× ××Ē ××× ×Š××¨× ({})", + "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_subtitle": "××ר ×Ļ××ĸ ×פ×ĸ××××Ē ×ĸ×קר×××Ē ××××׊××Ē.", "theme_setting_primary_color_title": "×Ļ××ĸ ×ĸ×קר×", "theme_setting_system_primary_color_title": "×׊×Ē×׊ ××Ļ××ĸ ×××ĸר××Ē", "theme_setting_system_theme_switch": "××××××× (×ĸ×§×× ×××¨× ×××ר×Ē ××ĸר××Ē)", @@ -1779,15 +1785,15 @@ "trash": "×׊פ×", "trash_all": "××ĸ×ר ××× ××׊פ×", "trash_count": "××ĸ×ר ×××Š×¤× {count, number}", - "trash_delete_asset": "××ĸ×ר ××׊פ×/×××§ × ×ץ", + "trash_delete_asset": "××ĸ×ר ××׊פ×/×××§ ×Ē××× ×", "trash_emptied": "×××Š×¤× ×¨××§× ×", "trash_no_results_message": "×Ē××× ××Ē ×ץר××× ×× ×Š×××ĸ××¨× ×××Š×¤× ××פ××ĸ× ×××.", "trash_page_delete_all": "×××§ ×××", - "trash_page_empty_trash_dialog_content": "××× ×ר×Ļ×× × ×ר××§× ××Ē ×× ×ץ×× ×Š××׊פ×? ×פר×××× ×××× ××××§× ××Ļ×××Ē××Ē ××׊ר×Ē", + "trash_page_empty_trash_dialog_content": "××× ×ר×Ļ×× × ×ר××§× ××Ē ××Ē××× ××Ē ×Š××׊פ×? ×פר×××× ×××× ××××§× ××Ļ×××Ē××Ē ××׊ר×Ē", "trash_page_info": "פר×××× ×××Š×¤× ××××§× ××Ļ×××Ē××Ē ×××ר {} ××××", - "trash_page_no_assets": "××× × ×ץ×× ××׊פ×", + "trash_page_no_assets": "××× ×Ē××× ××Ē ××׊פ×", "trash_page_restore_all": "׊××ר ×××", - "trash_page_select_assets_btn": "××ר × ×ץ××", + "trash_page_select_assets_btn": "××ר ×Ē××× ××Ē", "trash_page_title": "××Š×¤× ({})", "trashed_items_will_be_permanently_deleted_after": "פר×××× ×××Š×¤× ××××§× ××Ļ×××Ē××Ē ×××ר {days, plural, one {××× #} other {# ××××}}.", "type": "ץ××", @@ -1810,22 +1816,22 @@ "unselect_all": "××× ××××¨× ××××", "unselect_all_duplicates": "××× ×××ר×Ē ×× ××פ××××××Ē", "unstack": "××× ×ĸר×××", - "unstacked_assets_count": "{count, plural, one {× ×ץ # ××ץר} other {# × ×ץ×× ××ץר×}} ××ĸר×××", + "unstacked_assets_count": "{count, plural, one {×Ē××× × # ××ץר×} other {# ×Ē××× ××Ē ××ץר×}} ×××ĸר×××", "untracked_files": "×§××Ļ×× ××× ××ĸ×§×", "untracked_files_decription": "×§××Ļ×× ××× ××× × × ××Ļ××× ×××ĸ×§× ×Š× ×××׊××. ×× ×××××× ×××××Ē ×Ē××Ļ×××Ē ×Š× ××ĸ×ר××Ē ××׊×××Ē, ××ĸ××××Ē ×Š× ×§××ĸ×, ×× ×Š× ××Ē×¨× ××××ר ×××× ×Š×××׊ ××Ē××× ×", "up_next": "××× ××Ē×ר", "updated_password": "ץ×ץ×× ×ĸ×××× ×", "upload": "××ĸ×××", "upload_concurrency": "××-××× ×××Ē ×Š× ××ĸ×××", - "upload_dialog_info": "××× ×ר×Ļ×× × ×××××Ē ××Ē ×× ×ץ(××) ×Š× ×××¨× ×׊ר×Ē?", - "upload_dialog_title": "××ĸ×××Ē × ×ץ", - "upload_errors": "××ĸ××× ××׊××× ×ĸ× {count, plural, one {׊×××× #} other {# ׊×××××Ē}}, ר×ĸ× × ××Ē ×××Ŗ ××× ×ר×××Ē × ××Ą× ××ĸ××× ××׊××.", + "upload_dialog_info": "××× ×ר×Ļ×× × ×××××Ē ××Ē ××Ē××× ××Ē ×Š× ×××¨× ×׊ר×Ē?", + "upload_dialog_title": "××ĸ×××Ē ×Ē××× ×", + "upload_errors": "××ĸ××× ××׊××× ×ĸ× {count, plural, one {׊×××× #} other {# ׊×××××Ē}}, ר×ĸ× × ××Ē ×××Ŗ ××× ×ר×××Ē ×Ē××× ××Ē ×Š×××ĸ××.", "upload_progress": "× ××Ē×¨× {remaining, number} - ××פ×× {processed, number}/{total, number}", - "upload_skipped_duplicates": "×××× ×ĸ× {count, plural, one {× ×ץ ×פ×× #} other {# × ×ץ×× ×פ××××}}", + "upload_skipped_duplicates": "×××× ×ĸ× {count, plural, one {×Ē××× × ×פ××× #} other {# ×Ē××× ××Ē ×פ××××Ē}}", "upload_status_duplicates": "×פ××××××Ē", "upload_status_errors": "׊×××××Ē", "upload_status_uploaded": "×××ĸ××", - "upload_success": "×××ĸ××× ××Ļ××××, ר×ĸ× × ××Ē ×××Ŗ ××× ×ר×××Ē × ××Ą× ××ĸ××× ××׊××.", + "upload_success": "×××ĸ××× ×××Ļ×ĸ× ×××Ļ×××. ר×ĸ× × ××Ē ×××Ŗ ××× ××Ļפ××Ē ××Ē××× ××Ē ×Š×××ĸ××.", "upload_to_immich": "××ĸ×× ×׊ר×Ē ({})", "uploading": "××ĸ××", "url": "URL", @@ -1834,7 +1840,7 @@ "use_custom_date_range": "×׊×Ē×׊ ××××× ×Ē×ר×××× ×××Ē×× ×××§××", "user": "×׊×Ē×׊", "user_id": "×××× ×׊×Ē×׊", - "user_liked": "{user} ××× ××Ē {type, select, photo {××Ē××× × ××××Ē} video {×ץר××× ×××} asset {×× ×ץ ×××} other {××}}", + "user_liked": "{user} ××× ××Ē {type, select, photo {××Ē××× × ××××Ē} video {×ץר××× ×××} asset {××Ē××× × ××××Ē} other {××}}", "user_purchase_settings": "ר××׊×", "user_purchase_settings_description": "× ×××× ×ר×××Š× ×Š××", "user_role_set": "×××ר ××Ē {user} ××Ē×ר {role}", @@ -1853,7 +1859,7 @@ "version_announcement_overlay_release_notes": "××ĸר××Ē ×¤×¨×Ą××", "version_announcement_overlay_text_1": "×× ××ר/×, ×׊ ×××××¨× ×××Š× ×Š×", "version_announcement_overlay_text_2": "×× × ×§×/× ××Ē ×××× ×Š×× ××קר × ", - "version_announcement_overlay_text_3": " ×××××× ×Š××× × × docker-compose ×× env. ׊×× ×ĸ××× × ××× ××× ××ĸ ×Ē×Ļ×ר××Ē ×Š×××××Ē, ×××××× ×× ××Ē/× ×׊×Ē×׊/×Ē × WatchTower ×× ××× ×× ×× ×× ×Š×××¤× ××ĸ×××× ××׊×× ×׊ר×Ē ×Š×× ××××¤× ×××××××", + "version_announcement_overlay_text_3": " ×××××× ×Š××× × × docker-compose ×× env. ׊×× ×ĸ××× × ××× ××× ××ĸ ×Ē×Ļ×ר××Ē ×Š×××××Ē, ×××××× ×× ××Ē× ×׊×Ē×׊ × WatchTower ×× ××× ×× ×× ×Š×××¤× ××ĸ×××× ×׊ר×Ē ××××¤× ×××××××.", "version_announcement_overlay_title": "×רץ×Ē ×Š×¨×Ē ×××Š× ×××× × đ", "version_history": "××ץ××ר×××Ē ×רץ×××Ē", "version_history_item": "{version} ×××Ē×§× × ×-{date}", @@ -1870,11 +1876,12 @@ "view_link": "××Ļ× ×§×׊×ר", "view_links": "××Ļ× ×§×׊×ר××", "view_name": "××Ļ×", - "view_next_asset": "××Ļ× ××Ē ×× ×ץ ×××", - "view_previous_asset": "××Ļ× ××Ē ×× ×ץ ××§×××", + "view_next_asset": "××Ļ× ××Ē ××Ē××× × ××××", + "view_previous_asset": "××Ļ× ××Ē ××Ē××× × ××§××××Ē", + "view_qr_code": "××Ļ× ×רק××", "view_stack": "××Ļ× ×ĸר×××", "viewer_remove_from_stack": "×ץר ××ĸר×××", - "viewer_stack_use_as_main_asset": "×׊×Ē×׊ ×× ×ץ ר×׊×", + "viewer_stack_use_as_main_asset": "×׊×Ē×׊ ××Ē××× × ×¨×׊××Ē", "viewer_unstack": "××××× ×ĸר×××", "visibility_changed": "×× ×¨×××Ē ×׊×Ē× ×Ē× ×ĸ××ר {count, plural, one {××× #} other {# ×× ×Š××}}", "waiting": "×××Ē××", @@ -1882,7 +1889,7 @@ "week": "׊×××ĸ", "welcome": "×ר×××× ×××××", "welcome_to_immich": "×ר×××× ××××× ×× immich", - "wifi_name": "×Š× ××× ××¨× × ××××××", + "wifi_name": "×Š× ×ר׊×Ē ××××××××Ē", "year": "×Š× ×", "years_ago": "××¤× × {years, plural, one {×Š× × #} other {# ×Š× ××}}", "yes": "××", diff --git a/i18n/hi.json b/i18n/hi.json index 7f50906791..50c17d9e5d 100644 --- a/i18n/hi.json +++ b/i18n/hi.json @@ -1142,7 +1142,7 @@ "partner_page_partner_add_failed": "Failed to add partner", "partner_page_select_partner": "Select partner", "partner_page_shared_to_title": "Shared to", - "partner_page_stop_sharing_content": "{} will no longer be able to access your photos.", + "partner_page_stop_sharing_content": "{} will no longer be able to access your photosāĨ¤", "partner_sharing": "ā¤Ēā¤žā¤°āĨā¤ā¤¨ā¤° ā¤ļāĨ⤝⤰ā¤ŋā¤ā¤", "partners": "ā¤ā¤žā¤āĨā¤Ļā¤žā¤°āĨā¤", "password": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą", diff --git a/i18n/hr.json b/i18n/hr.json index 53a7cc2640..a30de885cd 100644 --- a/i18n/hr.json +++ b/i18n/hr.json @@ -4,6 +4,7 @@ "account_settings": "Postavke raÄuna", "acknowledge": "Potvrdi", "action": "Akcija", + "action_common_update": "AÅžuriranje", "actions": "Akcije", "active": "Aktivno", "activity": "Aktivnost", @@ -13,6 +14,7 @@ "add_a_location": "Dodaj lokaciju", "add_a_name": "Dodaj ime", "add_a_title": "Dodaj naslov", + "add_endpoint": "Dodaj krajnju toÄnu", "add_exclusion_pattern": "Dodaj uzorak izuzimanja", "add_import_path": "Dodaj import folder", "add_location": "Dodaj lokaciju", @@ -20,8 +22,10 @@ "add_partner": "Dodaj partnera", "add_path": "Dodaj putanju", "add_photos": "Dodaj slike", - "add_to": "Dodaj u...", + "add_to": "Dodaj uâĻ", "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_shared_album": "Dodaj u dijeljeni album", "add_url": "Dodaj URL", "added_to_archive": "Dodano u arhivu", @@ -41,6 +45,7 @@ "backup_settings": "Postavke sigurnosne kopije", "backup_settings_description": "Upravljanje postavkama sigurnosne kopije baze podataka", "check_all": "Provjeri sve", + "cleanup": "ÄiÅĄÄenje", "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,8 +70,13 @@ "forcing_refresh_library_files": "Prisilno osvjeÅžavanje svih datoteka knjiÅžnice", "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_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", "image_prefer_embedded_preview": "Preferiraj ugraÄeni pregled", - "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 artefakata kompresije.", + "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", @@ -96,7 +106,7 @@ "library_scanning_enable_description": "OmoguÄi periodiÄno skeniranje biblioteke", "library_settings": "Externa biblioteka", "library_settings_description": "Upravljajte postavkama vanjske biblioteke", - "library_tasks_description": "Obavljati bibliotekne zadatke", + "library_tasks_description": "Skeniraj eksterne biblioteke za nove i/ili promijenjene resurse", "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", @@ -131,7 +141,7 @@ "machine_learning_smart_search_description": "PretraÅžujte slike semantiÄki koristeÄi CLIP ugradnje", "machine_learning_smart_search_enabled": "OmoguÄi pametno pretraÅživanje", "machine_learning_smart_search_enabled_description": "Ako je onemoguÄeno, slike neÄe biti kodirane za pametno pretraÅživanje.", - "machine_learning_url_description": "URL posluÅžitelja strojnog uÄenja. Ako ste dodali viÅĄe od jednog URLa, svaki server Äe biti kontaktiraj jedanput dok jedan ne odgovori uspjeÅĄno, u redu od prvog do zadnjeg.", + "machine_learning_url_description": "URL posluÅžitelja strojnog uÄenja. Ako ste dodali viÅĄe od jednog URLa, svaki server Äe biti kontaktiraj jedanput dok jedan ne odgovori uspjeÅĄno, u redu od prvog do zadnjeg. Serveri koji ne odgovore Äe privremeno biti ignorirani dok ponovo ne postanu dostupni.", "manage_concurrency": "Upravljanje IstovremenoÅĄÄu", "manage_log_settings": "Upravljanje postavkama zapisivanje", "map_dark_style": "Tamni stil", @@ -147,6 +157,8 @@ "map_settings": "Karta", "map_settings_description": "Upravljanje postavkama karte", "map_style_description": "URL na style.json temu karte", + "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_faces_import_setting": "OmoguÄi uvoz lica", @@ -239,7 +251,7 @@ "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 <link>{template}</link> na prethodno prenesena sredstva", - "storage_template_migration_info": "Promjene predloÅĄka primjenjivat Äe se samo na nova sredstva. Za retroaktivnu primjenu predloÅĄka na prethodno prenesena sredstva, pokrenite <link>{job}</link>.", + "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 <link>{job}</link>.", "storage_template_migration_job": "Posao Migracije PredloÅĄka Pohrane", "storage_template_more_details": "Za viÅĄe pojedinosti o ovoj znaÄajci pogledajte <template-link>PredloÅžak pohrane</template-link> i njegove <implications-link>implikacije</implications-link>", "storage_template_onboarding_description": "Kada je omoguÄena, ova Äe znaÄajka automatski organizirati datoteke na temelju korisniÄki definiranog predloÅĄka. Zbog problema sa stabilnoÅĄÄu znaÄajka je iskljuÄena prema zadanim postavkama. Za viÅĄe informacija pogledajte <link>dokumentaciju</link>.", @@ -250,6 +262,15 @@ "system_settings": "Postavke Sustava", "tag_cleanup_job": "ÄiÅĄÄenje oznaka", "template_email_available_tags": "MoÅžete koristiti sljedeÄe varijable u vaÅĄem predloÅĄku:{tags}", + "template_email_if_empty": "Ukoliko je predloÅžak prazan, koristit Äe se zadana e-mail adresa.", + "template_email_invite_album": "PredloÅžak za pozivnicu u album", + "template_email_preview": "Pregled", + "template_email_settings": "E-mail PredloÅĄci", + "template_email_settings_description": "Upravljanje prilagoÄenim predloÅĄcima za obavijesti putem e-maila", + "template_email_update_album": "AÅžuriraj Album PredloÅžak", + "template_email_welcome": "PredloÅžak e-maila dobrodoÅĄlice", + "template_settings": "PredloÅžak Obavijesti", + "template_settings_description": "Upravljaj prilagoÄenim predloÅĄcima za obavijesti.", "theme_custom_css_settings": "PrilagoÄeni CSS", "theme_custom_css_settings_description": "Kaskadni listovi stilova (CSS) omoguÄuju prilagoÄavanje dizajna Immicha.", "theme_settings": "Postavke tema", @@ -279,6 +300,8 @@ "transcoding_constant_rate_factor": "Faktor konstantne stope (-crf)", "transcoding_constant_rate_factor_description": "Razina kvalitete videa. UobiÄajene vrijednosti su 23 za H.264, 28 za HEVC, 31 za VP9 i 35 za AV1. NiÅže je bolje, ali stvara veÄe datoteke.", "transcoding_disabled_description": "Nemojte transkodirati nijedan videozapis, moÅže prekinuti reprodukciju na nekim klijentima", + "transcoding_encoding_options": "Opcije Kodiranja", + "transcoding_encoding_options_description": "Postavi kodeke, rezoluciju, kvalitetu i druge opcije za kodirane videje", "transcoding_hardware_acceleration": "Hardversko Ubrzanje", "transcoding_hardware_acceleration_description": "Eksperimentalno; puno brÅže, ali Äe imati niÅžu kvalitetu pri istoj bitrate postavci", "transcoding_hardware_decoding": "Hardversko dekodiranje", @@ -291,6 +314,8 @@ "transcoding_max_keyframe_interval": "Maksimalni interval kljuÄnih sliÄica", "transcoding_max_keyframe_interval_description": "Postavlja maksimalnu udaljenost slika izmeÄu kljuÄnih kadrova. NiÅže vrijednosti pogorÅĄavaju uÄinkovitost kompresije, ali poboljÅĄavaju vrijeme traÅženja i mogu poboljÅĄati kvalitetu u scenama s brzim kretanjem. 0 automatski postavlja ovu vrijednost.", "transcoding_optimal_description": "Videozapisi koji su veÄi od ciljne rezolucije ili nisu u prihvatljivom formatu", + "transcoding_policy": "Politika Transkodiranja", + "transcoding_policy_description": "Postavi kada Äe video biti transkodiran", "transcoding_preferred_hardware_device": "Preferirani hardverski ureÄaj", "transcoding_preferred_hardware_device_description": "Odnosi se samo na VAAPI i QSV. Postavlja dri node koji se koristi za hardversko transkodiranje.", "transcoding_preset_preset": "Preset (-preset)", @@ -299,7 +324,7 @@ "transcoding_reference_frames_description": "Broj slika za referencu prilikom komprimiranja odreÄene slike. ViÅĄe vrijednosti poboljÅĄavaju uÄinkovitost kompresije, ali usporavaju kodiranje. 0 automatski postavlja ovu vrijednost.", "transcoding_required_description": "Samo videozapisi koji nisu u prihvaÄenom formatu", "transcoding_settings": "Postavke Video Transkodiranja", - "transcoding_settings_description": "Upravljajte informacijama o razluÄivosti i kodiranju video datoteka", + "transcoding_settings_description": "Upravljaj koji videozapisi Äe se transkodirati i kako ih obraditi", "transcoding_target_resolution": "Ciljana rezolucija", "transcoding_target_resolution_description": "VeÄe razluÄivosti mogu saÄuvati viÅĄe detalja, ali trebaju dulje za kodiranje, imaju veÄe veliÄine datoteka i mogu smanjiti odziv aplikacije.", "transcoding_temporal_aq": "Vremenski AQ", @@ -312,7 +337,7 @@ "transcoding_transcode_policy_description": "Pravila o tome kada se video treba transkodirati. HDR videozapisi uvijek Äe biti transkodirani (osim ako je transkodiranje onemoguÄeno).", "transcoding_two_pass_encoding": "Kodiranje u dva prolaza", "transcoding_two_pass_encoding_setting_description": "Transkodiranje u dva prolaza za proizvodnju bolje kodiranih videozapisa. Kada je omoguÄena maksimalna brzina prijenosa (potrebna za rad s H.264 i HEVC), ovaj naÄin rada koristi raspon brzine prijenosa na temelju maksimalne brzine prijenosa i zanemaruje CRF. Za VP9, CRF se moÅže koristiti ako je maksimalna brzina prijenosa onemoguÄena.", - "transcoding_video_codec": "Video Kodek", + "transcoding_video_codec": "Video kodek", "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", @@ -346,6 +371,20 @@ "admin_password": "Admin Lozinka", "administration": "Administracija", "advanced": "Napredno", + "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: {}", + "advanced_settings_prefer_remote_subtitle": "Neki ureÄaji sporo uÄitavaju sliÄice s resursa na ureÄaju. 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": "Zaglavlja Posrednika", + "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_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", + "advanced_settings_troubleshooting_title": "RjeÅĄavanje problema", "age_months": "Dob {months, plural, one {# month} other {# months}}", "age_year_months": "Dob 1 godina, {months, plural, one {# month} other {# months}}", "age_years": "{years, plural, other {Age #}}", @@ -354,6 +393,8 @@ "album_cover_updated": "Naslovnica albuma aÅžurirana", "album_delete_confirmation": "Jeste li sigurni da Åželite izbrisati album {album}?", "album_delete_confirmation_description": "Ako se ovaj album dijeli, drugi korisnici mu viÅĄe neÄe moÄi pristupiti.", + "album_info_card_backup_album_excluded": "IZUZETO", + "album_info_card_backup_album_included": "UKLJUÄENO", "album_info_updated": "Podaci o albumu aÅžurirani", "album_leave": "Napustiti album?", "album_leave_confirmation": "Jeste li sigurni da Åželite napustiti {album}?", @@ -362,10 +403,22 @@ "album_remove_user": "Ukloni korisnika?", "album_remove_user_confirmation": "Jeste li sigurni da Åželite ukloniti {user}?", "album_share_no_users": "Äini se da ste podijelili ovaj album sa svim korisnicima ili nemate nijednog korisnika s kojim biste ga dijelili.", + "album_thumbnail_card_item": "1 stavka", + "album_thumbnail_card_items": "{} stavki", + "album_thumbnail_card_shared": " ¡ Podijeljeno", + "album_thumbnail_shared_by": "Podijeljeno sa {}", "album_updated": "Album aÅžuriran", "album_updated_setting_description": "Primite obavijest e-poÅĄtom kada dijeljeni album ima nova sredstva", "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_title": "NeuspjeÅĄno mijenjanje naslova albuma", + "album_viewer_appbar_share_leave": "Napusti album", + "album_viewer_appbar_share_to": "Podijeli s", + "album_viewer_page_share_add_users": "Dodaj korisnike", "album_with_link_access": "Dopusti svima s poveznicom pristup fotografijama i osobama u ovom albumu.", "albums": "Albumi", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albumi}}", @@ -377,47 +430,139 @@ "allow_edits": "Dozvoli izmjene", "allow_public_user_to_download": "Dopusti javnom korisniku preuzimanje", "allow_public_user_to_upload": "Dopusti javnom korisniku uÄitavanje", + "alt_text_qr_code": "Slika QR koda", "anti_clockwise": "Suprotno smjeru kazaljke na satu", "api_key": "API KljuÄ", "api_key_description": "Ova Äe vrijednost biti prikazana samo jednom. Obavezno ju kopirajte prije zatvaranja prozora.", "api_key_empty": "Naziv vaÅĄeg API kljuÄa ne smije biti prazan", "api_keys": "API KljuÄevi", + "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", "appears_in": "Pojavljuje se u", "archive": "Arhiva", "archive_or_unarchive_photo": "Arhivirajte ili dearhivirajte fotografiju", + "archive_page_no_archived_assets": "Nema arhiviranih resursa", + "archive_page_title": "Arhiviraj {{}}", "archive_size": "VeliÄina arhive", "archive_size_description": "Konfigurirajte veliÄinu arhive za preuzimanja (u GiB)", + "archived": "Ahrivirano", "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_added_to_album": "Dodano u album", - "asset_adding_to_album": "Dodavanje 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": "Hashiranje...", + "asset_hashing": "SaÅžimanjeâĻ", + "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_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_skipped": "PreskoÄeno", "asset_skipped_in_trash": "U smeÄu", "asset_uploaded": "UÄitano", - "asset_uploading": "UÄitavanje...", + "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_name_count": "Dodano {count, plural, one {# asset} other {# assets}} u {hasName, select, true {<b>{name}</b>} other {new album}}", "assets_count": "{count, plural, one {# asset} other {# assets}}", + "assets_deleted_permanently": "{} resurs(i) uspjeÅĄno uklonjeni", + "assets_deleted_permanently_from_server": "{} resurs(i) trajno obrisan(i) sa Immich posluÅžitelja", "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}}", - "assets_restore_confirmation": "Jeste li sigurni da Åželite vratiti sve svoje resurse baÄene u otpad? Ne moÅžete poniÅĄtiti ovu radnju!", + "assets_removed_permanently_from_device": "{} 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}}", + "assets_restored_successfully": "{} resurs(i) uspjeÅĄno obnovljen(i)", + "assets_trashed": "{} resurs(i) premjeÅĄten(i) u smeÄe", "assets_trashed_count": "BaÄeno u smeÄe {count, plural, one {# asset} other {# assets}}", + "assets_trashed_from_server": "{} 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", "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", "back": "Nazad", "back_close_deselect": "Natrag, zatvorite ili poniÅĄtite odabir", + "background_location_permission": "Dozvola za lokaciju u pozadini", + "background_location_permission_content": "Kako bi prebacivao mreÅže dok radi u pozadini, Immich mora *uvijek* imati pristup preciznoj lokaciji kako bi aplikacija mogla proÄitati naziv Wi-Fi mreÅže", + "backup_album_selection_page_albums_device": "Albumi na ureÄaju {{}}", + "backup_album_selection_page_albums_tap": "Dodirnite za ukljuÄivanje, dvostruki dodir za iskljuÄivanje", + "backup_album_selection_page_assets_scatter": "Resursi mogu biti rasporeÄeni u viÅĄe albuma. Stoga, albumi mogu biti ukljuÄeni ili iskljuÄeni tijekom procesa sigurnosnog kopiranja.", + "backup_album_selection_page_select_albums": "Odabrani albumi", + "backup_album_selection_page_selection_info": "Informacije o odabiru", + "backup_album_selection_page_total_assets": "Ukupan broj jedinstvenih resursa", + "backup_all": "Sve", + "backup_background_service_backup_failed_message": "NeuspjeÅĄno sigurnosno kopiranje resursa. PokuÅĄavam ponovoâĻ", + "backup_background_service_connection_failed_message": "NeuspjeÅĄno povezivanje s posluÅžiteljem. PokuÅĄavam ponovoâĻ", + "backup_background_service_current_upload_notification": "Å aljem {}", + "backup_background_service_default_notification": "Provjera novih resursaâĻ", + "backup_background_service_error_title": "PogreÅĄka pri sigurnosnom kopiranju", + "backup_background_service_in_progress_notification": "Sigurnosno kopiranje vaÅĄih resursaâĻ", + "backup_background_service_upload_failure_notification": "NeuspjeÅĄno slanje {}", + "backup_controller_page_albums": "Sigurnosno kopiranje albuma", + "backup_controller_page_background_app_refresh_disabled_content": "OmoguÄite osvjeÅžavanje aplikacije u pozadini u Postavke > OpÄe Postavke > OsvjeÅžavanje Aplikacija u Pozadini kako biste koristili sigurnosno kopiranje u pozadini.", + "backup_controller_page_background_app_refresh_disabled_title": "OsvjeÅžavanje aplikacija u pozadini je onemoguÄeno", + "backup_controller_page_background_app_refresh_enable_button_text": "Idite u postavke", + "backup_controller_page_background_battery_info_link": "PokaÅži mi kako", + "backup_controller_page_background_battery_info_message": "Za najbolje iskustvo sigurnosnog kopiranja u pozadini, molimo onemoguÄite sve optimizacije baterije koje ograniÄavaju pozadinsku aktivnost Immicha.\n\nBuduÄi da je ovo specifiÄno za ureÄaj, molimo potraÅžite potrebne informacije za proizvoÄaÄa vaÅĄeg ureÄaja.", + "backup_controller_page_background_battery_info_ok": "U redu", + "backup_controller_page_background_battery_info_title": "Optimizacije baterije", + "backup_controller_page_background_charging": "Samo tijekom punjenja", + "backup_controller_page_background_configure_error": "NeuspjeÅĄno konfiguriranje pozadinske usluge", + "backup_controller_page_background_delay": "OdgoÄeno sigurnosno kopiranje novih resursa: {}", + "backup_controller_page_background_description": "UkljuÄite pozadinsku uslugu kako biste automatski sigurnosno kopirali nove resurse bez potrebe za otvaranjem aplikacije", + "backup_controller_page_background_is_off": "Automatsko sigurnosno kopiranje u pozadini je iskljuÄeno", + "backup_controller_page_background_is_on": "Automatsko sigurnosno kopiranje u pozadini je ukljuÄeno", + "backup_controller_page_background_turn_off": "IskljuÄite pozadinsku uslugu", + "backup_controller_page_background_turn_on": "UkljuÄite pozadinsku uslugu", + "backup_controller_page_background_wifi": "Samo na Wi-Fi mreÅži", + "backup_controller_page_backup": "Sigurnosna kopija", + "backup_controller_page_backup_selected": "Odabrani: ", + "backup_controller_page_backup_sub": "Sigurnosno kopirane fotografije i videozapisi", + "backup_controller_page_created": "Kreirano: {}", + "backup_controller_page_desc_backup": "UkljuÄite sigurnosno kopiranje u prvom planu kako biste automatski prenijeli nove resurse na posluÅžitelj prilikom otvaranja aplikacije.", + "backup_controller_page_excluded": "Izuzeto: ", + "backup_controller_page_failed": "NeuspjeÅĄno ({})", + "backup_controller_page_filename": "Naziv datoteke: {} [{}]", + "backup_controller_page_id": "ID: {}", + "backup_controller_page_info": "Informacije o sigurnosnom kopiranju", + "backup_controller_page_none_selected": "Nema odabranih", + "backup_controller_page_remainder": "Podsjetnik", + "backup_controller_page_remainder_sub": "Preostale fotografije i videozapisi za sigurnosno kopiranje iz odabira", + "backup_controller_page_server_storage": "Pohrana na posluÅžitelju", + "backup_controller_page_start_backup": "Pokreni Sigurnosno Kopiranje", + "backup_controller_page_status_off": "Automatsko sigurnosno kopiranje u prvom planu je iskljuÄeno", + "backup_controller_page_status_on": "Automatsko sigurnosno kopiranje u prvom planu je ukljuÄeno", + "backup_controller_page_storage_format": "{} od {} iskoriÅĄteno", + "backup_controller_page_to_backup": "Albumi za sigurnosno kopiranje", + "backup_controller_page_total_sub": "Sve jedinstvene fotografije i videozapisi iz odabranih albuma", + "backup_controller_page_turn_off": "IskljuÄite sigurnosno kopiranje u prvom planu", + "backup_controller_page_turn_on": "UkljuÄite sigurnosno kopiranje u prvom planu", + "backup_controller_page_uploading_file_info": "Slanje informacija o datoteci", + "backup_err_only_album": "Nije moguÄe ukloniti jedini album", + "backup_info_card_assets": "resursi", + "backup_manual_cancelled": "Otkazano", + "backup_manual_in_progress": "Slanje veÄ u tijeku. PokÅĄuajte nakon nekog vremena", + "backup_manual_success": "Uspijeh", + "backup_manual_title": "Status slanja", + "backup_options_page_title": "Opcije sigurnosnog kopiranja", + "backup_setting_subtitle": "Upravljajte postavkama uÄitavanja u pozadini i prvom planu", "backward": "Unazad", "birthdate_saved": "Datum roÄenja uspjeÅĄno spremljen", "birthdate_set_description": "Datum roÄenja se koristi za izraÄunavanje godina ove osobe u trenutku fotografije.", @@ -429,6 +574,16 @@ "bulk_keep_duplicates_confirmation": "Jeste li sigurni da Åželite zadrÅžati {count, plural, one {# duplicate asset} other {# duplicate asset}}? Ovo Äe rijeÅĄiti sve duplicirane grupe bez brisanja iÄega.", "bulk_trash_duplicates_confirmation": "Jeste li sigurni da Åželite na veliko baciti u smeÄe {count, plural, one {# duplicate asset} other {# duplicate asset}}? Ovo Äe zadrÅžati najveÄe sredstvo svake grupe i baciti sve ostale duplikate u smeÄe.", "buy": "Kupi Immich", + "cache_settings_album_thumbnails": "SliÄice na stranici biblioteke ({} resursa)", + "cache_settings_clear_cache_button": "OÄisti predmemoriju", + "cache_settings_clear_cache_button_title": "BriÅĄe predmemoriju aplikacije. Ovo Äe znaÄajno utjecati na performanse aplikacije dok se predmemorija ponovno ne izgradi.", + "cache_settings_duplicated_assets_clear_button": "OÄISTI", + "cache_settings_duplicated_assets_subtitle": "Fotografije i videozapisi koje je aplikacija stavila na crnu listu", + "cache_settings_duplicated_assets_title": "Duplicirani Resursi ({})", + "cache_settings_image_cache_size": "VeliÄina predmemorije slika ({} resursa)", + "cache_settings_statistics_album": "SliÄice biblioteke", + "cache_settings_statistics_assets": "{} resursa ({})", + "cache_settings_statistics_full": "Pune slike", "camera": "Kamera", "camera_brand": "Marka kamere", "camera_model": "Model kamere", @@ -760,6 +915,8 @@ "hide_unnamed_people": "Sakrij neimenovane osobe", "host": "DomaÄin", "hour": "Sat", + "ignore_icloud_photos": "Ignoriraj iCloud fotografije", + "ignore_icloud_photos_description": "Fotografije pohranjene na iCloudu neÄe biti uÄitane na Immich posluÅžitelj", "image": "Slika", "image_alt_text_date": "{isVideo, select, true {Video} other {Image}} snimljeno {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} snimljeno s {person1} {date}", @@ -771,6 +928,10 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} snimljeno u {city}, {country} s {person1} i {person2} {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} snimljeno u {city}, {country} s {person1}, {person2} i {person3} {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} snimljeno u {city}, {country} s {person1}, {person2} i {additionalCount, number} drugih {date}", + "image_saved_successfully": "Slika je spremljena", + "image_viewer_page_state_provider_download_started": "Preuzimanje zapoÄelo", + "image_viewer_page_state_provider_download_success": "UspjeÅĄno Preuzimanje", + "image_viewer_page_state_provider_share_error": "GreÅĄka pri dijeljenju", "immich_logo": "Immich Logo", "immich_web_interface": "Immich Web SuÄelje", "import_from_json": "Uvoz iz JSON-a", diff --git a/i18n/hu.json b/i18n/hu.json index 68dec7d036..88ce6033f8 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -529,11 +529,11 @@ "backup_controller_page_background_turn_on": "HÃĄttÊrszolgÃĄltatÃĄs bekapcsolÃĄsa", "backup_controller_page_background_wifi": "Csak WiFi-n", "backup_controller_page_backup": "MentÊs", - "backup_controller_page_backup_selected": "KivÃĄlasztva:", + "backup_controller_page_backup_selected": "KivÃĄlasztva: ", "backup_controller_page_backup_sub": "Mentett fotÃŗk Ês videÃŗk", "backup_controller_page_created": "LÊtrehozva: {}", "backup_controller_page_desc_backup": "Ha bekapcsolod az elÅtÊrben mentÊst, akkor az Ãēj elemek automatikusan feltÃļltÅdnek a szerverre, amikor megyitod az alkalmazÃĄst.", - "backup_controller_page_excluded": "KivÊve:", + "backup_controller_page_excluded": "KivÊve: ", "backup_controller_page_failed": "Sikertelen ({})", "backup_controller_page_filename": "FÃĄjlnÊv: {}[{}]", "backup_controller_page_id": "AzonosÃtÃŗ: {}", @@ -1540,7 +1540,7 @@ "search_result_page_new_search_hint": "Ãj KeresÊs", "search_settings": "KeresÊsi beÃĄllÃtÃĄsok", "search_state": "Megye/Ãllam keresÊse...", - "search_suggestion_list_smart_search_hint_1": "Az intelligens keresÊs alapÊrtelmezetten be van kapcsolva, metaadatokat Ãgy kereshetsz:", + "search_suggestion_list_smart_search_hint_1": "Az intelligens keresÊs alapÊrtelmezetten be van kapcsolva, metaadatokat Ãgy kereshetsz: ", "search_suggestion_list_smart_search_hint_2": "m:keresÊsi-kifejezÊs", "search_tags": "CÃmkÊk keresÊse...", "search_timezone": "IdÅzÃŗna keresÊse...", diff --git a/i18n/id.json b/i18n/id.json index e07e79faff..1d500654dc 100644 --- a/i18n/id.json +++ b/i18n/id.json @@ -39,11 +39,11 @@ "authentication_settings_disable_all": "Anda yakin untuk menonaktifkan semua cara login? Login akan dinonaktikan secara menyeluruh.", "authentication_settings_reenable": "Untuk mengaktifkan ulang, gunakan <link>Perintah Server</link>.", "background_task_job": "Tugas Latar Belakang", - "backup_database": "Basis Data Cadangan", + "backup_database": "Buat Cadangan Basis Data", "backup_database_enable_description": "Aktifkan pencadangan basis data", "backup_keep_last_amount": "Jumlah cadangan untuk disimpan", - "backup_settings": "Pengaturan Pencadangan", - "backup_settings_description": "Kelola pengaturan pencadangan basis data", + "backup_settings": "Pengaturan Pencadangan Basis Data", + "backup_settings_description": "Kelola pengaturan pencadangan basis data. Catatan: Tugas ini tidak dipantau dan Anda tidak akan diberi tahu jika ada kesalahan.", "check_all": "Periksa Semua", "cleanup": "Pembersihan", "cleared_jobs": "Tugas terselesaikan untuk: {job}", @@ -70,8 +70,13 @@ "forcing_refresh_library_files": "Memaksakan penyegaran semua berkas pustaka", "image_format": "Format", "image_format_description": "WebP menghasilkan ukuran berkas yang lebih kecil daripada JPEG, tetapi lebih lambat untuk dienkode.", + "image_fullsize_description": "Gambar berukuran penuh tanpa metadata, digunakan ketika diperbesar", + "image_fullsize_enabled": "Aktifkan pembuatan gambar berukuran penuh", + "image_fullsize_enabled_description": "Buat gambar berukuran penuh untuk format yang tidak ramah web. Ketika \"Utamakan pratinjau tersemat\" diaktifkan, pratinjau tersema digunakan secara langsung tanpa konversi. Tidak memengaruhi format ramah web seperti JPEG.", + "image_fullsize_quality_description": "Kualitas gambar berukuran penuh dari 1-100. Lebih tinggi lebih baik, tetapi menghasilkan berkas lebih besar.", + "image_fullsize_title": "Pengaturan Gambar Berukuran Penuh", "image_prefer_embedded_preview": "Utamakan pratinjau tersemat", - "image_prefer_embedded_preview_setting_description": "Gunakan pratinjau tersemat dalam foto RAW sebagai masukan dalam pemrosesan gambar ketika tersedia. Ini dapat menghasilkan warna yang lebih akurat untuk beberapa gambar, tetapi kualitas pratinjau bergantung pada kamera dan gambarnya dapat memiliki lebih banyak artefak kompresi.", + "image_prefer_embedded_preview_setting_description": "Gunakan pratinjau tersemat dalam foto RAW sebagai masukan dalam pemrosesan gambar dan ketika tersedia. Ini dapat menghasilkan warna yang lebih akurat untuk beberapa gambar, tetapi kualitas pratinjau bergantung pada kamera dan gambarnya dapat memiliki lebih banyak artefak kompresi.", "image_prefer_wide_gamut": "Utamakan gamut luas", "image_prefer_wide_gamut_setting_description": "Gunakan Display P3 untuk gambar kecil. Ini menjaga kecerahan gambar dengan ruang warna yang luas, tetapi gambar dapat terlihat beda pada perangkat lawas dengan versi peramban yang lawas. Gambar sRGB tetap dalam sRGB untuk menghindari perubahan warna.", "image_preview_description": "Gambar berukuran sedang tanpa metadata, digunakan ketika melihat aset satuan dan untuk pembelajaran mesin", @@ -366,13 +371,17 @@ "admin_password": "Kata Sandi Admin", "administration": "Administrasi", "advanced": "Tingkat lanjut", - "advanced_settings_log_level_title": "Log level: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "Gunakan opsi ini untuk menyaring media saat sinkronisasi berdasarkan kriteria alternatif. Hanya coba ini dengan aplikasi mendeteksi semua album.", + "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTAL] Gunakan saringan sinkronisasi album perangkat alternatif", + "advanced_settings_log_level_title": "Tingkat log: {}", "advanced_settings_prefer_remote_subtitle": "Beberapa perangkat tidak dapat memuat thumbnail dengan cepat.\nMenyalakan ini akan memuat thumbnail dari server.", "advanced_settings_prefer_remote_title": "Prefer remote images", "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", "advanced_settings_proxy_headers_title": "Proxy Headers", "advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.", "advanced_settings_self_signed_ssl_title": "Allow self-signed SSL certificates", + "advanced_settings_sync_remote_deletions_subtitle": "Hapus atau pulihkan aset pada perangkat ini secara otomatis ketika tindakan dilakukan di web", + "advanced_settings_sync_remote_deletions_title": "Sinkronisasi penghapusan jarak jauh [EKSPERIMENTAL]", "advanced_settings_tile_subtitle": "Advanced user's settings", "advanced_settings_troubleshooting_subtitle": "Enable additional features for troubleshooting", "advanced_settings_troubleshooting_title": "Troubleshooting", @@ -472,7 +481,7 @@ "assets_added_to_album_count": "Ditambahkan {count, plural, one {# aset} other {# aset}} ke album", "assets_added_to_name_count": "Ditambahkan {count, plural, one {# aset} other {# aset}} ke {hasName, select, true {<b>{name}</b>} other {album baru}}", "assets_count": "{count, plural, one {# aset} other {# aset}}", - "assets_deleted_permanently": "{} asset(s) deleted permanently", + "assets_deleted_permanently": "{} asset dihapus secara permanen", "assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server", "assets_moved_to_trash_count": "Dipindahkan {count, plural, one {# aset} other {# aset}} ke sampah", "assets_permanently_deleted_count": "{count, plural, one {# aset} other {# aset}} dihapus secara permanen", @@ -505,7 +514,7 @@ "backup_background_service_default_notification": "Memeriksa aset baru...", "backup_background_service_error_title": "Backup error", "backup_background_service_in_progress_notification": "Mencadangkan asetmu...", - "backup_background_service_upload_failure_notification": "Gagal unggah {}", + "backup_background_service_upload_failure_notification": "Gagal mengunggah {}", "backup_controller_page_albums": "Cadangkan album", "backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.", "backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled", @@ -524,11 +533,11 @@ "backup_controller_page_background_turn_on": "Nyalakan layanan latar belakang", "backup_controller_page_background_wifi": "Hanya melalui WiFi", "backup_controller_page_backup": "Cadangkan", - "backup_controller_page_backup_selected": "Terpilih:", + "backup_controller_page_backup_selected": "Terpilih: ", "backup_controller_page_backup_sub": "Foto dan video yang dicadangkan", "backup_controller_page_created": "Dibuat pada: {}", "backup_controller_page_desc_backup": "Aktifkan pencadangan di latar depan untuk mengunggah otomatis aset baru ke server secara otomatis saat aplikasi terbuka.", - "backup_controller_page_excluded": "Dikecualikan:", + "backup_controller_page_excluded": "Dikecualikan: ", "backup_controller_page_failed": "Gagal ({})", "backup_controller_page_filename": "Nama file: {} [{}]", "backup_controller_page_id": "ID: {}", @@ -601,7 +610,7 @@ "change_password": "Ubah Kata Sandi", "change_password_description": "Ini merupakan pertama kali Anda masuk ke sistem atau ada permintaan untuk mengubah kata sandi Anda. Silakan masukkan kata sandi baru di bawah.", "change_password_form_confirm_password": "Konfirmasi Sandi", - "change_password_form_description": "Halo {},\n\nIni pertama kali anda masuk ke dalam sistem atau terdapat permintaan penggantian password.\nHarap masukkan password baru.", + "change_password_form_description": "Halo {name},\n\nIni pertama kali anda masuk ke dalam sistem atau terdapat permintaan penggantian password.\nHarap masukkan password baru.", "change_password_form_new_password": "Sandi Baru", "change_password_form_password_mismatch": "Sandi tidak cocok", "change_password_form_reenter_new_password": "Masukkan Ulang Sandi Baru", @@ -814,7 +823,7 @@ "error_change_sort_album": "Failed to change album sort order", "error_delete_face": "Terjadi kesalahan menghapus wajah dari aset", "error_loading_image": "Terjadi eror memuat gambar", - "error_saving_image": "Error: {}", + "error_saving_image": "Kesalahan: {}", "error_title": "Eror - Ada yang salah", "errors": { "cannot_navigate_next_asset": "Tidak dapat menuju ke aset berikutnya", @@ -948,10 +957,10 @@ "exif_bottom_sheet_location": "LOKASI", "exif_bottom_sheet_people": "ORANG", "exif_bottom_sheet_person_add_person": "Tambah nama", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "Umur {}", + "exif_bottom_sheet_person_age_months": "Umur {} months", + "exif_bottom_sheet_person_age_year_months": "Umur 1 tahun, {} bulan", + "exif_bottom_sheet_person_age_years": "Umur {}", "exit_slideshow": "Keluar dari Salindia", "expand_all": "Buka semua", "experimental_settings_new_asset_list_subtitle": "Memproses", @@ -969,7 +978,7 @@ "external": "Eksternal", "external_libraries": "Pustaka Eksternal", "external_network": "External network", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network_sheet_info": "When not on the preferred Wi-Fi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", "face_unassigned": "Tidak ada nama", "failed": "Failed", "failed_to_load_assets": "Gagal memuat aset", @@ -987,6 +996,7 @@ "filetype": "Jenis berkas", "filter": "Filter", "filter_people": "Saring orang", + "filter_places": "Saring tempat", "find_them_fast": "Temukan dengan cepat berdasarkan nama dengan pencarian", "fix_incorrect_match": "Perbaiki pencocokan salah", "folder": "Folder", @@ -1155,6 +1165,7 @@ "loop_videos": "Ulangi video", "loop_videos_description": "Aktifkan untuk mengulangi video secara otomatis dalam penampil detail.", "main_branch_warning": "Anda menggunakan versi pengembangan; kami sangat menyarankan menggunakan versi rilis!", + "main_menu": "Menu utama", "make": "Merek", "manage_shared_links": "Kelola tautan terbagi", "manage_sharing_with_partners": "Kelola pembagian dengan partner", @@ -1276,6 +1287,7 @@ "onboarding_welcome_user": "Selamat datang, {user}", "online": "Daring", "only_favorites": "Hanya favorit", + "open": "Buka", "open_in_map_view": "Buka dalam tampilan peta", "open_in_openstreetmap": "Buka di OpenStreetMap", "open_the_search_filters": "Buka saringan pencarian", @@ -1299,7 +1311,7 @@ "partner_page_partner_add_failed": "Gagal menambahkan partner", "partner_page_select_partner": "Pilih partner", "partner_page_shared_to_title": "Dibagikan dengan", - "partner_page_stop_sharing_content": "{} tidak akan bisa mengakses foto kamu lagi.", + "partner_page_stop_sharing_content": "{} tidak akan bisa mengakses foto Anda lagi.", "partner_sharing": "Pembagian Partner", "partners": "Partner", "password": "Kata sandi", @@ -1420,6 +1432,8 @@ "recent_searches": "Pencarian terkini", "recently_added": "Recently added", "recently_added_page_title": "Baru Ditambahkan", + "recently_taken": "Diambil terkini", + "recently_taken_page_title": "Diambil Terkini", "refresh": "Segarkan", "refresh_encoded_videos": "Segarkan video terenkode", "refresh_faces": "Segarkan wajah", @@ -1620,7 +1634,7 @@ "shared_intent_upload_button_progress_text": "{} / {} Uploaded", "shared_link_app_bar_title": "Link Berbagi", "shared_link_clipboard_copied_massage": "Tersalin ke papan klip", - "shared_link_clipboard_text": "Link: {}\nSandi: {}", + "shared_link_clipboard_text": "Tautan: {}\nSandi: {}", "shared_link_create_error": "Terjadi kesalahan saat membuat link berbagi", "shared_link_edit_description_hint": "Masukkan deskripsi link", "shared_link_edit_expire_after_option_day": "1 hari", @@ -1866,6 +1880,7 @@ "view_name": "Tampilkan", "view_next_asset": "Tampilkan aset berikutnya", "view_previous_asset": "Tampilkan aset sebelumnya", + "view_qr_code": "Tampilkan kode QR", "view_stack": "Tampilkan Tumpukan", "viewer_remove_from_stack": "Keluarkan dari Tumpukan", "viewer_stack_use_as_main_asset": "Gunakan sebagai aset utama", diff --git a/i18n/it.json b/i18n/it.json index 01aeb93721..987ad34d2d 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -371,13 +371,17 @@ "admin_password": "Password Amministratore", "administration": "Amministrazione", "advanced": "Avanzate", + "advanced_settings_enable_alternate_media_filter_subtitle": "Usa questa opzione per filtrare i contenuti multimediali durante la sincronizzazione in base a criteri alternativi. Prova questa opzione solo se riscontri problemi con il rilevamento di tutti gli album da parte dell'app.", + "advanced_settings_enable_alternate_media_filter_title": "[SPERIMENTALE] Usa un filtro alternativo per la sincronizzazione degli album del dispositivo", "advanced_settings_log_level_title": "Livello log: {}", "advanced_settings_prefer_remote_subtitle": "Alcuni dispositivi sono molto lenti a caricare le anteprime delle immagini dal dispositivo. Attivare questa impostazione per caricare invece le immagini remote.", - "advanced_settings_prefer_remote_title": "Preferisci immagini remote.", - "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", + "advanced_settings_prefer_remote_title": "Preferisci immagini remote", + "advanced_settings_proxy_headers_subtitle": "Definisci gli header per i proxy che Immich dovrebbe inviare con ogni richiesta di rete", "advanced_settings_proxy_headers_title": "Proxy Headers", "advanced_settings_self_signed_ssl_subtitle": "Salta la verifica dei certificati SSL del server. Richiesto con l'uso di certificati self-signed.", "advanced_settings_self_signed_ssl_title": "Consenti certificati SSL self-signed", + "advanced_settings_sync_remote_deletions_subtitle": "Rimuovi o ripristina automaticamente un elemento su questo dispositivo se l'azione è stata fatta via web", + "advanced_settings_sync_remote_deletions_title": "Sincronizza le cancellazioni remote [SPERIMENTALE]", "advanced_settings_tile_subtitle": "Impostazioni aggiuntive utenti", "advanced_settings_troubleshooting_subtitle": "Attiva funzioni addizionali per la risoluzione dei problemi", "advanced_settings_troubleshooting_title": "Risoluzione problemi", @@ -399,19 +403,19 @@ "album_remove_user": "Rimuovi l'utente?", "album_remove_user_confirmation": "Sicuro di voler rimuovere l'utente {user}?", "album_share_no_users": "Sembra che tu abbia condiviso questo album con tutti gli utenti oppure non hai nessun utente con cui condividere.", - "album_thumbnail_card_item": "1 elemento ", + "album_thumbnail_card_item": "1 elemento", "album_thumbnail_card_items": "{} elementi", - "album_thumbnail_card_shared": "Condiviso", + "album_thumbnail_card_shared": " ¡ Condiviso", "album_thumbnail_shared_by": "Condiviso da {}", "album_updated": "Album aggiornato", "album_updated_setting_description": "Ricevi una notifica email quando un album condiviso ha nuovi media", "album_user_left": "{album} abbandonato", "album_user_removed": "Utente {user} rimosso", "album_viewer_appbar_delete_confirm": "Sei sicuro di voler rimuovere questo album dal tuo account?", - "album_viewer_appbar_share_err_delete": "Impossibile eliminare l'album ", - "album_viewer_appbar_share_err_leave": "Impossibile lasciare l'album ", - "album_viewer_appbar_share_err_remove": "Ci sono problemi nel rimuovere oggetti dall'album ", - "album_viewer_appbar_share_err_title": "Impossibile cambiare il titolo dell'album ", + "album_viewer_appbar_share_err_delete": "Impossibile eliminare l'album", + "album_viewer_appbar_share_err_leave": "Impossibile lasciare l'album", + "album_viewer_appbar_share_err_remove": "Ci sono problemi nel rimuovere oggetti dall'album", + "album_viewer_appbar_share_err_title": "Impossibile cambiare il titolo dell'album", "album_viewer_appbar_share_leave": "Lascia album", "album_viewer_appbar_share_to": "Condividi a", "album_viewer_page_share_add_users": "Aggiungi utenti", @@ -440,7 +444,7 @@ "archive": "Archivio", "archive_or_unarchive_photo": "Archivia o ripristina foto", "archive_page_no_archived_assets": "Nessuna oggetto archiviato", - "archive_page_title": "Archivia ({})", + "archive_page_title": "Archivio ({})", "archive_size": "Dimensioni Archivio", "archive_size_description": "Imposta le dimensioni dell'archivio per i download (in GiB)", "archived": "Archiviati", @@ -477,18 +481,18 @@ "assets_added_to_album_count": "{count, plural, one {# asset aggiunto} other {# asset aggiunti}} all'album", "assets_added_to_name_count": "Aggiunti {count, plural, one {# asset} other {# assets}} a {hasName, select, true {<b>{name}</b>} other {new album}}", "assets_count": "{count, plural, other {# asset}}", - "assets_deleted_permanently": "{} asset(s) deleted permanently", - "assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server", + "assets_deleted_permanently": "{} elementi rimossi definitivamente", + "assets_deleted_permanently_from_server": "{} elementi rimossi definitivamente dal server Immich", "assets_moved_to_trash_count": "{count, plural, one {# asset spostato} other {# asset spostati}} nel cestino", "assets_permanently_deleted_count": "{count, plural, one {# asset cancellato} other {# asset cancellati}} definitivamente", "assets_removed_count": "{count, plural, one {# asset rimosso} other {# asset rimossi}}", - "assets_removed_permanently_from_device": "{} asset(s) removed permanently from your device", + "assets_removed_permanently_from_device": "{} elementi rimossi definitivamente dal tuo dispositivo", "assets_restore_confirmation": "Sei sicuro di voler ripristinare tutti gli asset cancellati? Non puoi annullare questa azione! Tieni presente che eventuali risorse offline NON possono essere ripristinate in questo modo.", "assets_restored_count": "{count, plural, one {# asset ripristinato} other {# asset ripristinati}}", - "assets_restored_successfully": "{} asset(s) restored successfully", - "assets_trashed": "{} asset(s) trashed", + "assets_restored_successfully": "{} elementi ripristinati", + "assets_trashed": "{} elementi cestinati", "assets_trashed_count": "{count, plural, one {Spostato # asset} other {Spostati # assets}} nel cestino", - "assets_trashed_from_server": "{} asset(s) trashed from the Immich server", + "assets_trashed_from_server": "{} elementi cestinati dal server Immich", "assets_were_part_of_album_count": "{count, plural, one {L'asset era} other {Gli asset erano}} già parte dell'album", "authorized_devices": "Dispositivi autorizzati", "automatic_endpoint_switching_subtitle": "Connetti localmente quando la rete Wi-Fi specificata è disponibile e usa le connessioni alternative negli altri casi", @@ -498,7 +502,7 @@ "background_location_permission": "Permesso di localizzazione in background", "background_location_permission_content": "Per fare in modo che sia possibile cambiare rete quando è in esecuzione in background, Immich deve *sempre* avere accesso alla tua posizione precisa in modo da poter leggere il nome della rete Wi-Fi", "backup_album_selection_page_albums_device": "Album sul dispositivo ({})", - "backup_album_selection_page_albums_tap": "Tap per includere, doppio tap per escludere.", + "backup_album_selection_page_albums_tap": "Tap per includere, doppio tap per escludere", "backup_album_selection_page_assets_scatter": "Visto che le risorse possono trovarsi in piÚ album, questi possono essere inclusi o esclusi dal backup.", "backup_album_selection_page_select_albums": "Seleziona gli album", "backup_album_selection_page_selection_info": "Informazioni sulla selezione", @@ -529,11 +533,11 @@ "backup_controller_page_background_turn_on": "Abilita servizi in background", "backup_controller_page_background_wifi": "Solo su WiFi", "backup_controller_page_backup": "Backup", - "backup_controller_page_backup_selected": "Selezionati:", + "backup_controller_page_backup_selected": "Selezionati: ", "backup_controller_page_backup_sub": "Foto e video caricati", "backup_controller_page_created": "Creato il: {}", "backup_controller_page_desc_backup": "Attiva il backup per eseguire il caricamento automatico sul server all'apertura dell'applicazione.", - "backup_controller_page_excluded": "Esclusi:", + "backup_controller_page_excluded": "Esclusi: ", "backup_controller_page_failed": "Falliti: ({})", "backup_controller_page_filename": "Nome file: {} [{}]", "backup_controller_page_id": "ID: {}", @@ -543,18 +547,18 @@ "backup_controller_page_remainder_sub": "Foto e video che devono essere ancora caricati", "backup_controller_page_server_storage": "Spazio sul server", "backup_controller_page_start_backup": "Avvia backup", - "backup_controller_page_status_off": "Backup è disattivato ", + "backup_controller_page_status_off": "Backup è disattivato", "backup_controller_page_status_on": "Backup è attivato", "backup_controller_page_storage_format": "{} di {} usati", "backup_controller_page_to_backup": "Album da caricare", - "backup_controller_page_total_sub": "Tutte le foto e i video unici caricati dagli album selezionati ", + "backup_controller_page_total_sub": "Tutte le foto e i video unici caricati dagli album selezionati", "backup_controller_page_turn_off": "Disattiva backup", - "backup_controller_page_turn_on": "Attiva backup ", + "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_info_card_assets": "risorse", "backup_manual_cancelled": "Annullato", - "backup_manual_in_progress": "Caricamento già in corso. Riprova piÚ tardi.", + "backup_manual_in_progress": "Caricamento già in corso. Riprova piÚ tardi", "backup_manual_success": "Successo", "backup_manual_title": "Stato del caricamento", "backup_options_page_title": "Opzioni di Backup", @@ -570,21 +574,21 @@ "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.", "buy": "Acquista Immich", - "cache_settings_album_thumbnails": "Anteprime pagine librerie ({} risorse)", + "cache_settings_album_thumbnails": "Anteprime pagine librerie ({} elementi)", "cache_settings_clear_cache_button": "Pulisci cache", "cache_settings_clear_cache_button_title": "Pulisce la cache dell'app. Questo impatterà significativamente le prestazioni dell''app fino a quando la cache non sarà rigenerata.", "cache_settings_duplicated_assets_clear_button": "PULISCI", "cache_settings_duplicated_assets_subtitle": "Foto e video che sono nella black list dell'applicazione", "cache_settings_duplicated_assets_title": "Elementi duplicati ({})", - "cache_settings_image_cache_size": "Dimensione cache delle immagini ({} risorse)", + "cache_settings_image_cache_size": "Dimensione cache delle immagini ({} elementi)", "cache_settings_statistics_album": "Anteprime librerie", - "cache_settings_statistics_assets": "{} risorse ({})", + "cache_settings_statistics_assets": "{} elementi ({})", "cache_settings_statistics_full": "Immagini complete", "cache_settings_statistics_shared": "Anteprime album condivisi", "cache_settings_statistics_thumbnail": "Anteprime", "cache_settings_statistics_title": "Uso della cache", "cache_settings_subtitle": "Controlla il comportamento della cache dell'applicazione mobile immich", - "cache_settings_thumbnail_size": "Dimensione cache dei thumbnail ({} assets)", + "cache_settings_thumbnail_size": "Dimensione cache anteprime ({} elementi)", "cache_settings_tile_subtitle": "Controlla il comportamento dello storage locale", "cache_settings_tile_title": "Archiviazione locale", "cache_settings_title": "Impostazioni della Cache", @@ -593,7 +597,7 @@ "camera_model": "Modello fotocamera", "cancel": "Annulla", "cancel_search": "Annulla ricerca", - "canceled": "Canceled", + "canceled": "Annullato", "cannot_merge_people": "Impossibile unire le persone", "cannot_undo_this_action": "Non puoi annullare questa azione!", "cannot_update_the_description": "Impossibile aggiornare la descrizione", @@ -606,14 +610,14 @@ "change_password": "Modifica Password", "change_password_description": "à stato richiesto di cambiare la password (oppure è la prima volta che accedi). Inserisci la tua nuova password qui sotto.", "change_password_form_confirm_password": "Conferma Password", - "change_password_form_description": "Ciao {name},\n\nQuesto è la prima volta che accedi al sistema oppure è stato fatto una richiesta di cambiare la password. Per favore inserisca la nuova password qui sotto", + "change_password_form_description": "Ciao {name},\n\nQuesto è la prima volta che accedi al sistema oppure è stato fatto una richiesta di cambiare la password. Per favore inserisca la nuova password qui sotto.", "change_password_form_new_password": "Nuova Password", "change_password_form_password_mismatch": "Le password non coincidono", - "change_password_form_reenter_new_password": "Inserisci ancora la nuova password ", + "change_password_form_reenter_new_password": "Inserisci ancora la nuova password", "change_your_password": "Modifica la tua password", "changed_visibility_successfully": "Visibilità modificata con successo", "check_all": "Controlla Tutti", - "check_corrupt_asset_backup": "Verifica la presenza di backup di asset corrotti ", + "check_corrupt_asset_backup": "Verifica la presenza di backup di asset corrotti", "check_corrupt_asset_backup_button": "Effettua controllo", "check_corrupt_asset_backup_description": "Effettua questo controllo solo sotto rete Wi-Fi e quando tutti gli asset sono stati sottoposti a backup. La procedura potrebbe impiegare qualche minuto.", "check_logs": "Controlla i log", @@ -627,11 +631,11 @@ "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "Enter Password", "client_cert_import": "Import", - "client_cert_import_success_msg": "Client certificate is imported", - "client_cert_invalid_msg": "Invalid certificate file or wrong password", - "client_cert_remove_msg": "Client certificate is removed", - "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login", - "client_cert_title": "SSL Client Certificate", + "client_cert_import_success_msg": "Certificato client importato", + "client_cert_invalid_msg": "File certificato invalido o password errata", + "client_cert_remove_msg": "Certificato client rimosso", + "client_cert_subtitle": "Supporta solo il formato PKCS12 (.p12, .pfx). L'importazione/rimozione del certificato è disponibile solo prima del login", + "client_cert_title": "Certificato Client SSL", "clockwise": "Senso orario", "close": "Chiudi", "collapse": "Restringi", @@ -643,8 +647,8 @@ "comments_and_likes": "Commenti & mi piace", "comments_are_disabled": "I commenti sono disabilitati", "common_create_new_album": "Crea nuovo Album", - "common_server_error": "Si prega di controllare la connessione network, che il server sia raggiungibile e che le versione del server e app sono gli stessi", - "completed": "Completed", + "common_server_error": "Si prega di controllare la connessione network, che il server sia raggiungibile e che le versione del server e app sono gli stessi.", + "completed": "Completato", "confirm": "Conferma", "confirm_admin_password": "Conferma password dell'amministratore", "confirm_delete_face": "Sei sicuro di voler cancellare il volto di {name} dall'asset?", @@ -660,7 +664,7 @@ "control_bottom_app_bar_delete_from_local": "Elimina dal dispositivo", "control_bottom_app_bar_edit_location": "Modifica posizione", "control_bottom_app_bar_edit_time": "Modifica data e ora", - "control_bottom_app_bar_share_link": "Share Link", + "control_bottom_app_bar_share_link": "Condividi Link", "control_bottom_app_bar_share_to": "Condividi a", "control_bottom_app_bar_trash_from_immich": "Sposta nel cestino", "copied_image_to_clipboard": "Immagine copiata negli appunti.", @@ -772,12 +776,12 @@ "download_settings": "Scarica", "download_settings_description": "Gestisci le impostazioni relative al download delle risorse", "download_started": "Download avviato", - "download_sucess": "Download success", - "download_sucess_android": "The media has been downloaded to DCIM/Immich", + "download_sucess": "Download completato", + "download_sucess_android": "I contenuti multimediali sono stati scaricati in DCIM/Immich", "download_waiting_to_retry": "In attesa di riprovare", "downloading": "Scaricando", "downloading_asset_filename": "Scaricando la risorsa {filename}", - "downloading_media": "Downloading media", + "downloading_media": "Scaricamento file multimediali", "drop_files_to_upload": "Rilascia i file ovunque per caricarli", "duplicates": "Duplicati", "duplicates_description": "Risolvi ciascun gruppo indicando quali sono, se esistono, i duplicati", @@ -807,19 +811,19 @@ "editor_crop_tool_h2_aspect_ratios": "Proporzioni", "editor_crop_tool_h2_rotation": "Rotazione", "email": "Email", - "empty_folder": "This folder is empty", + "empty_folder": "La cartella è vuota", "empty_trash": "Svuota cestino", "empty_trash_confirmation": "Sei sicuro di volere svuotare il cestino? Questo rimuoverà tutte le risorse nel cestino in modo permanente da Immich.\nNon puoi annullare questa azione!", "enable": "Abilita", "enabled": "Abilitato", "end_date": "Data Fine", - "enqueued": "Enqueued", + "enqueued": "Accodato", "enter_wifi_name": "Inserisci il nome della rete Wi-Fi", "error": "Errore", "error_change_sort_album": "Errore nel cambiare l'ordine di degli album", "error_delete_face": "Errore nel cancellare la faccia dalla foto", "error_loading_image": "Errore nel caricamento dell'immagine", - "error_saving_image": "Error: {}", + "error_saving_image": "Errore: {}", "error_title": "Errore - Qualcosa è andato storto", "errors": { "cannot_navigate_next_asset": "Impossibile passare alla risorsa successiva", @@ -953,10 +957,10 @@ "exif_bottom_sheet_location": "POSIZIONE", "exif_bottom_sheet_people": "PERSONE", "exif_bottom_sheet_person_add_person": "Aggiungi nome", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "Età {}", + "exif_bottom_sheet_person_age_months": "Età {} mesi", + "exif_bottom_sheet_person_age_year_months": "Età 1 anno e {} mesi", + "exif_bottom_sheet_person_age_years": "Età {}", "exit_slideshow": "Esci dalla presentazione", "expand_all": "Espandi tutto", "experimental_settings_new_asset_list_subtitle": "Lavori in corso", @@ -976,9 +980,9 @@ "external_network": "Rete esterna", "external_network_sheet_info": "Quando non si è connessi alla rete Wi-Fi preferita, l'app si collegherà al server tramite il primo degli indirizzi della lista che riuscirà a raggiungere, dall'alto verso il basso", "face_unassigned": "Non assegnata", - "failed": "Failed", + "failed": "Fallito", "failed_to_load_assets": "Impossibile caricare gli asset", - "failed_to_load_folder": "Failed to load folder", + "failed_to_load_folder": "Impossibile caricare la cartella", "favorite": "Preferito", "favorite_or_unfavorite_photo": "Aggiungi o rimuovi foto da preferiti", "favorites": "Preferiti", @@ -992,10 +996,11 @@ "filetype": "Tipo file", "filter": "Filtro", "filter_people": "Filtra persone", + "filter_places": "Filtra luoghi", "find_them_fast": "Trovale velocemente con la ricerca", "fix_incorrect_match": "Correggi corrispondenza errata", - "folder": "Folder", - "folder_not_found": "Folder not found", + "folder": "Cartella", + "folder_not_found": "Cartella non trovata", "folders": "Cartelle", "folders_feature_description": "Navigare la visualizzazione a cartelle per le foto e i video sul file system", "forward": "Avanti", @@ -1016,12 +1021,12 @@ "haptic_feedback_switch": "Abilita feedback aptico", "haptic_feedback_title": "Feedback aptico", "has_quota": "Ha limite", - "header_settings_add_header_tip": "Add Header", - "header_settings_field_validator_msg": "Value cannot be empty", - "header_settings_header_name_input": "Header name", - "header_settings_header_value_input": "Header value", - "headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request", - "headers_settings_tile_title": "Custom proxy headers", + "header_settings_add_header_tip": "Aggiungi Header", + "header_settings_field_validator_msg": "Il valore non puÃ˛ essere vuoto", + "header_settings_header_name_input": "Nome header", + "header_settings_header_value_input": "Valore header", + "headers_settings_tile_subtitle": "Definisci gli header per i proxy che l'app deve inviare con ogni richiesta di rete", + "headers_settings_tile_title": "Header proxy personalizzati", "hi_user": "Ciao {name} ({email})", "hide_all_people": "Nascondi tutte le persone", "hide_gallery": "Nascondi galleria", @@ -1031,7 +1036,7 @@ "hide_unnamed_people": "Nascondi persone senza nome", "home_page_add_to_album_conflicts": "Aggiunti {added} elementi all'album {album}. {failed} elementi erano già presenti nell'album.", "home_page_add_to_album_err_local": "Non puoi aggiungere in album risorse non ancora caricate, azione ignorata", - "home_page_add_to_album_success": "Aggiunti {added} elementi all'album {album}", + "home_page_add_to_album_success": "Aggiunti {added} elementi all'album {album}.", "home_page_album_err_partner": "Non puoi aggiungere risorse del partner a un album, azione ignorata", "home_page_archive_err_local": "Non puoi archiviare immagini non ancora caricate, azione ignorata", "home_page_archive_err_partner": "Non puoi archiviare risorse del partner, azione ignorata", @@ -1040,7 +1045,7 @@ "home_page_delete_remote_err_local": "Risorse locali presenti nella selezione della eliminazione remota, azione ignorata", "home_page_favorite_err_local": "Non puoi aggiungere tra i preferiti delle risorse non ancora caricate, azione ignorata", "home_page_favorite_err_partner": "Non puoi mettere le risorse del partner nei preferiti, azione ignorata", - "home_page_first_time_notice": "Se è la prima volta che utilizzi l'app, assicurati di scegliere uno o piÚ album di backup, in modo che la timeline possa popolare le foto e i video presenti negli album.", + "home_page_first_time_notice": "Se è la prima volta che utilizzi l'app, assicurati di scegliere uno o piÚ album di backup, in modo che la timeline possa popolare le foto e i video presenti negli album", "home_page_share_err_local": "Non puoi condividere una risorsa locale tramite link, azione ignorata", "home_page_upload_err_limit": "Puoi caricare al massimo 30 file per volta, ignora quelli in eccesso", "host": "Host", @@ -1059,7 +1064,7 @@ "image_alt_text_date_place_3_people": "{isVideo, select, true {Video girato} other {Foto scattata}} a {city}, {country} con {person1}, {person2}, e {person3} il giorno {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video girato} other {Foto scattata}} a {city}, {country} con {person1}, {person2} e {additionalCount, number} altre persone il {date}", "image_saved_successfully": "Immagine salvata", - "image_viewer_page_state_provider_download_started": "Download Started", + "image_viewer_page_state_provider_download_started": "Download iniziato", "image_viewer_page_state_provider_download_success": "Download con successo", "image_viewer_page_state_provider_share_error": "Errore di condivisione", "immich_logo": "Logo Immich", @@ -1080,8 +1085,8 @@ "night_at_midnight": "Ogni notte a mezzanotte", "night_at_twoam": "Ogni notte alle 2" }, - "invalid_date": "Invalid date", - "invalid_date_format": "Invalid date format", + "invalid_date": "Data invalida", + "invalid_date_format": "Formato data invalido", "invite_people": "Invita Persone", "invite_to_album": "Invita nell'album", "items_count": "{count, plural, one {# elemento} other {# elementi}}", @@ -1132,24 +1137,24 @@ "logged_out_device": "Disconnesso dal dispositivo", "login": "Login", "login_disabled": "L'accesso è stato disattivato", - "login_form_api_exception": "API error, per favore ricontrolli URL del server e riprovi", + "login_form_api_exception": "API error, per favore ricontrolli URL del server e riprovi.", "login_form_back_button_text": "Indietro", "login_form_email_hint": "tuaemail@email.com", "login_form_endpoint_hint": "http://ip-del-tuo-server:port", - "login_form_endpoint_url": "Server Endpoint URL", + "login_form_endpoint_url": "URL dell'Endpoint del Server", "login_form_err_http": "Per favore specificare http:// o https://", "login_form_err_invalid_email": "Email non valida", "login_form_err_invalid_url": "URL invalido", - "login_form_err_leading_whitespace": "Whitespace all'inizio ", + "login_form_err_leading_whitespace": "Whitespace all'inizio", "login_form_err_trailing_whitespace": "Whitespace alla fine", "login_form_failed_get_oauth_server_config": "Errore di login usando OAuth, controlla l'URL del server", "login_form_failed_get_oauth_server_disable": "OAuth non è disponibile su questo server", "login_form_failed_login": "Errore nel login, controlla URL del server e le credenziali (email e password)", "login_form_handshake_exception": "Si è verificata un'eccezione di handshake con il server. Abilita il supporto del certificato self-signed nelle impostazioni se si utilizza questo tipo di certificato.", - "login_form_password_hint": "password ", - "login_form_save_login": "Rimani connesso ", - "login_form_server_empty": "Inserisci URL del server", - "login_form_server_error": "Non è possibile connettersi al server", + "login_form_password_hint": "password", + "login_form_save_login": "Rimani connesso", + "login_form_server_empty": "Inserisci URL del server.", + "login_form_server_error": "Non è possibile connettersi al server.", "login_has_been_disabled": "Il login è stato disabilitato.", "login_password_changed_error": "C'è stato un errore durante l'aggiornamento della password", "login_password_changed_success": "Password aggiornata con successo", @@ -1202,8 +1207,8 @@ "memories_setting_description": "Gestisci cosa vedi nei tuoi ricordi", "memories_start_over": "Ricomincia", "memories_swipe_to_close": "Scorri sopra per chiudere", - "memories_year_ago": "A year ago", - "memories_years_ago": "{} years ago", + "memories_year_ago": "Una anno fa", + "memories_years_ago": "{} anni fa", "memory": "Memoria", "memory_lane_title": "Sentiero dei Ricordi {title}", "menu": "Menu", @@ -1257,11 +1262,11 @@ "no_results_description": "Prova ad usare un sinonimo oppure una parola chiave piÚ generica", "no_shared_albums_message": "Crea un album per condividere foto e video con le persone nella tua rete", "not_in_any_album": "In nessun album", - "not_selected": "Not selected", + "not_selected": "Non selezionato", "note_apply_storage_label_to_previously_uploaded assets": "Nota: Per aggiungere l'etichetta dell'archiviazione agli asset caricati in precedenza, esegui", "notes": "Note", - "notification_permission_dialog_content": "Per attivare le notifiche, vai alle Impostazioni e seleziona concedi", - "notification_permission_list_tile_content": "Concedi i permessi per attivare le notifiche", + "notification_permission_dialog_content": "Per attivare le notifiche, vai alle Impostazioni e seleziona concedi.", + "notification_permission_list_tile_content": "Concedi i permessi per attivare le notifiche.", "notification_permission_list_tile_enable_button": "Attiva notifiche", "notification_permission_list_tile_title": "Permessi delle Notifiche", "notification_toggle_setting_description": "Attiva le notifiche via email", @@ -1282,6 +1287,7 @@ "onboarding_welcome_user": "Benvenuto, {user}", "online": "Online", "only_favorites": "Solo preferiti", + "open": "Apri", "open_in_map_view": "Apri nella visualizzazione mappa", "open_in_openstreetmap": "Apri su OpenStreetMap", "open_the_search_filters": "Apri filtri di ricerca", @@ -1301,9 +1307,9 @@ "partner_list_user_photos": "Foto di {user}", "partner_list_view_all": "Mostra tutto", "partner_page_empty_message": "Le tue foto non sono ancora condivise con alcun partner.", - "partner_page_no_more_users": "Nessun altro utente da aggiungere.", - "partner_page_partner_add_failed": "Aggiunta del partner non riuscita.", - "partner_page_select_partner": "Seleziona partner.", + "partner_page_no_more_users": "Nessun altro utente da aggiungere", + "partner_page_partner_add_failed": "Aggiunta del partner non riuscita", + "partner_page_select_partner": "Seleziona partner", "partner_page_shared_to_title": "Condividi con", "partner_page_stop_sharing_content": "{} non sarà piÚ in grado di accedere alle tue foto.", "partner_sharing": "Condivisione Compagno", @@ -1338,10 +1344,10 @@ "permission_onboarding_continue_anyway": "Continua lo stesso", "permission_onboarding_get_started": "Inizia", "permission_onboarding_go_to_settings": "Vai a Impostazioni", - "permission_onboarding_permission_denied": "Permessi negati. Per usare Immich concedi i permessi ai video e foto dalle impostazioni", - "permission_onboarding_permission_granted": "Concessi i permessi! Ora sei tutto apposto", + "permission_onboarding_permission_denied": "Permessi negati. Per usare Immich concedi i permessi ai video e foto dalle impostazioni.", + "permission_onboarding_permission_granted": "Concessi i permessi! Ora sei tutto apposto.", "permission_onboarding_permission_limited": "Permessi limitati. Per consentire a Immich di gestire e fare i backup di tutta la galleria, concedi i permessi Foto e Video dalle Impostazioni.", - "permission_onboarding_request": "Immich richiede i permessi per vedere le tue foto e video", + "permission_onboarding_request": "Immich richiede i permessi per vedere le tue foto e video.", "person": "Persona", "person_birthdate": "Nato il {date}", "person_hidden": "{name}{hidden, select, true { (nascosto)} other {}}", @@ -1368,7 +1374,7 @@ "previous_or_next_photo": "Precedente o prossima foto", "primary": "Primario", "privacy": "Privacy", - "profile_drawer_app_logs": "Logs", + "profile_drawer_app_logs": "Registri", "profile_drawer_client_out_of_date_major": "L'applicazione non è aggiornata. Per favore aggiorna all'ultima versione principale.", "profile_drawer_client_out_of_date_minor": "L'applicazione non è aggiornata. Per favore aggiorna all'ultima versione minore.", "profile_drawer_client_server_up_to_date": "Client e server sono aggiornati", @@ -1487,7 +1493,7 @@ "saved_profile": "Profilo salvato", "saved_settings": "Impostazioni salvate", "say_something": "Dici qualcosa", - "scaffold_body_error_occurred": "Si è verificato un errore.", + "scaffold_body_error_occurred": "Si è verificato un errore", "scan_all_libraries": "Analizza tutte le librerie", "scan_library": "Scansione", "scan_settings": "Impostazioni Analisi", @@ -1504,24 +1510,24 @@ "search_city": "Cerca città ...", "search_country": "Cerca paese...", "search_filter_apply": "Applica filtro", - "search_filter_camera_title": "Select camera type", + "search_filter_camera_title": "Seleziona il tipo di camera", "search_filter_date": "Date", "search_filter_date_interval": "{start} to {end}", - "search_filter_date_title": "Select a date range", + "search_filter_date_title": "Scegli un range di date", "search_filter_display_option_not_in_album": "Non nell'album", - "search_filter_display_options": "Display Options", - "search_filter_filename": "Search by file name", - "search_filter_location": "Location", - "search_filter_location_title": "Select location", + "search_filter_display_options": "Opzioni di Visualizzazione", + "search_filter_filename": "Cerca per nome file", + "search_filter_location": "Posizione", + "search_filter_location_title": "Seleziona posizione", "search_filter_media_type": "Media Type", "search_filter_media_type_title": "Seleziona il tipo di media", "search_filter_people_title": "Seleziona persone", "search_for": "Cerca per", "search_for_existing_person": "Cerca per persona esistente", - "search_no_more_result": "No more results", + "search_no_more_result": "Non ci sono altri risultati", "search_no_people": "Nessuna persona", "search_no_people_named": "Nessuna persona chiamate \"{name}\"", - "search_no_result": "No results found, try a different search term or combination", + "search_no_result": "Nessun risultato trovato, prova con un termine o combinazione diversi", "search_options": "Opzioni Ricerca", "search_page_categories": "Categoria", "search_page_motion_photos": "Foto in movimento", @@ -1532,16 +1538,16 @@ "search_page_selfies": "Selfie", "search_page_things": "Oggetti", "search_page_view_all_button": "Guarda tutto", - "search_page_your_activity": "Tua attività ", + "search_page_your_activity": "Le tua attività ", "search_page_your_map": "La tua mappa", "search_people": "Cerca persone", "search_places": "Cerca luoghi", "search_rating": "Cerca per valutazione...", - "search_result_page_new_search_hint": "Nuova ricerca ", + "search_result_page_new_search_hint": "Nuova ricerca", "search_settings": "Cerca Impostazioni", "search_state": "Cerca stato...", - "search_suggestion_list_smart_search_hint_1": "\nRicerca Smart è attiva di default, per usare la ricerca con i metadata usare la seguente sintassi", - "search_suggestion_list_smart_search_hint_2": "m:your-search-term", + "search_suggestion_list_smart_search_hint_1": "Ricerca Smart è attiva di default, per usare la ricerca con i metadata usare la seguente sintassi ", + "search_suggestion_list_smart_search_hint_2": "m:termine-di-ricerca", "search_tags": "Cerca tag...", "search_timezone": "Cerca fuso orario...", "search_type": "Cerca tipo", @@ -1562,7 +1568,7 @@ "select_new_face": "Seleziona nuovo volto", "select_photos": "Seleziona foto", "select_trash_all": "Seleziona cestina tutto", - "select_user_for_sharing_page_err_album": "Impossibile nel creare l'album ", + "select_user_for_sharing_page_err_album": "Impossibile nel creare l'album", "selected": "Selezionato", "selected_count": "{count, plural, one {# selezionato} other {# selezionati}}", "send_message": "Manda messaggio", @@ -1584,9 +1590,9 @@ "setting_image_viewer_help": "Il visualizzatore dettagliato carica una piccola thumbnail per prima, per poi caricare un immagine di media grandezza (se abilitato). Ed infine carica l'originale (se abilitato).", "setting_image_viewer_original_subtitle": "Abilita per caricare l'immagine originale a risoluzione massima (grande!). Disabilita per ridurre l'utilizzo di banda (sia sul network che nella cache del dispositivo).", "setting_image_viewer_original_title": "Carica l'immagine originale", - "setting_image_viewer_preview_subtitle": "Abilita per caricare un'immagine a risoluzione media.\nDisabilita per caricare direttamente l'immagine originale o usare la thumbnail.", + "setting_image_viewer_preview_subtitle": "Abilita per caricare un'immagine a risoluzione media. Disabilita per caricare direttamente l'immagine originale o usare la thumbnail.", "setting_image_viewer_preview_title": "Carica immagine di anteprima", - "setting_image_viewer_title": "Images", + "setting_image_viewer_title": "Immagini", "setting_languages_apply": "Applica", "setting_languages_subtitle": "Cambia la lingua dell'app", "setting_languages_title": "Lingue", @@ -1609,7 +1615,7 @@ "settings_saved": "Impostazioni salvate", "share": "Condivisione", "share_add_photos": "Aggiungi foto", - "share_assets_selected": "{} selected", + "share_assets_selected": "{} selezionati", "share_dialog_preparing": "PreparoâĻ", "shared": "Condivisi", "shared_album_activities_input_disable": "I commenti sono disabilitati", @@ -1623,7 +1629,7 @@ "shared_by_user": "Condiviso da {user}", "shared_by_you": "Condiviso da te", "shared_from_partner": "Foto da {partner}", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", + "shared_intent_upload_button_progress_text": "{} / {} Inviati", "shared_link_app_bar_title": "Link condivisi", "shared_link_clipboard_copied_massage": "Copiato negli appunti", "shared_link_clipboard_text": "Link: {}\nPassword: {}", @@ -1635,8 +1641,8 @@ "shared_link_edit_expire_after_option_hours": "{} ore", "shared_link_edit_expire_after_option_minute": "1 minuto", "shared_link_edit_expire_after_option_minutes": "{} minuti", - "shared_link_edit_expire_after_option_months": "{} months", - "shared_link_edit_expire_after_option_year": "{} year", + "shared_link_edit_expire_after_option_months": "{} mesi", + "shared_link_edit_expire_after_option_year": "{} anno", "shared_link_edit_password_hint": "Inserire la password di condivisione", "shared_link_edit_submit_button": "Aggiorna link", "shared_link_error_server_url_fetch": "Non è possibile trovare l'indirizzo del server", @@ -1649,7 +1655,7 @@ "shared_link_expires_never": "Scadenza â", "shared_link_expires_second": "Scade tra {} secondo", "shared_link_expires_seconds": "Scade tra {} secondi", - "shared_link_individual_shared": "Individual shared", + "shared_link_individual_shared": "Condiviso individualmente", "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gestisci link condivisi", "shared_link_options": "Opzioni link condiviso", @@ -1732,9 +1738,9 @@ "support_third_party_description": "La tua installazione di Immich è stata costruita da terze parti. I problemi che riscontri potrebbero essere causati da altri pacchetti, quindi ti preghiamo di sollevare il problema in prima istanza utilizzando i link sottostanti.", "swap_merge_direction": "Scambia direzione di unione", "sync": "Sincronizza", - "sync_albums": "Sync albums", - "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", - "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", + "sync_albums": "Sincronizza album", + "sync_albums_manual_subtitle": "Sincronizza tutti i video e le foto caricate sull'album di backup selezionato", + "sync_upload_album_setting_subtitle": "Crea e carica le tue foto e video sull'album selezionato in Immich", "tag": "Tag", "tag_assets": "Tagga risorse", "tag_created": "Tag creata: {tag}", @@ -1749,12 +1755,12 @@ "theme_selection": "Selezione tema", "theme_selection_description": "Imposta automaticamente il tema chiaro o scuro in base all'impostazione del tuo browser", "theme_setting_asset_list_storage_indicator_title": "Mostra indicatore dello storage nei titoli dei contenuti", - "theme_setting_asset_list_tiles_per_row_title": "Numero di contenuti per riga ({})", - "theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.", - "theme_setting_colorful_interface_title": "Colorful interface", + "theme_setting_asset_list_tiles_per_row_title": "Numero di elementi per riga ({})", + "theme_setting_colorful_interface_subtitle": "Applica il colore primario alle superfici di sfondo.", + "theme_setting_colorful_interface_title": "Interfaccia colorata", "theme_setting_image_viewer_quality_subtitle": "Cambia la qualità del dettaglio dell'immagine", "theme_setting_image_viewer_quality_title": "Qualità immagine", - "theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.", + "theme_setting_primary_color_subtitle": "Scegli un colore per le azioni primarie e accentate.", "theme_setting_primary_color_title": "Colore primario", "theme_setting_system_primary_color_title": "Usa colori di sistema", "theme_setting_system_theme_switch": "Automatico (Segue le impostazioni di sistema)", @@ -1780,7 +1786,7 @@ "trash_all": "Cestina Tutto", "trash_count": "Cancella {count, number}", "trash_delete_asset": "Cestina/Cancella Asset", - "trash_emptied": "Emptied trash", + "trash_emptied": "Cestino svuotato", "trash_no_results_message": "Le foto cestinate saranno mostrate qui.", "trash_page_delete_all": "Elimina tutti", "trash_page_empty_trash_dialog_content": "Vuoi eliminare gli elementi nel cestino? Questi elementi saranno eliminati definitivamente da Immich", @@ -1826,8 +1832,8 @@ "upload_status_errors": "Errori", "upload_status_uploaded": "Caricato", "upload_success": "Caricamento completato con successo, aggiorna la pagina per vedere i nuovi asset caricati.", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", + "upload_to_immich": "Invio ad Immich ({})", + "uploading": "Caricamento", "url": "URL", "usage": "Utilizzo", "use_current_connection": "usa la connessione attuale", @@ -1853,7 +1859,7 @@ "version_announcement_overlay_release_notes": "note di rilascio", "version_announcement_overlay_text_1": "Ciao, c'è una nuova versione di", "version_announcement_overlay_text_2": "per favore prenditi il tuo tempo per visitare le ", - "version_announcement_overlay_text_3": " e verifica che il tuo docker-compose e il file .env siano aggiornati per impedire qualsiasi errore di configurazione, specialmente se utilizzate WatchTower o altri strumenti per l'aggiornamento automatico dell'applicativo", + "version_announcement_overlay_text_3": " e verifica che il tuo docker-compose e il file .env siano aggiornati per impedire qualsiasi errore di configurazione, specialmente se utilizzate WatchTower o altri strumenti per l'aggiornamento automatico dell'applicativo.", "version_announcement_overlay_title": "Nuova versione del server disponibile đ", "version_history": "Storico delle Versioni", "version_history_item": "Versione installata {version} il {date}", diff --git a/i18n/ja.json b/i18n/ja.json index 39496cc7f5..eb6b355615 100644 --- a/i18n/ja.json +++ b/i18n/ja.json @@ -496,7 +496,7 @@ "back_close_deselect": "æģããéããã鏿觪é¤", "background_location_permission": "ããã¯ã°ãŠãĻãŗãäŊįŊŽæ å ąãĸã¯ãģãš", "background_location_permission_content": "æŖå¸¸ãĢWi-FiãŽåå(SSID)ãį˛åžãããĢã¯ãĸããĒã常ãĢčŠŗį´°ãĒäŊįŊŽæ å ąãĢãĸã¯ãģãšã§ããåŋ čĻããããžã", - "backup_album_selection_page_albums_device": "į̝æĢä¸ãŽãĸãĢãã æ°: {} ", + "backup_album_selection_page_albums_device": "į̝æĢä¸ãŽãĸãĢãã æ°: {}", "backup_album_selection_page_albums_tap": "ãŋããã§é¸æããããĢãŋããã§é¤å¤", "backup_album_selection_page_assets_scatter": "ãĸãĢãã ã鏿ãģé¤å¤ããĻããã¯ãĸããããåįãé¸ãļ (åãåįã褿°ãŽãĸãĢãã ãĢįģé˛ãããĻãããã¨ããããã)", "backup_album_selection_page_select_albums": "ãĸãĢãã ã鏿", @@ -505,7 +505,7 @@ "backup_all": "ããšãĻ", "backup_background_service_backup_failed_message": "ãĸããããŧããĢå¤ąæããžããããĒããŠã¤ä¸", "backup_background_service_connection_failed_message": "ãĩãŧããŧãĢæĨįļã§ããžããããĒããŠã¤ä¸", - "backup_background_service_current_upload_notification": " {}ããĸããããŧãä¸", + "backup_background_service_current_upload_notification": "{}ããĸããããŧãä¸", "backup_background_service_default_notification": "æ°ããåįãįĸēčĒä¸", "backup_background_service_error_title": "ããã¯ãĸããã¨ãŠãŧ", "backup_background_service_in_progress_notification": "ããã¯ãĸããä¸", @@ -534,7 +534,7 @@ "backup_controller_page_desc_backup": "ãĸããĒãéããĻããã¨ããĢåįã¨åįģãããã¯ãĸããããžã", "backup_controller_page_excluded": "é¤å¤ä¸ãŽãĸãĢãã :", "backup_controller_page_failed": "å¤ąæ: ({})", - "backup_controller_page_filename": "ããĄã¤ãĢå: {} [{}] ", + "backup_controller_page_filename": "ããĄã¤ãĢå: {} [{}]", "backup_controller_page_id": "ID: {}", "backup_controller_page_info": "ããã¯ãĸããæ å ą", "backup_controller_page_none_selected": "ãĒã", @@ -575,7 +575,7 @@ "cache_settings_duplicated_assets_clear_button": "ã¯ãĒãĸ", "cache_settings_duplicated_assets_subtitle": "ãĩãŧããŧãĢãĸããããŧãæ¸ãŋã¨čĒčãããåįãåįģãŽæ°", "cache_settings_duplicated_assets_title": "{}é įŽãŽéč¤", - "cache_settings_image_cache_size": "ããŖããˇãĨãŽãĩã¤ãē ({}æ) ", + "cache_settings_image_cache_size": "ããŖããˇãĨãŽãĩã¤ãē ({}æ)", "cache_settings_statistics_album": "ãŠã¤ããŠãĒãŽãĩã ãã¤ãĢ", "cache_settings_statistics_assets": "{}æ ({}æä¸)", "cache_settings_statistics_full": "ããĢįģå", diff --git a/i18n/ka.json b/i18n/ka.json index a521539d11..b07dcfe6fa 100644 --- a/i18n/ka.json +++ b/i18n/ka.json @@ -30,11 +30,11 @@ "authentication_settings_disable_all": "ááááááááá ááááá áááĸáá ááááĒááᥠá§áááá ááááááᥠááááá ááá? áááĸáá ááááĒááᥠáááĻáá ááááá áá á¨ááĢááá.", "authentication_settings_reenable": "á áááĨáĸááááĒáááĄááááĄ, áááááá§ááá <link>áĄáá ááá áᥠáá áĢááááá</link>.", "background_task_job": "á¤áááŖá á áááááááááá", - "backup_database": "á¨ááĨáááá áĄáá áááá áá ááĄáá", - "backup_database_enable_description": "áŠáá áá áĄáá áááá áá ááĄááááᥠá¤áŖááĨáĒáá", - "backup_keep_last_amount": "á¨ááĄááááŽá áĄáá áááá áá ááĄááááᥠá áááááááá", - "backup_settings": "áĄáá áááá áá ááĄááááᥠááá ááááĸá ááá", - "backup_settings_description": "áááááĒáááá ááááᥠáĄáá áááá áá ááĄááááᥠááá ááááĸá áááᥠááá ááá", + "backup_database": "ááááᥠáááááᥠá¨ááĨááá", + "backup_database_enable_description": "ááááᥠáááááááᥠáŠáá ááá", + "backup_keep_last_amount": "áŦááá áááááááᥠá¨ááĄáááá áŠáŖáááááá á áááááááá", + "backup_settings": "áááááĒáááá ááááᥠáááááᥠááá áááá", + "backup_settings_description": "áááááĒáááá ááááᥠááá ááááĸá áááᥠááá ááá. á¨áááá¨ááá: áá ááááááááááᥠáááááĸáá áááá áá áŽáááá áá ááĨááá áá ááááááá á¨ááĸá§ááááááá, ááŖ áᥠáŠáááá áááá.", "check_all": "á¨áááááŦáá á§áááá", "cleanup": "áááĄáŖá¤áááááá", "confirm_delete_library": "ááááááááá ááááá {library} ááááááááááᥠáŦáá¨áá?", diff --git a/i18n/kk.json b/i18n/kk.json index 0967ef424b..e3a1f51c3e 100644 --- a/i18n/kk.json +++ b/i18n/kk.json @@ -1 +1,16 @@ -{} +{ + "add_photos": "ŅŅŅĐĩŅŅĐĩŅĐ´Ņ ŌĐžŅŅ", + "add_to": "ŌĐžŅŅâĻ", + "add_to_album": "аĐģŅйОĐŧŌа ŌĐžŅŅ", + "add_to_album_bottom_sheet_added": "{album}'Ōа ŌĐžŅŅĐģŌаĐŊ", + "add_to_album_bottom_sheet_already_exists": "ĐĐŊŅŅС да {album} йОĐģŌаĐŊ", + "add_to_shared_album": "ĐąĶŠĐģŅŅĐēĐĩĐŊ аĐģŅйОĐŧŌа ŌĐžŅŅ", + "add_url": "URL ŅĐ°ŌŖĐ´Đ°Ņ", + "added_to_archive": "ĐŅŅ Đ¸Đ˛ĐēĐĩ ĐļŅĐąĐĩŅŅĐģĐŗĐĩĐŊ", + "added_to_favorites": "ŅĐ°ŌŖĐ´Đ°ŅĐģŅĐģаŅŌа ŌĐžŅŅĐģŌаĐŊ", + "admin": { + "check_all": "ĐĶŅŅĐŊ ŅĐĩĐēŅĐĩŅŅĐŋ аĐģŅ", + "create_job": "ĐŌąĐŧŅŅŅŅ ĐąĐ°ŅŅаŅ" + }, + "zoom_image": "ŅŅŅĐĩŅŅŅ Ō¯ĐģĐēĐĩĐšŅŅ" +} diff --git a/i18n/ko.json b/i18n/ko.json index def2ad2a5e..03db17c396 100644 --- a/i18n/ko.json +++ b/i18n/ko.json @@ -14,7 +14,7 @@ "add_a_location": "ėėš ėļę°", "add_a_name": "ė´ëĻ ėļę°", "add_a_title": "ė ëĒŠ ėļę°", - "add_endpoint": "Add endpoint", + "add_endpoint": "ėëíŦė¸í¸ ėļę°", "add_exclusion_pattern": "ė ė¸ ęˇėš ėļę°", "add_import_path": "ę°ė ¸ėŦ ę˛ŊëĄ ėļę°", "add_location": "ėėš ėļę°", @@ -25,29 +25,29 @@ "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_already_exists": "{album}ė ė´ë¯¸ ėĄ´ėŦíŠëë¤.", "add_to_shared_album": "ęŗĩė ė¨ë˛ė ėļę°", "add_url": "URL ėļę°", "added_to_archive": "ëŗ´ę´í¨ė ėļę°ëėėĩëë¤.", "added_to_favorites": "ėĻę˛¨ė°žę¸°ė ėļę°ëėėĩëë¤.", - "added_to_favorites_count": "ėĻę˛¨ė°žę¸°ė íëĒŠ {count, number}ę° ėļę°ë¨", + "added_to_favorites_count": "ėĻę˛¨ė°žę¸°ė {count, number}ę° ėļę°ë¨", "admin": { "add_exclusion_pattern_description": "ęˇėšė *, ** ë° ? ëĨŧ ėŦėŠí ė ėėĩëë¤. ė´ëĻė´ \"Raw\"ė¸ ëë í°ëĻŦė ëǍë íėŧė ė ė¸íë ¤ëŠ´ \"**/Raw/**\"ëĨŧ, \".tif\"ëĄ ëëë ëǍë íėŧė ė ė¸íë ¤ëŠ´ \"**/*.tif\"ëĨŧ ėŦėŠíęŗ , ė ë ę˛ŊëĄė ę˛Ŋė° \"/path/to/ignore/**\"ė ę°ė ë°ŠėėŧëĄ ėŦėŠíŠëë¤.", - "asset_offline_description": "ė¸ëļ ëŧė´ë¸ëŦëĻŦė íŦí¨ë ė´ íëĒŠė ëė¤íŦėė ëė´ė ė°žė ė ėė´ í´ė§íĩėŧëĄ ė´ëëėėĩëë¤. íėŧė´ ëŧė´ë¸ëŦëĻŦ ë´ėė ė´ëë ę˛Ŋė° íėëŧė¸ėė ėëĄ ė°ę˛°ë íëĒŠė íė¸íė¸ė. ė´ íëĒŠė ëŗĩėíë ¤ëŠ´ ėë íėŧ ę˛ŊëĄė Immichę° ė ęˇŧí ė ėëė§ íė¸íęŗ ëŧė´ë¸ëŦëĻŦ ė¤ėēė ė§ííė¸ė.", + "asset_offline_description": "ė¸ëļ ëŧė´ë¸ëŦëĻŦė íŦí¨ë ė´ íëĒŠė ëė¤íŦėė ëė´ė ė°žė ė ėė´ í´ė§íĩėŧëĄ ė´ëëėėĩëë¤. íėŧė´ ëŧė´ë¸ëŦëĻŦ ë´ėė ė´ëë ę˛Ŋė° íėëŧė¸ėė ėëĄ ė°ę˛°ë íëĒŠė íė¸íė¸ė. íëĒŠė ëŗĩėíë ¤ëŠ´ ėëė íėŧ ę˛ŊëĄė Immichę° ė ęˇŧí ė ėëė§ íė¸íęŗ ëŧė´ë¸ëŦëĻŦ ė¤ėēė ė§ííė¸ė.", "authentication_settings": "ė¸ėĻ ė¤ė ", "authentication_settings_description": "ëšë°ë˛í¸, OAuth ë° ę¸°í ė¸ėĻ ė¤ė ę´ëĻŦ", - "authentication_settings_disable_all": "ëĄęˇ¸ė¸ 기ëĨė ëǍë ëšíėąííėę˛ ėĩëęš? ëĄęˇ¸ė¸íė§ ėėë ėë˛ė ė ęˇŧí ė ėėĩëë¤.", - "authentication_settings_reenable": "ë¤ė íėąííë ¤ëŠ´ <link>ėë˛ ėģ¤ë§¨ë</link>ëĨŧ ėŦėŠíė¸ė.", + "authentication_settings_disable_all": "ëĄęˇ¸ė¸ ėë¨ė ëǍë ëšíėąííėę˛ ėĩëęš? ëĄęˇ¸ė¸ė´ ėė í ëšíėąíëŠëë¤.", + "authentication_settings_reenable": "ë¤ė íėąííë ¤ëŠ´ <link>ėë˛ ëĒ ë šė´</link>ëĨŧ ėŦėŠíė¸ė.", "background_task_job": "밹꡸ëŧė´ë ėė ", - "backup_database": "ë°ė´í°ë˛ ė´ė¤ ë°ąė ", - "backup_database_enable_description": "ë°ė´í°ë˛ ė´ė¤ ë°ąė íėąí", - "backup_keep_last_amount": "ëŗ´ę´í ë°ąė ė ę°ė", - "backup_settings": "ë°ąė ė¤ė ", - "backup_settings_description": "ë°ė´í°ë˛ ė´ė¤ ë°ąė ė¤ė ę´ëĻŦ", + "backup_database": "ë°ė´í°ë˛ ė´ė¤ ë¤í ėėą", + "backup_database_enable_description": "ë°ė´í°ë˛ ė´ė¤ ë¤í íėąí", + "backup_keep_last_amount": "ëŗ´ę´í ė´ė ë¤íė ė", + "backup_settings": "ë°ė´í°ë˛ ė´ė¤ ë¤í ė¤ė ", + "backup_settings_description": "ë°ė´í°ë˛ ė´ė¤ ë¤í ė¤ė ė ę´ëĻŦíŠëë¤. ė°¸ęŗ : ė´ ėė ë¤ė ëǍëí°ë§ëė§ ėėŧ늰, ė¤í¨ ė ėëĻŧė ë°ė§ ėėĩëë¤.", "check_all": "ëǍë íė¸", "cleanup": "ė ëĻŦ", "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}\" ė ë Ĩ", @@ -59,40 +59,40 @@ "cron_expression_presets": "Cron ííė ėŦė ė¤ė ", "disable_login": "ëĄęˇ¸ė¸ ëšíėąí", "duplicate_detection_job_description": "ę¸°ęŗ íėĩė íĩí´ ė ėŦí ė´ë¯¸ė§ëĨŧ ę°ė§íŠëë¤. ė¤ë§í¸ ę˛ėė´ íėąíëė´ ėė´ėŧ íŠëë¤.", - "exclusion_pattern_description": "ė ė¸ ęˇėšė ėŦėŠíėŦ ëŧė´ë¸ëŦëĻŦ ė¤ėē ė íšė íėŧęŗŧ í´ëëĨŧ ė ė¸í ė ėėĩëë¤. í´ëė ėíė§ ėë íėŧ(RAW íėŧ ëą)ė´ ėĄ´ėŦíë ę˛Ŋė° ė ėŠíŠëë¤.", + "exclusion_pattern_description": "ė ė¸ ęˇėšė ėŦėŠíėŦ íšė íėŧęŗŧ í´ëëĨŧ ëŧė´ë¸ëŦëĻŦ ė¤ėēėė ė ė¸í ė ėėĩëë¤. ę°ė ¸ė¤ę¸° ėíė§ ėë íėŧ(RAW íėŧ ëą)ė´ í´ëė ėĄ´ėŦíë ę˛Ŋė° ė ėŠíŠëë¤.", "external_library_created_at": "ė¸ëļ ëŧė´ë¸ëŦëĻŦ ({date}ė ėėąë¨)", "external_library_management": "ė¸ëļ ëŧė´ë¸ëŦëĻŦ ę´ëĻŦ", "face_detection": "ėŧęĩ´ ę°ė§", - "face_detection_description": "ę¸°ęŗ íėĩė íĩí´ íëĒŠė ėĄ´ėŦíë ėŧęĩ´ė ę°ė§íŠëë¤. ëėėė ę˛Ŋė° ėŦë¤ėŧë§ ėŦėŠíŠëë¤. \"ėëĄęŗ ėš¨\"ė ė´ë¯¸ ė˛ëĻŦë íëĒŠė íŦí¨í ëǍë íëĒŠė ë¤ė ė˛ëĻŦíŠëë¤. \"ė´ę¸°í\"ë ëǍë ėŧęĩ´ ë°ė´í°ëĨŧ ėė íŠëë¤. \"ëëŊ\"ė ė˛ëĻŦëė§ ėė íëĒŠė ë기ė´ė ėļę°íŠëë¤. ėŧęĩ´ ę°ė§ ėė ė´ ėëŖë늴 ėŧęĩ´ ė¸ė ėė ė´ ė§íëė´ ę°ė§ë ėŧęĩ´ė ę¸°ėĄ´ ė¸ëŦŧė´ë ė ė¸ëŦŧëĄ ęˇ¸ëŖšííŠëë¤.", - "facial_recognition_job_description": "ę°ė§ë ėŧęĩ´ė ė¸ëŦŧëĄ ęˇ¸ëŖšííŠëë¤. ė´ ėė ė ėŧęĩ´ ę°ė§ ėė ė´ ėëŖë í ė§íëŠëë¤. \"ė´ę¸°í\"ë ëǍë ėŧęĩ´ė ęˇ¸ëŖšíëĨŧ ë¤ė ė§ííŠëë¤. \"ëëŊ\"ė ęˇ¸ëŖšíëė§ ėė ėŧęĩ´ė ë기ė´ė ėļę°íŠëë¤.", + "face_detection_description": "ę¸°ęŗ íėĩė íĩí´ íëĒŠėė ėŧęĩ´ė ę°ė§íŠëë¤. ëėėė ę˛Ŋė° ėŦë¤ėŧë§ ëļėė ėŦėŠëŠëë¤. \"ėëĄęŗ ėš¨\"ė ëǍë íëĒŠė (ėŦ)ė˛ëĻŦí늰, \"ė´ę¸°í\"ë íėŦ ëǍë ėŧęĩ´ ë°ė´í°ëĨŧ ėļę°ëĄ ėė íŠëë¤. \"ëëŊë¨\"ė ėė§ ė˛ëĻŦëė§ ėė íëĒŠė ë기ė´ė ėļę°íŠëë¤. ėŧęĩ´ ę°ė§ę° ėëŖë늴 ę°ė§ë ėŧęĩ´ë¤ė ėŧęĩ´ ė¸ė ë¨ęŗëĄ ëė´ę°ëа, ę¸°ėĄ´ ė¸ëŦŧė´ë ėëĄė´ ė¸ëŦŧëĄ ęˇ¸ëŖšíëŠëë¤.", + "facial_recognition_job_description": "ę°ė§ë ėŧęĩ´ė ė¸ëŦŧëŗëĄ ęˇ¸ëŖšííŠëë¤. ė´ ėė ė ėŧęĩ´ ę°ė§ ėė ė´ ėëŖë í ė§íëŠëë¤. \"ė´ę¸°í\"ë ëǍë ėŧęĩ´ė ęˇ¸ëŖšíëĨŧ ë¤ė ė§ííŠëë¤. \"ëëŊ\"ė ęˇ¸ëŖšíëė§ ėė ėŧęĩ´ė ë기ė´ė ėļę°íŠëë¤.", "failed_job_command": "{job} ėė ėė {command} ė¤í¨", - "force_delete_user_warning": "ę˛Ŋęŗ : ėŦėŠė ë° ėŦėŠėę° ė ëĄëí ëǍë íëĒŠė´ ėĻė ėė ëŠëë¤. ė´ ėė ė ëëëĻ´ ė ėėŧ늰 íėŧė ëŗĩęĩŦí ė ėėĩëë¤.", - "forcing_refresh_library_files": "ëŧė´ë¸ëŦëĻŦė ëǍë íėŧė ë¤ė ė¤ėēíë ė¤...", + "force_delete_user_warning": "ę˛Ŋęŗ : ė´ ėė ė í´ëš ėŦėŠėė ꡸ ėŦėŠėę° ėė í ëǍë íëĒŠė ėĻė ėė íŠëë¤. ė´ ėė ė ëëëĻ´ ė ėėŧ늰, ėė ë íėŧė ëŗĩęĩŦí ė ėėĩëë¤.", + "forcing_refresh_library_files": "ëǍë ëŧė´ë¸ëŦëĻŦ íėŧ ę°ė ėëĄęŗ ėš¨ ė¤...", "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": "ėŦë¤ėŧ ė´ë¯¸ė§ė Display P3ëĨŧ ėŦėŠíŠëë¤. ë§ė ėėė ííí ė ėė´ ë ė íí ííė´ ę°ëĨíė§ë§, ė¤ëë ë¸ëŧė°ė ëĨŧ ėŦėŠíë ę˛Ŋė° ė´ë¯¸ė§ę° ë¤ëĨ´ę˛ ëŗ´ėŧ ė ėėĩëë¤. ėė ėęŗĄė ë°Šė§í기 ėí´ sRGB ė´ë¯¸ė§ë ė´ ė¤ė ė´ ė ėŠëė§ ėėĩëë¤.", - "image_preview_description": "ëŠíë°ė´í°ëĨŧ ė ęą°í ė¤ę° íŦ기ė ė´ë¯¸ė§, ë¨ėŧ íëĒŠė ëŗ´ë ę˛Ŋė° ë° ę¸°ęŗ íėĩė ėŦėŠë¨", - "image_preview_quality_description": "1ëļí° 100 ėŦė´ė 미ëĻŦëŗ´ę¸° íė§. ę°ė´ ëėėëĄ ėĸė§ë§ íėŧ íŦę¸°ę° ėģ¤ė ¸ ėąė ë°ėėąė´ ë¨ė´ė§ ė ėėŧ늰, ę°ė´ ëŽėŧ늴 ę¸°ęŗ íėĩė íė§ė´ ë¨ė´ė§ ė ėėĩëë¤.", + "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": "ėŦë¤ėŧė Display P3 ėėė ėŦėŠíŠëë¤. ę´ėė ė´ë¯¸ė§ëĨŧ ëŗ´ë¤ ėėíę˛ ííí ė ėė§ë§, ęĩŦí ë¸ëŧė°ė ë ėĨėšėėë ë¤ëĨ´ę˛ ëŗ´ėŧ ė ėėĩëë¤. sRGB ė´ë¯¸ė§ė ę˛Ŋė° ėė ėęŗĄė ë°Šė§í기 ėí´ ęˇ¸ëëĄ ė ė§ëŠëë¤.", + "image_preview_description": "ë¨ėŧ íëĒŠė ëŗ´ęą°ë ę¸°ęŗ íėĩė ėŦėŠëë, ëŠíë°ė´í°ę° ė ęą°ë ė¤ę° íŦ기 ė´ë¯¸ė§", + "image_preview_quality_description": "미ëĻŦëŗ´ę¸° íė§ (1~100). ėĢėę° ëėėëĄ íė§ė´ ėĸėė§ė§ë§, íėŧ íŦę¸°ę° ėģ¤ė§ęŗ ėą ë°ė ėëę° ëë ¤ė§ ė ėėĩëë¤. ëëŦ´ ëŽę˛ ė¤ė í늴 ę¸°ęŗ íėĩ íė§ė ėíĨė ė¤ ė ėėĩëë¤.", "image_preview_title": "미ëĻŦëŗ´ę¸° ė¤ė ", "image_quality": "íė§", "image_resolution": "í´ėë", - "image_resolution_description": "í´ėëę° ëė ėëĄ ëí ėŧė´ ëŗ´ėĄ´ëė§ë§ íėŧė´ íŦęŗ ė¸ėŊëŠė´ ė¤ë 깸ëĻŦ늰 ėą ėëĩėąė´ ë¨ė´ė§ ė ėėĩëë¤.", + "image_resolution_description": "í´ėëę° ëėėëĄ ė¸ëļ ė ëŗ´ę° ė ëŗ´ėĄ´ëė§ë§, ė¸ėŊëŠė´ ëë ¤ė§ęŗ íėŧė´ ėģ¤ė§ëа ėą ë°ė ėëę° ë¨ė´ė§ ė ėėĩëë¤.", "image_settings": "ė´ë¯¸ė§ ė¤ė ", "image_settings_description": "ėėąë ė´ë¯¸ė§ė íė§ ë° í´ėë ę´ëĻŦ", - "image_thumbnail_description": "ëŠíë°ė´í°ę° ė ęą°ë ėė ėŦë¤ėŧ ė´ë¯¸ė§, íėëŧė¸ ëą ėŦė§ė ęˇ¸ëŖšííėŦ ëŗ´ë ę˛Ŋė°ė ėŦėŠë¨", - "image_thumbnail_quality_description": "ėŦë¤ėŧ íė§(1~100). ëėėëĄ ėĸė§ë§ íėŧíŦę¸°ę° ėģ¤ė ¸ ėąė ë°ėėąė´ ë¨ė´ė§ ė ėėĩëë¤.", + "image_thumbnail_description": "ëŠíë°ė´í°ę° ė ęą°ë ėė ėŦë¤ėŧëĄ, ëŠė¸ íėëŧė¸ ëąėė ėŦëŦ íëĒŠė ëŗŧ ë ėŦėŠëŠëë¤.", + "image_thumbnail_quality_description": "ėŦë¤ėŧ íė§ (1~100). ėĢėę° ëėėëĄ íė§ė´ ėĸė§ë§, íėŧ íŦę¸°ę° ėģ¤ė§ęŗ ėą ë°ė ėëę° ëë ¤ė§ ė ėėĩëë¤.", "image_thumbnail_title": "ėŦë¤ėŧ ė¤ė ", "job_concurrency": "{job} ëėėą", "job_created": "ėė ė´ ėėąëėėĩëë¤.", - "job_not_concurrency_safe": "ė´ ėė ė ëė ė¤íė´ ė íëŠëë¤.", + "job_not_concurrency_safe": "ė´ ėė ė ëė ė¤íė ėė íė§ ėėĩëë¤.", "job_settings": "ėė ė¤ė ", "job_settings_description": "ėė ëėėą ę´ëĻŦ", "job_status": "ėė ėí", @@ -100,23 +100,23 @@ "jobs_failed": "{jobCount, plural, other {#ę°}} ė¤í¨", "library_created": "{library} ëŧė´ë¸ëŦëĻŦëĨŧ ėėąíėĩëë¤.", "library_deleted": "ëŧė´ë¸ëŦëĻŦę° ėė ëėėĩëë¤.", - "library_import_path_description": "ę°ė ¸ėŦ í´ëëĨŧ ė ííė¸ė. ė íí í´ë ë° íė í´ëėė ėŦė§ęŗŧ ëėėė ė¤ėēíŠëë¤.", + "library_import_path_description": "ę°ė ¸ėŦ í´ëëĨŧ ė§ė íė¸ė. í´ëš í´ëė ëǍë íė í´ëėė ė´ë¯¸ė§ė ëėėė ė¤ėēíŠëë¤.", "library_scanning": "ėŖŧ기ė ė¤ėē", - "library_scanning_description": "ėŖŧ기ė ė¸ ëŧė´ë¸ëŦëĻŦ ė¤ėē ęĩŦėą", - "library_scanning_enable_description": "ėŖŧ기ė ė¸ ëŧė´ë¸ëŦëĻŦ ė¤ėē íėąí", + "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": "ëĄęˇ¸ ė¤ė ", + "library_tasks_description": "ė¸ëļ ëŧė´ë¸ëŦëĻŦėė ė íëĒŠ ëë ëŗę˛Ŋë íëĒŠė ė¤ėē", + "library_watching_enable_description": "ė¸ëļ ëŧė´ë¸ëŦëĻŦ íėŧ ëŗę˛Ŋ ę°ė", + "library_watching_settings": "ëŧė´ë¸ëŦëĻŦ ę°ė (ė¤íė )", + "library_watching_settings_description": "íėŧ ëŗę˛Ŋ ėë ę°ė§", + "logging_enable_description": "ëĄęš íėąí", + "logging_level_description": "íėąí ė ėŦėŠí ëĄęˇ¸ ë 벨ė ė ííŠëë¤.", + "logging_settings": "ëĄęš ", "machine_learning_clip_model": "CLIP ëǍë¸", - "machine_learning_clip_model_description": "CLIP ëǍë¸ė ėĸ ëĨë <link>ė´ęŗŗ</link>ė ė°¸ėĄ°íė¸ė. íęĩė´ ëą ë¤ęĩė´ ę˛ėė ėŦėŠíë ¤ëŠ´ Multilingual CLIP ëǍë¸ė ė ííė¸ė. ëǍë¸ė ëŗę˛Ŋí í ëǍë íëĒŠė ëí ė¤ë§í¸ ę˛ė ėė ė ë¤ė ė§íí´ėŧ íŠëë¤.", - "machine_learning_duplicate_detection": "ëšėˇí íëĒŠ ę°ė§", - "machine_learning_duplicate_detection_enabled": "ëšėˇí íëĒŠ ę°ė§ íėąí", + "machine_learning_clip_model_description": "CLIP ëǍë¸ė ėĸ ëĨë <link>ė´ęŗŗ</link>ė ė°¸ėĄ°íė¸ė. íęĩė´ëĨŧ íŦí¨í ë¤ęĩė´ ę˛ėė ėŦėŠíë ¤ëŠ´ Multilingual 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": "ę¸°ęŗ íėĩ íėąí", @@ -128,7 +128,7 @@ "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_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": "ėĩė ė ëĸ°ë ė ė", @@ -157,8 +157,8 @@ "map_settings": "ė§ë", "map_settings_description": "ė§ë ė¤ė ę´ëĻŦ", "map_style_description": "ė§ë í ë§ style.json URL", - "memory_cleanup_job": "ëŠëǍëĻŦ ė ëĻŦ", - "memory_generate_job": "ëŠëǍëĻŦ ėėą", + "memory_cleanup_job": "ėļėĩ ė ëĻŦ", + "memory_generate_job": "ėļėĩ ėėą", "metadata_extraction_job": "ëŠíë°ė´í° ėļėļ", "metadata_extraction_job_description": "ę° íëĒŠėė GPS, ė¸ëŦŧ ë° í´ėë ëąė ëŠíë°ė´í° ė ëŗ´ ėļėļ", "metadata_faces_import_setting": "ėŧęĩ´ ę°ė ¸ė¤ę¸° íėąí", @@ -224,8 +224,8 @@ "registration": "ę´ëĻŦė ęŗė ėėą", "registration_description": "ė˛Ģ ë˛ė§¸ëĄ ėėąëë ėŦėŠėë ę´ëĻŦė ęļíė ëļėŦë°ėŧ늰, ę´ëĻŦ ë° ėŦėŠė ėėąė´ ę°ëĨíŠëë¤.", "repair_all": "ëǍë ėëĻŦ", - "repair_matched_items": "ëėŧ íëĒŠ {count, plural, one {#ę°} other {#ę°}}ëĨŧ íė¸íėĩëë¤.", - "repaired_items": "íëĒŠ {count, plural, one {#ę°} other {#ę°}}ëĨŧ ėëĻŦíėĩëë¤.", + "repair_matched_items": "íëĒŠ {count, plural, one {#ę°} other {#ę°}}ę° ėŧėšíŠëë¤.", + "repaired_items": "íëĒŠ {count, plural, one {#ę°} other {#ę°}}ëĨŧ ëŗĩęĩŦíėĩëë¤.", "require_password_change_on_login": "ė˛Ģ ëĄęˇ¸ė¸ ė ëšë°ë˛í¸ ëŗę˛Ŋ ėęĩŦ", "reset_settings_to_default": "ė¤ė ė ę¸°ëŗ¸ę°ėŧëĄ ëŗĩė", "reset_settings_to_recent_saved": "ë§ė§ë§ėŧëĄ ė ėĨë ė¤ė ėŧëĄ ëŗĩė", @@ -251,7 +251,7 @@ "storage_template_hash_verification_enabled_description": "í´ė ę˛ėĻė íėąííŠëë¤. ė´ ė¤ė ė 결ęŗŧëĨŧ íė¤í ė´í´íė§ ėë í ëšíėąííė§ ë§ė¸ė.", "storage_template_migration": "ė¤í ëĻŦė§ í íëĻŋ ë§ė´ęˇ¸ë ė´ė ", "storage_template_migration_description": "ė´ė ė ė ëĄëë íëĒŠė íėŦ <link>{template}</link> ė ėŠ", - "storage_template_migration_info": "ė ėĨė í íëĻŋė ëǍë íėĨėëĨŧ ėëŦ¸ėëĄ ëŗííŠëë¤. í íëĻŋ ëŗę˛Ŋ ėŦíė ė ėė°ėë§ ė ėŠëŠëë¤. ė´ė ė ė ëĄëí ėė°ė í íëĻŋė ė ėŠíë ¤ëŠ´ <link>{job}</link>ëĨŧ ė¤ííė¸ė.", + "storage_template_migration_info": "ė¤í ëĻŦė§ í íëĻŋė ëǍë íėĨėëĨŧ ėëŦ¸ėëĄ ëŗííŠëë¤. í íëĻŋ ëŗę˛Ŋ ėŦíė ėëĄ ė ëĄëí íëĒŠėë§ ė ėŠëŠëë¤. ę¸°ėĄ´ė ė ëĄëë íëĒŠė ė ėŠíë ¤ëŠ´ <link>{job}</link>ė ė¤ííė¸ė.", "storage_template_migration_job": "ė¤í ëĻŦė§ í íëĻŋ ë§ė´ęˇ¸ë ė´ė ėė ", "storage_template_more_details": "ė´ ę¸°ëĨė ëí ėė¸í ë´ėŠė <template-link>ė¤í ëĻŦė§ í íëĻŋ</template-link> ë° <implications-link>ė¤ëĒ </implications-link>ė ė°¸ėĄ°íė¸ė.", "storage_template_onboarding_description": "ė´ ę¸°ëĨė íėąíí늴 ėŦėŠė ė ė í íëĻŋė ėŦėŠíėŦ íėŧė ėëėŧëĄ ė ëĻŦí ė ėėĩëë¤. ėė ėą ëŦ¸ė ëĄ ė¸í´ í´ëš 기ëĨė ę¸°ëŗ¸ė ėŧëĄ ëšíėąíëė´ ėėĩëë¤. ėė¸í ë´ėŠė <link>ëŦ¸ė</link>ëĨŧ ė°¸ėĄ°íė¸ė.", @@ -263,12 +263,12 @@ "tag_cleanup_job": "í꡸ ė ëĻŦ", "template_email_available_tags": "í íëĻŋėė ë¤ė ëŗėëĨŧ ėŦėŠí ė ėėĩëë¤: {tags}", "template_email_if_empty": "ëšė´ ėë ę˛Ŋė° ę¸°ëŗ¸ í íëĻŋė´ ėŦėŠëŠëë¤.", - "template_email_invite_album": "ė¨ë˛ í íëĻŋ ė´ë", + "template_email_invite_album": "ė¨ë˛ ė´ë í íëĻŋ", "template_email_preview": "미ëĻŦëŗ´ę¸°", "template_email_settings": "ė´ëŠėŧ í íëĻŋ", "template_email_settings_description": "ėŦėŠė ė ė ė´ëŠėŧ í íëĻŋ ę´ëĻŦ", "template_email_update_album": "ė¨ë˛ í íëĻŋ ė ë°ė´í¸", - "template_email_welcome": "ė´ëŠėŧ í íëĻŋė ė¤ė ę˛ė íėíŠëë¤", + "template_email_welcome": "ė°ėģ´ ëŠėŧ í íëĻŋ", "template_settings": "ėëĻŧ í íëĻŋ", "template_settings_description": "ėëĻŧė ėí ėŦėŠė ė§ė í íëĻŋė ę´ëĻŦíŠëë¤.", "theme_custom_css_settings": "ėŦėŠė ė ė CSS", @@ -295,13 +295,13 @@ "transcoding_audio_codec_description": "Opusë ę°ėĨ ėĸė íė§ė ėĩė ė´ė§ë§ 기기 ë° ėíí¸ė¨ė´ę° ė¤ëë ę˛Ŋė° í¸íëė§ ėė ė ėėĩëë¤.", "transcoding_bitrate_description": "ėĩë ëší¸ë ė´í¸ëĨŧ ė´ęŗŧíë ëėė ëë íėŠëė§ ėë íėė ëėė", "transcoding_codecs_learn_more": "ėŦ기ėė ėŦėŠëë ėŠė´ė ëí ėė¸í ë´ėŠė FFmpeg ëŦ¸ėė <h264-link>H.264 ėŊëą</h264-link>, <hevc-link>HEVC ėŊëą</hevc-link> ë° <vp9-link>VP9 ėŊëą</vp9-link> íëĒŠė ė°¸ėĄ°íė¸ė.", - "transcoding_constant_quality_mode": "ęŗ ė íė§ ëǍë", + "transcoding_constant_quality_mode": "Constant quality mode", "transcoding_constant_quality_mode_description": "ICQë CQPëŗ´ë¤ ëė ėąëĨė ëŗ´ė´ë ėŧëļ 기기ė íëė¨ė´ ę°ėėė ė§ėëė§ ėė ė ėėĩëë¤. ė´ ėĩė ė ė¤ė í늴 íė§ ę¸°ë° ė¸ėŊëŠ ė ė§ė ë ëǍëëĨŧ ė°ė ė ėŧëĄ ėŦėŠíŠëë¤. NVENCėėë ICQëĨŧ ė§ėíė§ ėė ė´ ė¤ė ė´ ė ėŠëė§ ėėĩëë¤.", - "transcoding_constant_rate_factor": "ėė ëšė¨ ęŗė(-CRF)", + "transcoding_constant_rate_factor": "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_encoding_options_description": "ė¸ėŊëŠë ëėėė ėŊëą, í´ėë, íė§ ë° ę¸°í ėĩė ė¤ė ", "transcoding_hardware_acceleration": "íëė¨ė´ ę°ė", "transcoding_hardware_acceleration_description": "ė¤íė ė¸ ę¸°ëĨė ëë¤. ėëę° íĨėëė§ë§ ëėŧ ëší¸ë ė´í¸ėė íė§ė´ ėëė ėŧëĄ ëŽė ė ėėĩëë¤.", "transcoding_hardware_decoding": "íëė¨ė´ ëėŊëŠ", @@ -315,7 +315,7 @@ "transcoding_max_keyframe_interval_description": "í¤íë ė ėŦė´ ėĩë íë ė ęą°ëĻŦëĨŧ ė¤ė íŠëë¤. ę°ė´ ëŽėŧ늴 ėėļ í¨ė¨ė´ ė íëė§ë§ ę˛ė ėę°ė´ ę°ė ëęŗ ëš ëĨ¸ ėė§ėė´ ėë ėĨ늴ėė íė§ė´ íĨėëŠëë¤. 0ė ė ë Ĩí ę˛Ŋė° ėëėŧëĄ ė¤ė íŠëë¤.", "transcoding_optimal_description": "ëĒŠí í´ėëëŗ´ë¤ ëė ëėė ëë íėŠëė§ ėë íėė ëėė", "transcoding_policy": "í¸ëė¤ėŊë ė ėą ", - "transcoding_policy_description": "ëėė í¸ëė¤ėŊëŠ ė기 ė¤ė í기", + "transcoding_policy_description": "í¸ëė¤ėŊëŠ ëė ëėė ė¤ė ", "transcoding_preferred_hardware_device": "ė í¸íë íëė¨ė´ 기기", "transcoding_preferred_hardware_device_description": "íëė¨ė´ í¸ëė¤ėŊëŠė ėŦėŠí dri ë ¸ëëĨŧ ė¤ė íŠëë¤. (VAAPIė QSVë§ í´ëš)", "transcoding_preset_preset": "íëĻŦė (-preset)", @@ -324,10 +324,10 @@ "transcoding_reference_frames_description": "íšė íë ėė ėėļí ë ė°¸ėĄ°íë íë ė ėëĨŧ ė¤ė íŠëë¤. ę°ė´ ëėŧ늴 ėėļ í¨ė¨ė´ íĨėëë ė¸ėŊëŠ ėëę° ė íëŠëë¤. 0ė ė ë Ĩí ę˛Ŋė° ėëėŧëĄ ė¤ė íŠëë¤.", "transcoding_required_description": "íėŠë íėė´ ėë ëėėë§", "transcoding_settings": "ëėė í¸ëė¤ėŊëŠ ė¤ė ", - "transcoding_settings_description": "í¸ëė¤ėŊëŠí ëėėęŗŧ ė˛ëĻŦ ë°Šë˛ ę´ëĻŦí기", + "transcoding_settings_description": "í¸ëė¤ėŊëŠí ëėė ë° ė˛ëĻŦ ë°Šë˛ ę´ëĻŦ", "transcoding_target_resolution": "ëĒŠí í´ėë", "transcoding_target_resolution_description": "ëė í´ėëëĨŧ ė íí ę˛Ŋė° ė¸ëļ ëŦėŦė ėė¤ė ėĩėíí ė ėė§ë§, ė¸ėŊëŠ ėę°ęŗŧ íėŧ íŦę¸°ę° ėĻę°íėŦ ėąė ë°ė ėëę° ëë ¤ė§ ė ėėĩëë¤.", - "transcoding_temporal_aq": "ėŧėė AQ", + "transcoding_temporal_aq": "Temporal AQ", "transcoding_temporal_aq_description": "ė¸ëļ ëŦėŦę° ë§ęŗ ėė§ėė´ ė ė ėĨ늴ė íė§ė´ íĨėëŠëë¤. ė¤ëë 기기ė í¸íëė§ ėė ė ėėĩëë¤. (NVENCë§ í´ëš)", "transcoding_threads": "ė¤ë ë", "transcoding_threads_description": "ę°ė´ ëėŧ늴 ė¸ėŊëŠ ėëę° íĨėëė§ë§ ëĻŦėė¤ ėŦėŠëė´ ėĻę°íŠëë¤. ę°ė CPU ėŊė´ ėëŗ´ë¤ ėėėŧ í늰, ė¤ė íė§ ėėŧë ¤ëŠ´ 0ė ė ë ĨíŠëë¤.", @@ -371,13 +371,17 @@ "admin_password": "ę´ëĻŦė ëšë°ë˛í¸", "administration": "ę´ëĻŦ", "advanced": "ęŗ ę¸", + "advanced_settings_enable_alternate_media_filter_subtitle": "ė´ ėĩė ė ėŦėŠí늴 ë기í ė¤ ë¯¸ëė´ëĨŧ ë랴 기ė¤ėŧëĄ íí°ë§í ė ėėĩëë¤. ėąė´ ëǍë ė¨ë˛ė ė ëëĄ ę°ė§íė§ ëĒģí ëë§ ėŦėŠíė¸ė.", + "advanced_settings_enable_alternate_media_filter_title": "ë랴 기기 ė¨ë˛ ë기í íí° ėŦėŠ (ė¤íė )", "advanced_settings_log_level_title": "ëĄęˇ¸ ë 벨: {}", "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_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": "ëŦ¸ė í´ę˛°", @@ -408,10 +412,10 @@ "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_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": "ėŦėŠė ėļę°", @@ -426,7 +430,7 @@ "allow_edits": "í¸ė§ėëĄ ė¤ė ", "allow_public_user_to_download": "ëǍë ėŦėŠėė ë¤ė´ëĄë íėŠ", "allow_public_user_to_upload": "ëǍë ėŦėŠėė ė ëĄë íėŠ", - "alt_text_qr_code": "QRėŊë ė´ë¯¸ė§", + "alt_text_qr_code": "QR ėŊë ė´ë¯¸ė§", "anti_clockwise": "ë°ėęŗ ë°ŠíĨ", "api_key": "API í¤", "api_key_description": "ė´ ę°ė í ë˛ë§ íėëŠëë¤. ė°Ŋė ëĢ기 ė ë°ëė ëŗĩėŦí´ėŖŧė¸ė.", @@ -444,11 +448,11 @@ "archive_size": "ėėļ íėŧ íŦ기", "archive_size_description": "ë¤ė´ëĄëí ėėļ íėŧė íŦ기 ęĩŦėą (GiB ë¨ė)", "archived": "ëŗ´ę´í¨", - "archived_count": "ëŗ´ę´í¨ėŧëĄ íëĒŠ {count, plural, other {#ę°}} ė´ëë¨", + "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_action_delete_err_read_only": "ėŊ기 ė ėŠ íëĒŠė ėė í ė ėėŧë¯ëĄ ęą´ëëëë¤.", + "asset_action_share_err_offline": "ė¤íëŧė¸ íëĒŠė ę°ė ¸ėŦ ė ėėŧë¯ëĄ ęą´ëëëë¤.", "asset_added_to_album": "ė¨ë˛ė ėļę°ëėėĩëë¤.", "asset_adding_to_album": "ė¨ë˛ė ėļę° ė¤âĻ", "asset_description_updated": "íëĒŠė ė¤ëĒ ė´ ė ë°ė´í¸ëėėĩëë¤.", @@ -470,15 +474,15 @@ "asset_skipped_in_trash": "í´ė§íĩė íëĒŠ", "asset_uploaded": "ė ëĄë ėëŖ", "asset_uploading": "ė ëĄë ė¤âĻ", - "asset_viewer_settings_subtitle": "Manage your gallery viewer settings", + "asset_viewer_settings_subtitle": "ę°¤ëŦëĻŦ ëˇ°ė´ ė¤ė ę´ëĻŦ", "asset_viewer_settings_title": "ëŗ´ę¸° ėĩė ", "assets": "íëĒŠ", - "assets_added_count": "íëĒŠ {count, plural, one {#ę°} other {#ę°}}ę° ėļę°ëėėĩëë¤.", + "assets_added_count": "{count, plural, one {#ę°} other {#ę°}} íëĒŠ ėļę°ë¨", "assets_added_to_album_count": "ė¨ë˛ė íëĒŠ {count, plural, one {#ę°} other {#ę°}} ėļę°ë¨", "assets_added_to_name_count": "{hasName, select, true {<b>{name}</b>} other {ė ė¨ë˛}}ė íëĒŠ {count, plural, one {#ę°} other {#ę°}} ėļę°ë¨", "assets_count": "{count, plural, one {#ę°} other {#ę°}} íëĒŠ", "assets_deleted_permanently": "{}ę° íëĒŠė´ ėęĩŦė ėŧëĄ ėė ë¨", - "assets_deleted_permanently_from_server": "Immichėė íëĒŠ {}ę°ę° ėęĩŦė ėŧëĄ ėė ë¨", + "assets_deleted_permanently_from_server": "ėë˛ėė íëĒŠ {}ę°ę° ėęĩŦė ėŧëĄ ėė ë¨", "assets_moved_to_trash_count": "í´ė§íĩėŧëĄ íëĒŠ {count, plural, one {#ę°} other {#ę°}} ė´ëë¨", "assets_permanently_deleted_count": "íëĒŠ {count, plural, one {#ę°} other {#ę°}}ę° ėęĩŦė ėŧëĄ ėė ë¨", "assets_removed_count": "íëĒŠ {count, plural, one {#ę°} other {#ę°}}ëĨŧ ė ęą°íėĩëë¤.", @@ -486,30 +490,30 @@ "assets_restore_confirmation": "í´ė§íĩėŧëĄ ė´ëë íëĒŠė ëǍë ëŗĩėíėę˛ ėĩëęš? ė´ ėė ė ëëëĻ´ ė ėėĩëë¤! ëëŊë íëĒŠė ę˛Ŋė° ëŗĩėëė§ ėėĩëë¤.", "assets_restored_count": "íëĒŠ {count, plural, one {#ę°} other {#ę°}}ëĨŧ ëŗĩėíėĩëë¤.", "assets_restored_successfully": "íëĒŠ {}ę°ëĨŧ ëŗĩėíėĩëë¤.", - "assets_trashed": "í´ė§íĩėŧëĄ íëĒŠ {}ę°ę° ė´ëëėėĩëë¤.", + "assets_trashed": "í´ė§íĩėŧëĄ íëĒŠ {}ę° ė´ëë¨", "assets_trashed_count": "í´ė§íĩėŧëĄ íëĒŠ {count, plural, one {#ę°} other {#ę°}} ė´ëë¨", - "assets_trashed_from_server": "í´ė§íĩėŧëĄ Immich íëĒŠ {}ę°ę° ė´ëëėėĩëë¤.", + "assets_trashed_from_server": "í´ė§íĩėŧëĄ ėë˛ė ėë íëĒŠ {}ę°ę° ė´ëëėėĩëë¤.", "assets_were_part_of_album_count": "ė¨ë˛ė ė´ë¯¸ ėĄ´ėŦíë {count, plural, one {íëĒŠ} other {íëĒŠ}}ė ëë¤.", "authorized_devices": "ė¸ėĻë 기기", - "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", - "automatic_endpoint_switching_title": "Automatic URL switching", + "automatic_endpoint_switching_subtitle": "ė§ė ë Wi-Fię° ėŦėŠ ę°ëĨí ę˛Ŋė° ë´ëļë§ė íĩí´ ė°ę˛°íęŗ , ꡸ë ė§ ėėŧ늴 ë¤ëĨ¸ ė°ę˛° ë°Šėė ėŦėŠíŠëë¤.", + "automatic_endpoint_switching_title": "ėë URL ė í", "back": "ë¤ëĄ", "back_close_deselect": "ë¤ëĄ, ëĢ기, ė í 뎍ė", - "background_location_permission": "Background location permission", - "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", + "background_location_permission": "밹꡸ëŧė´ë ėėš ęļí", + "background_location_permission_content": "밹꡸ëŧė´ëėė ë¤í¸ėíŦëĨŧ ė ííë ¤ëŠ´, Immichę° Wi-Fi ë¤í¸ėíŦ ė´ëĻė íė¸í ė ėëëĄ 'ė íí ėėš' ęļíė íė íėŠí´ėŧ íŠëë¤.", "backup_album_selection_page_albums_device": "기기ė ė¨ë˛ ({})", - "backup_album_selection_page_albums_tap": "í ë˛ ëëŦ ė í, ë ë˛ ëëŦ ė ė¸íė¸ė.", + "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_all": "ëǍë", - "backup_background_service_backup_failed_message": "íëĒŠė ë°ąė íė§ ëĒģíėĩëë¤. ë¤ė ėëíë ė¤...", - "backup_background_service_connection_failed_message": "ėë˛ė ė°ę˛°íė§ ëĒģíėĩëë¤. ë¤ė ėëíë ė¤...", + "backup_background_service_backup_failed_message": "íëĒŠė ë°ąė íė§ ëĒģíėĩëë¤. ë¤ė ėëíë ė¤âĻ", + "backup_background_service_connection_failed_message": "ėë˛ė ė°ę˛°íė§ ëĒģíėĩëë¤. ë¤ė ėëíë ė¤âĻ", "backup_background_service_current_upload_notification": "{} ė ëĄë ė¤", - "backup_background_service_default_notification": "ë°ąė í íëĒŠė íė¸íë ė¤...", + "backup_background_service_default_notification": "ėëĄė´ íëĒŠė íė¸íë ė¤âĻ", "backup_background_service_error_title": "ë°ąė ė¤ëĨ", - "backup_background_service_in_progress_notification": "ė íí íëĒŠė ë°ąė íë ė¤...", + "backup_background_service_in_progress_notification": "íëĒŠė ë°ąė íë ė¤âĻ", "backup_background_service_upload_failure_notification": "{} ė ëĄë ė¤í¨", "backup_controller_page_albums": "ë°ąė í ė¨ë˛", "backup_controller_page_background_app_refresh_disabled_content": "밹꡸ëŧė´ë ë°ąė ė ėŦėŠíë ¤ëŠ´ ė¤ė > ėŧë° > 밹꡸ëŧė´ë ėą ėëĄ ęŗ ėš¨ėė 밹꡸ëŧė´ë ėą ėëĄ ęŗ ėš¨ė íėąííė¸ė.", @@ -521,30 +525,30 @@ "backup_controller_page_background_battery_info_title": "ë°°í°ëĻŦ ėĩė í", "backup_controller_page_background_charging": "ėļŠė ė¤ėë§", "backup_controller_page_background_configure_error": "밹꡸ëŧė´ë ėëšė¤ ęĩŦėą ė¤í¨", - "backup_controller_page_background_delay": "ė ėŊí ė¸ ë°ąė ę°ę˛Š: {}", - "backup_controller_page_background_description": "밹꡸ëŧė´ë ėëšė¤ëĨŧ íėąííėŦ ėąė ė¤ííė§ ėęŗ ė íëĒŠė ėëėŧëĄ ë°ąė íė¸ė.", - "backup_controller_page_background_is_off": "밹꡸ëŧė´ë ë°ąė ė´ ëšíėąíëėėĩëë¤.", - "backup_controller_page_background_is_on": "밹꡸ëŧė´ë ë°ąė ė´ íėąíëėėĩëë¤.", + "backup_controller_page_background_delay": "ė 미ëė´ ë°ąė ę°ę˛Š: {}", + "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": "Wi-Fiėėë§", "backup_controller_page_backup": "ë°ąė ", - "backup_controller_page_backup_selected": "ė íë¨:", + "backup_controller_page_backup_selected": "ė íë¨: ", "backup_controller_page_backup_sub": "ë°ąė ë ėŦė§ ë° ëėė", "backup_controller_page_created": "ėėąėŧ: {}", "backup_controller_page_desc_backup": "íŦ꡸ëŧė´ë ë°ąė ė íėąííėŦ ėąė ėėí ë ė íëĒŠė ėë˛ė ėëėŧëĄ ė ëĄëíė¸ė.", - "backup_controller_page_excluded": "ė ė¸ë¨:", + "backup_controller_page_excluded": "ė ė¸ë¨: ", "backup_controller_page_failed": "ė¤í¨ ({})", "backup_controller_page_filename": "íėŧëĒ : {} [{}]", "backup_controller_page_id": "ID: {}", "backup_controller_page_info": "ë°ąė ė ëŗ´", - "backup_controller_page_none_selected": "ė íí íëĒŠė´ ėėĩëë¤.", + "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_status_off": "íŦ꡸ëŧė´ë ėë ë°ąė ė´ ëšíėąíëėėĩëë¤.", + "backup_controller_page_status_on": "íŦ꡸ëŧė´ë ėë ë°ąė ė´ íėąíëėėĩëë¤.", "backup_controller_page_storage_format": "{} ėŦėŠ ė¤, ė 랴 {}", "backup_controller_page_to_backup": "ë°ąė í ė¨ë˛ ëĒŠëĄ", "backup_controller_page_total_sub": "ė íí ė¨ë˛ė ęŗ ė í ėŦė§ ë° ëėė", @@ -558,7 +562,7 @@ "backup_manual_success": "ėąęŗĩ", "backup_manual_title": "ė ëĄë ėí", "backup_options_page_title": "ë°ąė ėĩė ", - "backup_setting_subtitle": "Manage background and foreground upload settings", + "backup_setting_subtitle": "밹꡸ëŧė´ë ë° íŦ꡸ëŧė´ë ė ëĄë ė¤ė ę´ëĻŦ", "backward": "ë¤ëĄ", "birthdate_saved": "ėë ėėŧė´ ėąęŗĩė ėŧëĄ ė ėĨëėėĩëë¤.", "birthdate_set_description": "ėë ėėŧė ėŦė§ ė´Ŧė ëšė ė¸ëŦŧė ëė´ëĨŧ ęŗė°íë ë° ėŦėŠëŠëë¤.", @@ -593,12 +597,12 @@ "camera_model": "ėš´ëŠëŧ ëǍë¸", "cancel": "ëĢ기", "cancel_search": "ę˛ė ëĢ기", - "canceled": "Canceled", + "canceled": "ė¤ë¨ë¨", "cannot_merge_people": "ė¸ëŦŧė ëŗíŠí ė ėėĩëë¤.", "cannot_undo_this_action": "ė´ ėė ė ëëëĻ´ ė ėėĩëë¤!", "cannot_update_the_description": "ė¤ëĒ ė ëŗę˛Ŋí ė ėėĩëë¤.", "change_date": "ë ė§ ëŗę˛Ŋ", - "change_display_order": "Change display order", + "change_display_order": "íė ėė ëŗę˛Ŋ", "change_expiration_time": "ë§ëŖėŧ ëŗę˛Ŋ", "change_location": "ėėš ëŗę˛Ŋ", "change_name": "ė´ëĻ ëŗę˛Ŋ", @@ -613,9 +617,9 @@ "change_your_password": "ëšë°ë˛í¸ ëŗę˛Ŋ", "changed_visibility_successfully": "íė ėŦëļę° ėąęŗĩė ėŧëĄ ëŗę˛Ŋëėėĩëë¤.", "check_all": "ëǍë íė¸", - "check_corrupt_asset_backup": "Check for corrupt asset backups", - "check_corrupt_asset_backup_button": "Perform check", - "check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.", + "check_corrupt_asset_backup": "ë°ąė ë íëĒŠė ėė ėŦëļ íė¸", + "check_corrupt_asset_backup_button": "íė¸ ėí", + "check_corrupt_asset_backup_description": "ė´ ę˛ėŦë ëǍë íëĒŠė´ ë°ąė ë í Wi-Fię° ė°ę˛°ë ėíėėë§ ė¤ííė¸ė. ė´ ėė ė ëĒ ëļ ė ë ėėë ė ėėĩëë¤.", "check_logs": "ëĄęˇ¸ íė¸", "choose_matching_people_to_merge": "ëŗíŠí ė¸ëŦŧ ė í", "city": "ëė", @@ -627,10 +631,10 @@ "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_import_success_msg": "í´ëŧė´ė¸í¸ ė¸ėĻė ę°ė ¸ė¤ę¸° ėëŖ", + "client_cert_invalid_msg": "ė¸ėĻėę° ė í¨íė§ ėęą°ë ëšë°ë˛í¸ę° ėŦë°ëĨ´ė§ ėė", + "client_cert_remove_msg": "í´ëŧė´ė¸í¸ ė¸ėĻė ė ęą°ë¨", + "client_cert_subtitle": "PKCS12 (.p12, .pfx) íėė ė§ėíŠëë¤. ė¸ėĻė ę°ė ¸ė¤ę¸° ë° ė ęą°ë ëĄęˇ¸ė¸ ė ėë§ ę°ëĨíŠëë¤.", "client_cert_title": "SSL í´ëŧė´ė¸í¸ ė¸ėĻė", "clockwise": "ėęŗ ë°ŠíĨ", "close": "ëĢ기", @@ -644,12 +648,12 @@ "comments_are_disabled": "ëę¸ė´ ëšíėąíëėėĩëë¤.", "common_create_new_album": "ė¨ë˛ ėėą", "common_server_error": "ë¤í¸ėíŦ ė°ę˛° ėíëĨŧ íė¸íęŗ , ėë˛ė ė ėí ė ėëė§, ėą/ėë˛ ë˛ė ė´ í¸íëëė§ íė¸í´ėŖŧė¸ė.", - "completed": "Completed", + "completed": "ėëŖë¨", "confirm": "íė¸", "confirm_admin_password": "ę´ëĻŦė ëšë°ë˛í¸ íė¸", - "confirm_delete_face": "ėė ėė {name} ėŧęĩ´ė ėė íėę˛ ėĩëęš?", + "confirm_delete_face": "íëĒŠėė {name}ė ėŧęĩ´ė ėė íėę˛ ėĩëęš?", "confirm_delete_shared_link": "ė´ ęŗĩė ë§íŦëĨŧ ėė íėę˛ ėĩëęš?", - "confirm_keep_this_delete_others": "ė´ ėė ė ė ė¸í ė¤íė ë¤ëĨ¸ ëǍë ėė ė´ ėė ëŠëë¤. ęŗėíėę˛ ėĩëęš?", + "confirm_keep_this_delete_others": "ė´ íëĒŠė ė ė¸í ė¤íė ëǍë íëĒŠė´ ėė ëŠëë¤. ęŗėíėę˛ ėĩëęš?", "confirm_password": "ëšë°ë˛í¸ íė¸", "contain": "ë§ėļ¤", "context": "ë´ėŠ", @@ -659,8 +663,8 @@ "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": "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": "ė´ë¯¸ė§ę° í´ëĻŊëŗ´ëė ëŗĩėŦëėėĩëë¤.", @@ -695,7 +699,7 @@ "crop": "ėëĨ´ę¸°", "curated_object_page_title": "ėŦëŦŧ", "current_device": "íėŦ 기기", - "current_server_address": "Current server address", + "current_server_address": "íėŦ ėë˛ ėŖŧė", "custom_locale": "ėŦėŠė ė§ė ëĄėŧėŧ", "custom_locale_description": "ė¸ė´ ë° ė§ėė ë°ëĨ¸ ë ė§ ë° ėĢė íė ė§ė ", "daily_title_text_date": "Mė dėŧ EEEE", @@ -709,19 +713,19 @@ "date_range": "ë ė§ ë˛ė", "day": "ėŧ", "deduplicate_all": "ëǍë ėė ", - "deduplication_criteria_1": "ė´ë¯¸ė§ íŦ기(ë°ė´í¸)", - "deduplication_criteria_2": "EXIF ë°ė´í° ę°ė", + "deduplication_criteria_1": "ė´ë¯¸ė§ íŦ기 (ë°ė´í¸)", + "deduplication_criteria_2": "EXIF ė ëŗ´ íëĒŠ ė", "deduplication_info": "ė¤ëŗĩ ė ęą° ė ëŗ´", - "deduplication_info_description": "ėė°ė ėëėŧëĄ ë¯¸ëĻŦ ė ííęŗ ėŧę´ė ėŧëĄ ė¤ëŗĩė ė ęą°íë ¤ëŠ´ ë¤ėė ė´í´ëŗ´ė¸ė:", + "deduplication_info_description": "íëĒŠė ėëėŧëĄ ë¯¸ëĻŦ ė ííęŗ ė¤ëŗĩ íëĒŠė ėŧę´ ė ęą°íë ¤ëŠ´ ë¤ėė íė¸íė¸ė:", "default_locale": "ę¸°ëŗ¸ ëĄėŧėŧ", "default_locale_description": "ë¸ëŧė°ė ëĄėŧėŧė ë°ëĨ¸ ë ė§ ë° ėĢė íė ė§ė ", "delete": "ėė ", "delete_album": "ė¨ë˛ ėė ", "delete_api_key_prompt": "API í¤ëĨŧ ėė íėę˛ ėĩëęš?", - "delete_dialog_alert": "ė´ íëĒŠė´ Immich ë° ę¸°ę¸°ėė ėęĩŦė ėŧëĄ ėė ëŠëë¤.", - "delete_dialog_alert_local": "ė´ íëĒŠė´ ę¸°ę¸°ėė ėęĩŦė ėŧëĄ ėė ëŠëë¤. Immichėėë ėė ëė§ ėėĩëë¤.", - "delete_dialog_alert_local_non_backed_up": "ėŧëļ íëĒŠė´ ë°ąė ëė§ ėėėĩëë¤. ë°ąė ëė§ ėė íëĒŠė´ ę¸°ę¸°ėė ėęĩŦė ėŧëĄ ėė ëŠëë¤.", - "delete_dialog_alert_remote": "ė´ íëĒŠė´ Immichėė ėęĩŦė ėŧëĄ ėė ëŠëë¤.", + "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": "ëšėˇí íëĒŠë¤ė ėęĩŦė ėŧëĄ ėė íėę˛ ėĩëęš?", @@ -731,7 +735,7 @@ "delete_link": "ë§íŦ ėė ", "delete_local_dialog_ok_backed_up_only": "ë°ąė ë íëĒŠë§ ėė ", "delete_local_dialog_ok_force": "ëŦ´ėíęŗ ėė ", - "delete_others": "ë¤ëĨ¸ ėŦë ėė ", + "delete_others": "ë¤ëĨ¸ ė¸ëŦŧ ėė ", "delete_shared_link": "ęŗĩė ë§íŦ ėė ", "delete_shared_link_dialog_title": "ęŗĩė ë§íŦ ėė ", "delete_tag": "í꡸ ėė ", @@ -741,12 +745,12 @@ "deletes_missing_assets": "ëė¤íŦė ėĄ´ėŦíė§ ėë íëĒŠ ė ęą°", "description": "ė¤ëĒ ", "description_input_hint_text": "ė¤ëĒ ėļę°...", - "description_input_submit_error": "ė¤ëĒ ė ëŗę˛Ŋíë ė¤ ëŦ¸ė ę° ë°ėíėĩëë¤. ėė¸í ë´ėŠė ëĄęˇ¸ëĨŧ ė°¸ėĄ°íė¸ė.", + "description_input_submit_error": "ė¤ëĒ ė ë°ė´í¸ ė¤ ė¤ëĨę° ë°ėíėĩëë¤. ėė¸í ë´ėŠė ëĄęˇ¸ëĨŧ íė¸íė¸ė.", "details": "ėė¸ ė ëŗ´", "direction": "ë°ŠíĨ", "disabled": "ëšíėąíë¨", "disallow_edits": "롰ė´ëĄ ė¤ė ", - "discord": "ëė¤ėŊë", + "discord": "Discord", "discover": "íė", "dismiss_all_errors": "ëǍë ė¤ëĨ ëŦ´ė", "dismiss_error": "ė¤ëĨ ëŦ´ė", @@ -761,8 +765,8 @@ "download_canceled": "ë¤ė´ëĄëę° ėˇ¨ėëėėĩëë¤.", "download_complete": "ë¤ėëĄëę° ėëŖëėėĩëë¤.", "download_enqueue": "ë기ė´ė ë¤ė´ëĄë", - "download_error": "ë¤ė´ëĄë ė¤ ëŦ¸ė ę° ë°ėíėĩëë¤.", - "download_failed": "ë¤ė´ëĄëė ė¤í¨íėėĩëë¤.", + "download_error": "ë¤ė´ëĄë ė¤ëĨ", + "download_failed": "ë¤ė´ëĄë ė¤í¨", "download_filename": "íėŧ: {}", "download_finished": "ë¤ė´ëĄëę° ėëŖëėėĩëë¤.", "download_include_embedded_motion_videos": "ë´ėĨë ëėė", @@ -773,7 +777,7 @@ "download_settings_description": "ë¤ė´ëĄë ė¤ė ę´ëĻŦ", "download_started": "ë¤ė´ëĄëę° ėėëėėĩëë¤.", "download_sucess": "ë¤ė´ëĄëę° ėëŖëėėĩëë¤.", - "download_sucess_android": "미ëė´ę° DCIM/Immichė ė ėĨëėėĩëë¤.", + "download_sucess_android": "미ëė´ę° DCIM/Immich í´ëė ė ėĨëėėĩëë¤.", "download_waiting_to_retry": "ėŦėë ë기 ė¤", "downloading": "ë¤ė´ëĄë", "downloading_asset_filename": "{filename} ë¤ė´ëĄë ė¤...", @@ -807,17 +811,17 @@ "editor_crop_tool_h2_aspect_ratios": "ėĸ íĄëš", "editor_crop_tool_h2_rotation": "íė ", "email": "ė´ëŠėŧ", - "empty_folder": "This folder is empty", + "empty_folder": "í´ëę° ëšė´ ėė", "empty_trash": "í´ė§íĩ ëšė°ę¸°", "empty_trash_confirmation": "í´ė§íĩė ëšė°ėę˛ ėĩëęš? í´ė§íĩė ėë ëǍë íëĒŠė´ Immichėė ėęĩŦė ėŧëĄ ėė ëŠëë¤.\nė´ ėė ė ëëëĻ´ ė ėėĩëë¤!", "enable": "íėąí", "enabled": "íėąíë¨", "end_date": "ėĸ ëŖėŧ", - "enqueued": "Enqueued", + "enqueued": "ë기ė´ė ėļę°ë¨", "enter_wifi_name": "Enter WiFi name", "error": "ė¤ëĨ", - "error_change_sort_album": "Failed to change album sort order", - "error_delete_face": "ėė ėė ėŧęĩ´ ėė ė¤ëĨ", + "error_change_sort_album": "ė¨ë˛ íė ėė ëŗę˛Ŋ ė¤í¨", + "error_delete_face": "ėŧęĩ´ ėė ė¤ ė¤ëĨę° ë°ėíėĩëë¤.", "error_loading_image": "ė´ë¯¸ė§ ëĄë ė¤ëĨ", "error_saving_image": "ė¤ëĨ: {}", "error_title": "ė¤ëĨ - ëŦ¸ė ę° ë°ėíėĩëë¤", @@ -846,7 +850,7 @@ "failed_to_create_shared_link": "ęŗĩė ë§íŦëĨŧ ėėąíė§ ëĒģíėĩëë¤.", "failed_to_edit_shared_link": "ęŗĩė ë§íŦëĨŧ ėė íė§ ëĒģíėĩëë¤.", "failed_to_get_people": "ė¸ëŦŧ ëĄë ė¤í¨", - "failed_to_keep_this_delete_others": "ė´ ėė°ė ė ė§íęŗ ë¤ëĨ¸ ėė°ė ėė íė§ ëĒģíėĩëë¤", + "failed_to_keep_this_delete_others": "ė´ íëĒŠė ė ė§íęŗ ë¤ëĨ¸ íëĒŠė ėė íė§ ëĒģíėĩëë¤.", "failed_to_load_asset": "íëĒŠ ëĄë ė¤í¨", "failed_to_load_assets": "íëĒŠ ëĄë ė¤í¨", "failed_to_load_people": "ė¸ëŦŧ ëĄë ė¤í¨", @@ -953,10 +957,10 @@ "exif_bottom_sheet_location": "ėėš", "exif_bottom_sheet_people": "ė¸ëŦŧ", "exif_bottom_sheet_person_add_person": "ė´ëĻ ėļę°", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "{}ė¸", + "exif_bottom_sheet_person_age_months": "ėí {}ę°ė", + "exif_bottom_sheet_person_age_year_months": "ėí 1ë {}ę°ė", + "exif_bottom_sheet_person_age_years": "{}ė¸", "exit_slideshow": "ėŦëŧė´ë ėŧ ėĸ ëŖ", "expand_all": "ëǍë íėĨ", "experimental_settings_new_asset_list_subtitle": "ė§í ė¤", @@ -973,12 +977,12 @@ "extension": "íėĨė", "external": "ė¸ëļ", "external_libraries": "ė¸ëļ ëŧė´ë¸ëŦëĻŦ", - "external_network": "External network", + "external_network": "ė¸ëļ ë¤í¸ėíŦ", "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", "face_unassigned": "ė ė ėė", - "failed": "Failed", - "failed_to_load_assets": "ėė ëĄëė ė¤í¨íėĩëë¤", - "failed_to_load_folder": "Failed to load folder", + "failed": "ė¤í¨í¨", + "failed_to_load_assets": "íëĒŠ ëĄë ė¤í¨", + "failed_to_load_folder": "í´ë ëĄë ė¤í¨", "favorite": "ėĻę˛¨ė°žę¸°", "favorite_or_unfavorite_photo": "ėĻę˛¨ė°žę¸° ėļę°/ė ęą°", "favorites": "ėĻę˛¨ė°žę¸°", @@ -992,26 +996,27 @@ "filetype": "íėŧ íė", "filter": "íí°", "filter_people": "ė¸ëŦŧ íí°", + "filter_places": "ėĨė íí°ë§", "find_them_fast": "ė´ëĻėŧëĄ ę˛ėíėŦ ëš ëĨ´ę˛ ė°žę¸°", "fix_incorrect_match": "ėëĒģë ëļëĨ ėė ", - "folder": "Folder", - "folder_not_found": "Folder not found", + "folder": "í´ë", + "folder_not_found": "í´ëëĨŧ ė°žė ė ėė", "folders": "í´ë", "folders_feature_description": "íėŧ ėė¤í ė ėŦė§ ë° ëėėė í´ë ëˇ°ëĄ íė", "forward": "ėėŧëĄ", "general": "ėŧë°", "get_help": "ëė ėė˛", - "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", + "get_wifiname_error": "Wi-Fi ė´ëĻė ę°ė ¸ėŦ ė ėėĩëë¤. íėí ęļíė´ íėŠëė´ ėęŗ Wi-Fi ë¤í¸ėíŦė ė°ę˛°ëė´ ėëė§ íė¸íė¸ė.", "getting_started": "ėėí기", "go_back": "ë¤ëĄ", "go_to_folder": "í´ëëĄ ė´ë", "go_to_search": "ę˛ėėŧëĄ ė´ë", - "grant_permission": "Grant permission", + "grant_permission": "ęļí ëļėŦ", "group_albums_by": "ë¤ėėŧëĄ ė¨ë˛ ęˇ¸ëŖší...", - "group_country": "ęĩę°ëŗ ęˇ¸ëŖší", + "group_country": "ęĩę°ëŗëĄ ęˇ¸ëŖší", "group_no": "ęˇ¸ëŖší ėė", "group_owner": "ėė ėëĄ ęˇ¸ëŖší", - "group_places_by": "ėĨė ęˇ¸ëŖší 기ė¤...", + "group_places_by": "ë¤ėėŧëĄ ėĨė ęˇ¸ëŖšíâĻ", "group_year": "ė°ëëĄ ęˇ¸ëŖší", "haptic_feedback_switch": "í íą íŧëë°ą íėąí", "haptic_feedback_title": "í íą íŧëë°ą", @@ -1020,7 +1025,7 @@ "header_settings_field_validator_msg": "ę°ė ëšėë ė ėėĩëë¤.", "header_settings_header_name_input": "í¤ë ė´ëĻ", "header_settings_header_value_input": "í¤ë ę°", - "headers_settings_tile_subtitle": "ę° ë¤í¸ėíŦ ėė˛ė ëŗ´ëŧ ë ėŦėŠí íëĄė í¤ëëĨŧ ė ėíŠëë¤.", + "headers_settings_tile_subtitle": "ėąė´ ę° ë¤í¸ėíŦ ėė˛ė í¨ęģ ė ėĄí íëĄė í¤ëëĨŧ ė ėíŠëë¤.", "headers_settings_tile_title": "ėŦėŠė ė ė íëĄė í¤ë", "hi_user": "ėë íė¸ė {name}ë, ({email})", "hide_all_people": "ëǍë ė¸ëŦŧ ė¨ę¸°ę¸°", @@ -1040,13 +1045,13 @@ "home_page_delete_remote_err_local": "ėë˛ėė ėė ë íëĒŠė ëë¤. ęą´ëëëë¤.", "home_page_favorite_err_local": "기기ė íëĒŠė ėĻę˛¨ė°žę¸°ė ėļę°í ė ėėĩëë¤. ęą´ëëëë¤.", "home_page_favorite_err_partner": "íí¸ëė íëĒŠė ėĻę˛¨ė°žę¸°ė ėļę°í ė ėėĩëë¤. ęą´ëëëë¤.", - "home_page_first_time_notice": "ėąė ė˛ė ėŦėŠíë ę˛Ŋė° íėëŧė¸ė ė¨ë˛ė ėŦė§ęŗŧ ëėėė ėąė¸ ė ėëëĄ ë°ąė í ė¨ë˛ė ė ííė¸ė.", - "home_page_share_err_local": "기기ė íëĒŠė ë§íŦëĄ ęŗĩė í ė ėėĩëë¤. ęą´ëëëë¤.", + "home_page_first_time_notice": "ėąė ė˛ė ėŦėŠíë ę˛Ŋė°, íėëŧė¸ė ėŦė§ęŗŧ ëėėė´ íėë ė ėëëĄ ë°ąė ė¨ë˛ė ė íí´ėŖŧė¸ė.", + "home_page_share_err_local": "기기ėë§ ė ėĨë íëĒŠė ë§íŦëĄ ęŗĩė í ė ėė´ ęą´ëëëë¤.", "home_page_upload_err_limit": "í ë˛ė ėĩë 30ę°ė íëĒŠë§ ė ëĄëí ė ėėĩëë¤.", "host": "í¸ė¤í¸", "hour": "ėę°", "ignore_icloud_photos": "iCloud ėŦė§ ė ė¸", - "ignore_icloud_photos_description": "iCloudė ė ėĨë ėŦė§ė Immich ėë˛ė ė ëĄëëė§ ėėĩëë¤.", + "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 {ėŦė§}}", @@ -1080,16 +1085,16 @@ "night_at_midnight": "매ėŧ ë°¤ ėė ", "night_at_twoam": "매ėŧ ėë˛Ŋ 2ė" }, - "invalid_date": "ėëĒģë ë ė§ė ëë¤.", - "invalid_date_format": "ėëĒģë ë ė§ íėė ëë¤.", + "invalid_date": "ė í¨íė§ ėė ë ė§", + "invalid_date_format": "ė í¨íė§ ėė ë ė§ íė", "invite_people": "ėŦėŠė ė´ë", "invite_to_album": "ė¨ë˛ėŧëĄ ė´ë", "items_count": "{count, plural, one {#ę°} other {#ę°}} íëĒŠ", "jobs": "ėė ", "keep": "ė ė§", "keep_all": "ëǍë ė ė§", - "keep_this_delete_others": "ė´ íëĒŠė ëŗ´ę´íęŗ ë¤ëĨ¸ íëĒŠė ėė ", - "kept_this_deleted_others": "ė´ ėė°ė ė ė§íęŗ {count, plural, one {# asset} other {# assets}}ė ėė íėĩëë¤", + "keep_this_delete_others": "ė´ íëĒŠė ė ė§íęŗ ë머ė§ë ėė ", + "kept_this_deleted_others": "ė´ íëĒŠė ė ė§íęŗ {count, plural, one {#ę°ė íëĒŠ} other {#ę°ė íëĒŠ}}ė ėė íėĩëë¤.", "keyboard_shortcuts": "í¤ëŗ´ë ë¨ėļí¤", "language": "ė¸ė´", "language_setting_description": "ė í¸íë ė¸ė´ ė í", @@ -1118,9 +1123,9 @@ "loading": "ëĄë ė¤", "loading_search_results_failed": "ę˛ė 결ęŗŧ ëĄë ė¤í¨", "local_network": "Local network", - "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", - "location_permission": "Location permission", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", + "local_network_sheet_info": "ė§ė í Wi-Fiė ė°ę˛°ë ę˛Ŋė° ėąė í´ëš URLė íĩí´ ėë˛ė ė°ę˛°íŠëë¤.", + "location_permission": "ėėš ęļí", + "location_permission_content": "ėë ė í 기ëĨė ėŦėŠíë ¤ëŠ´ Immichę° íėŦ Wi-Fi ë¤í¸ėíŦ ė´ëĻė íė¸í기 ėí 'ė íí ėėš' ęļíė´ íėíŠëë¤.", "location_picker_choose_on_map": "ė§ëėė ė í", "location_picker_latitude_error": "ė í¨í ėëëĨŧ ė ë Ĩíė¸ė.", "location_picker_latitude_hint": "ė´ęŗŗė ėë ė ë Ĩ", @@ -1140,11 +1145,11 @@ "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_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_failed_login": "ëĄęˇ¸ė¸ ė¤ ė¤ëĨę° ë°ėíėĩëë¤. ėë˛ URL, ė´ëŠėŧ, ëšë°ë˛í¸ëĨŧ íė¸íė¸ė.", "login_form_handshake_exception": "ėë˛ė íĩė ė¤ ė¸ėĻė ėė¸ę° ë°ėíėĩëë¤. ė랴 ėëĒ ë ė¸ėĻėëĨŧ ėŦėŠ ė¤ė´ëŧ늴, ė¤ė ėė ė랴 ėëĒ ë ė¸ėĻė íėŠė íėąííė¸ė.", "login_form_password_hint": "ëšë°ë˛í¸", "login_form_save_login": "ëĄęˇ¸ė¸ ė ė§", @@ -1152,7 +1157,7 @@ "login_form_server_error": "ėë˛ė ė°ę˛°í ė ėėĩëë¤.", "login_has_been_disabled": "ëĄęˇ¸ė¸ė´ ëšíėąíëėėĩëë¤.", "login_password_changed_error": "ëšë°ë˛í¸ëĨŧ ëŗę˛Ŋíë ė¤ ëŦ¸ė ę° ë°ėíėĩëë¤.", - "login_password_changed_success": "ëšë°ë˛í¸ę° ëŗę˛Ŋëėėĩëë¤.", + "login_password_changed_success": "ëšë°ë˛í¸ę° ėąęŗĩė ėŧëĄ ëŗę˛Ŋëėėĩëë¤.", "logout_all_device_confirmation": "ëǍë 기기ėė ëĄęˇ¸ėėíėę˛ ėĩëęš?", "logout_this_device_confirmation": "ė´ ę¸°ę¸°ėė ëĄęˇ¸ėėíėę˛ ėĩëęš?", "longitude": "ę˛Ŋë", @@ -1172,7 +1177,7 @@ "map": "ė§ë", "map_assets_in_bound": "ėŦė§ {}ę°", "map_assets_in_bounds": "ėŦė§ {}ę°", - "map_cannot_get_user_location": "ėŦėŠėė ėėšëĨŧ ëļëŦėŦ ė ėėĩëë¤.", + "map_cannot_get_user_location": "ėŦėŠėė ėėšëĨŧ ę°ė ¸ėŦ ė ėėĩëë¤.", "map_location_dialog_yes": "ė", "map_location_picker_page_use_location": "ė´ ėėš ėŦėŠ", "map_location_service_disabled_content": "íėŦ ėėšė íëĒŠė íėíë ¤ëŠ´ ėėš ėëšė¤ëĨŧ íėąíí´ėŧ íŠëë¤. ė§ę¸ íėąííėę˛ ėĩëęš?", @@ -1227,8 +1232,8 @@ "my_albums": "ë´ ė¨ë˛", "name": "ė´ëĻ", "name_or_nickname": "ė´ëĻ ëë ëë¤ė", - "networking_settings": "Networking", - "networking_subtitle": "Manage the server endpoint settings", + "networking_settings": "ë¤í¸ėíš", + "networking_subtitle": "ėë˛ ėëíŦė¸í¸ ė¤ė ę´ëĻŦ", "never": "ėė", "new_album": "ė ė¨ë˛", "new_api_key": "API í¤ ėėą", @@ -1257,7 +1262,7 @@ "no_results_description": "ëėė´ ëë ë ėŧë°ė ė¸ ë¨ė´ëĨŧ ėŦėŠí´ ëŗ´ė¸ė.", "no_shared_albums_message": "ęŗĩė ė¨ë˛ė ë§ë¤ė´ ėŖŧëŗ ėŦëë¤ęŗŧ ėŦė§ ë° ëėė ęŗĩė ", "not_in_any_album": "ė¨ë˛ė ėė", - "not_selected": "Not selected", + "not_selected": "ė íëė§ ėė", "note_apply_storage_label_to_previously_uploaded assets": "ė°¸ęŗ : ė´ė ė ė ëĄëí íëĒŠėë ė¤í ëĻŦė§ ë ė´ë¸ė ė ėŠíë ¤ëŠ´ ë¤ėė ė¤ííŠëë¤,", "notes": "ė°¸ęŗ ", "notification_permission_dialog_content": "ėëĻŧė íėąííë ¤ëŠ´ ė¤ė ėė ėëĻŧ ęļíė íėŠíė¸ė.", @@ -1282,6 +1287,7 @@ "onboarding_welcome_user": "{user}ë, íėíŠëë¤", "online": "ė¨ëŧė¸", "only_favorites": "ėĻę˛¨ė°žę¸°ë§", + "open": "ė´ę¸°", "open_in_map_view": "ė§ë ëŗ´ę¸°ėė ė´ę¸°", "open_in_openstreetmap": "OpenStreetMapėė ė´ę¸°", "open_the_search_filters": "ę˛ė íí° ė´ę¸°", @@ -1371,7 +1377,7 @@ "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_server_up_to_date": "í´ëŧė´ė¸í¸ė ėë˛ę° ėĩė ėíė ëë¤.", "profile_drawer_github": "Github", "profile_drawer_server_out_of_date_major": "ėë˛ ë˛ė ė´ ėĩė ė´ ėëëë¤. ėĩė ë˛ė ėŧëĄ ė ë°ė´í¸íė¸ė.", "profile_drawer_server_out_of_date_minor": "ėë˛ ë˛ė ė´ ėĩė ė´ ėëëë¤. ėĩė ë˛ė ėŧëĄ ė ë°ė´í¸íė¸ė.", @@ -1426,6 +1432,7 @@ "recent_searches": "ėĩęˇŧ ę˛ė", "recently_added": "ėĩęˇŧ ėļę°", "recently_added_page_title": "ėĩęˇŧ ėļę°", + "recently_taken": "ėĩęˇŧ ė´Ŧėë¨", "refresh": "ėëĄęŗ ėš¨", "refresh_encoded_videos": "ëėė ėŦė¸ėŊëŠ", "refresh_faces": "ėŧęĩ´ ėëĄęŗ ėš¨", @@ -1447,15 +1454,15 @@ "remove_from_favorites": "ėĻę˛¨ė°žę¸°ėė ė ęą°", "remove_from_shared_link": "ęŗĩė ë§íŦėė ė ęą°", "remove_memory": "ėļėĩ ė ęą°", - "remove_photo_from_memory": "ė´ ėļėĩėė ėŦė§ ė ęą°", + "remove_photo_from_memory": "ėļėĩėė ėŦė§ ė ęą°", "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_memory": "ėļėĩė´ ė ęą°ëėėĩëë¤.", + "removed_photo_from_memory": "ėļėĩėė ėŦė§ė ė ęą°íėĩëë¤.", "removed_tagged_assets": "íëĒŠ {count, plural, one {#ę°} other {#ę°}}ėė í꡸ëĨŧ ė ęą°í¨", "rename": "ė´ëĻ ë°ęž¸ę¸°", "repair": "ėëĻŦ", @@ -1496,7 +1503,7 @@ "search_albums": "ė¨ë˛ ę˛ė", "search_by_context": "ë´ėŠ ę˛ė", "search_by_description": "ė¤ëĒ ėŧëĄ ę˛ė", - "search_by_description_example": "ėŦíėė ėĻ기ë íė´íš", + "search_by_description_example": "ė¤ė ė°ėė ėĻ기ë íė´íš", "search_by_filename": "íėŧëĒ ëë íėĨėëĄ ę˛ė", "search_by_filename_example": "ėė: IMG_1234.JPG or PNG", "search_camera_make": "ėš´ëŠëŧ ė ėĄ°ėŦ ę˛ė...", @@ -1510,7 +1517,7 @@ "search_filter_date_title": "ë ė§ ë˛ė ė í", "search_filter_display_option_not_in_album": "ė¨ë˛ė ėė", "search_filter_display_options": "íė ėĩė ", - "search_filter_filename": "Search by file name", + "search_filter_filename": "íėŧëĒ ėŧëĄ ę˛ė", "search_filter_location": "ėėš", "search_filter_location_title": "ėėš ė í", "search_filter_media_type": "미ëė´ ėĸ ëĨ", @@ -1518,7 +1525,7 @@ "search_filter_people_title": "ė¸ëŦŧ ė í", "search_for": "ę˛ė", "search_for_existing_person": "ėĄ´ėŦíë ė¸ëŦŧ ę˛ė", - "search_no_more_result": "No more results", + "search_no_more_result": "ëė´ė 결ęŗŧ ėė", "search_no_people": "ė¸ëŦŧė´ ėėĩëë¤.", "search_no_people_named": "\"{name}\" ė¸ëŦŧė ė°žė ė ėė", "search_no_result": "No results found, try a different search term or combination", @@ -1528,7 +1535,7 @@ "search_page_no_objects": "ėŦėŠ ę°ëĨí ėŦëŦŧ ė ëŗ´ ėė", "search_page_no_places": "ėŦėŠ ę°ëĨí ėėš ė ëŗ´ ėė", "search_page_screenshots": "ė¤íŦëϰėˇ", - "search_page_search_photos_videos": "Search for your photos and videos", + "search_page_search_photos_videos": "ėŦė§ ë° ëėėė ę˛ėíė¸ė", "search_page_selfies": "ė íŧ", "search_page_things": "ėŦëŦŧ", "search_page_view_all_button": "ëǍë ëŗ´ę¸°", @@ -1540,7 +1547,7 @@ "search_result_page_new_search_hint": "ė ę˛ė", "search_settings": "ė¤ė ę˛ė", "search_state": "ė§ė ę˛ė...", - "search_suggestion_list_smart_search_hint_1": "ė¤ë§í¸ ę˛ėė´ ę¸°ëŗ¸ė ėŧëĄ íėąíëė´ ėėĩëë¤. ëŠíë°ė´í°ëĄ ę˛ėíë ¤ëŠ´ ë¤ė ęĩŦëŦ¸ė ėŦėŠíė¸ė.", + "search_suggestion_list_smart_search_hint_1": "ė¤ë§í¸ ę˛ėė´ ę¸°ëŗ¸ė ėŧëĄ íėąíëė´ ėėĩëë¤. ëŠíë°ė´í°ëĄ ę˛ėíë ¤ëŠ´ ë¤ėė ėŦėŠíė¸ė. ", "search_suggestion_list_smart_search_hint_2": "m:your-search-term", "search_tags": "íęˇ¸ëĄ ę˛ė...", "search_timezone": "ėę°ë ę˛ė...", @@ -1564,10 +1571,10 @@ "select_trash_all": "ëǍë ėė ", "select_user_for_sharing_page_err_album": "ė¨ë˛ė ėėąíė§ ëĒģíėĩëë¤.", "selected": "ė íë¨", - "selected_count": "{count, plural, other {#ę°}} íëĒŠ ė íë¨", + "selected_count": "{count, plural, other {#ę°}} ė íë¨", "send_message": "ëŠėė§ ė ėĄ", "send_welcome_email": "íė ė´ëŠėŧ ė ėĄ", - "server_endpoint": "Server Endpoint", + "server_endpoint": "ėë˛ ėëíŦė¸í¸", "server_info_box_app_version": "ėą ë˛ė ", "server_info_box_server_url": "ėë˛ URL", "server_offline": "ė¤íëŧė¸", @@ -1576,7 +1583,7 @@ "server_version": "ėë˛ ë˛ė ", "set": "ė¤ė ", "set_as_album_cover": "ė¨ë˛ ėģ¤ë˛ëĄ ė¤ė ", - "set_as_featured_photo": "ėļė˛ ėŦė§ėŧëĄ ė¤ė ", + "set_as_featured_photo": "ëí ėŦė§ėŧëĄ ė¤ė ", "set_as_profile_picture": "íëĄí ėŦė§ėŧëĄ ė¤ė ", "set_date_of_birth": "ėë ėėŧ ė¤ė ", "set_profile_picture": "íëĄí ėŦė§ėŧëĄ ė¤ė ", @@ -1588,7 +1595,7 @@ "setting_image_viewer_preview_title": "미ëĻŦ ëŗ´ę¸° ė´ë¯¸ė§ ëļëŦė¤ę¸°", "setting_image_viewer_title": "ė´ë¯¸ė§", "setting_languages_apply": "ė ėŠ", - "setting_languages_subtitle": "Change the app's language", + "setting_languages_subtitle": "ėą ė¸ė´ ëŗę˛Ŋ", "setting_languages_title": "ė¸ė´", "setting_notifications_notify_failures_grace_period": "밹꡸ëŧė´ë ë°ąė ė¤í¨ ėëĻŧ: {}", "setting_notifications_notify_hours": "{}ėę° í", @@ -1603,13 +1610,13 @@ "setting_notifications_total_progress_title": "밹꡸ëŧė´ë ë°ąė ė 랴 ė§íëĨ íė", "setting_video_viewer_looping_title": "ë°ëŗĩ", "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", + "setting_video_viewer_original_video_title": "ėëŗ¸ ëėė ę°ė ėŦėŠ", "settings": "ė¤ė ", "settings_require_restart": "ė¤ė ė ė ėŠíë ¤ëŠ´ ImmichëĨŧ ë¤ė ėėíė¸ė.", "settings_saved": "ė¤ė ė´ ė ėĨëėėĩëë¤.", "share": "ęŗĩė ", "share_add_photos": "ėŦė§ ėļę°", - "share_assets_selected": "{}ę° íëĒŠ ė íë¨", + "share_assets_selected": "{}ę° ė íë¨", "share_dialog_preparing": "ė¤ëš ė¤...", "shared": "ęŗĩė ë¨", "shared_album_activities_input_disable": "ëę¸ė´ ëšíėąíëėėĩëë¤", @@ -1623,7 +1630,7 @@ "shared_by_user": "{user}ëė´ ęŗĩė í¨", "shared_by_you": "ë´ę° ęŗĩė í¨", "shared_from_partner": "{partner}ëė ėŦė§", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", + "shared_intent_upload_button_progress_text": "{} / {} ė ëĄëë¨", "shared_link_app_bar_title": "ęŗĩė ë§íŦ", "shared_link_clipboard_copied_massage": "í´ëĻŊëŗ´ëė ëŗĩėŦëėėĩëë¤.", "shared_link_clipboard_text": "ë§íŦ: {}\nëšë°ë˛í¸: {}", @@ -1703,7 +1710,7 @@ "sort_items": "íëĒŠ ė", "sort_modified": "ėė ë ë ė§", "sort_oldest": "ė¤ëë ėŦė§", - "sort_people_by_similarity": "ė ėŦėąė 기ė¤ėŧëĄ ėŦë ė ë Ŧ", + "sort_people_by_similarity": "ė ėŦėąė 기ė¤ėŧëĄ ė¸ëŦŧ ė ë Ŧ", "sort_recent": "ėĩęˇŧ ėŦė§", "sort_title": "ė ëĒŠ", "source": "ėė¤", @@ -1740,7 +1747,7 @@ "tag_created": "í꡸ ėėąë¨: {tag}", "tag_feature_description": "ėŦė§ ë° ëėėė ėŖŧė ëŗ ęˇ¸ëŖšíë íęˇ¸ëĄ íė", "tag_not_found_question": "í꡸ëĨŧ ė°žė ė ėëė? <link>ė í꡸ëĨŧ ėėąíė¸ė.</link>", - "tag_people": "ėŦë í꡸", + "tag_people": "ė¸ëŦŧ í꡸", "tag_updated": "í꡸ ė ë°ė´í¸ë¨: {tag}", "tagged_assets": "íëĒŠ {count, plural, one {#ę°} other {#ę°}}ė í꡸ëĨŧ ė ėŠí¨", "tags": "í꡸", @@ -1754,12 +1761,12 @@ "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_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_subtitle": "3ë¨ęŗ ëĄëŠė ëĄë ėąëĨė íĨėėíŦ ė ėėŧë, ë¤í¸ėíŦ ëļíę° íŦę˛ ėĻę°í ė ėėĩëë¤.", "theme_setting_three_stage_loading_title": "3ë¨ęŗ ëĄë íėąí", "they_will_be_merged_together": "ė íí ė¸ëŦŧë¤ė´ ëŗíŠëŠëë¤.", "third_party_resources": "ėë íí° ëĻŦėė¤", @@ -1802,7 +1809,7 @@ "unlink_motion_video": "ëǍė ëšëė¤ ë§íŦ í´ė ", "unlink_oauth": "OAuth ė°ę˛° í´ė ", "unlinked_oauth_account": "OAuth ęŗė ė°ę˛°ė´ í´ė ëėėĩëë¤.", - "unmute_memories": "ėļėĩ ėėęą° í´ė ", + "unmute_memories": "ėėęą° í´ė ", "unnamed_album": "ė´ëĻ ėë ė¨ë˛", "unnamed_album_delete_confirmation": "ė í í ė¨ë˛ė ėė íėę˛ ėĩëęš?", "unnamed_share": "ė´ëĻ ėë ęŗĩė ", @@ -1826,11 +1833,11 @@ "upload_status_errors": "ė¤ëĨ", "upload_status_uploaded": "ėëŖ", "upload_success": "ė ëĄëę° ėëŖëėėĩëë¤. ė ëĄëë íëĒŠė ëŗ´ë ¤ëŠ´ íė´ė§ëĨŧ ėëĄęŗ ėš¨íė¸ė.", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", + "upload_to_immich": "Immichė ė ëĄë ({})", + "uploading": "ė ëĄë ė¤", "url": "URL", "usage": "ėŦėŠë", - "use_current_connection": "use current connection", + "use_current_connection": "íėŦ ë¤í¸ėíŦ ėŦėŠ", "use_custom_date_range": "ëė ë§ėļ¤ ę¸°ę° ėŦėŠ", "user": "ėŦėŠė", "user_id": "ėŦėŠė ID", @@ -1845,15 +1852,15 @@ "users": "ėŦėŠė", "utilities": "ëęĩŦ", "validate": "ę˛ėĻ", - "validate_endpoint_error": "Please enter a valid URL", + "validate_endpoint_error": "ė í¨í URLė ė ë Ĩíė¸ė.", "variables": "ëŗė", "version": "ë˛ė ", "version_announcement_closing": "ëšė ė ėšęĩŦ, Alexę°", "version_announcement_message": "ėë íė¸ė! ė ë˛ė ė ImmichëĨŧ ėŦėŠí ė ėėĩëë¤. ėëĒģë ęĩŦėąė ë°Šė§íęŗ ImmichëĨŧ ėĩė ėíëĄ ė ė§í기 ėí´ ė ė ėę°ė ë´ė´ <link>ëĻ´ëĻŦė¤ ë ¸í¸</link>ëĨŧ ėŊė´ëŗ´ë ę˛ė ęļėĨíŠëë¤. íší WatchTower ëąė ėë ė ë°ė´í¸ 기ëĨė ėŦėŠíë ę˛Ŋė° ėëíė§ ėė ëėė ë°Šė§í기 ėí´ ëëėą ęļėĨëŠëë¤.", "version_announcement_overlay_release_notes": "ëĻ´ëĻŦė¤ ë ¸í¸", "version_announcement_overlay_text_1": "ėë íė¸ė,", - "version_announcement_overlay_text_2": "ė ë˛ė ė ImmichëĨŧ ėŦėŠí ė ėėĩëë¤.", - "version_announcement_overlay_text_3": "WatchTower ëąė ėë ė ë°ė´í¸ 기ëĨė ėŦėŠíë ę˛Ŋė° ėëíė§ ėė ëėė ë°Šė§í기 ėí´ docker-compose.yml ë° .env ęĩŦėąė´ ėĩė ė¸ė§ íė¸íė¸ė.", + "version_announcement_overlay_text_2": "ė ë˛ė ė ImmichëĨŧ ėŦėŠí ė ėėĩëë¤. ", + "version_announcement_overlay_text_3": " WatchTower ëąė ėë ė ë°ė´í¸ 기ëĨė ėŦėŠíë ę˛Ŋė° ėëíė§ ėė ëėė ë°Šė§í기 ėí´ docker-compose.yml ë° .env ęĩŦėąė´ ėĩė ė¸ė§ íė¸íė¸ė.", "version_announcement_overlay_title": "ė ėë˛ ë˛ė ėŦėŠ ę°ëĨ đ", "version_history": "ë˛ė 기ëĄ", "version_history_item": "{date} ë˛ė {version} ė¤ėš", @@ -1888,6 +1895,6 @@ "years_ago": "{years, plural, one {#ë } other {#ë }} ė ", "yes": "ë¤", "you_dont_have_any_shared_links": "ėėąí ęŗĩė ë§íŦę° ėėĩëë¤.", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "Wi-Fi ë¤í¸ėíŦ ė´ëĻ", "zoom_image": "ė´ë¯¸ė§ íë" } diff --git a/i18n/lv.json b/i18n/lv.json index fbefc9a521..c9002b559c 100644 --- a/i18n/lv.json +++ b/i18n/lv.json @@ -401,11 +401,11 @@ "backup_controller_page_background_turn_on": "IeslÄgt fona pakalpojumu", "backup_controller_page_background_wifi": "Tikai WiFi tÄĢklÄ", "backup_controller_page_backup": "DublÄÅĄana", - "backup_controller_page_backup_selected": "AtlasÄĢts:", + "backup_controller_page_backup_selected": "AtlasÄĢts: ", "backup_controller_page_backup_sub": "DublÄtie FotoattÄli un videoklipi", "backup_controller_page_created": "Izveidots: {}", "backup_controller_page_desc_backup": "IeslÄdziet priekÅĄplÄna dublÄÅĄanu, lai, atverot programmu, serverÄĢ automÄtiski augÅĄupielÄdÄtu jaunus aktÄĢvus.", - "backup_controller_page_excluded": "IzÅemot:", + "backup_controller_page_excluded": "IzÅemot: ", "backup_controller_page_failed": "NeizdevÄs ({})", "backup_controller_page_filename": "Faila nosaukums: {} [{}]", "backup_controller_page_id": "ID: {}", diff --git a/i18n/nb_NO.json b/i18n/nb_NO.json index df2fba7517..0995b81cc5 100644 --- a/i18n/nb_NO.json +++ b/i18n/nb_NO.json @@ -39,11 +39,11 @@ "authentication_settings_disable_all": "Er du sikker pÃĨ at du ønsker ÃĨ deaktivere alle innloggingsmetoder? Innlogging vil bli fullstendig deaktivert.", "authentication_settings_reenable": "For ÃĨ aktivere pÃĨ nytt, bruk en <link>Server Command</link>.", "background_task_job": "Bakgrunnsjobber", - "backup_database": "Backupdatabase", - "backup_database_enable_description": "Aktiver databasebackup", - "backup_keep_last_amount": "Antall backuper ÃĨ beholde", - "backup_settings": "Backupinnstillinger", - "backup_settings_description": "HÃĨndter innstillinger for databasebackup", + "backup_database": "Opprett database-dump", + "backup_database_enable_description": "Aktiver database-dump", + "backup_keep_last_amount": "Antall database-dumps ÃĨ beholde", + "backup_settings": "Database-dump instillinger", + "backup_settings_description": "HÃĨndter innstillinger for database-dump. Merk: Disse jobbene overvÃĨkes ikke, og du vil ikke bli varslet ved feil.", "check_all": "Merk Alle", "cleanup": "Opprydding", "cleared_jobs": "Ryddet opp jobber for: {job}", @@ -85,7 +85,7 @@ "image_quality": "Kvalitet", "image_resolution": "Oppløsning", "image_resolution_description": "Høyere oppløsninger kan bevare flere detaljer, men det tar lengre tid ÃĨ kode, har større filstørrelser og kan redusere appresponsen.", - "image_settings": "Bildeinnstilliinger", + "image_settings": "Bildeinnstillinger", "image_settings_description": "Administrer kvalitet og oppløsning pÃĨ genererte bilder", "image_thumbnail_description": "SmÃĨ miniatyrbilder med strippet metadata, brukt nÃĨr du ser pÃĨ grupper av bilder som hovedtidslinjen", "image_thumbnail_quality_description": "Miniatyrbildekvalitet fra 1-100. Høyere er bedre, men produserer større filer og kan redusere appens respons.", @@ -371,6 +371,8 @@ "admin_password": "Administrator Passord", "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.", + "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTELT] Bruk alternativ enhet album synk filter", "advanced_settings_log_level_title": "LoggnivÃĨ: {}", "advanced_settings_prefer_remote_subtitle": "Noen enheter er veldige trege til ÃĨ hente mikrobilder fra enheten. Aktiver denne innstillingen for ÃĨ hente de eksternt istedenfor.", "advanced_settings_prefer_remote_title": "Foretrekk eksterne bilder", @@ -378,6 +380,8 @@ "advanced_settings_proxy_headers_title": "Proxy headere", "advanced_settings_self_signed_ssl_subtitle": "Hopper over SSL sertifikatverifikasjon for server-endepunkt. PÃĨkrevet for selvsignerte sertifikater.", "advanced_settings_self_signed_ssl_title": "Tillat selvsignerte SSL sertifikater", + "advanced_settings_sync_remote_deletions_subtitle": "Automatisk slette eller gjenopprette filer pÃĨ denne enheten hvis den handlingen har blitt gjort pÃĨ nettsiden", + "advanced_settings_sync_remote_deletions_title": "Synk sletting fra nettsiden [EKSPERIMENTELT]", "advanced_settings_tile_subtitle": "Avanserte brukerinnstillinger", "advanced_settings_troubleshooting_subtitle": "Aktiver ekstra funksjoner for feilsøking", "advanced_settings_troubleshooting_title": "Feilsøking", @@ -529,11 +533,11 @@ "backup_controller_page_background_turn_on": "Skru pÃĨ bakgrunnstjenesten", "backup_controller_page_background_wifi": "Kun pÃĨ WiFi", "backup_controller_page_backup": "Sikkerhetskopier", - "backup_controller_page_backup_selected": "Valgte:", + "backup_controller_page_backup_selected": "Valgte: ", "backup_controller_page_backup_sub": "Opplastede bilder og videoer", "backup_controller_page_created": "Opprettet: {}", "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_excluded": "Ekskludert:", + "backup_controller_page_excluded": "Ekskludert: ", "backup_controller_page_failed": "Feilet ({})", "backup_controller_page_filename": "Filnavn: {} [{}]", "backup_controller_page_id": "ID: {}", @@ -992,6 +996,7 @@ "filetype": "Filtype", "filter": "Filter", "filter_people": "Filtrer personer", + "filter_places": "Filtrer steder", "find_them_fast": "Finn dem raskt ved søking av navn", "fix_incorrect_match": "Fiks feilaktig match", "folder": "Folder", @@ -1282,6 +1287,7 @@ "onboarding_welcome_user": "Velkommen, {user}", "online": "Tilkoblet", "only_favorites": "Bare favoritter", + "open": "à pne", "open_in_map_view": "à pne i kartvisning", "open_in_openstreetmap": "à pne i OpenStreetMap", "open_the_search_filters": "à pne søkefiltrene", @@ -1426,6 +1432,8 @@ "recent_searches": "Nylige søk", "recently_added": "Nylig lagt til", "recently_added_page_title": "Nylig lagt til", + "recently_taken": "Nylig tatt", + "recently_taken_page_title": "Nylig tatt", "refresh": "Oppdater", "refresh_encoded_videos": "Oppdater kodete videoer", "refresh_faces": "Oppdater ansikter", diff --git a/i18n/nl.json b/i18n/nl.json index 609d2add5c..99fb554d5a 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -371,6 +371,8 @@ "admin_password": "Beheerder wachtwoord", "administration": "Beheer", "advanced": "Geavanceerd", + "advanced_settings_enable_alternate_media_filter_subtitle": "Gebruik deze optie om media te filteren tijdens de synchronisatie op basis van alternatieve criteria. Gebruik dit enkel als de app problemen heeft met het detecteren van albums.", + "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTEEL] Gebruik een alternatieve album synchronisatie filter", "advanced_settings_log_level_title": "Log niveau: {}", "advanced_settings_prefer_remote_subtitle": "Sommige apparaten zijn traag met het laden van afbeeldingen die lokaal zijn opgeslagen op het apparaat. Activeer deze instelling om in plaats daarvan externe afbeeldingen te laden.", "advanced_settings_prefer_remote_title": "Externe afbeeldingen laden", @@ -378,6 +380,7 @@ "advanced_settings_proxy_headers_title": "Proxy headers", "advanced_settings_self_signed_ssl_subtitle": "Slaat SSL-certificaatverificatie voor de connectie met de server over. Deze optie is vereist voor zelfondertekende certificaten", "advanced_settings_self_signed_ssl_title": "Zelfondertekende SSL-certificaten toestaan", + "advanced_settings_sync_remote_deletions_title": "Synchroniseer verwijderingen op afstand [EXPERIMENTEEL]", "advanced_settings_tile_subtitle": "Geavanceerde gebruikersinstellingen", "advanced_settings_troubleshooting_subtitle": "Schakel extra functies voor probleemoplossing in ", "advanced_settings_troubleshooting_title": "Probleemoplossing", diff --git a/i18n/nn.json b/i18n/nn.json index 6de0f3401b..5fd9caa232 100644 --- a/i18n/nn.json +++ b/i18n/nn.json @@ -2,8 +2,9 @@ "about": "Om", "account": "Konto", "account_settings": "Kontoinnstillingar", - "acknowledge": "Bekreft", + "acknowledge": "Merk som lese", "action": "Handling", + "action_common_update": "Oppdater", "actions": "Handlingar", "active": "Aktive", "activity": "Aktivitet", @@ -121,7 +122,7 @@ "machine_learning_max_detection_distance_description": "Den største skilnaden mellom to bilete for ÃĨ rekne dei som duplikat, frÃĨ 0.001-0.1. Større verdiar finn fleire duplikat, men kan gje falske treff.", "machine_learning_max_recognition_distance": "Maksimal attkjenningsverdi", "machine_learning_min_detection_score": "Minimum deteksjonsresultat", - "machine_learning_min_detection_score_description": "Minimum tillitspoeng for at eit ansikt skal bli oppdaga, pÃĨ ein skala frÃĨ 0-1. LÃĨgare verdiar vil oppdaga fleire ansikt, men kan føre til falske positive", + "machine_learning_min_detection_score_description": "Minimum tillitspoeng for at eit ansikt skal bli oppdaga, pÃĨ ein skala frÃĨ 0 til 1. LÃĨgare verdiar vil oppdage fleire ansikt, men kan føre til feilaktige treff.", "machine_learning_min_recognized_faces": "Minimum gjenkjende ansikt", "machine_learning_settings": "Innstillingar for maskinlÃĻring", "machine_learning_settings_description": "Administrer maskinlÃĻringsfunksjonar og innstillingar", @@ -200,14 +201,45 @@ "backward": "Bakover", "camera": "Kamera", "cancel": "Avbryt", + "change_password_form_confirm_password": "Stadfest passord", "city": "By", - "clear": "Fjern", + "clear": "Tøm", + "clear_all": "Tøm alt", + "clear_all_recent_searches": "Tøm alle nylige søk", + "clear_message": "Tøm melding", + "clear_value": "Tøm verdi", + "client_cert_dialog_msg_confirm": "OK", + "client_cert_enter_password": "Oppgi passord", + "client_cert_import": "Importer", + "client_cert_import_success_msg": "Klientsertifikat vart importert", + "client_cert_invalid_msg": "Ugyldig sertifikatfil eller feil passord", + "client_cert_remove_msg": "Klientsertifikat er fjerna", + "client_cert_subtitle": "Støttar berre PKCS12-formatet (.p12, .pfx). Import og fjerning av sertifikat er berre tilgjengeleg før innlogging", + "client_cert_title": "SSL-klientsertifikat", "clockwise": "Med klokka", "close": "Lukk", + "collapse": "Gøym", + "collapse_all": "Gøym alle", "color": "Farge", - "confirm": "Bekreft", - "contain": "Inneheld", + "color_theme": "Fargetema", + "comment_deleted": "Kommentar vart sletta", + "comment_options": "Kommentarval", + "comments_and_likes": "Kommentarar og likerklikk", + "comments_are_disabled": "Kommentering er slÃĨtt av", + "common_create_new_album": "Lag nytt album", + "common_server_error": "Kontroller nettverkstilkoplinga di, sørg for at tenaren er tilgjengeleg, og at app- og tenarversjonane er kompatible.", + "completed": "Fullført", + "confirm": "Stadfest", + "confirm_admin_password": "Stadfest administratorpassord", + "confirm_delete_face": "Er du sikker pÃĨ at du vil slette {name} sitt ansikt frÃĨ ressursen?", + "confirm_delete_shared_link": "Er du sikker pÃĨ at du vil slette denne delte lenka?", + "confirm_keep_this_delete_others": "Alle andre ressursar i bunken vil bli sletta, bortsett frÃĨ denne. Er du sikker pÃĨ at du vil halde fram?", + "confirm_password": "Stadfest passord", + "contain": "Tilpass til vindauget", + "context": "Samanheng", "continue": "Hald fram", + "control_bottom_app_bar_album_info_shared": "{} element ¡ Delt", + "control_bottom_app_bar_create_new_album": "Lag nytt album", "country": "Land", "cover": "Dekk", "covers": "Dekker", @@ -313,21 +345,154 @@ "purchase_server_title": "Server", "reassign": "Vel pÃĨ nytt", "recent": "Nyleg", - "refresh": "Oppdater", + "refresh": "Last inn pÃĨ nytt", + "refresh_encoded_videos": "Oppfrisk ferdigbehandla videoa", + "refresh_faces": "Oppfrisk ansikt", + "refresh_metadata": "Oppfrisk metadata", + "refresh_thumbnails": "Oppfrisk miniatyrbilete", "refreshed": "Oppdatert", + "refreshes_every_file": "Les alle eksisterande og nye filer pÃĨ nytt", + "refreshing_encoded_video": "Lastar inn ferdigbehandla video pÃĨ nytt", + "refreshing_faces": "Oppfriskar ansiktsdata", + "refreshing_metadata": "Oppfriskar metadata", + "regenerating_thumbnails": "Regenererer miniatyrbilete", "remove": "Fjern", - "rename": "Endre namn", - "repair": "Reparasjon", + "remove_assets_album_confirmation": "Er du sikker pÃĨ at du vil fjerne {count, plural, one {# asset} other {# assets}} fra albumet?", + "remove_assets_shared_link_confirmation": "Er du sikker pÃĨ at du vil fjerne {count, plural, one {# asset} other {# assets}} frÃĨ denne delte lenka?", + "remove_assets_title": "Fjern ressursar?", + "remove_custom_date_range": "Fjern egendefinert datoperiode", + "remove_deleted_assets": "Fjern sletta ressursar", + "remove_from_album": "Fjern frÃĨ album", + "remove_from_favorites": "Fjern frÃĨ favorittar", + "remove_from_shared_link": "Fjern frÃĨ delt lenke", + "remove_memory": "Fjern minne", + "remove_photo_from_memory": "Fjern bilete frÃĨ dette minne", + "remove_url": "Fjern URL", + "remove_user": "Fjern brukar", + "removed_api_key": "Fjerna API-nøkkel: {name}", + "removed_from_archive": "Fjerna frÃĨ arkiv", + "removed_from_favorites": "Fjerna frÃĨ favorittar", + "removed_from_favorites_count": "{count, plural, other {Fjerna #}} frÃĨ favorittar", + "removed_memory": "Fjerna minne", + "removed_photo_from_memory": "Fjerna bilete frÃĨ minne", + "removed_tagged_assets": "Fjerna tagg frÃĨ {count, plural, one {# ressurs} other {# ressursar}}", + "rename": "Gi nytt namn", + "repair": "Reparer", + "repair_no_results_message": "Uspora og manglande filer vil visast her", + "replace_with_upload": "Erstatt med opplasting", + "repository": "Lager", + "require_password": "Krev passord", + "require_user_to_change_password_on_first_login": "Krev at brukaren endrar passord ved første innlogging", + "rescan": "Skann pÃĨ nytt", "reset": "Tilbakestill", - "restore": "Tilbakestill", - "resume": "Fortsett", + "reset_password": "Tilbakestill passord", + "reset_people_visibility": "Tilbakestill synlegheit for personar", + "reset_to_default": "Tilbakestill til standard", + "resolve_duplicates": "Handter duplikat", + "resolved_all_duplicates": "Alle duplikat er handterte", + "restore": "Gjenopprett", + "restore_all": "Gjenopprett alle", + "restore_user": "Gjenopprett brukar", + "restored_asset": "Ressurs gjenoppretta", + "resume": "Gjenoppta", + "retry_upload": "Prøv opplasting pÃĨ nytt", + "review_duplicates": "GÃĨ gjennom duplikat", "role": "Rolle", + "role_editor": "Redaktør", + "role_viewer": "Observatør", "save": "Lagre", + "save_to_gallery": "Lagre til galleri", + "saved_api_key": "API-nøkkel lagra", + "saved_profile": "Profil lagra", + "saved_settings": "Innstillingar lagra", + "say_something": "Skriv ein kommentar", + "scaffold_body_error_occurred": "Det oppstod ein feil", + "scan_all_libraries": "Skann gjennom alle bibliotek", "scan_library": "Skann", + "scan_settings": "Skann innstillingar", + "scanning_for_album": "Skanning for album...", "search": "Søk", + "search_albums": "Søk album", + "search_by_context": "Søk etter samanheng", + "search_by_description": "Søk etter beskrivelse", + "search_by_description_example": "Søndagstur med kvikklunsj", + "search_by_filename": "Søk etter filnamn eller filformat", + "search_by_filename_example": "t.d. IMG_1234.JPG eller PNG", + "search_camera_make": "Søk etter kamera produsent...", + "search_camera_model": "Søk etter kamera modell...", + "search_city": "Søk etter by...", + "search_country": "Søk etter land...", + "search_filter_apply": "Bruk filter", + "search_filter_camera_title": "Vel kameratype", + "search_filter_date": "Dato", + "search_filter_date_interval": "{start} til {end}", + "search_filter_date_title": "Vel eit datointervall", + "search_filter_display_option_not_in_album": "Ikkje i album", + "search_filter_display_options": "Visingsval", + "search_filter_filename": "Søk etter filnamn", + "search_filter_location": "Lokasjon", + "search_filter_location_title": "Vel lokasjon", + "search_filter_media_type": "Mediatype", + "search_filter_media_type_title": "Vel mediatype", + "search_filter_people_title": "Vel personar", + "search_for": "Søk etter", + "search_for_existing_person": "Søk etter ein eksisterande person", + "search_no_more_result": "Ingen fleire resultat", + "search_no_people": "Ingen personar", + "search_no_people_named": "Ingen personar ved namn \"{name}\"", + "search_no_result": "Fann ingen resultat â prøv eit anna søkjeord eller ei anna kombinasjon", + "search_options": "Søkjeval", + "search_page_categories": "Kategoriar", + "search_page_motion_photos": "Levande bilete", + "search_page_no_objects": "Ingen objektinformasjon tilgjengeleg", + "search_page_no_places": "Ingen stadinformasjon tilgjengeleg", + "search_page_screenshots": "Skjermbilete", + "search_page_search_photos_videos": "Søk etter bileta og videoane dine", + "search_page_selfies": "Sjølvbilete", + "search_page_things": "Ting", + "search_page_view_all_button": "Vis alle", + "search_page_your_activity": "Din aktivitet", + "search_page_your_map": "Ditt kart", + "search_people": "Søk etter personar", + "search_places": "Søk etter stad", + "search_rating": "Søk etter vurdering âĻ", + "search_result_page_new_search_hint": "Nytt søk", + "search_settings": "Søkjeinnstillingar", + "search_state": "Søk etter fylke âĻ", + "search_suggestion_list_smart_search_hint_1": "Smart søk er aktivert som standard. For ÃĨ søkje etter metadata, bruk denne syntaksen: ", + "search_suggestion_list_smart_search_hint_2": "m:søkjeord", + "search_tags": "Søk etter taggar âĻ", + "search_timezone": "Søk etter tidssone âĻ", + "search_type": "Søketype", "search_your_photos": "Søk i dine bilete", + "searching_locales": "Søkjer etter sprÃĨkinnstillingarâĻ", "second": "Sekund", + "see_all_people": "SjÃĨ alle personar", + "select": "Vel", + "select_album_cover": "Vel forsidebilete", + "select_all": "Vel alle", + "select_all_duplicates": "Vel alle duplikatar", + "select_avatar_color": "Vel avatarfarge", + "select_face": "Vel ansikt", + "select_featured_photo": "Vel framheva bilete", + "select_from_computer": "Vel frÃĨ datamaskin", + "select_keep_all": "Vel ÃĨ behald alle", + "select_library_owner": "Vel bibliotekeigar", + "select_new_face": "Vel nytt ansikt", + "select_photos": "Vel bilete", + "select_trash_all": "Vel fjern alle", + "select_user_for_sharing_page_err_album": "Feil ved oppretting av album", "selected": "Valgt", + "selected_count": "{count, plural, other {# valgt}}", + "send_message": "Send melding", + "send_welcome_email": "Send velkomst-e-post", + "server_endpoint": "Tenar-endepunkt", + "server_info_box_app_version": "App Versjon", + "server_info_box_server_url": "Tenar URL", + "server_offline": "Tenar Frakopla", + "server_online": "Tenar i drift", + "server_stats": "Tenarstatistikk", + "server_version": "Tenarversjon", "set": "Sett", "settings": "Innstillingar", "share": "Del", diff --git a/i18n/pl.json b/i18n/pl.json index 1fc130ff56..eb48a69e51 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -478,14 +478,14 @@ "assets_added_to_name_count": "Dodano {count, plural, one {# zasÃŗb} few {# zasoby} many {# zasobÃŗw} other {# zasobÃŗw}} do {hasName, select, true {<b>{name}</b>} other {new album}}", "assets_count": "{count, plural, one {# zasÃŗb} few {# zasoby} many {# zasobÃŗw} other {# zasobÃŗw}}", "assets_deleted_permanently": "{} zasoby trwale usuniÄto", - "assets_deleted_permanently_from_server": " {} zasoby zostaÅy trwale usuniÄte z serwera Immich", + "assets_deleted_permanently_from_server": "{} zasoby zostaÅy trwale usuniÄte z serwera Immich", "assets_moved_to_trash_count": "Przeniesiono {count, plural, one {# zasÃŗb} few {# zasoby} many {# zasobÃŗw} other {# zasobÃŗw}} do kosza", "assets_permanently_deleted_count": "Trwale usuniÄto {count, plural, one {# zasÃŗb} few {# zasoby} many {# zasobÃŗw} other {# zasobÃŗw}}", "assets_removed_count": "UsuniÄto {count, plural, one {# zasÃŗb} few {# zasoby} many {# zasobÃŗw} other {# zasobÃŗw}}", - "assets_removed_permanently_from_device": " {} zasoby zostaÅy trwale usuniÄte z Twojego urzÄ dzenia", + "assets_removed_permanently_from_device": "{} zasoby zostaÅy trwale usuniÄte z Twojego urzÄ dzenia", "assets_restore_confirmation": "Na pewno chcesz przywrÃŗciÄ wszystkie zasoby z kosza? Nie da siÄ tego cofnÄ Ä! NaleÅŧy pamiÄtaÄ, Åŧe w ten sposÃŗb nie moÅŧna przywrÃŗciÄ zasobÃŗw offline.", "assets_restored_count": "PrzywrÃŗcono {count, plural, one {# zasÃŗb} few {# zasoby} many {# zasobÃŗw} other {# zasobÃŗw}}", - "assets_restored_successfully": " {} zasoby pomyÅlnie przywrÃŗcono", + "assets_restored_successfully": "{} zasoby pomyÅlnie przywrÃŗcono", "assets_trashed": "{} zasoby zostaÅy usuniÄte", "assets_trashed_count": "Wrzucono do kosza {count, plural, one {# zasÃŗb} few {# zasoby} many {# zasobÃŗw} other {# zasobÃŗw}}", "assets_trashed_from_server": "{} zasoby usuniÄte z serwera Immich", @@ -1609,7 +1609,7 @@ "settings_saved": "Ustawienia zapisane", "share": "UdostÄpnij", "share_add_photos": "Dodaj zdjÄcia", - "share_assets_selected": "{} wybrano ", + "share_assets_selected": "{} wybrano", "share_dialog_preparing": "Przygotowywanie...", "shared": "UdostÄpnione", "shared_album_activities_input_disable": "Komentarz jest wyÅÄ czony", diff --git a/i18n/pt.json b/i18n/pt.json index 3e1bd463ef..05b2ebfdd9 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -39,11 +39,11 @@ "authentication_settings_disable_all": "Tem a certeza que deseja desativar todos os mÊtodos de inÃcio de sessÃŖo? O inÃcio de sessÃŖo serÃĄ completamente desativado.", "authentication_settings_reenable": "Para reativar, use um <link>Comando de servidor</link>.", "background_task_job": "Tarefas em segundo plano", - "backup_database": "CÃŗpia de Segurança da Base de Dados", - "backup_database_enable_description": "Ativar cÃŗpias de segurança da base de dados", - "backup_keep_last_amount": "Quantidade de cÃŗpias de segurança anteriores a manter", - "backup_settings": "DefiniçÃĩes de CÃŗpia de Segurança", - "backup_settings_description": "Gerir definiçÃĩes de cÃŗpia de segurança da base de dados", + "backup_database": "Criar CÃŗpia da Base de Dados", + "backup_database_enable_description": "Ativar cÃŗpias da base de dados", + "backup_keep_last_amount": "Quantidade de cÃŗpias anteriores a manter", + "backup_settings": "DefiniçÃĩes de CÃŗpia da Base de Dados", + "backup_settings_description": "Gerir definiçÃĩes de cÃŗpia da base de dados. Aviso: Estas tarefas nÃŖo sÃŖo monitorizadas, pelo que nÃŖo serÃĄ notificado(a) em caso de erro.", "check_all": "Selecionar Tudo", "cleanup": "Limpeza", "cleared_jobs": "Eliminadas as tarefas de: {job}", @@ -371,13 +371,17 @@ "admin_password": "Palavra-passe do administrador", "administration": "AdministraÃ§ÃŖo", "advanced": "Avançado", - "advanced_settings_log_level_title": "NÃvel de log: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "Utilize esta definiÃ§ÃŖo para filtrar ficheiros durante a sincronizaÃ§ÃŖo baseada em critÊrios alternativos. Utilize apenas se a aplicaÃ§ÃŖo estiver com problemas a detetar todos os ÃĄlbuns.", + "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Utilizar um filtro alternativo de sincronizaÃ§ÃŖo de ÃĄlbuns em dispositivos", + "advanced_settings_log_level_title": "NÃvel de registo: {}", "advanced_settings_prefer_remote_subtitle": "Alguns dispositivos sÃŖo extremamente lentos para carregar miniaturas da memÃŗria. Ative esta opÃ§ÃŖo para preferir imagens do servidor.", "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_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", + "advanced_settings_sync_remote_deletions_title": "Sincronizar ficheiros eliminados remotamente [EXPERIMENTAL]", "advanced_settings_tile_subtitle": "ConfiguraçÃĩes avançadas do usuÃĄrio", "advanced_settings_troubleshooting_subtitle": "Ativar funcionalidades adicionais para a resoluÃ§ÃŖo de problemas", "advanced_settings_troubleshooting_title": "ResoluÃ§ÃŖo de problemas", @@ -400,9 +404,9 @@ "album_remove_user_confirmation": "Tem a certeza de que quer remover {user}?", "album_share_no_users": "Parece que tem este ÃĄlbum partilhado com todos os utilizadores ou que nÃŖo existem utilizadores com quem o partilhar.", "album_thumbnail_card_item": "1 arquivo", - "album_thumbnail_card_items": "{} arquivos", + "album_thumbnail_card_items": "{} ficheiros", "album_thumbnail_card_shared": " ¡ Compartilhado", - "album_thumbnail_shared_by": "Compartilhado por {}", + "album_thumbnail_shared_by": "Partilhado por {}", "album_updated": "Ãlbum atualizado", "album_updated_setting_description": "Receber uma notificaÃ§ÃŖo por e-mail quando um ÃĄlbum partilhado tiver novos ficheiros", "album_user_left": "SaÃu do {album}", @@ -440,7 +444,7 @@ "archive": "Arquivo", "archive_or_unarchive_photo": "Arquivar ou desarquivar foto", "archive_page_no_archived_assets": "Nenhum arquivo encontrado", - "archive_page_title": "Arquivado ({})", + "archive_page_title": "Arquivo ({})", "archive_size": "Tamanho do arquivo", "archive_size_description": "Configure o tamanho do arquivo para transferÃĒncias (em GiB)", "archived": "Arquivado", @@ -477,18 +481,18 @@ "assets_added_to_album_count": "{count, plural, one {# ficheiro adicionado} other {# ficheiros adicionados}} ao ÃĄlbum", "assets_added_to_name_count": "{count, plural, one {# ficheiro adicionado} other {# ficheiros adicionados}} a {hasName, select, true {<b>{name}</b>} other {novo ÃĄlbum}}", "assets_count": "{count, plural, one {# ficheiro} other {# ficheiros}}", - "assets_deleted_permanently": "{} arquivo(s) excluÃdo permanentemente", - "assets_deleted_permanently_from_server": "{} arquivo(s) excluÃdos permanentemente do servidor", + "assets_deleted_permanently": "{} ficheiro(s) eliminado(s) permanentemente", + "assets_deleted_permanently_from_server": "{} ficheiro(s) eliminado(s) permanentemente do servidor Immich", "assets_moved_to_trash_count": "{count, plural, one {# ficheiro movido} other {# ficheiros movidos}} para a reciclagem", "assets_permanently_deleted_count": "{count, plural, one {# ficheiro} other {# ficheiros}} eliminados permanentemente", "assets_removed_count": "{count, plural, one {# ficheiro eliminado} other {# ficheiros eliminados}}", - "assets_removed_permanently_from_device": "{} arquivo(s) removidos permanentemente do seu dispositivo", + "assets_removed_permanently_from_device": "{} ficheiro(s) removido(s) permanentemente do seu dispositivo", "assets_restore_confirmation": "Tem a certeza de que quer recuperar todos os ficheiros apagados? NÃŖo Ê possÃvel anular esta aÃ§ÃŖo! Tenha em conta de que quaisquer ficheiros indisponÃveis nÃŖo podem ser restaurados desta forma.", "assets_restored_count": "{count, plural, one {# ficheiro restaurado} other {# ficheiros restaurados}}", - "assets_restored_successfully": "{} arquivo(s) restaurados com sucesso", - "assets_trashed": "{} arquivo(s) enviados para a lixeira", + "assets_restored_successfully": "{} ficheiro(s) restaurados com sucesso", + "assets_trashed": "{} ficheiro(s) enviado(s) para a reciclagem", "assets_trashed_count": "{count, plural, one {# ficheiro enviado} other {# ficheiros enviados}} para a reciclagem", - "assets_trashed_from_server": "{} arquivo(s) do servidor foram enviados para a lixeira", + "assets_trashed_from_server": "{} 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", "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", @@ -506,11 +510,11 @@ "backup_all": "Tudo", "backup_background_service_backup_failed_message": "Falha ao fazer backup dos arquivos. Tentando novamenteâĻ", "backup_background_service_connection_failed_message": "Falha na conexÃŖo com o servidor. Tentando novamente...", - "backup_background_service_current_upload_notification": "Enviando {}", + "backup_background_service_current_upload_notification": "A enviar {}", "backup_background_service_default_notification": "Verificando novos arquivosâĻ", "backup_background_service_error_title": "Erro de backup", "backup_background_service_in_progress_notification": "Fazendo backup dos arquivosâĻ", - "backup_background_service_upload_failure_notification": "Falha ao carregar {}", + "backup_background_service_upload_failure_notification": "Ocorreu um erro ao enviar {}", "backup_controller_page_albums": "Backup Ãlbuns", "backup_controller_page_background_app_refresh_disabled_content": "Para utilizar o backup em segundo plano, ative a atualizaÃ§ÃŖo da aplicaÃ§ÃŖo em segundo plano em ConfiguraçÃĩes > Geral > AtualizaÃ§ÃŖo do app em segundo plano ", "backup_controller_page_background_app_refresh_disabled_title": "AtualizaÃ§ÃŖo do app em segundo plano desativada", @@ -521,7 +525,7 @@ "backup_controller_page_background_battery_info_title": "OtimizaçÃĩes de bateria", "backup_controller_page_background_charging": "Apenas enquanto carrega a bateria", "backup_controller_page_background_configure_error": "Falha ao configurar o serviço em segundo plano", - "backup_controller_page_background_delay": "Atrasar o backup de novos arquivos: {}", + "backup_controller_page_background_delay": "Atrasar a cÃŗpia de segurança de novos ficheiros: {}", "backup_controller_page_background_description": "Ative o serviço em segundo plano para fazer backup automÃĄtico de novos arquivos sem precisar abrir o aplicativo", "backup_controller_page_background_is_off": "O backup automÃĄtico em segundo plano estÃĄ desativado", "backup_controller_page_background_is_on": "O backup automÃĄtico em segundo plano estÃĄ ativado", @@ -529,14 +533,14 @@ "backup_controller_page_background_turn_on": "Ativar o serviço em segundo plano", "backup_controller_page_background_wifi": "Apenas no WiFi", "backup_controller_page_backup": "Backup", - "backup_controller_page_backup_selected": "Selecionado:", + "backup_controller_page_backup_selected": "Selecionado: ", "backup_controller_page_backup_sub": "Fotos e vÃdeos salvos em backup", "backup_controller_page_created": "Criado em: {}", "backup_controller_page_desc_backup": "Ative o backup para enviar automÃĄticamente novos arquivos para o servidor.", - "backup_controller_page_excluded": "ExcluÃdos:", + "backup_controller_page_excluded": "Eliminado: ", "backup_controller_page_failed": "Falhou ({})", - "backup_controller_page_filename": "Nome do arquivo: {} [{}]", - "backup_controller_page_id": "ID:{}", + "backup_controller_page_filename": "Nome do ficheiro: {} [{}]", + "backup_controller_page_id": "ID: {}", "backup_controller_page_info": "InformaçÃĩes do backup", "backup_controller_page_none_selected": "Nenhum selecionado", "backup_controller_page_remainder": "Restante", @@ -545,7 +549,7 @@ "backup_controller_page_start_backup": "Iniciar Backup", "backup_controller_page_status_off": "Backup automÃĄtico desativado", "backup_controller_page_status_on": "Backup automÃĄtico ativado", - "backup_controller_page_storage_format": "{} de {} usados", + "backup_controller_page_storage_format": "{} de {} utilizado", "backup_controller_page_to_backup": "Ãlbuns para fazer backup", "backup_controller_page_total_sub": "Todas as fotos e vÃdeos dos ÃĄlbuns selecionados", "backup_controller_page_turn_off": "Desativar backup", @@ -570,21 +574,21 @@ "bulk_keep_duplicates_confirmation": "Tem a certeza de que deseja manter {count, plural, one {# ficheiro duplicado} other {# ficheiros duplicados}}? Isto resolverÃĄ todos os grupos duplicados sem eliminar nada.", "bulk_trash_duplicates_confirmation": "Tem a certeza de que deseja mover para a reciclagem {count, plural, one {# ficheiro duplicado} other {# ficheiros duplicados}}? Isto manterÃĄ o maior ficheiro de cada grupo e irÃĄ mover para a reciclagem todos os outros duplicados.", "buy": "Comprar Immich", - "cache_settings_album_thumbnails": "Miniaturas da pÃĄgina da biblioteca ({} arquivos)", + "cache_settings_album_thumbnails": "Miniaturas da pÃĄgina da biblioteca ({} ficheiros)", "cache_settings_clear_cache_button": "Limpar cache", "cache_settings_clear_cache_button_title": "Limpa o cache do aplicativo. Isso afetarÃĄ significativamente o desempenho do aplicativo atÊ que o cache seja reconstruÃdo.", "cache_settings_duplicated_assets_clear_button": "LIMPAR", "cache_settings_duplicated_assets_subtitle": "Fotos e vÃdeos que estÃŖo na lista negra da aplicaÃ§ÃŖo", - "cache_settings_duplicated_assets_title": "Arquivos duplicados ({})", - "cache_settings_image_cache_size": "Tamanho do cache de imagem ({} arquivos)", + "cache_settings_duplicated_assets_title": "Ficheiros duplicados ({})", + "cache_settings_image_cache_size": "Tamanho da cache de imagem ({} ficheiros)", "cache_settings_statistics_album": "Miniaturas da biblioteca", - "cache_settings_statistics_assets": "{} arquivos ({})", + "cache_settings_statistics_assets": "{} ficheiros ({})", "cache_settings_statistics_full": "Imagens completas", "cache_settings_statistics_shared": "Miniaturas de ÃĄlbuns compartilhados", "cache_settings_statistics_thumbnail": "Miniaturas", "cache_settings_statistics_title": "Uso de cache", "cache_settings_subtitle": "Controle o comportamento de cache do aplicativo Immich", - "cache_settings_thumbnail_size": "Tamanho do cache de miniaturas ({} arquivos)", + "cache_settings_thumbnail_size": "Tamanho da cache das miniaturas ({} ficheiros)", "cache_settings_tile_subtitle": "Controlar o comportamento do armazenamento local", "cache_settings_tile_title": "Armazenamento local", "cache_settings_title": "ConfiguraçÃĩes de cache", @@ -606,7 +610,7 @@ "change_password": "Alterar a palavra-passe", "change_password_description": "Esta Ê a primeira vez que estÃĄ a entrar no sistema ou um pedido foi feito para alterar a sua palavra-passe. Insira a nova palavra-passe abaixo.", "change_password_form_confirm_password": "Confirme a senha", - "change_password_form_description": "Esta Ê a primeira vez que vocÃĒ estÃĄ acessando o sistema ou foi feita uma solicitaÃ§ÃŖo para alterar sua senha. Por favor, insira a nova senha abaixo.", + "change_password_form_description": "OlÃĄ, {name}\n\nEsta Ê a primeira vez que estÃĄ a aceder ao sistema, ou entÃŖo foi feito um pedido para alterar a palavra-passe. Por favor insira uma nova palavra-passe abaixo.", "change_password_form_new_password": "Nova senha", "change_password_form_password_mismatch": "As senhas nÃŖo estÃŖo iguais", "change_password_form_reenter_new_password": "Confirme a nova senha", @@ -654,7 +658,7 @@ "contain": "Ajustar", "context": "Contexto", "continue": "Continuar", - "control_bottom_app_bar_album_info_shared": "{} arquivos ¡ Compartilhado", + "control_bottom_app_bar_album_info_shared": "{} ficheiros ¡ Partilhado", "control_bottom_app_bar_create_new_album": "Criar novo ÃĄlbum", "control_bottom_app_bar_delete_from_immich": "Excluir do Immich", "control_bottom_app_bar_delete_from_local": "Excluir do dispositivo", @@ -763,7 +767,7 @@ "download_enqueue": "Na fila", "download_error": "Erro ao baixar", "download_failed": "Falha", - "download_filename": "arquivo: {}", + "download_filename": "ficheiro: {}", "download_finished": "ConcluÃdo", "download_include_embedded_motion_videos": "VÃdeos incorporados", "download_include_embedded_motion_videos_description": "Incluir vÃdeos incorporados em fotos em movimento como um ficheiro separado", @@ -953,10 +957,10 @@ "exif_bottom_sheet_location": "LOCALIZAÃÃO", "exif_bottom_sheet_people": "PESSOAS", "exif_bottom_sheet_person_add_person": "Adicionar nome", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "Idade {}", + "exif_bottom_sheet_person_age_months": "Idade {} meses", + "exif_bottom_sheet_person_age_year_months": "Idade 1 ano, {} meses", + "exif_bottom_sheet_person_age_years": "Idade {}", "exit_slideshow": "Sair da apresentaÃ§ÃŖo", "expand_all": "Expandir tudo", "experimental_settings_new_asset_list_subtitle": "Trabalho em andamento", @@ -974,7 +978,7 @@ "external": "Externo", "external_libraries": "Bibliotecas externas", "external_network": "Rede externa", - "external_network_sheet_info": "Quando nÃŖo estiver na rede Wi-Fi especificada, o aplicativo irÃĄ se conectar usando a primeira URL abaixo que obtiver sucesso, começando do topo da lista para baixo.", + "external_network_sheet_info": "Quando nÃŖo estiver ligado à rede Wi-Fi especificada, a aplicaÃ§ÃŖo irÃĄ ligar-se utilizando o primeiro URL abaixo que conseguir aceder, a começar do topo da lista para baixo.", "face_unassigned": "Sem atribuiÃ§ÃŖo", "failed": "Falhou", "failed_to_load_assets": "Falha ao carregar ficheiros", @@ -992,6 +996,7 @@ "filetype": "Tipo de ficheiro", "filter": "Filtro", "filter_people": "Filtrar pessoas", + "filter_places": "Filtrar lugares", "find_them_fast": "Encontre-as mais rapidamente pelo nome numa pesquisa", "fix_incorrect_match": "Corrigir correspondÃĒncia incorreta", "folder": "Folder", @@ -1203,7 +1208,7 @@ "memories_start_over": "Ver de novo", "memories_swipe_to_close": "Deslize para cima para fechar", "memories_year_ago": "Um ano atrÃĄs", - "memories_years_ago": "{} anos atrÃĄs", + "memories_years_ago": "HÃĄ {} anos atrÃĄs", "memory": "MemÃŗria", "memory_lane_title": "MemÃŗrias {title}", "menu": "Menu", @@ -1282,6 +1287,7 @@ "onboarding_welcome_user": "Bem-vindo(a), {user}", "online": "Online", "only_favorites": "Apenas favoritos", + "open": "Abrir", "open_in_map_view": "Abrir na visualizaÃ§ÃŖo de mapa", "open_in_openstreetmap": "Abrir no OpenStreetMap", "open_the_search_filters": "Abrir os filtros de pesquisa", @@ -1305,7 +1311,7 @@ "partner_page_partner_add_failed": "Falha ao adicionar parceiro", "partner_page_select_partner": "Selecionar parceiro", "partner_page_shared_to_title": "Compartilhar com", - "partner_page_stop_sharing_content": "{} nÃŖo poderÃĄ mais acessar as suas fotos.", + "partner_page_stop_sharing_content": "{} irÃĄ deixar de ter acesso à s suas fotos.", "partner_sharing": "Partilha com Parceiro", "partners": "Parceiros", "password": "Palavra-passe", @@ -1590,7 +1596,7 @@ "setting_languages_apply": "Aplicar", "setting_languages_subtitle": "Alterar o idioma do aplicativo", "setting_languages_title": "Idioma", - "setting_notifications_notify_failures_grace_period": "Notifique falhas de backup em segundo plano: {}", + "setting_notifications_notify_failures_grace_period": "Notificar erros da cÃŗpia de segurança em segundo plano: {}", "setting_notifications_notify_hours": "{} horas", "setting_notifications_notify_immediately": "imediatamente", "setting_notifications_notify_minutes": "{} minutos", @@ -1626,7 +1632,7 @@ "shared_intent_upload_button_progress_text": "Enviados {} de {}", "shared_link_app_bar_title": "Links compartilhados", "shared_link_clipboard_copied_massage": "Copiado para a ÃĄrea de transferÃĒncia", - "shared_link_clipboard_text": "Link: {}\nPassword: {}", + "shared_link_clipboard_text": "Link: {}\nPalavra-passe: {}", "shared_link_create_error": "Erro ao criar o link compartilhado", "shared_link_edit_description_hint": "Digite a descriÃ§ÃŖo do compartilhamento", "shared_link_edit_expire_after_option_day": "1 dia", @@ -1635,7 +1641,7 @@ "shared_link_edit_expire_after_option_hours": "{} horas", "shared_link_edit_expire_after_option_minute": "1 minuto", "shared_link_edit_expire_after_option_minutes": "{} minutos", - "shared_link_edit_expire_after_option_months": "{} MÃĒses", + "shared_link_edit_expire_after_option_months": "{} meses", "shared_link_edit_expire_after_option_year": "{} ano", "shared_link_edit_password_hint": "Digite uma senha para proteger este link", "shared_link_edit_submit_button": "Atualizar link", @@ -1749,7 +1755,7 @@ "theme_selection": "Selecionar tema", "theme_selection_description": "Definir automaticamente o tema como claro ou escuro com base na preferÃĒncia do sistema do seu navegador", "theme_setting_asset_list_storage_indicator_title": "Mostrar indicador de armazenamento na grade de fotos", - "theme_setting_asset_list_tiles_per_row_title": "Quantidade de arquivos por linha ({})", + "theme_setting_asset_list_tiles_per_row_title": "Quantidade de ficheiros por linha ({})", "theme_setting_colorful_interface_subtitle": "Aplica a cor primÃĄria ao fundo", "theme_setting_colorful_interface_title": "Interface colorida", "theme_setting_image_viewer_quality_subtitle": "Ajuste a qualidade do visualizador de imagens detalhadas", @@ -1784,11 +1790,11 @@ "trash_no_results_message": "Fotos e vÃdeos enviados para a reciclagem aparecem aqui.", "trash_page_delete_all": "Excluir tudo", "trash_page_empty_trash_dialog_content": "Deseja esvaziar a lixera? Estes arquivos serÃŖo apagados de forma permanente do Immich", - "trash_page_info": "Arquivos na lixeira sÃŖo excluÃdos de forma permanente apÃŗs {} dias", + "trash_page_info": "Ficheiros na reciclagem irÃŖo ser eliminados permanentemente apÃŗs {} dias", "trash_page_no_assets": "Lixeira vazia", "trash_page_restore_all": "Restaurar tudo", "trash_page_select_assets_btn": "Selecionar arquivos", - "trash_page_title": "Lixeira ({})", + "trash_page_title": "Reciclagem ({})", "trashed_items_will_be_permanently_deleted_after": "Os itens da reciclagem sÃŖo eliminados permanentemente apÃŗs {days, plural, one {# dia} other {# dias}}.", "type": "Tipo", "unarchive": "Desarquivar", diff --git a/i18n/pt_BR.json b/i18n/pt_BR.json index 982455a697..3db825d567 100644 --- a/i18n/pt_BR.json +++ b/i18n/pt_BR.json @@ -4,6 +4,7 @@ "account_settings": "ConfiguraçÃĩes da Conta", "acknowledge": "Entendi", "action": "AÃ§ÃŖo", + "action_common_update": "Atualizar", "actions": "AçÃĩes", "active": "Em execuÃ§ÃŖo", "activity": "Atividade", @@ -832,6 +833,7 @@ "filename": "Nome do arquivo", "filetype": "Tipo de arquivo", "filter_people": "Filtrar pessoas", + "filter_places": "Filtrar lugares", "find_them_fast": "Encontre pelo nome em uma pesquisa", "fix_incorrect_match": "Corrigir correspondÃĒncia incorreta", "folders": "Pastas", diff --git a/i18n/ro.json b/i18n/ro.json index 7b4913e348..82bdc6d1dd 100644 --- a/i18n/ro.json +++ b/i18n/ro.json @@ -371,6 +371,8 @@ "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.", + "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] UtilizaČi filtrul alternativ de sincronizare a albumelor de pe dispozitiv", "advanced_settings_log_level_title": "Nivel log: {}", "advanced_settings_prefer_remote_subtitle": "Unele dispozitive ÃŽntÃĸmpinÄ dificultÄČi ÃŽn ÃŽncÄrcarea miniaturilor pentru resursele de pe dispozitiv. ActiveazÄ aceastÄ setare pentru a ÃŽncÄrca imaginile de la distanČÄ ÃŽn schimb.", "advanced_settings_prefer_remote_title": "PreferÄ fotografii la distanČÄ", @@ -378,6 +380,8 @@ "advanced_settings_proxy_headers_title": "Proxy Headers", "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", + "advanced_settings_sync_remote_deletions_subtitle": "ČtergeČi sau restauraČi automat un element de pe acest dispozitiv atunci cÃĸnd acČiunea este efectuatÄ pe web", + "advanced_settings_sync_remote_deletions_title": "SincronizeazÄ stergerile efectuate la distanČÄ [EXPERIMENTAL]", "advanced_settings_tile_subtitle": "SetÄri avansate pentru utilizator", "advanced_settings_troubleshooting_subtitle": "ActiveazÄ funcČionalitÄČi suplimentare pentru depanare", "advanced_settings_troubleshooting_title": "Depanare", @@ -401,7 +405,7 @@ "album_share_no_users": "Se pare cÄ ai partajat acest album cu toČi utilizatorii sau nu ai niciun utilizator cu care sÄ-l partajezi.", "album_thumbnail_card_item": "1 element", "album_thumbnail_card_items": "{} elemente", - "album_thumbnail_card_shared": "Distribuit", + "album_thumbnail_card_shared": " ¡ Distribuit", "album_thumbnail_shared_by": "Distribuit de {}", "album_updated": "Album actualizat", "album_updated_setting_description": "PrimiČi o notificare prin e-mail cÃĸnd un album partajat are elemente noi", @@ -504,12 +508,12 @@ "backup_album_selection_page_selection_info": "InformaČii selecČie", "backup_album_selection_page_total_assets": "Total resurse unice", "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...", + "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âĻ", "backup_background_service_current_upload_notification": "ÃncÄrcare {}", - "backup_background_service_default_notification": "Verificare resurse noi...", + "backup_background_service_default_notification": "Verificare resurse noiâĻ", "backup_background_service_error_title": "Eroare backup", - "backup_background_service_in_progress_notification": "Se face backup al resurselor tale...", + "backup_background_service_in_progress_notification": "Se face backup al resurselor taleâĻ", "backup_background_service_upload_failure_notification": "ÃncÄrcare eČuatÄ {}", "backup_controller_page_albums": "Backup albume", "backup_controller_page_background_app_refresh_disabled_content": "ActiveazÄ reÃŽmprospÄtarea aplicaČiei ÃŽn fundal ÃŽn SetÄri > General > ReÃŽmprospÄtare aplicaČii ÃŽn fundal pentru a folosi copia de siguranČÄ ÃŽn fundal.", @@ -529,11 +533,11 @@ "backup_controller_page_background_turn_on": "ActiveazÄ serviciul ÃŽn fundal", "backup_controller_page_background_wifi": "Doar conectat la WiFi", "backup_controller_page_backup": "Backup", - "backup_controller_page_backup_selected": "Selectat(e):", + "backup_controller_page_backup_selected": "Selectat(e): ", "backup_controller_page_backup_sub": "S-a fÄcut backup pentru fotografii Či videoclipuri", "backup_controller_page_created": "Creat la: {}", - "backup_controller_page_desc_backup": "ActiveazÄ backup-ul ÃŽn prim-plan pentru a ÃŽncÄrca resursele pe server cÃĸnd deschizi aplicaČia ", - "backup_controller_page_excluded": "Exclus(e):", + "backup_controller_page_desc_backup": "ActiveazÄ backup-ul ÃŽn prim-plan pentru a ÃŽncÄrca resursele pe server cÃĸnd deschizi aplicaČia.", + "backup_controller_page_excluded": "Exclus(e): ", "backup_controller_page_failed": "EČuate ({})", "backup_controller_page_filename": "Nume fiČier: {} [{}]", "backup_controller_page_id": "ID: {}", @@ -721,7 +725,7 @@ "delete_dialog_alert": "Aceste elemente vor fi Čterse permanent de pe server-ul Immich Či din dispozitivul tÄu", "delete_dialog_alert_local": "Aceste fiČiere vor fi Čterse permanent din dispozitiv, dar vor fi disponibile pe server-ul Immich", "delete_dialog_alert_local_non_backed_up": "Pentru unele fiČere nu s-a fÄcut backup ÃŽn Immich Či vor fi Čterse permanent din dispozitiv", - "delete_dialog_alert_remote": "Aceste fiČiere vor fi Čterse permanent de pe server-ul Immich.", + "delete_dialog_alert_remote": "Aceste fiČiere vor fi Čterse permanent de pe server-ul Immich", "delete_dialog_ok_force": "Čterge oricum", "delete_dialog_title": "Čterge permanent", "delete_duplicates_confirmation": "SunteČi sigur cÄ doriČi sÄ ČtergeČi permanent aceste duplicate?", @@ -960,7 +964,7 @@ "exit_slideshow": "IeČire din Prezentare", "expand_all": "ExtindeČi-le pe toate", "experimental_settings_new_asset_list_subtitle": "AcČiune ÃŽn desfÄČurare", - "experimental_settings_new_asset_list_title": "ActiveazÄ grila experimentalÄ de fotografii.", + "experimental_settings_new_asset_list_title": "ActiveazÄ grila experimentalÄ de fotografii", "experimental_settings_subtitle": "FoloseČte pe propria rÄspundere!", "experimental_settings_title": "Experimental", "expire_after": "ExpirÄ dupÄ", @@ -992,6 +996,7 @@ "filetype": "Tipul fiČierului", "filter": "Filter", "filter_people": "FiltraČi persoanele", + "filter_places": "FiltreazÄ locurile", "find_them_fast": "GÄsiČi-le rapid prin cÄutare dupÄ nume", "fix_incorrect_match": "RemediaČi potrivirea incorectÄ", "folder": "Folder", @@ -1040,7 +1045,7 @@ "home_page_delete_remote_err_local": "Resursele locale sunt ÃŽn selecČia pentru Čtergere la distanČÄ, omitere", "home_page_favorite_err_local": "Resursele locale nu pot fi adÄugate la favorite ÃŽncÄ, omitere", "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_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_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Ä", @@ -1282,6 +1287,7 @@ "onboarding_welcome_user": "Bun venit, {user}", "online": "Online", "only_favorites": "Doar favorite", + "open": "Deschide", "open_in_map_view": "DeschideČi ÃŽn vizualizarea hÄrČii", "open_in_openstreetmap": "DeschideČi ÃŽn OpenStreetMap", "open_the_search_filters": "DeschideČi filtrele de cÄutare", @@ -1339,7 +1345,7 @@ "permission_onboarding_get_started": "Ãncepe", "permission_onboarding_go_to_settings": "Mergi la setÄri", "permission_onboarding_permission_denied": "Permisiune refuzatÄ. Pentru a utiliza Immich, acordÄ permisiuni pentru fotografii Či videoclipuri ÃŽn SetÄri.", - "permission_onboarding_permission_granted": "Permisiune acordatÄ!", + "permission_onboarding_permission_granted": "Permisiune acordatÄ! SunteČi gata.", "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Į", @@ -1526,7 +1532,7 @@ "search_page_categories": "Categorii", "search_page_motion_photos": "Fotografii ÃŽn miČcare", "search_page_no_objects": "Nu sunt informaČii disponibile despre obiecte", - "search_page_no_places": "Nici o informaČie disponibilÄ despre locuri ", + "search_page_no_places": "Nici o informaČie disponibilÄ despre locuri", "search_page_screenshots": "Capturi de ecran", "search_page_search_photos_videos": "Search for your photos and videos", "search_page_selfies": "Selfie-uri", @@ -1540,7 +1546,7 @@ "search_result_page_new_search_hint": "CÄutare nouÄ", "search_settings": "SetÄri de cÄutare", "search_state": "Starea cÄutÄrii...", - "search_suggestion_list_smart_search_hint_1": "CÄutarea inteligentÄ este activatÄ ÃŽn mod implicit, pentru a cÄuta metadata, utilizeazÄ sintaxa\n", + "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...", "search_timezone": "CÄutaČi fusul orar...", @@ -1615,7 +1621,7 @@ "shared_album_activities_input_disable": "Cometariile sunt dezactivate", "shared_album_activity_remove_content": "DoreČti sÄ Čtergi aceastÄ activitate?", "shared_album_activity_remove_title": "Čterge activitate", - "shared_album_section_people_action_error": "Eroare la pÄrÄsirea/Čtergerea din album.", + "shared_album_section_people_action_error": "Eroare la pÄrÄsirea/Čtergerea din album", "shared_album_section_people_action_leave": "Čterge utilizator din album", "shared_album_section_people_action_remove_user": "Čterge utilizator din album", "shared_album_section_people_title": "PERSOANE", @@ -1852,9 +1858,9 @@ "version_announcement_message": "BunÄ! Este disponibilÄ o nouÄ versiune de Immich. VÄ rugÄm sÄ vÄ faceČi timp sÄ citiČi <link>notele de lansare</link> pentru a vÄ asigura cÄ configuraČia dvs. este actualizatÄ pentru a preveni orice configurare greČitÄ, mai ales dacÄ utilizaČi WatchTower sau orice mecanism care se ocupÄ de actualizarea automatÄ a instanČei dvs. Immich.", "version_announcement_overlay_release_notes": "informaČii update", "version_announcement_overlay_text_1": "Salut, existÄ un update nou pentru", - "version_announcement_overlay_text_2": "te rugÄm verificÄ", - "version_announcement_overlay_text_3": "Či asigurÄ-te cÄ fiČierul .env Či configuraČia ta docker-compose sunt actualizate pentru a preveni orice erori de configuraČie, ÃŽn special dacÄ foloseČti WatchTower sau orice mecanism care gestioneazÄ actualizarea automatÄ a aplicaČiei server-ului tÄu.", - "version_announcement_overlay_title": "O nouÄ versiune pentru server este disponibilÄ đ", + "version_announcement_overlay_text_2": "te rugÄm verificÄ ", + "version_announcement_overlay_text_3": " Či asigurÄ-te cÄ fiČierul .env Či configuraČia ta docker-compose sunt actualizate pentru a preveni orice erori de configuraČie, ÃŽn special dacÄ foloseČti WatchTower sau orice mecanism care gestioneazÄ actualizarea automatÄ a aplicaČiei server-ului tÄu.", + "version_announcement_overlay_title": "O nouÄ versiune pentru server este disponibilÄ đ", "version_history": "Istoric Versiuni", "version_history_item": "Instalat {version} pe data de {date}", "video": "Videoclip", diff --git a/i18n/ru.json b/i18n/ru.json index ab883f9aec..a78a9d6701 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -39,11 +39,11 @@ "authentication_settings_disable_all": "ĐŅ ŅвĐĩŅĐĩĐŊŅ, ŅŅĐž Ņ ĐžŅиŅĐĩ ĐžŅĐēĐģŅŅиŅŅ Đ˛ŅĐĩ ĐŧĐĩŅĐžĐ´Ņ Đ˛Ņ ĐžĐ´Đ°? ĐŅ ĐžĐ´ ĐąŅĐ´ĐĩŅ ĐŋĐžĐģĐŊĐžŅŅŅŅ ĐžŅĐēĐģŅŅĐĩĐŊ.", "authentication_settings_reenable": "ЧŅĐžĐąŅ ŅĐŊОва вĐēĐģŅŅиŅŅ, иŅĐŋĐžĐģŅСŅĐšŅĐĩ <link>ĐĐžĐŧаĐŊĐ´Ņ ŅĐĩŅвĐĩŅа</link>.", "background_task_job": "ФОĐŊОвŅĐĩ СадаŅи", - "backup_database": "Đ ĐĩСĐĩŅвĐŊĐžĐĩ ĐēĐžĐŋиŅОваĐŊиĐĩ ĐąĐ°ĐˇŅ Đ´Đ°ĐŊĐŊŅŅ ", - "backup_database_enable_description": "ĐĐēĐģŅŅиŅŅ ŅĐĩСĐĩŅвĐŊĐžĐĩ ĐēĐžĐŋиŅОваĐŊиĐĩ ĐąĐ°ĐˇŅ Đ´Đ°ĐŊĐŊŅŅ ", - "backup_keep_last_amount": "ĐĐžĐģиŅĐĩŅŅвО Ņ ŅаĐŊиĐŧŅŅ ŅĐĩСĐĩŅвĐŊŅŅ ĐēĐžĐŋиК", - "backup_settings": "ĐаŅŅŅОКĐēи ŅĐĩСĐĩŅвĐŊĐžĐŗĐž ĐēĐžĐŋиŅОваĐŊиŅ", - "backup_settings_description": "ĐŖĐŋŅавĐģĐĩĐŊиĐĩ ĐŊаŅŅŅОКĐēаĐŧи ŅĐĩСĐĩŅвĐŊĐžĐŗĐž ĐēĐžĐŋиŅОваĐŊĐ¸Ņ ĐąĐ°ĐˇŅ Đ´Đ°ĐŊĐŊŅŅ ", + "backup_database": "ХОСдаŅŅ ŅĐĩСĐĩŅвĐŊŅŅ ĐēĐžĐŋĐ¸Ņ ĐąĐ°ĐˇŅ Đ´Đ°ĐŊĐŊŅŅ ", + "backup_database_enable_description": "ĐĐēĐģŅŅиŅŅ Đ´Đ°ĐŧĐŋŅ ĐąĐ°ĐˇŅ Đ´Đ°ĐŊĐŊŅŅ ", + "backup_keep_last_amount": "ĐĐžĐģиŅĐĩŅŅвО Ņ ŅаĐŊиĐŧŅŅ ŅĐĩСĐĩŅвĐŊŅŅ ĐēĐžĐŋиК ĐąĐ°ĐˇŅ Đ´Đ°ĐŊĐŊŅŅ ", + "backup_settings": "ĐаŅŅŅОКĐēи ŅĐĩСĐĩŅвĐŊĐžĐŗĐž ĐēĐžĐŋиŅОваĐŊĐ¸Ņ ĐąĐ°ĐˇŅ Đ´Đ°ĐŊĐŊŅŅ ", + "backup_settings_description": "ĐаŅŅŅОКĐēи авŅĐžĐŧаŅиŅĐĩŅĐēĐžĐŗĐž ŅОСдаĐŊĐ¸Ņ ŅĐĩСĐĩŅвĐŊŅŅ ĐēĐžĐŋиК ĐąĐ°ĐˇŅ Đ´Đ°ĐŊĐŊŅŅ . ĐŅиĐŧĐĩŅаĐŊиĐĩ: вŅĐŋĐžĐģĐŊĐĩĐŊиĐĩ ĐŊĐĩ ĐēĐžĐŊŅŅĐžĐģиŅŅĐĩŅŅŅ, Đ˛Ņ ĐŊĐĩ ĐŋĐžĐģŅŅиŅĐĩ ŅвĐĩĐ´ĐžĐŧĐģĐĩĐŊиĐĩ в ŅĐģŅŅаĐĩ ŅйОŅ.", "check_all": "ĐŅОвĐĩŅиŅŅ Đ˛ŅĐĩ", "cleanup": "ĐŅиŅŅĐēа", "cleared_jobs": "ĐŅиŅĐĩĐŊŅ ĐˇĐ°Đ´Đ°Ņи Đ´ĐģŅ: {job}", @@ -54,9 +54,9 @@ "confirm_reprocess_all_faces": "ĐŅ ŅвĐĩŅĐĩĐŊŅ, ŅŅĐž Ņ ĐžŅиŅĐĩ ĐŋОвŅĐžŅĐŊĐž ĐžĐŋŅĐĩĐ´ĐĩĐģиŅŅ Đ˛ŅĐĩ ĐģиŅа? ĐŅĐ´ŅŅ ŅаĐēĐļĐĩ ŅдаĐģĐĩĐŊŅ Đ¸ĐŧĐĩĐŊа ŅĐž вŅĐĩŅ ĐģиŅ.", "confirm_user_password_reset": "ĐŅ ŅвĐĩŅĐĩĐŊŅ, ŅŅĐž Ņ ĐžŅиŅĐĩ ŅĐąŅĐžŅиŅŅ ĐŋаŅĐžĐģŅ ĐŋĐžĐģŅСОваŅĐĩĐģŅ {user}?", "create_job": "ХОСдаŅŅ ĐˇĐ°Đ´Đ°ĐŊиĐĩ", - "cron_expression": "ĐŅŅаĐļĐĩĐŊиĐĩ cron", - "cron_expression_description": "ĐадаКŅĐĩ иĐŊŅĐĩŅваĐģ ŅĐēаĐŊиŅОваĐŊиК в ŅĐžŅĐŧаŅĐĩ cron. ĐĐģŅ ĐŋĐžĐģŅŅĐĩĐŊĐ¸Ņ Đ´ĐžĐŋĐžĐģĐŊиŅĐĩĐģŅĐŊОК иĐŊŅĐžŅĐŧаŅии, ОСĐŊаĐēĐžĐŧŅŅĐĩŅŅ Ņ <link>Crontab Guru</link>", - "cron_expression_presets": "ĐŅĐĩĐ´ŅŅŅаĐŊОвĐēи вŅŅаĐļĐĩĐŊиК cron", + "cron_expression": "РаŅĐŋиŅаĐŊиĐĩ (вŅŅаĐļĐĩĐŊиĐĩ ĐŋĐģаĐŊиŅОвŅиĐēа cron)", + "cron_expression_description": "ЧаŅŅĐžŅа и вŅĐĩĐŧŅ Đ˛ŅĐŋĐžĐģĐŊĐĩĐŊĐ¸Ņ ĐˇĐ°Đ´Đ°ĐŊĐ¸Ņ Đ˛ ŅĐžŅĐŧаŅĐĩ ĐŋĐģаĐŊиŅОвŅиĐēа cron. ĐĐžŅĐŋĐžĐģŅСŅĐšŅĐĩŅŅ ĐžĐŊĐģаКĐŊ ĐŗĐĩĐŊĐĩŅаŅĐžŅĐžĐŧ <link>Crontab Guru</link> ĐŋŅи ĐŊĐĩĐžĐąŅ ĐžĐ´Đ¸ĐŧĐžŅŅи.", + "cron_expression_presets": "РаŅĐŋиŅаĐŊиĐĩ (ĐŋŅĐĩĐ´ŅŅŅаĐŊОвĐģĐĩĐŊĐŊŅĐĩ ваŅиаĐŊŅŅ)", "disable_login": "ĐŅĐēĐģŅŅиŅŅ Đ˛Ņ ĐžĐ´", "duplicate_detection_job_description": "ĐаĐŋŅŅĐēаĐĩŅ ĐžĐŋŅĐĩĐ´ĐĩĐģĐĩĐŊиĐĩ ĐŋĐžŅ ĐžĐļĐ¸Ņ Đ¸ĐˇĐžĐąŅаĐļĐĩĐŊиК ĐŋŅи ĐŋĐžĐŧĐžŅи ĐŧаŅиĐŊĐŊĐžĐŗĐž СŅĐĩĐŊĐ¸Ņ (СавиŅĐ¸Ņ ĐžŅ ŅĐŧĐŊĐžĐŗĐž ĐŋОиŅĐēа)", "exclusion_pattern_description": "ШайĐģĐžĐŊŅ Đ¸ŅĐēĐģŅŅĐĩĐŊĐ¸Ņ ĐŋОСвОĐģŅŅŅ Đ¸ĐŗĐŊĐžŅиŅОваŅŅ ŅаКĐģŅ Đ¸ ĐŋаĐŋĐēи ĐŋŅи ŅĐēаĐŊиŅОваĐŊии ваŅĐĩĐš йийĐģиОŅĐĩĐēи. ĐŅĐž ĐŋĐžĐģĐĩСĐŊĐž, ĐĩŅĐģи Ņ Đ˛Đ°Ņ ĐĩŅŅŅ ĐŋаĐŋĐēи, ŅОдĐĩŅĐļаŅиĐĩ ŅаКĐģŅ, ĐēĐžŅĐžŅŅĐĩ Đ˛Ņ ĐŊĐĩ Ņ ĐžŅиŅĐĩ иĐŧĐŋĐžŅŅиŅОваŅŅ, ĐŊаĐŋŅиĐŧĐĩŅ, RAW-ŅаКĐģŅ.", @@ -371,13 +371,17 @@ "admin_password": "ĐаŅĐžĐģŅ Đ°Đ´ĐŧиĐŊиŅŅŅаŅĐžŅа", "administration": "ĐŖĐŋŅавĐģĐĩĐŊиĐĩ ŅĐĩŅвĐĩŅĐžĐŧ", "advanced": "РаŅŅиŅĐĩĐŊĐŊŅĐĩ", - "advanced_settings_log_level_title": "ĐŖŅОвĐĩĐŊŅ ĐģĐžĐŗĐ¸ŅОваĐŊиŅ:", + "advanced_settings_enable_alternate_media_filter_subtitle": "ĐŅĐŋĐžĐģŅСŅĐšŅĐĩ ŅŅĐžŅ ĐŋаŅаĐŧĐĩŅŅ Đ´ĐģŅ ŅиĐģŅŅŅаŅии ĐŧĐĩдиаŅаКĐģОв вО вŅĐĩĐŧŅ ŅиĐŊŅ ŅĐžĐŊиСаŅии ĐŊа ĐžŅĐŊОвĐĩ аĐģŅŅĐĩŅĐŊаŅивĐŊŅŅ ĐēŅиŅĐĩŅиĐĩв. ĐŅОйŅĐšŅĐĩ ŅĐžĐģŅĐēĐž в ŅĐžĐŧ ŅĐģŅŅаĐĩ, ĐĩŅĐģи Ņ Đ˛Đ°Ņ ĐĩŅŅŅ ĐŋŅОйĐģĐĩĐŧŅ Ņ ĐžĐąĐŊаŅŅĐļĐĩĐŊиĐĩĐŧ ĐŋŅиĐģĐžĐļĐĩĐŊиĐĩĐŧ вŅĐĩŅ Đ°ĐģŅйОĐŧОв.", + "advanced_settings_enable_alternate_media_filter_title": "[ĐĐĐĄĐĐĐ ĐĐĐĐĐĸĐĐĐŦĐĐ] ĐŅĐŋĐžĐģŅСОваĐŊиĐĩ ŅиĐģŅŅŅа ŅиĐŊŅ ŅĐžĐŊиСаŅии аĐģŅйОĐŧОв аĐģŅŅĐĩŅĐŊаŅивĐŊŅŅ ŅŅŅŅОКŅŅв", + "advanced_settings_log_level_title": "ĐŖŅОвĐĩĐŊŅ ĐģĐžĐŗĐ¸ŅОваĐŊиŅ: {}", "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_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": "Đ ĐĩŅĐĩĐŊиĐĩ ĐŋŅОйĐģĐĩĐŧ", @@ -401,7 +405,7 @@ "album_share_no_users": "ĐĐžŅ ĐžĐļĐĩ, Đ˛Ņ ĐŋОдĐĩĐģиĐģиŅŅ ŅŅиĐŧ аĐģŅйОĐŧĐžĐŧ ŅĐž вŅĐĩĐŧи ĐŋĐžĐģŅСОваŅĐĩĐģŅĐŧи иĐģи Ņ Đ˛Đ°Ņ ĐŊĐĩŅ ĐŋĐžĐģŅСОваŅĐĩĐģĐĩĐš, Ņ ĐēĐžŅĐžŅŅĐŧи ĐŧĐžĐļĐŊĐž ĐŋОдĐĩĐģиŅŅŅŅ.", "album_thumbnail_card_item": "1 ŅĐģĐĩĐŧĐĩĐŊŅ", "album_thumbnail_card_items": "{} ŅĐģĐĩĐŧĐĩĐŊŅОв", - "album_thumbnail_card_shared": "¡ ĐĐąŅиК", + "album_thumbnail_card_shared": " ¡ ĐĐąŅиК", "album_thumbnail_shared_by": "ĐОдĐĩĐģиĐģŅŅ {}", "album_updated": "ĐĐģŅйОĐŧ ОйĐŊОвĐģŅĐŊ", "album_updated_setting_description": "ĐĐžĐģŅŅаŅŅ ŅвĐĩĐ´ĐžĐŧĐģĐĩĐŊиĐĩ ĐŋĐž ŅĐģĐĩĐēŅŅĐžĐŊĐŊОК ĐŋĐžŅŅĐĩ ĐŋŅи дОйавĐģĐĩĐŊии ĐŊОвŅŅ ŅĐĩŅŅŅŅОв в ОйŅиК аĐģŅйОĐŧ", @@ -477,15 +481,15 @@ "assets_added_to_album_count": "РаĐģŅйОĐŧ дОйавĐģĐĩĐŊĐž {count, plural, one {# ОйŅĐĩĐēŅ} few {# ОйŅĐĩĐēŅа} other {# ОйŅĐĩĐēŅОв}}", "assets_added_to_name_count": "ĐОйавĐģĐĩĐŊĐž {count, plural, one {# ОйŅĐĩĐēŅ} few {# ОйŅĐĩĐēŅа} other {# ОйŅĐĩĐēŅОв}} в {hasName, select, true {<b>{name}</b>} other {ĐŊОвŅĐš аĐģŅйОĐŧ}}", "assets_count": "{count, plural, one {# ОйŅĐĩĐēŅ} few {# ОйŅĐĩĐēŅа} other {# ОйŅĐĩĐēŅОв}}", - "assets_deleted_permanently": "{} ОйŅĐĩĐēŅ(Ņ) ŅдаĐģĐĩĐŊ(Ņ) ĐŊавŅĐĩĐŗĐ´Đ°", - "assets_deleted_permanently_from_server": "{} ОйŅĐĩĐēŅ(Ņ) ŅдаĐģĐĩĐŊ(Ņ) ĐŊавŅĐĩĐŗĐ´Đ° Ņ ŅĐĩŅвĐĩŅа Immich", + "assets_deleted_permanently": "{} ОйŅĐĩĐēŅ(Ņ) ŅдаĐģĐĩĐŊ(Ņ) ĐŊавŅĐĩĐŗĐ´Đ°", + "assets_deleted_permanently_from_server": "{} ОйŅĐĩĐēŅ(Ņ) ŅдаĐģĐĩĐŊ(Ņ) ĐŊавŅĐĩĐŗĐ´Đ° Ņ ŅĐĩŅвĐĩŅа Immich", "assets_moved_to_trash_count": "{count, plural, one {# ОйŅĐĩĐēŅ} few {# ОйŅĐĩĐēŅа} other {# ОйŅĐĩĐēŅОв}} ĐŋĐĩŅĐĩĐŧĐĩŅĐĩĐŊĐž в ĐēĐžŅСиĐŊŅ", "assets_permanently_deleted_count": "{count, plural, one {# ОйŅĐĩĐēŅ} few {# ОйŅĐĩĐēŅа} other {# ОйŅĐĩĐēŅОв}} ŅдаĐģĐĩĐŊĐž ĐŊавŅĐĩĐŗĐ´Đ°", "assets_removed_count": "{count, plural, one {# ОйŅĐĩĐēŅ} few {# ОйŅĐĩĐēŅа} other {# ОйŅĐĩĐēŅОв}} ŅдаĐģĐĩĐŊĐž", - "assets_removed_permanently_from_device": "{} ОйŅĐĩĐēŅ(Ņ) ŅдаĐģĐĩĐŊ(Ņ) ĐŊавŅĐĩĐŗĐ´Đ° Ņ Đ˛Đ°ŅĐĩĐŗĐž ŅŅŅŅОКŅŅва", + "assets_removed_permanently_from_device": "{} ОйŅĐĩĐēŅ(Ņ) ŅдаĐģĐĩĐŊ(Ņ) ĐŊавŅĐĩĐŗĐ´Đ° Ņ Đ˛Đ°ŅĐĩĐŗĐž ŅŅŅŅОКŅŅва", "assets_restore_confirmation": "ĐŅ ŅвĐĩŅĐĩĐŊŅ, ŅŅĐž Ņ ĐžŅиŅĐĩ вОŅŅŅаĐŊОвиŅŅ Đ˛ŅĐĩ ОйŅĐĩĐēŅŅ Đ¸Đˇ ĐēĐžŅСиĐŊŅ? ĐŅĐž Đ´ĐĩĐšŅŅвиĐĩ ĐŊĐĩĐģŅĐˇŅ ĐžŅĐŧĐĩĐŊиŅŅ! ĐĐąŅаŅиŅĐĩ вĐŊиĐŧаĐŊиĐĩ, ŅŅĐž ĐģŅĐąŅĐĩ ĐžŅŅĐģаКĐŊ-ОйŅĐĩĐēŅŅ ĐŊĐĩ ĐŧĐžĐŗŅŅ ĐąŅŅŅ Đ˛ĐžŅŅŅаĐŊОвĐģĐĩĐŊŅ ŅаĐēиĐŧ ŅĐŋĐžŅОйОĐŧ.", "assets_restored_count": "{count, plural, one {# ОйŅĐĩĐēŅ} few {# ОйŅĐĩĐēŅа} other {# ОйŅĐĩĐēŅОв}} вОŅŅŅаĐŊОвĐģĐĩĐŊĐž", - "assets_restored_successfully": "{} ОйŅĐĩĐēŅ(Ņ) ŅŅĐŋĐĩŅĐŊĐž вОŅŅŅаĐŊОвĐģĐĩĐŊ(Ņ)", + "assets_restored_successfully": "{} ОйŅĐĩĐēŅ(Ņ) ŅŅĐŋĐĩŅĐŊĐž вОŅŅŅаĐŊОвĐģĐĩĐŊ(Ņ)", "assets_trashed": "{} ОйŅĐĩĐēŅ(Ņ) ĐŋĐžĐŧĐĩŅĐĩĐŊ(Ņ) в ĐēĐžŅСиĐŊŅ", "assets_trashed_count": "{count, plural, one {# ОйŅĐĩĐēŅ} few {# ОйŅĐĩĐēŅа} other {# ОйŅĐĩĐēŅОв}} ĐŋĐĩŅĐĩĐŧĐĩŅĐĩĐŊĐž в ĐēĐžŅСиĐŊŅ", "assets_trashed_from_server": "{} ОйŅĐĩĐēŅ(Ņ) ĐŋĐžĐŧĐĩŅĐĩĐŊ(Ņ) в ĐēĐžŅСиĐŊŅ ĐŊа ŅĐĩŅвĐĩŅĐĩ Immich", @@ -498,14 +502,14 @@ "background_location_permission": "ĐĐžŅŅŅĐŋ Đē ĐŧĐĩŅŅĐžĐŋĐžĐģĐžĐļĐĩĐŊĐ¸Ņ Đ˛ ŅĐžĐŊĐĩ", "background_location_permission_content": "ЧŅĐžĐąŅ ŅŅиŅŅваŅŅ Đ¸ĐŧŅ Wi-Fi ŅĐĩŅи в ŅĐžĐŊĐĩ, ĐŋŅиĐģĐžĐļĐĩĐŊĐ¸Ņ *вŅĐĩĐŗĐ´Đ°* ĐŊĐĩĐžĐąŅ ĐžĐ´Đ¸Đŧ Đ´ĐžŅŅŅĐŋ Đē ŅĐžŅĐŊĐžĐŧŅ ĐŧĐĩŅŅĐžĐŋĐžĐģĐžĐļĐĩĐŊĐ¸Ņ ŅŅŅŅОКŅŅва", "backup_album_selection_page_albums_device": "ĐĐģŅйОĐŧŅ ĐŊа ŅŅŅŅОКŅŅвĐĩ ({})", - "backup_album_selection_page_albums_tap": "ĐаĐļĐŧиŅĐĩ, ŅŅĐžĐąŅ Đ˛ĐēĐģŅŅиŅŅ,\nĐŊаĐļĐŧиŅĐĩ дваĐļĐ´Ņ, ŅŅĐžĐąŅ Đ¸ŅĐēĐģŅŅиŅŅ", + "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_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": "ĐĐ°ĐŗŅŅĐļаĐĩŅŅŅ {}", "backup_background_service_default_notification": "ĐОиŅĐē ĐŊОвŅŅ ĐžĐąŅĐĩĐēŅОвâĻ", "backup_background_service_error_title": "ĐŅийĐēа ŅĐĩСĐĩŅвĐŊĐžĐŗĐž ĐēĐžĐŋиŅОваĐŊиŅ", @@ -533,7 +537,7 @@ "backup_controller_page_backup_sub": "ĐĐ°ĐŗŅŅĐļĐĩĐŊĐŊŅĐĩ ŅĐžŅĐž и видĐĩĐž", "backup_controller_page_created": "ХОСдаĐŊĐž: {}", "backup_controller_page_desc_backup": "ĐĐēĐģŅŅиŅĐĩ ŅĐĩСĐĩŅвĐŊĐžĐĩ ĐēĐžĐŋиŅОваĐŊиĐĩ в аĐēŅивĐŊĐžĐŧ ŅĐĩĐļиĐŧĐĩ, ŅŅĐžĐąŅ Đ°Đ˛ŅĐžĐŧаŅиŅĐĩŅĐēи ĐˇĐ°ĐŗŅŅĐļаŅŅ ĐŊОвŅĐĩ ОйŅĐĩĐēŅŅ ĐŋŅи ĐžŅĐēŅŅŅии ĐŋŅиĐģĐžĐļĐĩĐŊиŅ.", - "backup_controller_page_excluded": "ĐŅĐēĐģŅŅĐĩĐŊŅ:", + "backup_controller_page_excluded": "ĐŅĐēĐģŅŅĐĩĐŊŅ: ", "backup_controller_page_failed": "ĐĐĩŅдаŅĐŊŅŅ ({})", "backup_controller_page_filename": "ĐĐŧŅ ŅаКĐģа: {} [{}]", "backup_controller_page_id": "ID: {}", @@ -615,7 +619,7 @@ "check_all": "ĐŅĐąŅаŅŅ Đ˛ŅŅ", "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": "ĐĐžŅОд", @@ -631,7 +635,7 @@ "client_cert_invalid_msg": "ĐĐĩвĐĩŅĐŊŅĐš ŅаКĐģ ŅĐĩŅŅиŅиĐēаŅа иĐģи ĐŊĐĩвĐĩŅĐŊŅĐš ĐŋаŅĐžĐģŅ", "client_cert_remove_msg": "ĐĐģиĐĩĐŊŅŅĐēиК ŅĐĩŅŅиŅиĐēĐ°Ņ ŅдаĐģĐĩĐŊ", "client_cert_subtitle": "ĐОддĐĩŅĐļиваĐĩŅŅŅ ŅĐžĐģŅĐēĐž ŅĐžŅĐŧĐ°Ņ PKCS12 (.p12, .pfx). ĐĐŧĐŋĐžŅŅ/ŅдаĐģĐĩĐŊиĐĩ ŅĐĩŅŅиŅиĐēаŅа Đ´ĐžŅŅŅĐŋĐŊĐž ŅĐžĐģŅĐēĐž ĐŋĐĩŅĐĩĐ´ Đ˛Ņ ĐžĐ´ĐžĐŧ в ŅиŅŅĐĩĐŧŅ", - "client_cert_title": "ĐĐģиĐĩĐŊŅŅĐēиК SSL-ŅĐĩŅŅиŅиĐēĐ°Ņ ", + "client_cert_title": "ĐĐģиĐĩĐŊŅŅĐēиК SSL-ŅĐĩŅŅиŅиĐēаŅ", "clockwise": "ĐĐž ŅаŅОвОК", "close": "ĐаĐēŅŅŅŅ", "collapse": "ХвĐĩŅĐŊŅŅŅ", @@ -656,11 +660,11 @@ "continue": "ĐŅОдОĐģĐļиŅŅ", "control_bottom_app_bar_album_info_shared": "{} ŅĐģĐĩĐŧĐĩĐŊŅОв ¡ ĐĐąŅиК", "control_bottom_app_bar_create_new_album": "ХОСдаŅŅ Đ°ĐģŅйОĐŧ", - "control_bottom_app_bar_delete_from_immich": "ĐŖĐ´Đ°ĐģиŅŅ Đ¸Đˇ Immich\n", + "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": "Share Link", + "control_bottom_app_bar_share_link": "ĐОдĐĩĐģиŅŅŅŅ ŅŅŅĐģĐēОК", "control_bottom_app_bar_share_to": "ĐОдĐĩĐģиŅŅŅŅ", "control_bottom_app_bar_trash_from_immich": "Đ ĐēĐžŅСиĐŊŅ", "copied_image_to_clipboard": "ĐСОйŅаĐļĐĩĐŊиĐĩ ŅĐēĐžĐŋиŅОваĐŊĐž в ĐąŅŅĐĩŅ ĐžĐąĐŧĐĩĐŊа.", @@ -807,7 +811,7 @@ "editor_crop_tool_h2_aspect_ratios": "ХООŅĐŊĐžŅĐĩĐŊĐ¸Ņ ŅŅĐžŅĐžĐŊ", "editor_crop_tool_h2_rotation": "ĐŅаŅĐĩĐŊиĐĩ", "email": "ĐĐģĐĩĐēŅŅĐžĐŊĐŊĐ°Ņ ĐŋĐžŅŅа", - "empty_folder": "This folder is empty", + "empty_folder": "ĐŅŅŅĐ°Ņ ĐŋаĐŋĐēа", "empty_trash": "ĐŅиŅŅиŅŅ ĐēĐžŅСиĐŊŅ", "empty_trash_confirmation": "ĐŅ ŅвĐĩŅĐĩĐŊŅ, ŅŅĐž Ņ ĐžŅиŅĐĩ ĐžŅиŅŅиŅŅ ĐēĐžŅСиĐŊŅ? ĐŅĐĩ ОйŅĐĩĐēŅŅ Đ˛ ĐēĐžŅСиĐŊĐĩ ĐąŅĐ´ŅŅ ĐŊавŅĐĩĐŗĐ´Đ° ŅдаĐģĐĩĐŊŅ Đ¸Đˇ Immich.\nĐŅ ĐŊĐĩ ŅĐŧĐžĐļĐĩŅĐĩ ĐžŅĐŧĐĩĐŊиŅŅ ŅŅĐž Đ´ĐĩĐšŅŅвиĐĩ!", "enable": "ĐĐēĐģŅŅиŅŅ", @@ -953,10 +957,10 @@ "exif_bottom_sheet_location": "ĐĐĐĄĐĸĐ", "exif_bottom_sheet_people": "ĐĐŽĐĐ", "exif_bottom_sheet_person_add_person": "ĐОйавиŅŅ Đ¸ĐŧŅ", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "ĐОСŅаŅŅ {}", + "exif_bottom_sheet_person_age_months": "ĐОСŅаŅŅ {} ĐŧĐĩŅŅŅĐĩв", + "exif_bottom_sheet_person_age_year_months": "ĐОСŅаŅŅ 1 ĐŗĐžĐ´, {} ĐŧĐĩŅŅŅĐĩв", + "exif_bottom_sheet_person_age_years": "ĐОСŅаŅŅ {}", "exit_slideshow": "ĐŅĐšŅи иС ŅĐģаКд-ŅĐžŅ", "expand_all": "РаСвĐĩŅĐŊŅŅŅ Đ˛ŅŅ", "experimental_settings_new_asset_list_subtitle": "Đ ŅаСŅайОŅĐēĐĩ", @@ -978,7 +982,7 @@ "face_unassigned": "ĐĐĩ ĐŊаСĐŊаŅĐĩĐŊĐž", "failed": "ĐŅийĐēа", "failed_to_load_assets": "ĐĐĩ ŅдаĐģĐžŅŅ ĐˇĐ°ĐŗŅŅСиŅŅ ĐžĐąŅĐĩĐēŅŅ", - "failed_to_load_folder": "Failed to load folder", + "failed_to_load_folder": "ĐŅийĐēа ĐŋŅи ĐˇĐ°ĐŗŅŅСĐēĐĩ ĐŋаĐŋĐēи", "favorite": "ĐСйŅаĐŊĐŊĐžĐĩ", "favorite_or_unfavorite_photo": "ĐОйавиŅŅ Đ¸Đģи ŅдаĐģиŅŅ ŅĐžŅĐžĐŗŅаŅĐ¸Ņ Đ¸Đˇ иСйŅаĐŊĐŊĐžĐŗĐž", "favorites": "ĐСйŅаĐŊĐŊĐžĐĩ", @@ -992,10 +996,11 @@ "filetype": "ĐĸиĐŋ ŅаКĐģа", "filter": "ФиĐģŅŅŅ", "filter_people": "ФиĐģŅŅŅ ĐŋĐž ĐģŅĐ´ŅĐŧ", + "filter_places": "ФиĐģŅŅŅ ĐŋĐž ĐŧĐĩŅŅаĐŧ", "find_them_fast": "ĐŅŅŅŅĐž ĐŊаКдиŅĐĩ Đ¸Ņ ĐŋĐž иĐŧĐĩĐŊи Ņ ĐŋĐžĐŧĐžŅŅŅ ĐŋОиŅĐēа", "fix_incorrect_match": "ĐŅĐŋŅавиŅŅ ĐŊĐĩĐŋŅавиĐģŅĐŊĐžĐĩ ŅООŅвĐĩŅŅŅвиĐĩ", - "folder": "Folder", - "folder_not_found": "Folder not found", + "folder": "ĐаĐŋĐēа", + "folder_not_found": "ĐаĐŋĐēа ĐŊĐĩ ĐŊаКдĐĩĐŊа", "folders": "ĐаĐŋĐēи", "folders_feature_description": "ĐŅĐžŅĐŧĐžŅŅ ĐŋаĐŋĐžĐē Ņ ŅĐžŅĐžĐŗŅаŅиŅĐŧи и видĐĩĐž в ŅаКĐģОвОК ŅиŅŅĐĩĐŧĐĩ", "forward": "ĐĐŋĐĩŅŅĐ´", @@ -1040,7 +1045,7 @@ "home_page_delete_remote_err_local": "ĐĐĩвОСĐŧĐžĐļĐŊĐž ŅдаĐģиŅŅ ĐģĐžĐēаĐģŅĐŊŅĐĩ ŅаКĐģŅ Ņ ŅĐĩŅвĐĩŅа, ĐŋŅĐžĐŋŅŅĐē", "home_page_favorite_err_local": "ĐĐžĐēа ĐŊĐĩĐģŅĐˇŅ Đ´ĐžĐąĐ°Đ˛Đ¸ŅŅ Đ˛ иСйŅаĐŊĐŊĐžĐĩ ĐģĐžĐēаĐģŅĐŊŅĐĩ ŅаКĐģŅ, ĐŋŅĐžĐŋŅŅĐē", "home_page_favorite_err_partner": "ĐĐžĐēа ĐŊĐĩĐģŅĐˇŅ Đ´ĐžĐąĐ°Đ˛Đ¸ŅŅ Đ˛ иСйŅаĐŊĐŊĐžĐĩ ĐŧĐĩдиа ĐŋаŅŅĐŊĐĩŅа, ĐŋŅĐžĐŋŅŅĐē", - "home_page_first_time_notice": "ĐŅĐģи Đ˛Ņ Đ¸ŅĐŋĐžĐģŅСŅĐĩŅĐĩ ĐŋŅиĐģĐžĐļĐĩĐŊиĐĩ вĐŋĐĩŅвŅĐĩ, вŅĐąĐĩŅиŅĐĩ аĐģŅйОĐŧŅ Đ´ĐģŅ ŅĐĩСĐĩŅвĐŊĐžĐŗĐž ĐēĐžĐŋиŅОваĐŊĐ¸Ņ Đ¸Đģи ĐˇĐ°ĐŗŅŅСиŅĐĩ Đ¸Ņ Đ˛ŅŅŅĐŊŅŅ, ŅŅĐžĐąŅ ĐˇĐ°ĐŋĐžĐģĐŊиŅŅ Đ¸Đŧи вŅĐĩĐŧĐĩĐŊĐŊŅŅ ŅĐēаĐģŅ.", + "home_page_first_time_notice": "ĐĐĩŅĐĩĐ´ ĐŊаŅаĐģĐžĐŧ иŅĐŋĐžĐģŅСОваĐŊĐ¸Ņ ĐŋŅиĐģĐžĐļĐĩĐŊĐ¸Ņ Đ˛ŅĐąĐĩŅиŅĐĩ аĐģŅйОĐŧ Ņ ĐžĐąŅĐĩĐēŅаĐŧи Đ´ĐģŅ ŅĐĩСĐĩŅвĐŊĐžĐŗĐž ĐēĐžĐŋиŅОваĐŊиŅ, ŅŅĐžĐąŅ ĐžĐŊи ĐžŅОйŅаСиĐģиŅŅ ĐŊа вŅĐĩĐŧĐĩĐŊĐŊОК ŅĐēаĐģĐĩ", "home_page_share_err_local": "ĐĐĩĐģŅĐˇŅ ĐŋОдĐĩĐģиŅŅŅŅ ĐģĐžĐēаĐģŅĐŊŅĐŧи ŅаКĐģаĐŧи ĐŋĐž ŅŅŅĐģĐēĐĩ, ĐŋŅĐžĐŋŅŅĐē", "home_page_upload_err_limit": "ĐŅ ĐŧĐžĐļĐĩŅĐĩ ĐˇĐ°ĐŗŅŅСиŅŅ ĐŧаĐēŅиĐŧŅĐŧ 30 ŅаКĐģОв Са ŅаС, ĐŋŅĐžĐŋŅŅĐē", "host": "ĐĨĐžŅŅ", @@ -1145,7 +1150,7 @@ "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_handshake_exception": "ĐŅийĐēа ĐŋŅОвĐĩŅĐēи ŅĐĩŅŅиŅиĐēаŅа. ĐŅĐģи Đ˛Ņ Đ¸ŅĐŋĐžĐģŅСŅĐĩŅĐĩ ŅаĐŧĐžĐŋОдĐŋиŅаĐŊĐŊŅĐš ŅĐĩŅŅиŅиĐēаŅ, вĐēĐģŅŅиŅĐĩ ĐŋОддĐĩŅĐļĐēŅ ŅаĐŧĐžĐŋОдĐŋиŅаĐŊĐŊŅŅ ŅĐĩŅŅиŅиĐēаŅОв в ĐŊаŅŅŅОКĐēĐ°Ņ .", "login_form_password_hint": "ĐŋаŅĐžĐģŅ", "login_form_save_login": "ĐŅŅаваŅŅŅŅ Đ˛ ŅиŅŅĐĩĐŧĐĩ", "login_form_server_empty": "ĐвĐĩдиŅĐĩ URL-адŅĐĩŅ ŅĐĩŅвĐĩŅа.", @@ -1185,7 +1190,7 @@ "map_settings": "ĐаŅŅŅОКĐēи ĐēаŅŅŅ", "map_settings_dark_mode": "ĐĸĐĩĐŧĐŊŅĐš ŅĐĩĐļиĐŧ", "map_settings_date_range_option_day": "24 ŅаŅа", - "map_settings_date_range_option_days": "{} Đ´ĐŊĐĩĐš", + "map_settings_date_range_option_days": "ĐĐžŅĐģĐĩĐ´ĐŊиĐĩ {} Đ´ĐŊĐĩĐš", "map_settings_date_range_option_year": "ĐОд", "map_settings_date_range_option_years": "{} ĐŗĐžĐ´Đ°", "map_settings_dialog_title": "ĐаŅŅŅОКĐēи ĐēаŅŅŅ", @@ -1261,7 +1266,7 @@ "note_apply_storage_label_to_previously_uploaded assets": "ĐŅиĐŧĐĩŅаĐŊиĐĩ: ЧŅĐžĐąŅ ĐŋŅиĐŧĐĩĐŊиŅŅ ŅĐĩĐŗ Ņ ŅаĐŊиĐģиŅа Đē ŅаĐŊĐĩĐĩ ĐˇĐ°ĐŗŅŅĐļĐĩĐŊĐŊŅĐŧ ŅĐĩŅŅŅŅаĐŧ, СаĐŋŅŅŅиŅĐĩ", "notes": "ĐŅиĐŧĐĩŅаĐŊиĐĩ", "notification_permission_dialog_content": "ЧŅĐžĐąŅ Đ˛ĐēĐģŅŅиŅŅ ŅвĐĩĐ´ĐžĐŧĐģĐĩĐŊиŅ, ĐŋĐĩŅĐĩКдиŅĐĩ в ÂĢĐаŅŅŅОКĐēиÂģ и вŅĐąĐĩŅиŅĐĩ ÂĢРаСŅĐĩŅиŅŅÂģ.", - "notification_permission_list_tile_content": "ĐŅĐĩĐ´ĐžŅŅавŅŅĐĩ ŅаСŅĐĩŅĐĩĐŊиĐĩ ĐŊа вĐēĐģŅŅĐĩĐŊиĐĩ ŅвĐĩĐ´ĐžĐŧĐģĐĩĐŊиК", + "notification_permission_list_tile_content": "ĐŅĐĩĐ´ĐžŅŅавŅŅĐĩ ŅаСŅĐĩŅĐĩĐŊиĐĩ ĐŊа ĐŋĐžĐēаС ŅвĐĩĐ´ĐžĐŧĐģĐĩĐŊиК.", "notification_permission_list_tile_enable_button": "ĐĐēĐģŅŅиŅŅ ŅвĐĩĐ´ĐžĐŧĐģĐĩĐŊиŅ", "notification_permission_list_tile_title": "РаСŅĐĩŅĐĩĐŊиĐĩ ĐŊа ŅвĐĩĐ´ĐžĐŧĐģĐĩĐŊиĐĩ", "notification_toggle_setting_description": "ĐĐēĐģŅŅиŅŅ ŅвĐĩĐ´ĐžĐŧĐģĐĩĐŊĐ¸Ņ ĐŋĐž ŅĐģĐĩĐēŅŅĐžĐŊĐŊОК ĐŋĐžŅŅĐĩ", @@ -1282,6 +1287,7 @@ "onboarding_welcome_user": "ĐОйŅĐž ĐŋĐžĐļаĐģОваŅŅ, {user}", "online": "ĐĐžŅŅŅĐŋĐĩĐŊ", "only_favorites": "ĐĸĐžĐģŅĐēĐž иСйŅаĐŊĐŊĐžĐĩ", + "open": "ĐŅĐēŅŅŅŅ", "open_in_map_view": "ĐŅĐēŅŅŅŅ Đ˛ ŅĐĩĐļиĐŧĐĩ ĐŋŅĐžŅĐŧĐžŅŅа ĐēаŅŅŅ", "open_in_openstreetmap": "ĐŅĐēŅŅŅŅ Đ˛ OpenStreetMap", "open_the_search_filters": "ĐŅĐēŅŅŅŅ ŅиĐģŅŅŅŅ ĐŋОиŅĐēа", @@ -1298,14 +1304,14 @@ "partner_can_access": "{partner} иĐŧĐĩĐĩŅ Đ´ĐžŅŅŅĐŋ", "partner_can_access_assets": "ĐŅĐĩ ваŅи ŅĐžŅĐžĐŗŅаŅии и видĐĩОСаĐŋиŅи, ĐēŅĐžĐŧĐĩ ŅĐĩŅ , ĐēĐžŅĐžŅŅĐĩ ĐŊĐ°Ņ ĐžĐ´ŅŅŅŅ Đ˛ ĐŅŅ Đ¸Đ˛Đĩ и ĐĐžŅСиĐŊĐĩ", "partner_can_access_location": "ĐĐĩŅŅĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ, ĐŗĐ´Đĩ ĐąŅĐģи ŅĐ´ĐĩĐģаĐŊŅ Đ˛Đ°Ņи ŅĐžŅĐžĐŗŅаŅии", - "partner_list_user_photos": "ФОŅĐžĐŗŅаŅии {user}", + "partner_list_user_photos": "ФОŅĐžĐŗŅаŅии ĐŋĐžĐģŅСОваŅĐĩĐģŅ {user}", "partner_list_view_all": "ĐĐžŅĐŧĐžŅŅĐĩŅŅ Đ˛ŅĐĩ", - "partner_page_empty_message": "ĐŖ ваŅĐĩĐŗĐž ĐŋаŅŅĐŊŅŅа ĐĩŅĐĩ ĐŊĐĩŅ Đ´ĐžŅŅŅĐŋа Đē ваŅиĐŧ ŅĐžŅĐž", + "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_page_stop_sharing_content": "{} йОĐģŅŅĐĩ ĐŊĐĩ ŅĐŧĐžĐļĐĩŅ ĐŋĐžĐģŅŅиŅŅ Đ´ĐžŅŅŅĐŋ Đē ваŅиĐŧ ŅĐžŅĐžĐŗŅаŅиŅĐŧ.", "partner_sharing": "ХОвĐŧĐĩŅŅĐŊĐžĐĩ иŅĐŋĐžĐģŅСОваĐŊиĐĩ", "partners": "ĐаŅŅĐŊŅŅŅ", "password": "ĐаŅĐžĐģŅ", @@ -1341,7 +1347,7 @@ "permission_onboarding_permission_denied": "ĐĐĩ ŅдаĐģĐžŅŅ ĐŋĐžĐģŅŅиŅŅ Đ´ĐžŅŅŅĐŋ. ЧŅĐžĐąŅ Đ¸ŅĐŋĐžĐģŅСОваŅŅ ĐŋŅиĐģĐžĐļĐĩĐŊиĐĩ, ŅаСŅĐĩŅиŅĐĩ Đ´ĐžŅŅŅĐŋ Đē \"ФОŅĐž и видĐĩĐž\" в ĐŊаŅŅŅОКĐēĐ°Ņ .", "permission_onboarding_permission_granted": "ĐĐžŅŅŅĐŋ ĐŋĐžĐģŅŅĐĩĐŊ! ĐŅŅ ĐŗĐžŅОвО.", "permission_onboarding_permission_limited": "ĐĐžŅŅŅĐŋ Đē ŅаКĐģаĐŧ ĐžĐŗŅаĐŊиŅĐĩĐŊ. ЧŅĐžĐąŅ Immich ĐŧĐžĐŗ ŅОСдаваŅŅ ŅĐĩСĐĩŅвĐŊŅĐĩ ĐēĐžĐŋии и ŅĐŋŅавĐģŅŅŅ Đ˛Đ°ŅĐĩĐš ĐŗĐ°ĐģĐĩŅĐĩĐĩĐš, ĐŋĐžĐļаĐģŅĐšŅŅа, ĐŋŅĐĩĐ´ĐžŅŅавŅŅĐĩ ĐŋŅиĐģĐžĐļĐĩĐŊĐ¸Ņ ŅаСŅĐĩŅĐĩĐŊиĐĩ ĐŊа Đ´ĐžŅŅŅĐŋ Đē \"ФОŅĐž и видĐĩĐž\" в ĐŊаŅŅŅОКĐēĐ°Ņ .", - "permission_onboarding_request": "ĐŅиĐģĐžĐļĐĩĐŊĐ¸Ņ ĐŊĐĩĐžĐąŅ ĐžĐ´Đ¸ĐŧĐž ŅаСŅĐĩŅĐĩĐŊиĐĩ ĐŊа Đ´ĐžŅŅŅĐŋ Đē ваŅиĐŧ ŅĐžŅĐž и видĐĩĐž", + "permission_onboarding_request": "ĐŅиĐģĐžĐļĐĩĐŊĐ¸Ņ ĐŊĐĩĐžĐąŅ ĐžĐ´Đ¸ĐŧĐž ŅаСŅĐĩŅĐĩĐŊиĐĩ ĐŊа Đ´ĐžŅŅŅĐŋ Đē ваŅиĐŧ ŅĐžŅĐž и видĐĩĐž.", "person": "ЧĐĩĐģОвĐĩĐē", "person_birthdate": "ĐаŅа ŅĐžĐļĐ´ĐĩĐŊиŅ: {date}", "person_hidden": "{name}{hidden, select, true { (ŅĐēŅŅŅ)} other {}}", @@ -1510,7 +1516,7 @@ "search_filter_date_title": "ĐŅĐąĐĩŅиŅĐĩ ĐŋŅĐžĐŧĐĩĐļŅŅĐžĐē", "search_filter_display_option_not_in_album": "ĐĐĩ в аĐģŅйОĐŧĐĩ", "search_filter_display_options": "ĐаŅŅŅОКĐēи ĐžŅОйŅаĐļĐĩĐŊиŅ", - "search_filter_filename": "Search by file name", + "search_filter_filename": "ĐОиŅĐē ĐŋĐž иĐŧĐĩĐŊи ŅаКĐģа", "search_filter_location": "ĐĐĩŅŅĐž", "search_filter_location_title": "ĐŅĐąĐĩŅиŅĐĩ ĐŧĐĩŅŅĐž", "search_filter_media_type": "ĐĸиĐŋ ŅаКĐģа", @@ -1518,10 +1524,10 @@ "search_filter_people_title": "ĐŅĐąĐĩŅиŅĐĩ ĐģŅĐ´ĐĩĐš", "search_for": "ĐОиŅĐē ĐŋĐž", "search_for_existing_person": "ĐОиŅĐē ŅŅŅĐĩŅŅвŅŅŅĐĩĐŗĐž ŅĐĩĐģОвĐĩĐēа", - "search_no_more_result": "No more results", + "search_no_more_result": "ĐĐžĐģŅŅĐĩ ŅĐĩСŅĐģŅŅаŅОв ĐŊĐĩŅ", "search_no_people": "ĐĐĩŅ ĐģŅĐ´ĐĩĐš", "search_no_people_named": "ĐĐĩŅ ĐģŅĐ´ĐĩĐš Ņ Đ¸ĐŧĐĩĐŊĐĩĐŧ \"{name}\"", - "search_no_result": "No results found, try a different search term or combination", + "search_no_result": "ĐиŅĐĩĐŗĐž ĐŊĐĩ ĐŊаКдĐĩĐŊĐž, ĐŋĐžĐŋŅОйŅĐšŅĐĩ иСĐŧĐĩĐŊиŅŅ ĐŋОиŅĐēОвŅĐš СаĐŋŅĐžŅ", "search_options": "ĐаŅаĐŧĐĩŅŅŅ ĐŋОиŅĐēа", "search_page_categories": "ĐаŅĐĩĐŗĐžŅии", "search_page_motion_photos": "ĐиĐŊаĐŧиŅĐĩŅĐēиĐĩ ŅĐžŅĐž", @@ -1538,9 +1544,9 @@ "search_places": "ĐОиŅĐē ĐŧĐĩŅŅ", "search_rating": "ĐОиŅĐē ĐŋĐž ŅĐĩĐšŅиĐŊĐŗŅ...", "search_result_page_new_search_hint": "ĐОвŅĐš ĐŋОиŅĐē", - "search_settings": "ĐаŅŅŅОКĐēи ĐŋОиŅĐēа", + "search_settings": "ĐОиŅĐē ĐŊаŅŅŅĐžĐĩĐē", "search_state": "ĐОиŅĐē ŅĐĩĐŗĐ¸ĐžĐŊа...", - "search_suggestion_list_smart_search_hint_1": "ĐĐŊŅĐĩĐģĐģĐĩĐēŅŅаĐģŅĐŊŅĐš ĐŋОиŅĐē вĐēĐģŅŅĐĩĐŊ ĐŋĐž ŅĐŧĐžĐģŅаĐŊиŅ, Đ´ĐģŅ ĐŋОиŅĐēа ĐŧĐĩŅадаĐŊĐŊŅŅ Đ¸ŅĐŋĐžĐģŅСŅĐšŅĐĩ ŅĐŋĐĩŅиаĐģŅĐŊŅĐš ŅиĐŊŅаĐēŅиŅ", + "search_suggestion_list_smart_search_hint_1": "ĐĐŊŅĐĩĐģĐģĐĩĐēŅŅаĐģŅĐŊŅĐš ĐŋОиŅĐē вĐēĐģŅŅĐĩĐŊ ĐŋĐž ŅĐŧĐžĐģŅаĐŊиŅ, Đ´ĐģŅ ĐŋОиŅĐēа ĐŧĐĩŅадаĐŊĐŊŅŅ Đ¸ŅĐŋĐžĐģŅСŅĐšŅĐĩ ŅĐŋĐĩŅиаĐģŅĐŊŅĐš ŅиĐŊŅаĐēŅĐ¸Ņ ", "search_suggestion_list_smart_search_hint_2": "m:ваŅ-ĐŋОиŅĐēОвŅĐš-СаĐŋŅĐžŅ", "search_tags": "ĐОиŅĐē ĐŋĐž ŅĐĩĐŗĐ°Đŧ...", "search_timezone": "ĐОиŅĐē ŅаŅĐžĐ˛ĐžĐŗĐž ĐŋĐžŅŅа...", @@ -1581,10 +1587,10 @@ "set_date_of_birth": "ĐŖŅŅаĐŊОвиŅŅ Đ´Đ°ŅŅ ŅĐžĐļĐ´ĐĩĐŊиŅ", "set_profile_picture": "ĐŖŅŅаĐŊОвиŅŅ Đ¸ĐˇĐžĐąŅаĐļĐĩĐŊиĐĩ ĐŋŅĐžŅиĐģŅ", "set_slideshow_to_fullscreen": "ĐĐĩŅĐĩвĐĩдиŅĐĩ ŅĐģаКд-ŅĐžŅ Đ˛ ĐŋĐžĐģĐŊĐžŅĐēŅаĐŊĐŊŅĐš ŅĐĩĐļиĐŧ", - "setting_image_viewer_help": "ĐŅи ĐŋŅĐžŅĐŧĐžŅŅĐĩ иСОйŅаĐļĐĩĐŊĐ¸Ņ ŅĐŋĐĩŅва ĐˇĐ°ĐŗŅŅĐļаĐĩŅŅŅ ĐŧиĐŊиаŅŅŅа, СаŅĐĩĐŧ \nŅĐŧĐĩĐŊŅŅĐĩĐŊĐŊĐžĐĩ иСОйŅаĐļĐĩĐŊиĐĩ ŅŅĐĩĐ´ĐŊĐĩĐŗĐž ĐēаŅĐĩŅŅва (ĐĩŅĐģи вĐēĐģŅŅĐĩĐŊĐž), а СаŅĐĩĐŧ ĐžŅĐ¸ĐŗĐ¸ĐŊаĐģ (ĐĩŅĐģи вĐēĐģŅŅĐĩĐŊĐž).", - "setting_image_viewer_original_subtitle": "ĐĐēĐģŅŅиŅĐĩ Đ´ĐģŅ ĐˇĐ°ĐŗŅŅСĐēи иŅŅ ĐžĐ´ĐŊĐžĐŗĐž иСОйŅаĐļĐĩĐŊĐ¸Ņ Đ˛ ĐŋĐžĐģĐŊĐžĐŧ ŅаСŅĐĩŅĐĩĐŊии (йОĐģŅŅĐžĐĩ!).\nĐŅĐēĐģŅŅиŅĐĩ, ŅŅĐžĐąŅ ŅĐŧĐĩĐŊŅŅиŅŅ ĐžĐąŅĐĩĐŧ даĐŊĐŊŅŅ (ĐēаĐē ŅĐĩŅи, ŅаĐē и ĐēŅŅа ŅŅŅŅОКŅŅва).", + "setting_image_viewer_help": "ĐŅи ĐŋŅĐžŅĐŧĐžŅŅĐĩ иСОйŅаĐļĐĩĐŊĐ¸Ņ ŅĐŋĐĩŅва ĐˇĐ°ĐŗŅŅĐļаĐĩŅŅŅ ĐŧиĐŊиаŅŅŅа, СаŅĐĩĐŧ ŅĐŧĐĩĐŊŅŅĐĩĐŊĐŊĐžĐĩ иСОйŅаĐļĐĩĐŊиĐĩ ŅŅĐĩĐ´ĐŊĐĩĐŗĐž ĐēаŅĐĩŅŅва (ĐĩŅĐģи вĐēĐģŅŅĐĩĐŊĐž), а СаŅĐĩĐŧ ĐžŅĐ¸ĐŗĐ¸ĐŊаĐģ (ĐĩŅĐģи вĐēĐģŅŅĐĩĐŊĐž).", + "setting_image_viewer_original_subtitle": "ĐĐēĐģŅŅиŅĐĩ Đ´ĐģŅ ĐˇĐ°ĐŗŅŅСĐēи иŅŅ ĐžĐ´ĐŊĐžĐŗĐž иСОйŅаĐļĐĩĐŊĐ¸Ņ Đ˛ ĐŋĐžĐģĐŊĐžĐŧ ŅаСŅĐĩŅĐĩĐŊии (йОĐģŅŅĐžĐĩ!). ĐŅĐēĐģŅŅиŅĐĩ Đ´ĐģŅ ŅĐŧĐĩĐŊŅŅĐĩĐŊĐ¸Ņ ĐžĐąŅŅĐŧа даĐŊĐŊŅŅ (ĐēаĐē ŅĐĩŅи, ŅаĐē и ĐēŅŅа ŅŅŅŅОКŅŅва).", "setting_image_viewer_original_title": "ĐĐ°ĐŗŅŅĐļаŅŅ Đ¸ŅŅ ĐžĐ´ĐŊĐžĐĩ иСОйŅаĐļĐĩĐŊиĐĩ", - "setting_image_viewer_preview_subtitle": "ĐĐēĐģŅŅиŅĐĩ Đ´ĐģŅ ĐˇĐ°ĐŗŅŅСĐēи иСОйŅаĐļĐĩĐŊĐ¸Ņ ŅŅĐĩĐ´ĐŊĐĩĐŗĐž ŅаСŅĐĩŅĐĩĐŊиŅ.\nĐŅĐēĐģŅŅиŅĐĩ, ŅŅĐžĐąŅ ĐˇĐ°ĐŗŅŅĐļаŅŅ ŅĐžĐģŅĐēĐž ĐžŅĐ¸ĐŗĐ¸ĐŊаĐģ иĐģи ĐŧиĐŊиаŅŅŅŅ.", + "setting_image_viewer_preview_subtitle": "ĐĐēĐģŅŅиŅĐĩ Đ´ĐģŅ ĐˇĐ°ĐŗŅŅСĐēи иСОйŅаĐļĐĩĐŊĐ¸Ņ ŅŅĐĩĐ´ĐŊĐĩĐŗĐž ŅаСŅĐĩŅĐĩĐŊиŅ. ĐŅĐēĐģŅŅиŅĐĩ, ŅŅĐžĐąŅ ĐˇĐ°ĐŗŅŅĐļаŅŅ ŅĐžĐģŅĐēĐž ĐžŅĐ¸ĐŗĐ¸ĐŊаĐģ иĐģи ĐŧиĐŊиаŅŅŅŅ.", "setting_image_viewer_preview_title": "ĐĐ°ĐŗŅŅĐļаŅŅ ŅĐŧĐĩĐŊŅŅĐĩĐŊĐŊĐžĐĩ иСОйŅаĐļĐĩĐŊиĐĩ", "setting_image_viewer_title": "ĐСОйŅаĐļĐĩĐŊиŅ", "setting_languages_apply": "ĐŅиĐŧĐĩĐŊиŅŅ", @@ -1602,7 +1608,7 @@ "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_subtitle": "ĐŅи вОŅĐŋŅОиСвĐĩĐ´ĐĩĐŊии видĐĩĐž Ņ ŅĐĩŅвĐĩŅа ĐˇĐ°ĐŗŅŅĐļаŅŅ ĐžŅĐ¸ĐŗĐ¸ĐŊаĐģ, даĐļĐĩ ĐĩŅĐģи Đ´ĐžŅŅŅĐŋĐŊа ŅŅаĐŊŅĐēОдиŅОваĐŊĐŊĐ°Ņ Đ˛ĐĩŅŅиŅ. ĐĐžĐļĐĩŅ ĐŋŅивĐĩŅŅи Đē ĐąŅŅĐĩŅиСаŅии. ĐидĐĩĐž, Đ´ĐžŅŅŅĐŋĐŊŅĐĩ ĐģĐžĐēаĐģŅĐŊĐž, вŅĐĩĐŗĐ´Đ° вОŅĐŋŅОиСвОдŅŅŅŅ Đ˛ иŅŅ ĐžĐ´ĐŊĐžĐŧ ĐēаŅĐĩŅŅвĐĩ.", "setting_video_viewer_original_video_title": "ĐĸĐžĐģŅĐēĐž ĐžŅĐ¸ĐŗĐ¸ĐŊаĐģŅĐŊĐžĐĩ видĐĩĐž", "settings": "ĐаŅŅŅОКĐēи", "settings_require_restart": "ĐĐžĐļаĐģŅĐšŅŅа, ĐŋĐĩŅĐĩСаĐŋŅŅŅиŅĐĩ ĐŋŅиĐģĐžĐļĐĩĐŊиĐĩ, ŅŅĐžĐąŅ Đ¸ĐˇĐŧĐĩĐŊĐĩĐŊĐ¸Ņ Đ˛ŅŅŅĐŋиĐģи в ŅиĐģŅ", @@ -1750,7 +1756,7 @@ "theme_selection_description": "ĐвŅĐžĐŧаŅиŅĐĩŅĐēи ŅŅŅаĐŊавĐģиваŅŅ ŅĐĩĐŧŅ Đ˛ СавиŅиĐŧĐžŅŅи ĐžŅ ŅиŅŅĐĩĐŧĐŊŅŅ ĐŊаŅŅŅĐžĐĩĐē ваŅĐĩĐŗĐž ĐąŅаŅСĐĩŅа", "theme_setting_asset_list_storage_indicator_title": "ĐĐžĐēаСаŅŅ Đ¸ĐŊдиĐēаŅĐžŅ Ņ ŅаĐŊиĐģиŅа ĐŊа ĐŋĐģиŅĐēĐ°Ņ ĐžĐąŅĐĩĐēŅОв", "theme_setting_asset_list_tiles_per_row_title": "ĐĐžĐģиŅĐĩŅŅвО ОйŅĐĩĐēŅОв в ŅŅŅĐžĐēĐĩ ({})", - "theme_setting_colorful_interface_subtitle": "ĐОйавиŅŅ ĐžŅŅĐĩĐŊĐžĐē Đē ŅĐžĐŊŅ", + "theme_setting_colorful_interface_subtitle": "ĐОйавиŅŅ ĐžŅŅĐĩĐŊĐžĐē Đē ŅĐžĐŊŅ.", "theme_setting_colorful_interface_title": "ĐĻвĐĩŅ ŅĐžĐŊа", "theme_setting_image_viewer_quality_subtitle": "ĐаŅŅŅОКĐēа ĐēаŅĐĩŅŅва ĐŋŅĐžŅĐŧĐžŅŅа иСОйŅаĐļĐĩĐŊиŅ", "theme_setting_image_viewer_quality_title": "ĐаŅĐĩŅŅвО ĐŋŅĐžŅĐŧĐžŅŅа иСОйŅаĐļĐĩĐŊиК", @@ -1783,8 +1789,8 @@ "trash_emptied": "ĐĐžŅСиĐŊа ĐžŅиŅĐĩĐŊа", "trash_no_results_message": "ĐĐ´ĐĩŅŅ ĐąŅĐ´ŅŅ ĐžŅОйŅаĐļаŅŅŅŅ ŅдаĐģŅĐŊĐŊŅĐĩ ŅĐžŅĐžĐŗŅаŅии и видĐĩĐž.", "trash_page_delete_all": "ĐŖĐ´Đ°ĐģиŅŅ Đ˛ŅĐĩ", - "trash_page_empty_trash_dialog_content": "ĐŅиŅŅиŅŅ ĐēĐžŅСиĐŊŅ? ĐŅи ŅаКĐģŅ ĐąŅĐ´ŅŅ ĐŊавŅĐĩĐŗĐ´Đ° ŅдаĐģĐĩĐŊŅ Đ¸Đˇ Immich.", - "trash_page_info": "ĐĐģĐĩĐŧĐĩĐŊŅŅ Đ˛ ĐēĐžŅСиĐŊĐĩ ĐąŅĐ´ŅŅ ĐžĐēĐžĐŊŅаŅĐĩĐģŅĐŊĐž ŅдаĐģĐĩĐŊŅ ŅĐĩŅĐĩС {} Đ´ĐŊĐĩĐš", + "trash_page_empty_trash_dialog_content": "ĐŅиŅŅиŅŅ ĐēĐžŅСиĐŊŅ? ĐĐąŅĐĩĐēŅŅ Đ˛ ĐŊĐĩĐš ĐąŅĐ´ŅŅ ĐŊавŅĐĩĐŗĐ´Đ° ŅдаĐģĐĩĐŊŅ Đ¸Đˇ Immich.", + "trash_page_info": "ĐĐąŅĐĩĐēŅŅ Đ˛ ĐēĐžŅСиĐŊĐĩ ĐąŅĐ´ŅŅ ĐžĐēĐžĐŊŅаŅĐĩĐģŅĐŊĐž ŅдаĐģĐĩĐŊŅ ŅĐĩŅĐĩС {} Đ´ĐŊĐĩĐš", "trash_page_no_assets": "ĐĐžŅСиĐŊа ĐŋŅŅŅа", "trash_page_restore_all": "ĐĐžŅŅŅаĐŊОвиŅŅ Đ˛ŅĐĩ", "trash_page_select_assets_btn": "ĐŅĐąŅаĐŊĐŊŅĐĩ ОйŅĐĩĐēŅŅ", @@ -1817,7 +1823,7 @@ "updated_password": "ĐаŅĐžĐģŅ ĐžĐąĐŊОвĐģŅĐŊ", "upload": "ĐĐ°ĐŗŅŅСиŅŅ", "upload_concurrency": "ĐаŅаĐģĐģĐĩĐģŅĐŊĐžŅŅŅ ĐˇĐ°ĐŗŅŅСĐēи", - "upload_dialog_info": "ĐĨĐžŅиŅĐĩ ŅОСдаŅŅ ŅĐĩСĐĩŅвĐŊŅŅ ĐēĐžĐŋĐ¸Ņ Đ˛ŅĐąŅаĐŊĐŊŅŅ ĐžĐąŅĐĩĐēŅОв ĐŊа ŅĐĩŅвĐĩŅĐĩ?", + "upload_dialog_info": "ĐĨĐžŅиŅĐĩ ĐˇĐ°ĐŗŅŅСиŅŅ Đ˛ŅĐąŅаĐŊĐŊŅĐĩ ОйŅĐĩĐēŅŅ ĐŊа ŅĐĩŅвĐĩŅ?", "upload_dialog_title": "ĐĐ°ĐŗŅŅСиŅŅ ĐžĐąŅĐĩĐēŅ", "upload_errors": "ĐĐ°ĐŗŅŅСĐēа СавĐĩŅŅĐĩĐŊа Ņ {count, plural, one {# ĐžŅийĐēОК} other {# ĐžŅийĐēаĐŧи}}, ОйĐŊОвиŅĐĩ ŅŅŅаĐŊиŅŅ, ŅŅĐžĐąŅ ŅвидĐĩŅŅ ĐŊОвŅĐĩ ĐˇĐ°ĐŗŅŅĐļĐĩĐŊĐŊŅĐĩ ŅĐĩŅŅŅŅŅ.", "upload_progress": "ĐŅŅаĐģĐžŅŅ {remaining, number} - ĐĐąŅайОŅаĐŊĐž {processed, number}/{total, number}", @@ -1849,11 +1855,11 @@ "variables": "ĐĐĩŅĐĩĐŧĐĩĐŊĐŊŅĐĩ", "version": "ĐĐĩŅŅиŅ", "version_announcement_closing": "ĐĸвОК Đ´ŅŅĐŗ ĐĐģĐĩĐēŅ", - "version_announcement_message": "ĐĐ´ŅавŅŅвŅĐšŅĐĩ! ĐĐžŅŅŅĐŋĐŊа ĐŊĐžĐ˛Đ°Ņ Đ˛ĐĩŅŅĐ¸Ņ ĐŋŅиĐģĐžĐļĐĩĐŊиŅ. ĐĐžĐļаĐģŅĐšŅŅа, ĐŋŅĐžŅŅиŅĐĩ <link>СаĐŧĐĩŅĐēи Đē вŅĐŋŅŅĐēŅ</link> и ŅĐąĐĩдиŅĐĩŅŅ, ŅŅĐž ваŅи ĐŋаŅаĐŧĐĩŅŅŅ <code>docker-compose.yml</code> и <code>.env</code> аĐēŅŅаĐģŅĐŊŅ, ŅŅĐžĐąŅ Đ¸ĐˇĐąĐĩĐļаŅŅ ĐžŅийОĐē в ĐēĐžĐŊŅĐ¸ĐŗŅŅаŅии, ĐžŅОйĐĩĐŊĐŊĐž ĐĩŅĐģи Đ˛Ņ Đ¸ŅĐŋĐžĐģŅСŅĐĩŅĐĩ WatchTower иĐģи Đ´ŅŅĐŗĐžĐš ĐŧĐĩŅ Đ°ĐŊиСĐŧ авŅĐžĐŧаŅиŅĐĩŅĐēĐžĐŗĐž ОйĐŊОвĐģĐĩĐŊĐ¸Ņ ĐŋŅиĐģĐžĐļĐĩĐŊиŅ.", + "version_announcement_message": "ĐĐ´ŅавŅŅвŅĐšŅĐĩ! ĐĐžŅŅŅĐŋĐŊа ĐŊĐžĐ˛Đ°Ņ Đ˛ĐĩŅŅĐ¸Ņ ĐŋŅиĐģĐžĐļĐĩĐŊиŅ. ĐĐžĐļаĐģŅĐšŅŅа, ĐŋŅĐžŅŅиŅĐĩ <link>СаĐŧĐĩŅĐēи Đē вŅĐŋŅŅĐēŅ</link> и ŅĐąĐĩдиŅĐĩŅŅ, ŅŅĐž ĐŋаŅаĐŧĐĩŅŅŅ ĐēĐžĐŊŅĐ¸ĐŗŅŅаŅии аĐēŅŅаĐģŅĐŊŅ, Đ´Đ°ĐąŅ Đ¸ĐˇĐąĐĩĐļаŅŅ ĐžŅийОĐē, ĐžŅОйĐĩĐŊĐŊĐž ĐĩŅĐģи иŅĐŋĐžĐģŅСŅĐĩŅŅŅ WatchTower иĐģи Đ´ŅŅĐŗĐžĐš ĐŧĐĩŅ Đ°ĐŊиСĐŧ авŅĐžĐŧаŅиŅĐĩŅĐēĐžĐŗĐž ОйĐŊОвĐģĐĩĐŊĐ¸Ņ ĐŋŅиĐģĐžĐļĐĩĐŊиŅ.", "version_announcement_overlay_release_notes": "ĐŋŅиĐŧĐĩŅаĐŊĐ¸Ņ Đē вŅĐŋŅŅĐēŅ", "version_announcement_overlay_text_1": "ĐŅивĐĩŅ, Đ´ŅŅĐŗ! ĐŅŅĐģа ĐŊĐžĐ˛Đ°Ņ Đ˛ĐĩŅŅиŅ", - "version_announcement_overlay_text_2": "ĐŋĐžĐļаĐģŅĐšŅŅа, ĐŋĐžŅĐĩŅиŅĐĩ", - "version_announcement_overlay_text_3": " и ŅĐąĐĩдиŅĐĩŅŅ, ŅŅĐž ваŅи ĐŊаŅŅŅОКĐēи docker-compose и .env ОйĐŊОвĐģĐĩĐŊŅ, ĐžŅОйĐĩĐŊĐŊĐž ĐĩŅĐģи Đ˛Ņ Đ¸ŅĐŋĐžĐģŅСŅĐĩŅĐĩ WatchTower иĐģи ĐģŅйОК Đ´ŅŅĐŗĐžĐš ĐŧĐĩŅ Đ°ĐŊиСĐŧ, ĐēĐžŅĐžŅŅĐš авŅĐžĐŧаŅиŅĐĩŅĐēи ОйĐŊОвĐģŅĐĩŅ ŅĐĩŅвĐĩŅ.", + "version_announcement_overlay_text_2": "ĐŋĐžĐļаĐģŅĐšŅŅа, ĐŋĐžŅĐĩŅиŅĐĩ ", + "version_announcement_overlay_text_3": " и ŅĐąĐĩдиŅĐĩŅŅ, ŅŅĐž ĐŋаŅаĐŧĐĩŅŅŅ Đ˛ docker-compose и .env ŅаКĐģĐ°Ņ Đ°ĐēŅŅаĐģŅĐŊŅ, ĐžŅОйĐĩĐŊĐŊĐž ĐĩŅĐģи иŅĐŋĐžĐģŅСŅĐĩŅŅŅ WatchTower иĐģи ĐģŅйОК Đ´ŅŅĐŗĐžĐš ĐŧĐĩŅ Đ°ĐŊиСĐŧ Đ´ĐģŅ Đ°Đ˛ŅĐžĐŧаŅиŅĐĩŅĐēĐžĐŗĐž ОйĐŊОвĐģĐĩĐŊĐ¸Ņ ĐŋŅиĐģĐžĐļĐĩĐŊиК.", "version_announcement_overlay_title": "ĐĐžŅŅŅĐŋĐŊа ĐŊĐžĐ˛Đ°Ņ Đ˛ĐĩŅŅĐ¸Ņ ŅĐĩŅвĐĩŅа đ", "version_history": "ĐŅŅĐžŅĐ¸Ņ Đ˛ĐĩŅŅиК", "version_history_item": "ĐĐĩŅŅĐ¸Ņ {version} ŅŅŅаĐŊОвĐģĐĩĐŊа {date}", diff --git a/i18n/sl.json b/i18n/sl.json index 1839212037..01f62d204e 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -25,7 +25,7 @@ "add_to": "Dodaj vâĻ", "add_to_album": "Dodaj v album", "add_to_album_bottom_sheet_added": "Dodano v {album}", - "add_to_album_bottom_sheet_already_exists": "ÅŊe v {albumu}", + "add_to_album_bottom_sheet_already_exists": "ÅŊe v {album}", "add_to_shared_album": "Dodaj k deljenemu albumu", "add_url": "Dodaj URL", "added_to_archive": "Dodano v arhiv", @@ -39,17 +39,17 @@ "authentication_settings_disable_all": "Ali zares Åželite onemogoÄiti vse prijavne metode? Prijava bo popolnoma onemogoÄena.", "authentication_settings_reenable": "Ponovno omogoÄi z uporabo <link>streÅžniÅĄkega ukaza</link>.", "background_task_job": "Opravila v ozadju", - "backup_database": "Varnostna kopija baze", - "backup_database_enable_description": "OmogoÄi varnostno kopiranje baze", - "backup_keep_last_amount": "Å tevilo prejÅĄnjih obdrÅžanih varnostnih kopij", - "backup_settings": "Nastavitve varnostnega kopiranja", - "backup_settings_description": "Upravljanje nastavitev varnostnih kopij", + "backup_database": "Ustvari izpis baze podatkov", + "backup_database_enable_description": "OmogoÄi izpise baze podatkov", + "backup_keep_last_amount": "Å tevilo prejÅĄnjih odlagaliÅĄÄ, ki jih je treba obdrÅžati", + "backup_settings": "Nastavitve izpisa baze podatkov", + "backup_settings_description": "Upravljanje nastavitev izpisa baze podatkov. Opomba: Ta opravila se ne spremljajo in o neuspehu ne boste obveÅĄÄeni.", "check_all": "OznaÄi vse", "cleanup": "ÄiÅĄÄenje", "cleared_jobs": "RazÄiÅĄÄeno opravilo za: {job}", "config_set_by_file": "Konfiguracija je trenutno nastavljena s konfiguracijsko datoteko", "confirm_delete_library": "Ali ste prepriÄani, da Åželite izbrisati knjiÅžnico {library}?", - "confirm_delete_library_assets": "Ali ste prepriÄani, da Åželite izbrisati to knjiÅžnico? To bo iz Immicha izbrisalo {count, plural, one {# contained asset} other {all # vsebovanih virov}} in tega ni moÅžno razveljaviti. Datoteke bodo ostale na disku.", + "confirm_delete_library_assets": "Ali ste prepriÄani, da Åželite izbrisati to knjiÅžnico? To bo iz Immicha izbrisalo {count, plural, one {# vsebovani vir} two {# vsebovana vira} few {# vsebovane vire} other {vseh # vsebovanih virov}} in tega ni moÅžno razveljaviti. Datoteke bodo ostale na disku.", "confirm_email_below": "Za potrditev vnesite \"{email}\" spodaj", "confirm_reprocess_all_faces": "Ali ste prepriÄani, da Åželite znova obdelati vse obraze? S tem boste poÄistili tudi Åže imenovane osebe.", "confirm_user_password_reset": "Ali ste prepriÄani, da Åželite ponastaviti geslo uporabnika {user}?", @@ -347,7 +347,7 @@ "untracked_files": "Nesledene datoteke", "untracked_files_description": "Tem datotekam aplikacija ne sledi. Lahko so posledica neuspelih premikov, prekinjenih nalaganj ali zaostalih zaradi hroÅĄÄa", "user_cleanup_job": "ÄiÅĄÄenje uporabnika", - "user_delete_delay": "RaÄun in sredstva <b>{user}</b> bodo naÄrtovani za trajno brisanje Äez {delay, plural, one {# day} other {# days}}.", + "user_delete_delay": "RaÄun in sredstva <b>{user}</b> bodo naÄrtovani za trajno brisanje Äez {delay, plural, one {# dan} other {# dni}}.", "user_delete_delay_settings": "Zamakni izbris", "user_delete_delay_settings_description": "Å tevilo dni po odstranitvi za trajno brisanje uporabnikovega raÄuna in sredstev. Opravilo za brisanje uporabnikov se izvaja ob polnoÄi, da se preveri, ali so uporabniki pripravljeni na izbris. Spremembe te nastavitve bodo ovrednotene pri naslednji izvedbi.", "user_delete_immediately": "RaÄun in sredstva uporabnika <b>{user}</b> bodo v Äakalni vrsti za trajno brisanje <b>takoj</b>.", @@ -371,6 +371,8 @@ "admin_password": "SkrbniÅĄko geslo", "administration": "Administracija", "advanced": "Napredno", + "advanced_settings_enable_alternate_media_filter_subtitle": "Uporabite to moÅžnost za filtriranje medijev med sinhronizacijo na podlagi alternativnih meril. To poskusite le, Äe imate teÅžave z aplikacijo, ki zaznava vse albume.", + "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTALNO] Uporabite alternativni filter za sinhronizacijo albuma v napravi", "advanced_settings_log_level_title": "Nivo dnevnika: {}", "advanced_settings_prefer_remote_subtitle": "Nekatere naprave zelo poÄasi nalagajo sliÄice iz sredstev v napravi. Aktivirajte to nastavitev, Äe Åželite namesto tega naloÅžiti oddaljene slike.", "advanced_settings_prefer_remote_title": "Uporabi raje oddaljene slike", @@ -378,12 +380,14 @@ "advanced_settings_proxy_headers_title": "Proxy glave", "advanced_settings_self_signed_ssl_subtitle": "PreskoÄi preverjanje potrdila SSL za konÄno toÄko streÅžnika. Zahtevano za samopodpisana potrdila.", "advanced_settings_self_signed_ssl_title": "Dovoli samopodpisana SSL potrdila", + "advanced_settings_sync_remote_deletions_subtitle": "Samodejno izbriÅĄi ali obnovi sredstvo v tej napravi, ko je to dejanje izvedeno v spletu", + "advanced_settings_sync_remote_deletions_title": "Sinhroniziraj oddaljene izbrise [EKSPERIMENTALNO]", "advanced_settings_tile_subtitle": "Napredne uporabniÅĄke nastavitve", "advanced_settings_troubleshooting_subtitle": "OmogoÄite dodatne funkcije za odpravljanje teÅžav", "advanced_settings_troubleshooting_title": "Odpravljanje teÅžav", - "age_months": "Starost {months, plural, one {# month} other {# months}}", - "age_year_months": "Starost 1 leto, {months, plural, one {# month} other {# months}}", - "age_years": "{years, plural, other {starost #}}", + "age_months": "Starost {months, plural, one {# mesec} two {# meseca} few {# mesece} other {# mesecev}}", + "age_year_months": "Starost 1 leto, {months, plural, one {# mesec} two {# meseca} few {# mesece} other {# mesecev}}", + "age_years": "{years, plural, one {# leto} two {# leti} few {# leta} other {# let}}", "album_added": "Album dodan", "album_added_notification_setting_description": "Prejmite e-poÅĄtno obvestilo, ko ste dodani v album v skupni rabi", "album_cover_updated": "Naslovnica albuma posodobljena", @@ -400,9 +404,9 @@ "album_remove_user_confirmation": "Ali ste prepriÄani, da Åželite odstraniti {user}?", "album_share_no_users": "Videti je, da ste ta album dali v skupno rabo z vsemi uporabniki ali pa nimate nobenega uporabnika, s katerim bi ga lahko delili.", "album_thumbnail_card_item": "1 element", - "album_thumbnail_card_items": "{} elementov", - "album_thumbnail_card_shared": "¡ V skupni rabi", - "album_thumbnail_shared_by": "Delil {}", + "album_thumbnail_card_items": "{count, plural, one {# element} two {# elementa} few {# elementi} other {# elementov}}", + "album_thumbnail_card_shared": " ¡ V skupni rabi", + "album_thumbnail_shared_by": "Delil {user}", "album_updated": "Album posodobljen", "album_updated_setting_description": "Prejmite e-poÅĄtno obvestilo, ko ima album v skupni rabi nova sredstva", "album_user_left": "Zapustil {album}", @@ -413,11 +417,11 @@ "album_viewer_appbar_share_err_remove": "Pri odstranjevanju sredstev iz albuma so teÅžave", "album_viewer_appbar_share_err_title": "Naslova albuma ni bilo mogoÄe spremeniti", "album_viewer_appbar_share_leave": "Zapusti album", - "album_viewer_appbar_share_to": "Deli z", + "album_viewer_appbar_share_to": "Deli s/z", "album_viewer_page_share_add_users": "Dodaj uporabnike", "album_with_link_access": "OmogoÄite vsem s povezavo ogled fotografij in ljudi v tem albumu.", "albums": "Albumi", - "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albumi}}", + "albums_count": "{count, plural, one {{count, number} album} two {{count, number} albuma} few {{count, number} albumi} other {{count, number} albumov}}", "all": "Vse", "all_albums": "Vsi albumi", "all_people": "Vsi ljudje", @@ -427,7 +431,7 @@ "allow_public_user_to_download": "Dovoli javnemu uporabniku prenos", "allow_public_user_to_upload": "Dovolite javnemu uporabniku nalaganje", "alt_text_qr_code": "Slika QR kode", - "anti_clockwise": "V nasprotni smeri urnega kazalca", + "anti_clockwise": "V nasprotni smeri urinega kazalca", "api_key": "API kljuÄ", "api_key_description": "Ta vrednost bo prikazana samo enkrat. Ne pozabite jo kopirati, preden zaprete okno.", "api_key_empty": "Ime kljuÄa API ne sme biti prazno", @@ -440,14 +444,14 @@ "archive": "Arhiv", "archive_or_unarchive_photo": "Arhivirajte ali odstranite fotografijo iz arhiva", "archive_page_no_archived_assets": "Ni arhiviranih sredstev", - "archive_page_title": "Arhiv ({})\n", + "archive_page_title": "Arhiv ({number})", "archive_size": "Velikost arhiva", "archive_size_description": "Konfigurirajte velikost arhiva za prenose (v GiB)", "archived": "Arhivirano", - "archived_count": "{count, plural, other {arhivirano #}}", + "archived_count": "{count, plural, one {# arhiviran} two {# arhivirana} few {# arhivirani} other {# arhiviranih}}", "are_these_the_same_person": "Ali je to ista oseba?", "are_you_sure_to_do_this": "Ste prepriÄani, da Åželite to narediti?", - "asset_action_delete_err_read_only": "Sredstev samo za branje ni mogoÄe izbrisati, preskoÄim\n", + "asset_action_delete_err_read_only": "Sredstev samo za branje ni mogoÄe izbrisati, preskoÄim", "asset_action_share_err_offline": "Ni mogoÄe pridobiti sredstev brez povezave, preskoÄim", "asset_added_to_album": "Dodano v album", "asset_adding_to_album": "Dodajanje v albumâĻ", @@ -473,23 +477,23 @@ "asset_viewer_settings_subtitle": "Upravljaj nastavitve pregledovalnika galerije", "asset_viewer_settings_title": "Pregledovalnik sredstev", "assets": "Sredstva", - "assets_added_count": "Dodano{count, plural, one {# sredstvo} other {# sredstev}}", - "assets_added_to_album_count": "Dodano{count, plural, one {# sredstvo} other {# sredstev}} v album", - "assets_added_to_name_count": "Dodano {count, plural, one {# sredstvo} other {# sredstev}} v {hasName, select, true {<b>{name}</b>} other {new album}}", - "assets_count": "{count, plural, one {# sredstvo} other {# sredstev}}", - "assets_deleted_permanently": "Å t. za vedno izbrisanih sredstev: {}", - "assets_deleted_permanently_from_server": "Å t. za vedno izbrisanih sredstev iz sreÅžnika Immich: {}", - "assets_moved_to_trash_count": "Premaknjeno {count, plural, one {# sredstev} other {# sredstev}} v smetnjak", - "assets_permanently_deleted_count": "Trajno izbrisano {count, plural, one {# sredstvo} other {# sredstev}}", - "assets_removed_count": "Odstranjeno {count, plural, one {# sredstvo} other {# sredstev}}", - "assets_removed_permanently_from_device": "Å t. za vedno izbrisanih sredstev iz vaÅĄe naprave: {}", + "assets_added_count": "Dodano{count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", + "assets_added_to_album_count": "Dodano {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v album", + "assets_added_to_name_count": "Dodano {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v {hasName, select, true {<b>{name}</b>} other {new album}}", + "assets_count": "{count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", + "assets_deleted_permanently": "{count, plural, one {# sredstvo trajno izbrisano} two {# sredstvi trajno izbrisani} few {# sredstva trajno izbrisana} other {# sredstev trajno izbrisanih}}", + "assets_deleted_permanently_from_server": "{count, plural, one {# sredstvo trajno izbrisano iz streÅžnika Immich} two {# sredstvi trajno izbrisani iz streÅžnika Immich} few {# sredstva trajno izbrisana iz streÅžnika Immich} other {# sredstev trajno izbrisanih iz streÅžnika Immich}}", + "assets_moved_to_trash_count": "Premaknjeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v smetnjak", + "assets_permanently_deleted_count": "Trajno izbrisano {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", + "assets_removed_count": "Odstranjeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", + "assets_removed_permanently_from_device": "{count, plural, one {# sredstvo trajno izbrisano iz naprave} two {# sredstvi strajno izbrisani iz naprave} few {# sredstva trajno izbrisana iz naprave} other {# sredstve trajno izbrisanih iz naprave}}", "assets_restore_confirmation": "Ali ste prepriÄani, da Åželite obnoviti vsa sredstva, ki ste jih odstranili? Tega dejanja ne morete razveljaviti! UpoÅĄtevajte, da sredstev brez povezave ni mogoÄe obnoviti na ta naÄin.", - "assets_restored_count": "Obnovljeno {count, plural, one {# sredstvo} other {# sredstev}}", - "assets_restored_successfully": "Å t. uspeÅĄno obnovljenih sredstev: {}", - "assets_trashed": "Å t. sredstev premaknjenih v smetnjak: {}", - "assets_trashed_count": "V smetnjak {count, plural, one {# sredstvo} other {# sredstev}}", - "assets_trashed_from_server": "Å t sredstev izbrisanih iz streÅžnika Immich: {}", - "assets_were_part_of_album_count": "{count, plural, one {sredstvo je} other {sredstev je}} Åže del albuma", + "assets_restored_count": "Obnovljeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", + "assets_restored_successfully": "{count, plural, one {# sredstvo uspeÅĄno obnovljeno} two {# sredstvi uspeÅĄno obnovljeni} few {# sredstva uspeÅĄno obnovljena} other {# sredstev uspeÅĄno obnovljenih}}", + "assets_trashed": "{count, plural, one {# sredstvo premaknjeno v smetnjak} two {# sredstvi premaknjeni v smetnjak} few {# sredstva premaknjena v smetnjak} other {# sredstev premaknjenih v smetnjak}}", + "assets_trashed_count": "V smetnjak {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", + "assets_trashed_from_server": "{count, plural, one {# sredstvo iz Immich streÅžnika v smetnjak} two {# sredstvi iz Immich streÅžnika v smetnjak} few {# sredstva iz Immich streÅžnika v smetnjak} other {# sredstev iz Immich streÅžnika v smetnjak}}", + "assets_were_part_of_album_count": "{count, plural, one {sredstvo je} two {sredstvi sta} few {sredstva so} other {sredstev je}} Åže del albuma", "authorized_devices": "PooblaÅĄÄene naprave", "automatic_endpoint_switching_subtitle": "PoveÅžite se lokalno prek doloÄenega omreÅžja Wi-Fi, ko je na voljo, in uporabite druge povezave drugje", "automatic_endpoint_switching_title": "Samodejno preklapljanje URL-jev", @@ -497,7 +501,7 @@ "back_close_deselect": "Nazaj, zaprite ali prekliÄite izbiro", "background_location_permission": "Dovoljenje za iskanje lokacije v ozadju", "background_location_permission_content": "Ko deluje v ozadju mora imeti Immich za zamenjavo omreÅžij, *vedno* dostop do natanÄne lokacije, da lahko aplikacija prebere ime omreÅžja Wi-Fi", - "backup_album_selection_page_albums_device": "Albumi v napravi ({})", + "backup_album_selection_page_albums_device": "Albumi v napravi ({number})", "backup_album_selection_page_albums_tap": "Tapnite za vkljuÄitev, dvakrat tapnite za izkljuÄitev", "backup_album_selection_page_assets_scatter": "Sredstva so lahko razprÅĄena po veÄ albumih. Tako je mogoÄe med postopkom varnostnega kopiranja albume vkljuÄiti ali izkljuÄiti.", "backup_album_selection_page_select_albums": "Izberi albume", @@ -509,7 +513,7 @@ "backup_background_service_current_upload_notification": "Nalagam {}", "backup_background_service_default_notification": "Preverjam za novimi sredstviâĻ", "backup_background_service_error_title": "Napaka varnostnega kopiranja", - "backup_background_service_in_progress_notification": "Varnostno kopiranje vaÅĄih sredstev ...", + "backup_background_service_in_progress_notification": "Varnostno kopiranje vaÅĄih sredstevâĻ", "backup_background_service_upload_failure_notification": "Nalaganje {} ni uspelo", "backup_controller_page_albums": "Varnostno kopiranje albumov", "backup_controller_page_background_app_refresh_disabled_content": "OmogoÄite osveÅževanje aplikacij v ozadju v Nastavitve > SploÅĄno > OsveÅžitev aplikacij v ozadju, Äe Åželite uporabiti varnostno kopiranje v ozadju.", @@ -529,11 +533,11 @@ "backup_controller_page_background_turn_on": "Vklopi storitev v ozadju", "backup_controller_page_background_wifi": "Samo na WiFi", "backup_controller_page_backup": "Varnostna kopija", - "backup_controller_page_backup_selected": "Izbrano", + "backup_controller_page_backup_selected": "Izbrano: ", "backup_controller_page_backup_sub": "Varnostno kopirane fotografije in videoposnetki", - "backup_controller_page_created": "Ustvarjeno: {}", + "backup_controller_page_created": "Ustvarjeno: {date}", "backup_controller_page_desc_backup": "Vklopite varnostno kopiranje v ospredju za samodejno nalaganje novih sredstev na streÅžnik, ko odprete aplikacijo.", - "backup_controller_page_excluded": "IzkljuÄeno:", + "backup_controller_page_excluded": "IzkljuÄeno: ", "backup_controller_page_failed": "NeuspeÅĄno ({})", "backup_controller_page_filename": "Ime datoteke: {} [{}]", "backup_controller_page_id": "ID: {}", @@ -545,7 +549,7 @@ "backup_controller_page_start_backup": "ZaÅženi varnostno kopiranje", "backup_controller_page_status_off": "Samodejno varnostno kopiranje v ospredju je izklopljeno", "backup_controller_page_status_on": "Samodejno varnostno kopiranje v ospredju je vklopljeno", - "backup_controller_page_storage_format": "Uporabljeno {} od {}", + "backup_controller_page_storage_format": "{used} od {available} uporabljeno", "backup_controller_page_to_backup": "Albumi, ki bodo varnostno kopirani", "backup_controller_page_total_sub": "Vse edinstvene fotografije in videi iz izbranih albumov", "backup_controller_page_turn_off": "Izklopite varnostno kopiranje v ospredju", @@ -566,25 +570,25 @@ "bugs_and_feature_requests": "Napake in zahteve po funkcijah", "build": "RazliÄica", "build_image": "RazliÄica slike", - "bulk_delete_duplicates_confirmation": "Ali ste prepriÄani, da Åželite mnoÅžiÄno izbrisati {count, plural, one {# dvojnik} other {# dvojnikov}}? S tem boste ohranili najveÄje sredstvo vsake skupine in trajno izbrisali vse druge dvojnike. Tega dejanja ne morete razveljaviti!", - "bulk_keep_duplicates_confirmation": "Ali ste prepriÄani, da Åželite obdrÅžati {count, plural, one {# dvojnik} other {# dvojnikov}}? S tem boste razreÅĄili vse podvojene skupine, ne da bi karkoli izbrisali.", - "bulk_trash_duplicates_confirmation": "Ali ste prepriÄani, da Åželite mnoÅžiÄno vreÄi v smetnjak {count, plural, one {# dvojnik} other {# dvojnikov}}? S tem boste obdrÅžali najveÄje sredstvo vsake skupine in odstranili vse druge dvojnike.", + "bulk_delete_duplicates_confirmation": "Ali ste prepriÄani, da Åželite mnoÅžiÄno izbrisati {count, plural, one {# dvojnik} two {# dvojnika} few {# dvojnike} other {# dvojnikov}}? S tem boste ohranili najveÄje sredstvo vsake skupine in trajno izbrisali vse druge dvojnike. Tega dejanja ne morete razveljaviti!", + "bulk_keep_duplicates_confirmation": "Ali ste prepriÄani, da Åželite obdrÅžati {count, plural, one {# dvojnik} two {# dvojnika} few {# dvojnike} other {# dvojnikov}}? S tem boste razreÅĄili vse podvojene skupine, ne da bi karkoli izbrisali.", + "bulk_trash_duplicates_confirmation": "Ali ste prepriÄani, da Åželite mnoÅžiÄno vreÄi v smetnjak {count, plural, one {# dvojnik} two {# dvojnika} few {# dvojnike} other {# dvojnikov}}? S tem boste obdrÅžali najveÄje sredstvo vsake skupine in odstranili vse druge dvojnike.", "buy": "Kupi Immich", - "cache_settings_album_thumbnails": "SliÄice strani knjiÅžnice ({} sredstev)", + "cache_settings_album_thumbnails": "SliÄice strani knjiÅžnice ({count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}})", "cache_settings_clear_cache_button": "PoÄisti predpomnilnik", "cache_settings_clear_cache_button_title": "PoÄisti predpomnilnik aplikacije. To bo znatno vplivalo na delovanje aplikacije, dokler se predpomnilnik ne obnovi.", "cache_settings_duplicated_assets_clear_button": "POÄISTI", "cache_settings_duplicated_assets_subtitle": "Fotografije in videoposnetki, ki jih je aplikacija uvrstila na Ärni seznam", - "cache_settings_duplicated_assets_title": "Podvojena sredstva ({})", - "cache_settings_image_cache_size": "Velikost predpomnilnika slik ({} sredstev)", + "cache_settings_duplicated_assets_title": "{count, plural, one {# podvojeno sredstvo} two {# podvojeni sredstvi} few {# podvojena sredstva} other {# podvojenih sredstev}}", + "cache_settings_image_cache_size": "Velikost predpomnilnika slik ({count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}})", "cache_settings_statistics_album": "SliÄice knjiÅžnice", "cache_settings_statistics_assets": "{} sredstva ({})", - "cache_settings_statistics_full": "Polne slike", + "cache_settings_statistics_full": "Izvirne slike", "cache_settings_statistics_shared": "SliÄice albuma v skupni rabi", "cache_settings_statistics_thumbnail": "SliÄice", "cache_settings_statistics_title": "Uporaba predpomnilnika", "cache_settings_subtitle": "Nadzirajte delovanje predpomnjenja mobilne aplikacije Immich", - "cache_settings_thumbnail_size": "Velikost predpomnilnika sliÄic ({} sredstev)", + "cache_settings_thumbnail_size": "Velikost predpomnilnika sliÄic ({count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}})", "cache_settings_tile_subtitle": "Nadzoruj vedenje lokalnega shranjevanja", "cache_settings_tile_title": "Lokalna shramba", "cache_settings_title": "Nastavitve predpomnjenja", @@ -654,15 +658,15 @@ "contain": "Vsebuje", "context": "Kontekst", "continue": "Nadaljuj", - "control_bottom_app_bar_album_info_shared": "{} elementov ¡ V skupni rabi", + "control_bottom_app_bar_album_info_shared": "{count, plural, one {# element v skupni rabi} two {# elementa v skupni rabi} few {# elementi v skupni rabi} other {# elementov v skupni rabi}}", "control_bottom_app_bar_create_new_album": "Ustvari nov album", "control_bottom_app_bar_delete_from_immich": "IzbriÅĄi iz Immicha", "control_bottom_app_bar_delete_from_local": "IzbriÅĄi iz naprave", "control_bottom_app_bar_edit_location": "Uredi lokacijo", "control_bottom_app_bar_edit_time": "Uredi datum in uro", "control_bottom_app_bar_share_link": "Deli povezavo", - "control_bottom_app_bar_share_to": "Deli z", - "control_bottom_app_bar_trash_from_immich": "Prestavi v smeti", + "control_bottom_app_bar_share_to": "Deli s/z", + "control_bottom_app_bar_trash_from_immich": "Prestavi v smetnjak", "copied_image_to_clipboard": "Slika kopirana v odloÅžiÅĄÄe.", "copied_to_clipboard": "Kopirano v odloÅžiÅĄÄe!", "copy_error": "Napaka pri kopiranju", @@ -686,7 +690,7 @@ "create_new_person": "Ustvari novo osebo", "create_new_person_hint": "Dodeli izbrana sredstva novi osebi", "create_new_user": "Ustvari novega uporabnika", - "create_shared_album_page_share_add_assets": "DODAJ SREDSTVO", + "create_shared_album_page_share_add_assets": "DODAJ SREDSTVA", "create_shared_album_page_share_select_photos": "Izberi fotografije", "create_tag": "Ustvari oznako", "create_tag_description": "Ustvarite novo oznako. Za ugnezdene oznake vnesite celotno pot oznake, vkljuÄno s poÅĄevnicami.", @@ -711,7 +715,7 @@ "deduplicate_all": "Odstrani vse podvojene", "deduplication_criteria_1": "Velikost slike v bajtih", "deduplication_criteria_2": "Å tevilo podatkov EXIF", - "deduplication_info": "Informacije o deduplikaciji", + "deduplication_info": "Informacije o zaznavanju dvojnikov", "deduplication_info_description": "Za samodejno vnaprejÅĄnjo izbiro sredstev in mnoÅžiÄno odstranjevanje dvojnikov si ogledamo:", "default_locale": "Privzeti jezik", "default_locale_description": "Oblikujte datume in ÅĄtevilke glede na lokalne nastavitve brskalnika", @@ -742,7 +746,7 @@ "description": "Opis", "description_input_hint_text": "Dodaj opis ...", "description_input_submit_error": "Napaka pri posodabljanju opisa, preverite dnevnik za veÄ podrobnosti", - "details": "PODROBNOSTI", + "details": "Podrobnosti", "direction": "Usmeritev", "disabled": "OnemogoÄeno", "disallow_edits": "OnemogoÄi urejanje", @@ -808,7 +812,7 @@ "editor_crop_tool_h2_rotation": "Vrtenje", "email": "E-poÅĄta", "empty_folder": "Ta mapa je prazna", - "empty_trash": "Izprazni smeti", + "empty_trash": "Izprazni smetnjak", "empty_trash_confirmation": "Ste prepriÄani, da Åželite izprazniti smetnjak? S tem boste iz Immicha trajno odstranili vsa sredstva v smetnjaku.\nTega dejanja ne morete razveljaviti!", "enable": "OmogoÄi", "enabled": "OmogoÄeno", @@ -855,7 +859,7 @@ "failed_to_unstack_assets": "Sredstev ni bilo mogoÄe razloÅžiti", "import_path_already_exists": "Ta uvozna pot Åže obstaja.", "incorrect_email_or_password": "NapaÄen e-poÅĄtni naslov ali geslo", - "paths_validation_failed": "{paths, plural, one {# pot} other {# poti}} ni bilo uspeÅĄno preverjeno", + "paths_validation_failed": "{paths, plural, one {# pot ni bila uspeÅĄno preverjena} two {# poti nista bili uspeÅĄno preverjeni} few {# poti niso bile uspeÅĄno preverjene} other {# poti ni bilo uspeÅĄno preverjenih}}", "profile_picture_transparent_pixels": "Profilne slike ne smejo imeti prosojnih slikovnih pik. PoveÄajte in/ali premaknite sliko.", "quota_higher_than_disk_size": "Nastavili ste kvoto, ki je viÅĄja od velikosti diska", "repair_unable_to_check_items": "Ni mogoÄe preveriti {count, select, one {predmeta} other {predmetov}}", @@ -873,7 +877,7 @@ "unable_to_change_favorite": "Ni mogoÄe spremeniti priljubljenega za sredstvo", "unable_to_change_location": "Lokacije ni mogoÄe spremeniti", "unable_to_change_password": "Gesla ni mogoÄe spremeniti", - "unable_to_change_visibility": "Ni mogoÄe spremeniti vidnosti za {count, plural, one {# osebo} other {# oseb}}", + "unable_to_change_visibility": "Ni mogoÄe spremeniti vidnosti za {count, plural, one {# osebo} two {# osebi} few {# osebe} other {# oseb}}", "unable_to_complete_oauth_login": "Prijave OAuth ni mogoÄe dokonÄati", "unable_to_connect": "Ni mogoÄe vzpostaviti povezave", "unable_to_connect_to_server": "Ni mogoÄe vzpostaviti povezave s streÅžnikom", @@ -953,10 +957,10 @@ "exif_bottom_sheet_location": "LOKACIJA", "exif_bottom_sheet_people": "OSEBE", "exif_bottom_sheet_person_add_person": "Dodaj ime", - "exif_bottom_sheet_person_age": "Starost {}", - "exif_bottom_sheet_person_age_months": "Starost {} mesecev", - "exif_bottom_sheet_person_age_year_months": "Starost 1 leto, {} mesecev", - "exif_bottom_sheet_person_age_years": "Starost {}", + "exif_bottom_sheet_person_age": "Starost {count, plural, one {# leto} two {# leti} few {# leta} other {# let}}", + "exif_bottom_sheet_person_age_months": "Starost {months, plural, one {# mesec} two {# meseca} few {# mesece} other {# mesecev}}", + "exif_bottom_sheet_person_age_year_months": "Starost 1 leto, {months, plural, one {# mesec} two {# meseca} few {# mesece} other {# mesecev}}", + "exif_bottom_sheet_person_age_years": "Starost {years, plural, one {# leto} two {# leti} few {# leta} other {# let}}", "exit_slideshow": "Zapustite diaprojekcijo", "expand_all": "RazÅĄiri vse", "experimental_settings_new_asset_list_subtitle": "Delo v teku", @@ -974,7 +978,7 @@ "external": "Zunanji", "external_libraries": "Zunanje knjiÅžnice", "external_network": "Zunanje omreÅžje", - "external_network_sheet_info": "Ko aplikacija ni v Åželenem omreÅžju WiFi, se bo povezala s streÅžnikom prek prvega od spodnjih URL-jev, ki jih lahko doseÅže, zaÄenÅĄi od zgoraj navzdol", + "external_network_sheet_info": "Ko aplikacija ni v Åželenem omreÅžju Wi-Fi, se bo povezala s streÅžnikom prek prvega od spodnjih URL-jev, ki jih lahko doseÅže, zaÄenÅĄi od zgoraj navzdol", "face_unassigned": "Nedodeljen", "failed": "Ni uspelo", "failed_to_load_assets": "Sredstev ni bilo mogoÄe naloÅžiti", @@ -992,6 +996,7 @@ "filetype": "Vrsta datoteke", "filter": "Filter", "filter_people": "Filtriraj ljudi", + "filter_places": "Filtriraj kraje", "find_them_fast": "Z iskanjem jih hitro poiÅĄÄite po imenu", "fix_incorrect_match": "Popravi napaÄno ujemanje", "folder": "Mapa", @@ -1040,7 +1045,7 @@ "home_page_delete_remote_err_local": "Lokalna sredstva pri brisanju oddaljenega izbora, preskakujem", "home_page_favorite_err_local": "Lokalnih sredstev ÅĄe ni mogoÄe dodati med priljubljene, preskakujem", "home_page_favorite_err_partner": "Sredstev partnerja ÅĄe ni mogoÄe dodati med priljubljene, preskakujem", - "home_page_first_time_notice": "Äe aplikacijo uporabljate prviÄ, se prepriÄajte, da ste izbrali rezervne albume, tako da lahko Äasovna premica zapolni fotografije in videoposnetke v albumih.", + "home_page_first_time_notice": "Äe aplikacijo uporabljate prviÄ, se prepriÄajte, da ste izbrali rezervne albume, tako da lahko Äasovna premica zapolni fotografije in videoposnetke v albumih", "home_page_share_err_local": "Lokalnih sredstev ni mogoÄe deliti prek povezave, preskakujem", "home_page_upload_err_limit": "Hkrati lahko naloÅžite najveÄ 30 sredstev, preskakujem", "host": "Gostitelj", @@ -1066,7 +1071,7 @@ "immich_web_interface": "Immich spletni vmesnik", "import_from_json": "Uvoz iz JSON", "import_path": "Pot uvoza", - "in_albums": "V {count, plural, one {# album} other {# albumov}}", + "in_albums": "V {count, plural, one {# album} two {# albuma} few {# albume} other {# albumov}}", "in_archive": "V arhiv", "include_archived": "VkljuÄi arhivirane", "include_shared_albums": "VkljuÄite skupne albume", @@ -1076,7 +1081,7 @@ "info": "Info", "interval": { "day_at_onepm": "Vsak dan ob 13h", - "hours": "Vsakih {hours, plural, one {uro} other {{hours, number} ur/e}}", + "hours": "Vsakih {hours, plural, one {uro} two {uri} few {ure} other {{hours, number} ur}}", "night_at_midnight": "Vsak veÄer ob polnoÄi", "night_at_twoam": "Vsako noÄ ob 2h" }, @@ -1084,12 +1089,12 @@ "invalid_date_format": "Neveljavna oblika datuma", "invite_people": "Povabi ljudi", "invite_to_album": "Povabi v album", - "items_count": "{count, plural, one {# predmet} other {# predmetov}}", + "items_count": "{count, plural, one {# predmet} two {# predmeta} few {# predmeti} other {# predmetov}}", "jobs": "Opravila", "keep": "ObdrÅži", "keep_all": "ObdrÅži vse", "keep_this_delete_others": "ObdrÅži to, izbriÅĄi ostalo", - "kept_this_deleted_others": "ObdrÅži to sredstvo in izbriÅĄi {count, plural, one {# sredstvo} other {# sredstev}}", + "kept_this_deleted_others": "ObdrÅži to sredstvo in izbriÅĄi {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", "keyboard_shortcuts": "BliÅžnjice na tipkovnici", "language": "Jezik", "language_setting_description": "Izberite Åželeni jezik", @@ -1120,7 +1125,7 @@ "local_network": "Lokalno omreÅžje", "local_network_sheet_info": "Aplikacija se bo povezala s streÅžnikom prek tega URL-ja, ko bo uporabljala navedeno omreÅžje Wi-Fi", "location_permission": "Dovoljenje za lokacijo", - "location_permission_content": "Za uporabo funkcije samodejnega preklapljanja potrebuje Immich dovoljenje za natanÄno lokacijo, da lahko prebere ime trenutnega omreÅžja WiFi", + "location_permission_content": "Za uporabo funkcije samodejnega preklapljanja potrebuje Immich dovoljenje za natanÄno lokacijo, da lahko prebere ime trenutnega omreÅžja Wi-Fi", "location_picker_choose_on_map": "Izberi na zemljevidu", "location_picker_latitude_error": "Vnesi veljavno zemljepisno ÅĄirino", "location_picker_latitude_hint": "Tukaj vnesi svojo zemljepisno ÅĄirino", @@ -1170,8 +1175,8 @@ "manage_your_devices": "Upravljajte svoje prijavljene naprave", "manage_your_oauth_connection": "Upravljajte svojo OAuth povezavo", "map": "Zemljevid", - "map_assets_in_bound": "{} slika", - "map_assets_in_bounds": "{} slik", + "map_assets_in_bound": "{count, plural, one {# slika}}", + "map_assets_in_bounds": "{count, plural, two {# sliki} few {# slike} other {# slik}}", "map_cannot_get_user_location": "Lokacije uporabnika ni mogoÄe dobiti", "map_location_dialog_yes": "Da", "map_location_picker_page_use_location": "Uporabi to lokacijo", @@ -1185,9 +1190,9 @@ "map_settings": "Nastavitve zemljevida", "map_settings_dark_mode": "Temni naÄin", "map_settings_date_range_option_day": "Zadnjih 24 ur", - "map_settings_date_range_option_days": "Zadnjih {} dni", + "map_settings_date_range_option_days": "{count, plural, one {Zadnji # dan} two {Zadnja # dneva} few {Zadnje # dni} other {Zadnjih # dni}}", "map_settings_date_range_option_year": "Zadnje leto", - "map_settings_date_range_option_years": "Zadnjih {} let", + "map_settings_date_range_option_years": "{count, plural, two {Zadnje # leti} few {# Zadnja # leta} other {Zadnjih # let}}", "map_settings_dialog_title": "Nastavitve zemljevida", "map_settings_include_show_archived": "VkljuÄi arhivirane", "map_settings_include_show_partners": "VkljuÄi partnerjeve", @@ -1203,7 +1208,7 @@ "memories_start_over": "ZaÄni od zaÄetka", "memories_swipe_to_close": "Podrsaj gor za zapiranje", "memories_year_ago": "Leto dni nazaj", - "memories_years_ago": "{} let nazaj", + "memories_years_ago": "{years, plural, two {# leti} few {# leta} other {# let}} nazaj", "memory": "Spomin", "memory_lane_title": "Spominski trak {title}", "menu": "Meni", @@ -1282,6 +1287,7 @@ "onboarding_welcome_user": "Pozdravljen/a, {user}", "online": "Povezano", "only_favorites": "Samo priljubljene", + "open": "Odpri", "open_in_map_view": "Odpri v pogledu zemljevida", "open_in_openstreetmap": "Odpri v OpenStreetMap", "open_the_search_filters": "Odpri iskalne filtre", @@ -1298,14 +1304,14 @@ "partner_can_access": "{partner} ima dostop", "partner_can_access_assets": "Vse vaÅĄe fotografije in videoposnetki, razen tistih v arhivu in izbrisanih", "partner_can_access_location": "Lokacija, kjer so bile vaÅĄe fotografije posnete", - "partner_list_user_photos": "{user}ovih fotografij", + "partner_list_user_photos": "fotografije od {user}", "partner_list_view_all": "Poglej vse", "partner_page_empty_message": "VaÅĄe fotografije ÅĄe niso v skupni rabi z nobenim partnerjem.", "partner_page_no_more_users": "Ni veÄ uporabnikov za dodajanje", "partner_page_partner_add_failed": "Partnerja ni bilo mogoÄe dodati", "partner_page_select_partner": "Izberi partnerja", "partner_page_shared_to_title": "V skupni rabi z", - "partner_page_stop_sharing_content": "{} ne bo imel veÄ dostopa do vaÅĄih fotografij.", + "partner_page_stop_sharing_content": "{user} ne bo imel veÄ dostopa do vaÅĄih fotografij.", "partner_sharing": "Skupna raba s partnerjem", "partners": "Partnerji", "password": "Geslo", @@ -1313,9 +1319,9 @@ "password_required": "Zahtevano je geslo", "password_reset_success": "Ponastavitev gesla je uspela", "past_durations": { - "days": "Pretek-el/-lih {days, plural, one {dan} other {# dni}}", - "hours": "Pretek-lo/-lih {hours, plural, one {uro} other {# ur}}", - "years": "Pretek-lo/-lih {years, plural, one {leto} other {# let}}" + "days": "{days, plural, one {Pretekel dan} two {Pretekla # dni} few {Pretekle # dni} other {Preteklih # dni}}", + "hours": "{hours, plural, one {Preteklo uro} two {Pretekli # uri} few {Pretekle # ure} other {Preteklih # ur}}", + "years": "{years, plural, one {Preteklo leto} two {Pretekli # leti} few {Pretekla # leta} other {Preteklih # let}}" }, "path": "Pot", "pattern": "Vzorec", @@ -1324,13 +1330,13 @@ "paused": "Zaustavljeno", "pending": "V teku", "people": "Osebe", - "people_edits_count": "Urejen-a/-ih {count, plural, one {# oseba} other {# oseb}}", + "people_edits_count": "{count, plural, one {Urejena # oseba} two {Urejeni # osebi} few {Urejene # osebe} other {Urejenih # oseb}}", "people_feature_description": "Brskanje po fotografijah in videoposnetkih, razvrÅĄÄenih po osebah", "people_sidebar_description": "PrikaÅžite povezavo do Ljudje v stranski vrstici", "permanent_deletion_warning": "Opozorilo o trajnem izbrisu", "permanent_deletion_warning_setting_description": "PokaÅži opozorilo pri trajnem brisanju sredstev", "permanently_delete": "Trajno izbriÅĄi", - "permanently_delete_assets_count": "Trajno izbriÅĄi {count, plural, one {sredstvo} other {sredstev}}", + "permanently_delete_assets_count": "Trajno izbriÅĄi {count, plural, one {sredstvo} two {sredstvi} few {sredstva} other {sredstev}}", "permanently_delete_assets_prompt": "Ali ste prepriÄani, da Åželite trajno izbrisati {count, plural, one {to sredstvo?} other {ta <b>#</b> sredstva?}} S tem boste odstranili tudi {count, plural, one {tega od teh} other {telih iz telih}} album- /-ov.", "permanently_deleted_asset": "Trajno izbrisano sredstvo", "permanently_deleted_assets_count": "Trajno izbrisano {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", @@ -1348,12 +1354,12 @@ "photo_shared_all_users": "Videti je, da ste svoje fotografije delili z vsemi uporabniki ali pa nimate nobenega uporabnika, s katerim bi jih delili.", "photos": "Slike", "photos_and_videos": "Fotografije & videi", - "photos_count": "{count, plural, one {{count, number} slika} other {{count, number} slik}}", + "photos_count": "{count, plural, one {{count, number} slika} two {{count, number} sliki} few {{count, number} slike} other {{count, number} slik}}", "photos_from_previous_years": "Fotografije iz prejÅĄnjih let", "pick_a_location": "Izberi lokacijo", "place": "Lokacija", "places": "Lokacije", - "places_count": "{count, plural, one {{count, number} kraj} other {{count, number} krajev}}", + "places_count": "{count, plural, one {{count, number} kraj} two {{count, number} kraja} few {{count, number} kraji} other {{count, number} krajev}}", "play": "Predvajaj", "play_memories": "Predvajaj spomine", "play_motion_photo": "Predvajaj premikajoÄo fotografijo", @@ -1418,7 +1424,7 @@ "reaction_options": "MoÅžnosti reakcije", "read_changelog": "Preberi dnevnik sprememb", "reassign": "Prerazporedi", - "reassigned_assets_to_existing_person": "Ponovno dodeljeno {count, plural, one {# sredstvo} other {# sredstev}} za {name, select, null {an existing person} other {{name}}}", + "reassigned_assets_to_existing_person": "Ponovno dodeljeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} za {name, select, null {an existing person} other {{name}}}", "reassigned_assets_to_new_person": "Ponovno dodeljeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} za novo osebo", "reassing_hint": "Dodeli izbrana sredstva obstojeÄi osebi", "recent": "Nedavno", @@ -1426,6 +1432,8 @@ "recent_searches": "Nedavna iskanja", "recently_added": "Nedavno dodano", "recently_added_page_title": "Nedavno dodano", + "recently_taken": "Nedavno uporabljen", + "recently_taken_page_title": "Nedavno Uporabljen", "refresh": "OsveÅži", "refresh_encoded_videos": "OsveÅži kodirane videoposnetke", "refresh_faces": "OsveÅži obraze", @@ -1540,7 +1548,7 @@ "search_result_page_new_search_hint": "Novo iskanje", "search_settings": "Nastavitve iskanja", "search_state": "Iskanje deÅžele...", - "search_suggestion_list_smart_search_hint_1": "Pametno iskanje je privzeto omogoÄeno, za iskanje metapodatkov uporabite sintakso", + "search_suggestion_list_smart_search_hint_1": "Pametno iskanje je privzeto omogoÄeno, za iskanje metapodatkov uporabite sintakso ", "search_suggestion_list_smart_search_hint_2": "m:vaÅĄ-iskani-pojem", "search_tags": "Iskanje oznak...", "search_timezone": "Iskanje Äasovnega pasu...", @@ -1590,12 +1598,12 @@ "setting_languages_apply": "Uporabi", "setting_languages_subtitle": "Spremeni jezik aplikacije", "setting_languages_title": "Jeziki", - "setting_notifications_notify_failures_grace_period": "Obvesti o napakah varnostnega kopiranja v ozadju: {}", - "setting_notifications_notify_hours": "{} ur", + "setting_notifications_notify_failures_grace_period": "Obvesti o napakah varnostnega kopiranja v ozadju: {user}", + "setting_notifications_notify_hours": "{count, plural, one {# ura} two {# uri} few {# ure} other {# ur}}", "setting_notifications_notify_immediately": "takoj", - "setting_notifications_notify_minutes": "{} minut", + "setting_notifications_notify_minutes": "{count, plural, one {# minuta} two {# minuti} few {# minute} other {# minut}}", "setting_notifications_notify_never": "nikoli", - "setting_notifications_notify_seconds": "{} sekund", + "setting_notifications_notify_seconds": "{count, plural, one {# sekunda} two {# sekundi} few {# sekunde} other {# sekund}}", "setting_notifications_single_progress_subtitle": "Podrobne informacije o napredku nalaganja po sredstvih", "setting_notifications_single_progress_title": "PokaÅži napredek varnostnega kopiranja v ozadju", "setting_notifications_subtitle": "Prilagodite svoje nastavitve obvestil", @@ -1609,7 +1617,7 @@ "settings_saved": "Nastavitve shranjene", "share": "Deli", "share_add_photos": "Dodaj fotografije", - "share_assets_selected": "{} izbrano", + "share_assets_selected": "{count, plural, one {# izbrana} two {# izbrani} few {# izbrane} other {# izbranih}}", "share_dialog_preparing": "Priprava...", "shared": "V skupni rabi", "shared_album_activities_input_disable": "Komentiranje je onemogoÄeno", @@ -1618,7 +1626,7 @@ "shared_album_section_people_action_error": "Napaka pri zapuÅĄÄanju/odstranjevanju iz albuma", "shared_album_section_people_action_leave": "Odstrani uporabnika iz albuma", "shared_album_section_people_action_remove_user": "Odstrani uporabnika iz albuma", - "shared_album_section_people_title": "LJUDJE", + "shared_album_section_people_title": "OSEBE", "shared_by": "Skupna raba s/z", "shared_by_user": "Skupna raba s/z {user}", "shared_by_you": "DeliÅĄ", @@ -1630,25 +1638,25 @@ "shared_link_create_error": "Napaka pri ustvarjanju povezave skupne rabe", "shared_link_edit_description_hint": "Vnesi opis skupne rabe", "shared_link_edit_expire_after_option_day": "1 dan", - "shared_link_edit_expire_after_option_days": "{} dni", + "shared_link_edit_expire_after_option_days": "{count, plural, other {# dni}}", "shared_link_edit_expire_after_option_hour": "1 ura", - "shared_link_edit_expire_after_option_hours": "{} ur", + "shared_link_edit_expire_after_option_hours": "{count, plural, two {# uri} few {# ure} other {# ur}}", "shared_link_edit_expire_after_option_minute": "1 minuta", - "shared_link_edit_expire_after_option_minutes": "{} minut", - "shared_link_edit_expire_after_option_months": "{} mesecev", - "shared_link_edit_expire_after_option_year": "{} let", + "shared_link_edit_expire_after_option_minutes": "{count, plural, two {# minuti} few {# minute} other {# minut}}", + "shared_link_edit_expire_after_option_months": "{count, plural, one {# mesec} two {# meseca} few {# mesece} other {# mesecev}}", + "shared_link_edit_expire_after_option_year": "{count, plural, one {# leto} two {# leti} few {# leta} other {# let}}", "shared_link_edit_password_hint": "Vnesi geslo za skupno rabo", "shared_link_edit_submit_button": "Posodobi povezavo", "shared_link_error_server_url_fetch": "URL-ja streÅžnika ni mogoÄe pridobiti", - "shared_link_expires_day": "PoteÄe Äez {} dan", - "shared_link_expires_days": "PoteÄe Äez {} dni", - "shared_link_expires_hour": "PoteÄe Äez {} uro", - "shared_link_expires_hours": "PoteÄe Äez {} ur", - "shared_link_expires_minute": "PoteÄe Äez {} minuto\n", - "shared_link_expires_minutes": "PoteÄe Äez {} minut", + "shared_link_expires_day": "PoteÄe Äez {count, plural, one {# dan}}", + "shared_link_expires_days": "PoteÄe Äez {count, plural, other {# dni}}", + "shared_link_expires_hour": "PoteÄe Äez {count, plural, one {# uro}}", + "shared_link_expires_hours": "PoteÄe Äez {count, plural, two {# uri} few {# ure} other {# ur}}", + "shared_link_expires_minute": "PoteÄe Äez {count, plural, one {# minuto}}", + "shared_link_expires_minutes": "PoteÄe Äez {count, plural, two {# minuti} few {# minute} other {# minut}}", "shared_link_expires_never": "PoteÄe â", - "shared_link_expires_second": "PoteÄe Äez {} sekundo", - "shared_link_expires_seconds": "PoteÄe Äez {} sekund", + "shared_link_expires_second": "PoteÄe Äez {count,plural, one {# sekundo}}", + "shared_link_expires_seconds": "PoteÄe Äez {count, plural, two {# sekundi} few {# sekunde} other {# sekund}}", "shared_link_individual_shared": "Individualno deljeno", "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Upravljanje povezav v skupni rabi", @@ -1784,11 +1792,11 @@ "trash_no_results_message": "Fotografije in videoposnetki, ki so v smetnjaku, bodo prikazani tukaj.", "trash_page_delete_all": "IzbriÅĄi vse", "trash_page_empty_trash_dialog_content": "Ali Åželite izprazniti svoja sredstva v smeti? Ti elementi bodo trajno odstranjeni iz Immicha", - "trash_page_info": "Elementi v smeteh bodo trajno izbrisani po {} dneh", + "trash_page_info": "Elementi v smeteh bodo trajno izbrisani po {number} dneh", "trash_page_no_assets": "Ni sredstev v smeteh", "trash_page_restore_all": "Obnovi vse", "trash_page_select_assets_btn": "Izberite sredstva", - "trash_page_title": "Smetnjak ({})", + "trash_page_title": "Smetnjak ({count, number})", "trashed_items_will_be_permanently_deleted_after": "Elementi v smetnjaku bodo trajno izbrisani po {days, plural, one {# dnevu} two {# dnevih} few {# dnevih} other {# dneh}}.", "type": "Vrsta", "unarchive": "Odstrani iz arhiva", @@ -1852,8 +1860,8 @@ "version_announcement_message": "Pozdravljeni! Na voljo je nova razliÄica Immich. Vzemite si nekaj Äasa in preberite <link>opombe ob izdaji</link>, da zagotovite, da so vaÅĄe nastavitve posodobljene, da prepreÄite morebitne napaÄne konfiguracije, zlasti Äe uporabljate WatchTower ali kateri koli mehanizem, ki samodejno posodablja vaÅĄ primerek Immich.", "version_announcement_overlay_release_notes": "opombe ob izdaji", "version_announcement_overlay_text_1": "ÅŊivjo prijatelj, na voljo je nova izdaja", - "version_announcement_overlay_text_2": "vzemi si Äas in obiÅĄÄi", - "version_announcement_overlay_text_3": "in zagotovite, da sta vaÅĄa nastavitev docker-compose in .env posodobljena, da prepreÄite morebitne napaÄne konfiguracije, zlasti Äe uporabljate WatchTower ali kateri koli mehanizem, ki samodejno posodablja vaÅĄo streÅžniÅĄko aplikacijo.", + "version_announcement_overlay_text_2": "vzemi si Äas in obiÅĄÄi ", + "version_announcement_overlay_text_3": " in zagotovite, da sta vaÅĄa nastavitev docker-compose in .env posodobljena, da prepreÄite morebitne napaÄne konfiguracije, zlasti Äe uporabljate WatchTower ali kateri koli mehanizem, ki samodejno posodablja vaÅĄo streÅžniÅĄko aplikacijo.", "version_announcement_overlay_title": "Na voljo je nova razliÄica streÅžnika đ", "version_history": "Zgodovina razliÄic", "version_history_item": "{version} nameÅĄÄena {date}", diff --git a/i18n/sr_Cyrl.json b/i18n/sr_Cyrl.json index c8aa6a9f27..97fe461afd 100644 --- a/i18n/sr_Cyrl.json +++ b/i18n/sr_Cyrl.json @@ -1,5 +1,5 @@ { - "about": "Đ ĐĐŋĐģиĐēаŅиŅи", + "about": "РаĐŋĐģиĐēаŅиŅи", "account": "ĐŅĐžŅиĐģ", "account_settings": "ĐОдĐĩŅаваŅа Са ĐŅĐžŅиĐģ", "acknowledge": "ĐĐžŅвŅди", @@ -14,8 +14,8 @@ "add_a_location": "ĐĐžĐ´Đ°Ņ ĐĐžĐēаŅиŅŅ", "add_a_name": "ĐĐžĐ´Đ°Ņ Đ¸ĐŧĐĩ", "add_a_title": "ĐĐžĐ´Đ°Ņ ĐŊаŅĐģОв", - "add_endpoint": "Add endpoint", - "add_exclusion_pattern": "ĐĐžĐ´Đ°Ņ ĐžĐąŅĐ°ĐˇĐ°Ņ Đ¸ĐˇŅСиĐŧаŅа", + "add_endpoint": "ĐОдаŅŅĐĩ ĐēŅаŅŅŅ ŅаŅĐēŅ", + "add_exclusion_pattern": "ĐОдаŅŅĐĩ ОйŅĐ°ĐˇĐ°Ņ Đ¸ĐˇŅСиĐŧаŅа", "add_import_path": "ĐĐžĐ´Đ°Ņ ĐŋŅŅаŅŅ ĐˇĐ° ĐŋŅĐĩŅСиĐŧаŅĐĩ", "add_location": "ĐĐžĐ´Đ°Ņ ĐģĐžĐēаŅиŅŅ", "add_more_users": "ĐĐžĐ´Đ°Ņ ĐēĐžŅиŅĐŊиĐēĐĩ", @@ -24,8 +24,8 @@ "add_photos": "ĐĐžĐ´Đ°Ņ ŅĐžŅĐžĐŗŅаŅиŅĐĩ", "add_to": "ĐĐžĐ´Đ°Ņ ŅâĻ", "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_added": "ĐОдаŅĐž Ņ {album}", + "add_to_album_bottom_sheet_already_exists": "ĐĐĩŅ Ņ {album}", "add_to_shared_album": "ĐĐžĐ´Đ°Ņ Ņ Đ´ĐĩŅĐĩĐŊ аĐģĐąŅĐŧ", "add_url": "ĐĐžĐ´Đ°Ņ URL", "added_to_archive": "ĐОдаŅĐž Ņ Đ°ŅŅ Đ¸Đ˛Ņ", @@ -106,7 +106,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": "ĐŅŅĐžĐŧаŅŅĐēи ĐŋŅаŅиŅĐĩ ĐŋŅĐžĐŧĐĩŅĐĩĐŊĐĩ даŅĐžŅĐĩĐēĐĩ", @@ -141,7 +141,7 @@ "machine_learning_smart_search_description": "ĐĐžŅŅаĐļиŅĐĩ ŅĐģиĐēĐĩ ŅĐĩĐŧаĐŊŅиŅĐēи ĐēĐžŅиŅŅĐĩŅи ŅĐŗŅаŅĐĩĐŊи ĐĻĐĐĐ", "machine_learning_smart_search_enabled": "ĐĐŧĐžĐŗŅŅиŅĐĩ ĐŋаĐŧĐĩŅĐŊŅ ĐŋŅĐĩŅŅĐ°ĐŗŅ", "machine_learning_smart_search_enabled_description": "ĐĐēĐž ŅĐĩ oneĐŧĐžĐŗŅŅĐĩĐŊĐž, ŅĐģиĐēĐĩ ĐŊĐĩŅĐĩ йиŅи ĐēОдиŅаĐŊĐĩ Са ĐŋаĐŧĐĩŅĐŊŅ ĐŋŅĐĩŅŅĐ°ĐŗŅ.", - "machine_learning_url_description": "URL ŅĐĩŅвĐĩŅа Са ĐŧаŅиĐŊŅĐēĐž ŅŅĐĩŅĐĩ. ĐĐēĐž ŅĐĩ ĐŊавĐĩĐ´ĐĩĐŊĐž виŅĐĩ Од ŅĐĩĐ´ĐŊĐĩ URL адŅĐĩŅĐĩ, ŅваĐēи ŅĐĩŅвĐĩŅ ŅĐĩ ŅĐĩ ĐŋĐžĐēŅŅаваŅи ŅĐĩдаĐŊ ĐŋĐž ŅĐĩдаĐŊ Đ´ĐžĐē ŅĐĩдаĐŊ ĐŊĐĩ ĐžĐ´ĐŗĐžĐ˛ĐžŅи ŅŅĐŋĐĩŅĐŊĐž, ŅĐĩĐ´ĐžĐŧ Од ĐŋŅĐ˛ĐžĐŗ Đ´Đž ĐŋĐžŅĐģĐĩĐ´ŅĐĩĐŗ. ĐĄĐĩŅвĐĩŅи ĐēĐžŅи ĐŊĐĩ ŅĐĩĐ°ĐŗŅŅŅ ĐąĐ¸ŅĐĩ ĐŋŅивŅĐĩĐŧĐĩĐŊĐž СаĐŊĐĩĐŧаŅĐĩĐŊи Đ´ĐžĐē ŅĐĩ ĐŊĐĩ вŅаŅĐĩ ĐŊа ĐŧŅĐĩĐļŅ.", + "machine_learning_url_description": "ĐŖĐ Đ ŅĐĩŅвĐĩŅа Са ĐŧаŅиĐŊŅĐēĐž ŅŅĐĩŅĐĩ. ĐĐēĐž ŅĐĩ ĐŊавĐĩĐ´ĐĩĐŊĐž виŅĐĩ Од ŅĐĩĐ´ĐŊĐĩ ĐŖĐ Đ Đ°Đ´ŅĐĩŅĐĩ, ŅваĐēи ŅĐĩŅвĐĩŅ ŅĐĩ ŅĐĩ ĐŋĐžĐēŅŅаваŅи ŅĐĩдаĐŊ ĐŋĐž ŅĐĩдаĐŊ Đ´ĐžĐē ŅĐĩдаĐŊ ĐŊĐĩ ĐžĐ´ĐŗĐžĐ˛ĐžŅи ŅŅĐŋĐĩŅĐŊĐž, ŅĐĩĐ´ĐžĐŧ Од ĐŋŅĐ˛ĐžĐŗ Đ´Đž ĐŋĐžŅĐģĐĩĐ´ŅĐĩĐŗ. ĐĄĐĩŅвĐĩŅи ĐēĐžŅи ĐŊĐĩ ŅĐĩĐ°ĐŗŅŅŅ ĐąĐ¸ŅĐĩ ĐŋŅивŅĐĩĐŧĐĩĐŊĐž СаĐŊĐĩĐŧаŅĐĩĐŊи Đ´ĐžĐē ŅĐĩ ĐŊĐĩ вŅаŅĐĩ ĐŊа ĐŧŅĐĩĐļŅ.", "manage_concurrency": "ĐŖĐŋŅавŅаŅĐĩ ĐŋаŅаĐģĐĩĐģĐŊĐžŅŅŅ", "manage_log_settings": "ĐŖĐŋŅавŅаŅŅĐĩ ĐŋОдĐĩŅаваŅиĐŧа ĐĩвидĐĩĐŊŅиŅĐĩ", "map_dark_style": "ĐĸаĐŧĐŊи ŅŅиĐģ", @@ -251,7 +251,7 @@ "storage_template_hash_verification_enabled_description": "ĐĐŧĐžĐŗŅŅава Ņ ĐĩŅ Đ˛ĐĩŅиŅиĐēаŅиŅŅ, ĐŊĐĩ oneĐŧĐžĐŗŅŅаваŅŅĐĩ ОвО ĐžŅиĐŧ аĐēĐž ĐŊиŅŅĐĩ ŅĐ¸ĐŗŅŅĐŊи Ņ ĐŋĐžŅĐģĐĩдиŅĐĩ", "storage_template_migration": "ĐĐ¸ĐŗŅаŅиŅа ŅайĐģĐžĐŊа Са ŅĐēĐģадиŅŅĐĩŅĐĩ", "storage_template_migration_description": "ĐŅиĐŧĐĩĐŊиŅĐĩ ŅŅĐĩĐŊŅŅĐŊи <link>{template}</link> ĐŊа ĐŋŅĐĩŅŅ ĐžĐ´ĐŊĐž ĐžŅĐŋŅĐĩĐŧŅĐĩĐŊĐĩ ĐĩĐģĐĩĐŧĐĩĐŊŅĐĩ", - "storage_template_migration_info": "ШайĐģĐžĐŊ Са ŅĐēĐģадиŅŅĐĩŅĐĩ ŅĐĩ ĐŋŅĐĩŅвОŅиŅи ŅвĐĩ ĐĩĐēŅŅĐĩĐŊСиŅĐĩ Ņ ĐŧаĐģа ŅĐģОва. ĐŅĐžĐŧĐĩĐŊĐĩ ŅайĐģĐžĐŊа ŅĐĩ ŅĐĩ ĐŋŅиĐŧĐĩĐŊиŅи ŅаĐŧĐž ĐŊа ĐŊОвĐĩ даŅĐžŅĐĩĐēĐĩ. Đа йиŅŅĐĩ ŅĐĩŅŅОаĐēŅивĐŊĐž ĐŋŅиĐŧĐĩĐŊиĐģи ŅайĐģĐžĐŊ ĐŊа ĐŋŅĐĩŅŅ ĐžĐ´ĐŊĐž ĐžŅĐŋŅĐĩĐŧŅĐĩĐŊĐĩ даŅĐžŅĐĩĐēĐĩ, ĐŋĐžĐēŅĐĩĐŊиŅĐĩ <link>{job}</link>.", + "storage_template_migration_info": "ĐŅĐžĐŧĐĩĐŊĐĩ ŅайĐģĐžĐŊа ŅĐĩ ŅĐĩ ĐŋŅиĐŧĐĩĐŊиŅи ŅаĐŧĐž ĐŊа ĐŊОвĐĩ даŅĐžŅĐĩĐēĐĩ. Đа йиŅŅĐĩ ŅĐĩŅŅОаĐēŅивĐŊĐž ĐŋŅиĐŧĐĩĐŊиĐģи ŅайĐģĐžĐŊ ĐŊа ĐŋŅĐĩŅŅ ĐžĐ´ĐŊĐž ĐžŅĐŋŅĐĩĐŧŅĐĩĐŊĐĩ даŅĐžŅĐĩĐēĐĩ, ĐŋĐžĐēŅĐĩĐŊиŅĐĩ <link>{job}</link>.", "storage_template_migration_job": "ĐĐžŅаО ĐŧĐ¸ĐŗŅаŅиŅĐĩ ŅĐēĐģадиŅŅа", "storage_template_more_details": "Đа виŅĐĩ Đ´ĐĩŅаŅа Đž ĐžĐ˛ĐžŅ ŅŅĐŊĐēŅиŅи ĐŋĐžĐŗĐģĐĩдаŅŅĐĩ <template-link>ШайĐģĐžĐŊ Са ŅĐēĐģадиŅŅĐĩ</template-link> и ŅĐĩĐŗĐžĐ˛Đĩ <implications-link>иĐŧĐŋĐģиĐēаŅиŅĐĩ</implications-link>", "storage_template_onboarding_description": "Đада ŅĐĩ ĐžĐŧĐžĐŗŅŅĐĩĐŊа, Ова ŅŅĐŊĐēŅиŅа ŅĐĩ аŅŅĐžĐŧаŅŅĐēи ĐžŅĐŗĐ°ĐŊиСОваŅи даŅĐžŅĐĩĐēĐĩ ĐŊа ĐžŅĐŊĐžĐ˛Ņ ŅайĐģĐžĐŊа ĐēĐžŅи Đ´ĐĩŅиĐŊиŅĐĩ ĐēĐžŅиŅĐŊиĐē. ĐĐąĐžĐŗ ĐŋŅОйĐģĐĩĐŧа Ņа ŅŅайиĐģĐŊĐžŅŅŅ ĐžĐ˛Đ° ŅŅĐŊĐēŅиŅа ŅĐĩ ĐŋОдŅаСŅĐŧĐĩваĐŊĐž иŅĐēŅŅŅĐĩĐŊа. Đа виŅĐĩ иĐŊŅĐžŅĐŧаŅиŅа ĐŋĐžĐŗĐģĐĩдаŅŅĐĩ <link>Đ´ĐžĐēŅĐŧĐĩĐŊŅаŅиŅŅ</link>.", @@ -310,7 +310,7 @@ "transcoding_max_b_frames": "ĐаĐēŅиĐŧаĐģĐŊи Đ-ĐēадŅи", "transcoding_max_b_frames_description": "ĐиŅĐĩ вŅĐĩĐ´ĐŊĐžŅŅи ĐŋОйОŅŅаваŅŅ ĐĩŅиĐēаŅĐŊĐžŅŅ ĐēĐžĐŧĐŋŅĐĩŅиŅĐĩ, аĐģи ŅŅĐŋĐžŅаваŅŅ ĐēОдиŅаŅĐĩ. ĐĐžĐļда ĐŊиŅĐĩ ĐēĐžĐŧĐŋаŅийиĐģĐŊĐž Ņа Ņ Đ°ŅдвĐĩŅŅĐēиĐŧ ŅĐąŅСаŅĐĩĐŧ ĐŊа ŅŅаŅиŅиĐŧ ŅŅĐĩŅаŅиĐŧа. 0 oneĐŧĐžĐŗŅŅава Đ-ĐēадŅĐĩ, Đ´ĐžĐē -1 аŅŅĐžĐŧаŅŅĐēи ĐŋĐžŅŅавŅа ĐžĐ˛Ņ Đ˛ŅĐĩĐ´ĐŊĐžŅŅ.", "transcoding_max_bitrate": "ĐаĐēŅиĐŧаĐģĐŊи йиŅŅаŅĐĩ", - "transcoding_max_bitrate_description": "ĐОдĐĩŅаваŅĐĩ ĐŧаĐēŅиĐŧаĐģĐŊĐžĐŗ йиŅŅаŅĐĩ-а ĐŧĐžĐļĐĩ ŅŅиĐŊиŅи вĐĩĐģиŅиĐŊĐĩ даŅĐžŅĐĩĐēа ĐŋŅĐĩдвидŅивиŅиĐŧ ŅС ĐŧаŅŅ ŅĐĩĐŊŅ ĐēваĐģиŅĐĩŅа. ĐŅи 720Đŋ, ŅиĐŋиŅĐŊĐĩ вŅĐĩĐ´ĐŊĐžŅŅи ŅŅ 2600kbit/s Са ĐĐ9 иĐģи ĐĨĐĐĐĻ, иĐģи 4500kbit/s Са ĐĨ.264. oneĐŧĐžĐŗŅŅĐĩĐŊĐž аĐēĐž ŅĐĩ ĐŋĐžŅŅавŅĐĩĐŊĐž ĐŊа 0.", + "transcoding_max_bitrate_description": "ĐОдĐĩŅаваŅĐĩ ĐŧаĐēŅиĐŧаĐģĐŊĐžĐŗ йиŅŅаŅĐĩ-а ĐŧĐžĐļĐĩ ŅŅиĐŊиŅи вĐĩĐģиŅиĐŊĐĩ даŅĐžŅĐĩĐēа ĐŋŅĐĩдвидŅивиŅиĐŧ ŅС ĐŧаŅŅ ŅĐĩĐŊŅ ĐēваĐģиŅĐĩŅа. ĐŅи 720Đŋ, ŅиĐŋиŅĐŊĐĩ вŅĐĩĐ´ĐŊĐžŅŅи ŅŅ 2600Đē Са ĐĐ9 иĐģи ĐĨĐĐĐĻ, иĐģи 4500Đē Са ĐĨ.264. oneĐŧĐžĐŗŅŅĐĩĐŊĐž аĐēĐž ŅĐĩ ĐŋĐžŅŅавŅĐĩĐŊĐž ĐŊа 0.", "transcoding_max_keyframe_interval": "ĐаĐēŅиĐŧаĐģĐŊи иĐŊŅĐĩŅваĐģ keyframe-a", "transcoding_max_keyframe_interval_description": "ĐĐžŅŅавŅа ĐŧаĐēŅиĐŧаĐģĐŊŅ ŅдаŅĐĩĐŊĐžŅŅ ĐēадŅОва иСĐŧĐĩŅŅ ĐēŅŅŅĐŊĐ¸Ņ ĐēадŅОва. ĐиĐļĐĩ вŅĐĩĐ´ĐŊĐžŅŅи ĐŋĐžĐŗĐžŅŅаваŅŅ ĐĩŅиĐēаŅĐŊĐžŅŅ ĐēĐžĐŧĐŋŅĐĩŅиŅĐĩ, аĐģи ĐŋОйОŅŅаваŅŅ Đ˛ŅĐĩĐŧĐĩ ŅŅаĐļĐĩŅа и ĐŧĐžĐŗŅ ĐŋОйОŅŅаŅи ĐēваĐģиŅĐĩŅ ŅŅĐĩĐŊа Ņа ĐąŅСиĐŧ ĐēŅĐĩŅаŅĐĩĐŧ. 0 аŅŅĐžĐŧаŅŅĐēи ĐŋĐžŅŅавŅа ĐžĐ˛Ņ Đ˛ŅĐĩĐ´ĐŊĐžŅŅ.", "transcoding_optimal_description": "ĐидĐĩĐž ŅĐŊиĐŧŅи вĐĩŅи Од ŅиŅĐŊĐĩ ŅĐĩСОĐģŅŅиŅĐĩ иĐģи ĐŊиŅŅ Ņ ĐŋŅĐ¸Ņ Đ˛Đ°ŅĐĩĐŊĐžĐŧ ŅĐžŅĐŧаŅŅ", @@ -324,7 +324,7 @@ "transcoding_reference_frames_description": "ĐŅĐžŅ ĐžĐēвиŅа (ŅŅаĐŧĐĩŅ) Са ŅĐĩŅĐĩŅĐĩĐŊŅŅ ĐŋŅиĐģиĐēĐžĐŧ ĐēĐžĐŧĐŋŅĐĩŅиŅĐĩ даŅĐžĐŗ ĐžĐēвиŅа. ĐиŅĐĩ вŅĐĩĐ´ĐŊĐžŅŅи ĐŋОйОŅŅаваŅŅ ĐĩŅиĐēаŅĐŊĐžŅŅ ĐēĐžĐŧĐŋŅĐĩŅиŅĐĩ, аĐģи ŅŅĐŋĐžŅаваŅŅ ĐēОдиŅаŅĐĩ. 0 аŅŅĐžĐŧаŅŅĐēи ĐŋĐžŅŅавŅа ĐžĐ˛Ņ Đ˛ŅĐĩĐ´ĐŊĐžŅŅ.", "transcoding_required_description": "ХаĐŧĐž видĐĩĐž ŅĐŊиĐŧŅи ĐēĐžŅи ĐŊиŅŅ Ņ ĐŋŅĐ¸Ņ Đ˛Đ°ŅĐĩĐŊĐžĐŧ ŅĐžŅĐŧаŅŅ", "transcoding_settings": "ĐОдĐĩŅаваŅа видĐĩĐž ŅŅаĐŊŅĐēОдиŅаŅа", - "transcoding_settings_description": "ĐŖĐŋŅавŅаŅŅĐĩ ĐēĐžŅĐĩ видĐĩĐž ŅĐŊиĐŧĐēĐĩ ĐļĐĩĐģиŅĐĩ да ŅŅаĐŊŅĐēОдŅŅĐĩŅĐĩ и ĐēаĐēĐž Đ¸Ņ ĐžĐąŅадиŅи", + "transcoding_settings_description": "ĐŖĐŋŅавŅаŅŅĐĩ ŅĐĩСОĐģŅŅиŅĐžĐŧ и иĐŊŅĐžŅĐŧаŅиŅаĐŧа Đž ĐēОдиŅаŅŅ Đ˛Đ¸Đ´ĐĩĐž даŅĐžŅĐĩĐēа", "transcoding_target_resolution": "ĐĻиŅаĐŊа ŅĐĩСОĐģŅŅиŅа", "transcoding_target_resolution_description": "ĐĐĩŅĐĩ ŅĐĩСОĐģŅŅиŅĐĩ ĐŧĐžĐŗŅ Đ´Đ° ŅаŅŅваŅŅ Đ˛Đ¸ŅĐĩ Đ´ĐĩŅаŅа, аĐģи иĐŧ ŅĐĩ ĐŋĐžŅŅĐĩĐąĐŊĐž виŅĐĩ вŅĐĩĐŧĐĩĐŊа Са ĐēОдиŅаŅĐĩ, иĐŧаŅŅ Đ˛ĐĩŅĐĩ вĐĩĐģиŅиĐŊĐĩ даŅĐžŅĐĩĐēа и ĐŧĐžĐŗŅ Đ´Đ° ŅĐŧаŅĐĩ ĐąŅСиĐŊŅ Đ°ĐŋĐģиĐēаŅиŅĐĩ.", "transcoding_temporal_aq": "ĐŅĐĩĐŧĐĩĐŊŅĐēи (ĐĸĐĩĐŧĐŋĐžŅаĐģ) AQ", @@ -371,13 +371,17 @@ "admin_password": "ĐĐ´ĐŧиĐŊиŅŅŅаŅĐžŅŅĐēа ĐОСиĐŊĐēа", "administration": "ĐĐ´ĐŧиĐŊиŅŅŅаŅиŅа", "advanced": "ĐаĐŋŅĐĩĐ´ĐŊĐž", - "advanced_settings_log_level_title": "Log level: {}", - "advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from assets on the device. Activate this setting to load remote images instead.", - "advanced_settings_prefer_remote_title": "Prefer remote images", - "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", - "advanced_settings_proxy_headers_title": "Proxy Headers", - "advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.", - "advanced_settings_self_signed_ssl_title": "Allow self-signed SSL certificates", + "advanced_settings_enable_alternate_media_filter_subtitle": "ĐĐžŅиŅŅиŅĐĩ ĐžĐ˛Ņ ĐžĐŋŅиŅŅ ĐˇĐ° ŅиĐģŅŅиŅаŅĐĩ ĐŧĐĩдиŅа ŅĐžĐēĐžĐŧ ŅиĐŊŅ ŅĐžĐŊиСаŅиŅĐĩ ĐŊа ĐžŅĐŊĐžĐ˛Ņ Đ°ĐģŅĐĩŅĐŊаŅивĐŊĐ¸Ņ ĐēŅиŅĐĩŅиŅŅĐŧа. ĐĐžĐēŅŅаŅŅĐĩ ОвО ŅаĐŧĐž аĐēĐž иĐŧаŅĐĩ ĐŋŅОйĐģĐĩĐŧа Ņа аĐŋĐģиĐēаŅиŅĐžĐŧ да ĐžŅĐēŅиŅĐĩ ŅвĐĩ аĐģĐąŅĐŧĐĩ.", + "advanced_settings_enable_alternate_media_filter_title": "[ĐĐĐĄĐĐĐ ĐĐĐĐĐĸĐĐĐĐ] ĐĐžŅиŅŅиŅĐĩ ŅиĐģŅĐĩŅ ĐˇĐ° ŅиĐŊŅ ŅĐžĐŊиСаŅиŅŅ Đ°ĐģĐąŅĐŧа ĐŊа аĐģŅĐĩŅĐŊаŅивĐŊĐžĐŧ ŅŅĐĩŅаŅŅ", + "advanced_settings_log_level_title": "ĐивО ĐĩвидĐĩĐŊŅиŅĐĩ(log): {}", + "advanced_settings_prefer_remote_subtitle": "ĐĐĩĐēи ŅŅĐĩŅаŅи вĐĩĐžĐŧа ŅĐŋĐžŅĐž ŅŅиŅаваŅŅ ŅĐģиŅиŅĐĩ Ņа ŅŅĐĩĐ´ŅŅава ĐŊа ŅŅĐĩŅаŅŅ. ĐĐēŅивиŅаŅŅĐĩ ОвО ĐŋОдĐĩŅаваŅĐĩ да йиŅŅĐĩ ŅĐŧĐĩŅŅĐž ŅĐžĐŗĐ° ŅŅиŅаĐģи ŅдаŅĐĩĐŊĐĩ ŅĐģиĐēĐĩ.", + "advanced_settings_prefer_remote_title": "ĐŅĐĩŅĐĩŅиŅаŅŅĐĩ ŅдаŅĐĩĐŊĐĩ ŅĐģиĐēĐĩ", + "advanced_settings_proxy_headers_subtitle": "ĐĐĩŅиĐŊиŅиŅĐĩ ĐŋŅĐžĐēŅи ĐˇĐ°ĐŗĐģавŅа ĐēĐžŅĐĩ ĐĐŧĐ¸Ņ ŅŅĐĩйа да ĐŋĐžŅаŅĐĩ Ņа ŅваĐēиĐŧ ĐŧŅĐĩĐļĐŊиĐŧ ĐˇĐ°Ņ ŅĐĩвОĐŧ", + "advanced_settings_proxy_headers_title": "ĐŅĐžĐēŅи ĐĨĐĩадĐĩŅи (headers)", + "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 user's settings", "advanced_settings_troubleshooting_subtitle": "Enable additional features for troubleshooting", "advanced_settings_troubleshooting_title": "Troubleshooting", @@ -400,9 +404,9 @@ "album_remove_user_confirmation": "Đа Đģи ŅŅĐĩ ŅĐ¸ĐŗŅŅĐŊи да ĐļĐĩĐģиŅĐĩ да ŅĐēĐģĐžĐŊиŅĐĩ {user}?", "album_share_no_users": "ĐĐˇĐŗĐģĐĩда да ŅŅĐĩ ĐŋОдĐĩĐģиĐģи ĐžĐ˛Đ°Ņ Đ°ĐģĐąŅĐŧ Ņа ŅвиĐŧ ĐēĐžŅиŅĐŊиŅиĐŧа иĐģи да ĐŊĐĩĐŧаŅĐĩ ĐŊиŅĐĩĐ´ĐŊĐžĐŗ ĐēĐžŅиŅĐŊиĐēа Ņа ĐēĐžŅиĐŧ йиŅŅĐĩ Đ´ĐĩĐģиĐģи.", "album_thumbnail_card_item": "1 item", - "album_thumbnail_card_items": "{} items", + "album_thumbnail_card_items": "{} ŅŅавĐēĐĩ", "album_thumbnail_card_shared": " ¡ Shared", - "album_thumbnail_shared_by": "Shared by {}", + "album_thumbnail_shared_by": "ĐĐĩĐģи {}", "album_updated": "ĐĐģĐąŅĐŧ аĐļŅŅиŅаĐŊ", "album_updated_setting_description": "ĐŅиĐŧиŅĐĩ ОйавĐĩŅŅĐĩŅĐĩ Đĩ-ĐŋĐžŅŅĐžĐŧ Đēада Đ´ĐĩŅĐĩĐŊи аĐģĐąŅĐŧ иĐŧа ĐŊОва ŅвОŅŅŅва", "album_user_left": "ĐаĐŋŅŅŅиО/Đģа {album}", @@ -443,7 +447,7 @@ "archive_page_title": "Archive ({})", "archive_size": "ĐĐĩĐģиŅиĐŊа аŅŅ Đ¸Đ˛Đĩ", "archive_size_description": "ĐОдĐĩŅи вĐĩĐģиŅиĐŊŅ Đ°ŅŅ Đ¸Đ˛Đĩ Са ĐŋŅĐĩŅСиĐŧаŅĐĩ (Ņ ĐиĐ)", - "archived": "Archived", + "archived": "Arhivirano", "archived_count": "{count, plural, other {ĐŅŅ Đ¸Đ˛Đ¸ŅаĐŊĐž #}}", "are_these_the_same_person": "Đа Đģи ŅŅ ĐžĐ˛Đž иŅŅа ĐžŅОйа?", "are_you_sure_to_do_this": "ĐĐĩŅŅĐĩ Đģи ŅĐ¸ĐŗŅŅĐŊи да ĐļĐĩĐģиŅĐĩ ОвО да ŅŅадиŅĐĩ?", @@ -992,6 +996,7 @@ "filetype": "ĐŅŅŅа Đ´ĐžĐēŅĐŧĐĩĐŊŅа", "filter": "Filter", "filter_people": "ФиĐģŅŅиŅаŅĐĩ ĐžŅОйа", + "filter_places": "ФиĐģŅŅиŅаŅŅĐĩ ĐŧĐĩŅŅа", "find_them_fast": "ĐŅСО Đ¸Ņ ĐŋŅĐžĐŊаŅиŅĐĩ ĐŋĐž иĐŧĐĩĐŊŅ ĐŋĐžĐŧĐžŅŅ ĐŋŅĐĩŅŅĐ°ĐŗĐĩ", "fix_incorrect_match": "ĐŅĐŋŅавиŅĐĩ ĐŊĐĩŅаŅĐŊĐž ĐŋОдŅдаŅаŅĐĩ", "folder": "Folder", @@ -1282,6 +1287,7 @@ "onboarding_welcome_user": "ĐОйŅОдОŅĐģи, {user}", "online": "ĐĐžŅŅŅĐŋаĐŊ (ĐĐŊĐģиĐŊĐĩ)", "only_favorites": "ХаĐŧĐž ŅавОŅиŅи", + "open": "ĐŅвОŅи", "open_in_map_view": "ĐŅвОŅи Ņ ĐŋŅиĐēĐ°ĐˇŅ ĐŧаĐŋĐĩ", "open_in_openstreetmap": "ĐŅвОŅиŅĐĩ Ņ ĐĐŋĐĩĐŊĐĄŅŅĐĩĐĩŅĐаĐŋ-Ņ", "open_the_search_filters": "ĐŅвОŅиŅĐĩ ŅиĐģŅĐĩŅĐĩ Са ĐŋŅĐĩŅŅĐ°ĐŗŅ", @@ -1849,7 +1855,7 @@ "variables": "ĐŅĐžĐŧĐĩĐŊŅивĐĩ (ваŅиайĐģĐĩŅ)", "version": "ĐĐĩŅСиŅа", "version_announcement_closing": "ĐĸĐ˛ĐžŅ ĐŋŅиŅаŅĐĩŅ, ĐĐģĐĩĐēŅ", - "version_announcement_message": "ĐĐ´ŅавО ĐŋŅиŅаŅĐĩŅŅ, ĐŋĐžŅŅĐžŅи ĐŊОва вĐĩŅСиŅа аĐŋĐģиĐēаŅиŅĐĩ, ĐŧĐžĐģиĐŧĐž Đ˛Đ°Ņ Đ´Đ° ОдвОŅиŅĐĩ вŅĐĩĐŧĐĩ да ĐŋĐžŅĐĩŅиŅĐĩ <link>ĐŊаĐŋĐžĐŧĐĩĐŊĐĩ Đž иСдаŅŅ</link> и ŅвĐĩŅиŅĐĩ ŅĐĩ да ŅĐĩ ŅĐĩŅвĐĩŅ Đ°ĐļŅŅиŅаĐŊ ĐēаĐēĐž йи ŅĐĩ ŅĐŋŅĐĩŅиĐģĐĩ йиĐģĐž ĐēаĐēвĐĩ ĐŋĐžĐŗŅĐĩŅĐŊĐĩ ĐēĐžĐŊŅĐ¸ĐŗŅŅаŅиŅĐĩ, ĐŋĐžŅĐĩĐąĐŊĐž аĐēĐž ĐēĐžŅиŅŅиŅĐĩ WatchTower иĐģи йиĐģĐž ĐēĐžŅи ĐŧĐĩŅ Đ°ĐŊиСаĐŧ ĐēĐžŅи аŅŅĐžĐŧаŅŅĐēи ŅĐŋŅавŅа аĐļŅŅиŅаŅĐĩĐŧ ваŅĐĩ аĐŋĐģиĐēаŅиŅĐĩ.", + "version_announcement_message": "ĐĐ´ŅавО ĐŋŅиŅаŅĐĩŅŅ, ĐŋĐžŅŅĐžŅи ĐŊОва вĐĩŅСиŅа аĐŋĐģиĐēаŅиŅĐĩ, ĐŧĐžĐģиĐŧĐž Đ˛Đ°Ņ Đ´Đ° ОдвОŅиŅĐĩ вŅĐĩĐŧĐĩ да ĐŋĐžŅĐĩŅиŅĐĩ <link>ĐŊаĐŋĐžĐŧĐĩĐŊĐĩ Đž иСдаŅŅ</link> и ŅвĐĩŅиŅĐĩ ŅĐĩ Ņ ŅвОŅĐĩ <code>docker-compose.yml</code>, и <code>.env</code> ĐŋОдĐĩŅаваŅĐĩ ŅĐĩ аĐļŅŅиŅаĐŊĐž ĐēаĐēĐž йи ŅĐĩ ŅĐŋŅĐĩŅиĐģĐĩ йиĐģĐž ĐēаĐēвĐĩ ĐŋĐžĐŗŅĐĩŅĐŊĐĩ ĐēĐžĐŊŅĐ¸ĐŗŅŅаŅиŅĐĩ, ĐŋĐžŅĐĩĐąĐŊĐž аĐēĐž ĐēĐžŅиŅŅиŅĐĩ WatchTower иĐģи йиĐģĐž ĐēĐžŅи ĐŧĐĩŅ Đ°ĐŊиСаĐŧ ĐēĐžŅи аŅŅĐžĐŧаŅŅĐēи ŅĐŋŅавŅа аĐļŅŅиŅаŅĐĩĐŧ ваŅĐĩ аĐŋĐģиĐēаŅиŅĐĩ.", "version_announcement_overlay_release_notes": "release notes", "version_announcement_overlay_text_1": "Hi friend, there is a new release of", "version_announcement_overlay_text_2": "please take your time to visit the ", diff --git a/i18n/sr_Latn.json b/i18n/sr_Latn.json index c3280ee4fb..093bf06df7 100644 --- a/i18n/sr_Latn.json +++ b/i18n/sr_Latn.json @@ -14,7 +14,7 @@ "add_a_location": "Dodaj Lokaciju", "add_a_name": "Dodaj ime", "add_a_title": "Dodaj naslov", - "add_endpoint": "Add endpoint", + "add_endpoint": "Dodajte krajnju taÄku", "add_exclusion_pattern": "Dodaj obrazac izuzimanja", "add_import_path": "Dodaj putanju za preuzimanje", "add_location": "Dodaj lokaciju", @@ -371,13 +371,17 @@ "admin_password": "Administratorska Lozinka", "administration": "Administracija", "advanced": "Napredno", - "advanced_settings_log_level_title": "Log level: {}", - "advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from assets on the device. Activate this setting to load remote images instead.", - "advanced_settings_prefer_remote_title": "Prefer remote images", - "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", - "advanced_settings_proxy_headers_title": "Proxy Headers", - "advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.", + "advanced_settings_enable_alternate_media_filter_subtitle": "Koristite ovu opciju za filtriranje medija tokom sinhronizacije na osnovu alternativnih kriterijuma. PokuÅĄajte ovo samo ako imate problema sa aplikacijom da otkrije sve albume.", + "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTALNO] Koristite filter za sinhronizaciju albuma na alternativnom ureÄaju", + "advanced_settings_log_level_title": "Nivo evidencije (log): {}", + "advanced_settings_prefer_remote_subtitle": "Neki ureÄaji veoma sporo uÄitavaju sliÄice sa sredstava na ureÄaju. Aktivirajte ovo podeÅĄavanje da biste umesto toga uÄitali udaljene slike.", + "advanced_settings_prefer_remote_title": "Preferirajte udaljene slike", + "advanced_settings_proxy_headers_subtitle": "DefiniÅĄite proksi zaglavlja koje Immich treba da poÅĄalje sa svakim mreÅžnim zahtevom", + "advanced_settings_proxy_headers_title": "Proksi Headeri (headers)", + "advanced_settings_self_signed_ssl_subtitle": "PreskaÄe verifikaciju SSL sertifikata za krajnju taÄku servera. Obavezno za samopotpisane sertifikate.", "advanced_settings_self_signed_ssl_title": "Allow self-signed SSL certificates", + "advanced_settings_sync_remote_deletions_subtitle": "Automatski izbriÅĄite ili vratite sredstvo na ovom ureÄaju kada se ta radnja preduzme na vebu", + "advanced_settings_sync_remote_deletions_title": "Sinhronizujte udaljena brisanja [EKSPERIMENTALNO]", "advanced_settings_tile_subtitle": "Advanced user's settings", "advanced_settings_troubleshooting_subtitle": "Enable additional features for troubleshooting", "advanced_settings_troubleshooting_title": "Troubleshooting", @@ -402,7 +406,7 @@ "album_thumbnail_card_item": "1 stavka", "album_thumbnail_card_items": "{} stavki", "album_thumbnail_card_shared": "Deljeno", - "album_thumbnail_shared_by": "Shared by {}", + "album_thumbnail_shared_by": "Deli {}", "album_updated": "Album aÅžuriran", "album_updated_setting_description": "Primite obaveÅĄtenje e-poÅĄtom kada deljeni album ima nova svojstva", "album_user_left": "Napustio/la {album}", @@ -529,11 +533,11 @@ "backup_controller_page_background_turn_on": "UkljuÄi pozadinski servis", "backup_controller_page_background_wifi": "Samo na WiFi", "backup_controller_page_backup": "Napravi rezervnu kopiju", - "backup_controller_page_backup_selected": "Odabrano:", + "backup_controller_page_backup_selected": "Odabrano: ", "backup_controller_page_backup_sub": "ZavrÅĄeno pravljenje rezervne kopije fotografija i videa", "backup_controller_page_created": "Napravljeno:{}", "backup_controller_page_desc_backup": "UkljuÄi pravljenje rezervnih kopija u prvom planu da automatski napravite rezervne kopije kada otvorite aplikaciju.", - "backup_controller_page_excluded": "IskljuÄeno:", + "backup_controller_page_excluded": "IskljuÄeno: ", "backup_controller_page_failed": "NeuspeÅĄno ({})", "backup_controller_page_filename": "Ime fajla:{} [{}]", "backup_controller_page_id": "ID:{}", @@ -992,6 +996,7 @@ "filetype": "Vrsta dokumenta", "filter": "Filter", "filter_people": "Filtriranje osoba", + "filter_places": "Filtrirajte mesta", "find_them_fast": "Brzo ih pronaÄite po imenu pomocĖu pretrage", "fix_incorrect_match": "Ispravite netaÄno podudaranje", "folder": "Folder", @@ -1282,6 +1287,7 @@ "onboarding_welcome_user": "DobrodoÅĄli, {user}", "online": "Dostupan (Online)", "only_favorites": "Samo favoriti", + "open": "Otvori", "open_in_map_view": "Otvorite u prikaz karte", "open_in_openstreetmap": "Otvorite u OpenStreetMap-u", "open_the_search_filters": "Otvorite filtere za pretragu", diff --git a/i18n/sv.json b/i18n/sv.json index a727aa68e2..fff282f99f 100644 --- a/i18n/sv.json +++ b/i18n/sv.json @@ -39,11 +39,11 @@ "authentication_settings_disable_all": "Ãr du säker pÃĨ att du vill inaktivera alla inloggningsmetoder? Inloggning kommer att helt inaktiveras.", "authentication_settings_reenable": "FÃļr att ÃĨteraktivera, använd <link>Server Command</link>.", "background_task_job": "Bakgrundsaktiviteter", - "backup_database": "Databassäkerhetskopia", - "backup_database_enable_description": "Aktivera säkerhetskopiering av databas", - "backup_keep_last_amount": "Antal säkerhetskopior att behÃĨlla", - "backup_settings": "Säkerhetskopieringsinställningar", - "backup_settings_description": "Hantera inställningar fÃļr säkerhetskopiering av databas", + "backup_database": "Skapa Databasdump", + "backup_database_enable_description": "Aktivera dumpning av databas", + "backup_keep_last_amount": "Antal databasdumpar att behÃĨlla", + "backup_settings": "Inställningar databasdump", + "backup_settings_description": "Hantera inställningar fÃļr databasdumpning. Observera: Dessa jobb Ãļvervakas inte och du blir inte notifierad om misslyckanden.", "check_all": "Välj alla", "cleanup": "Uppstädning", "cleared_jobs": "Rensade jobben fÃļr:{job}", @@ -378,6 +378,7 @@ "advanced_settings_proxy_headers_title": "Proxy-headers", "advanced_settings_self_signed_ssl_subtitle": "Hoppar Ãļver SSL-certifikatverifiering fÃļr serverändpunkten. Krävs fÃļr självsignerade certifikat.", "advanced_settings_self_signed_ssl_title": "TillÃĨt självsignerade SSL-certifikat", + "advanced_settings_sync_remote_deletions_title": "Synkonisera fjärradering [EXPERIMENTELL]", "advanced_settings_tile_subtitle": "Avancerade användarinställningar", "advanced_settings_troubleshooting_subtitle": "Aktivera funktioner fÃļr felsÃļkning", "advanced_settings_troubleshooting_title": "FelsÃļkning", @@ -992,6 +993,7 @@ "filetype": "Filtyp", "filter": "Filter", "filter_people": "Filtrera personer", + "filter_places": "Filtrera platser", "find_them_fast": "Hitta dem snabbt efter namn med sÃļk", "fix_incorrect_match": "Fixa inkorrekt matchning", "folder": "Mapp", diff --git a/i18n/th.json b/i18n/th.json index d797dab583..939ab431a9 100644 --- a/i18n/th.json +++ b/i18n/th.json @@ -524,11 +524,11 @@ "backup_controller_page_background_turn_on": "āšā¸ā¸´ā¸ā¸ā¸Ŗā¸´ā¸ā¸˛ā¸Ŗāšā¸ā¸ˇāšā¸ā¸ā¸Ģā¸Ĩā¸ąā¸", "backup_controller_page_background_wifi": "ā¸ā¸ WiFi āšā¸āšā¸˛ā¸ā¸ąāšā¸", "backup_controller_page_backup": "ā¸Ē⏺⏪ā¸ā¸ā¸āšā¸ā¸Ąā¸šā¸Ĩ", - "backup_controller_page_backup_selected": "ā¸ā¸ĩāšāšā¸Ĩ⏎ā¸ā¸:", + "backup_controller_page_backup_selected": "ā¸ā¸ĩāšāšā¸Ĩ⏎ā¸ā¸: ", "backup_controller_page_backup_sub": "ā¸Ŗā¸šā¸ā¸ ⏞ā¸āšā¸Ĩ⏰⏧⏴ā¸ā¸ĩāšā¸ā¸ā¸ĩāšā¸Ē⏺⏪ā¸ā¸āšā¸Ĩāšā¸§", "backup_controller_page_created": "ā¸Ē⏪āšā¸˛ā¸āšā¸Ąā¸ˇāšā¸: {}", "backup_controller_page_desc_backup": "āšā¸ā¸´ā¸ā¸ā¸˛ā¸Ŗā¸Ē⏺⏪ā¸ā¸ā¸āšā¸ā¸Ąā¸šā¸Ĩāšā¸ā¸ā¸˛ā¸ā¸Ģā¸āšā¸˛āšā¸ā¸ˇāšā¸ā¸ā¸ĩāšā¸ā¸°ā¸ā¸ąā¸āšā¸Ģā¸Ĩā¸ā¸ā¸Ŗā¸ąā¸ā¸ĸ⏞ā¸ā¸Ŗāšā¸Ģā¸Ąāšāšā¸ā¸ĸā¸ąā¸āšā¸ā¸´ā¸Ŗāšā¸āšā¸§ā¸ā¸Ŗāšāšā¸Ąā¸ˇāšā¸āšā¸ā¸´ā¸āšā¸ā¸", - "backup_controller_page_excluded": "ā¸ā¸šā¸ā¸ĸā¸āšā¸§āšā¸:", + "backup_controller_page_excluded": "ā¸ā¸šā¸ā¸ĸā¸āšā¸§āšā¸: ", "backup_controller_page_failed": "ā¸Ĩāšā¸Ąāšā¸Ģā¸Ĩ⏧ ({})", "backup_controller_page_filename": "ā¸ā¸ˇāšā¸āšā¸ā¸Ĩāš: {} [{}]", "backup_controller_page_id": "ID: {}", diff --git a/i18n/tr.json b/i18n/tr.json index 35c90b7f90..db9088f5e7 100644 --- a/i18n/tr.json +++ b/i18n/tr.json @@ -524,11 +524,11 @@ "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_selected": "Seçili:", + "backup_controller_page_backup_selected": "Seçili: ", "backup_controller_page_backup_sub": "Yedeklenen ÃļÄeler", "backup_controller_page_created": "OluÅturma tarihi: {}", "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_excluded": "Hariç tutuldu: ", "backup_controller_page_failed": "BaÅarÄąsÄąz ({})", "backup_controller_page_filename": "Dosya adÄą: {} [{}]", "backup_controller_page_id": "ID: {}", @@ -1842,7 +1842,7 @@ "version_announcement_message": "Merhaba! Immich'in yeni bir sÃŧrÃŧmÃŧ mevcut. LÃŧtfen yapÄąlandÄąrmanÄązÄąn gÃŧncel olduÄundan emin olmak için <link>sÃŧrÃŧm notlarÄąnÄą</link> okumak için biraz zaman ayÄąrÄąn, Ãļzellikle WatchTower veya Immich kurulumunuzu otomatik olarak gÃŧncelleyen bir mekanizma kullanÄąyorsanÄąz yanlÄąÅ yapÄąlandÄąrmalarÄąn ÃļnÃŧne geçmek adÄąna bu Ãļnemlidir.", "version_announcement_overlay_release_notes": "sÃŧrÃŧm notlarÄą", "version_announcement_overlay_text_1": "Merhaba arkadaÅÄąm, yeni bir sÃŧrÃŧm mevcut", - "version_announcement_overlay_text_2": "lÃŧtfen biraz zaman ayÄąrÄąn ve inceleyin:", + "version_announcement_overlay_text_2": "lÃŧtfen biraz zaman ayÄąrÄąn ve inceleyin: ", "version_announcement_overlay_text_3": "ve Ãļzellikle WatchTower veya sunucu uygulamanÄązÄą otomatik olarak gÃŧncelleyen herhangi bir mekanizma kullanÄąyorsanÄąz, herhangi bir yanlÄąÅ yapÄąlandÄąrmayÄą Ãļnlemek için docker-compose ve .env kurulumunuzun gÃŧncel olduÄundan emin olun.", "version_announcement_overlay_title": "Yeni Sunucu SÃŧrÃŧmÃŧ Mevcut đ", "version_history": "Versiyon geçmiÅi", diff --git a/i18n/uk.json b/i18n/uk.json index 5c6a809f26..061d9bd78f 100644 --- a/i18n/uk.json +++ b/i18n/uk.json @@ -39,11 +39,11 @@ "authentication_settings_disable_all": "Đи вĐŋĐĩвĐŊĐĩĐŊŅ, ŅĐž Ņ ĐžŅĐĩŅĐĩ виĐŧĐēĐŊŅŅи вŅŅ ĐŧĐĩŅОди Đ˛Ņ ĐžĐ´Ņ? ĐŅ ŅĐ´ ĐąŅĐ´Đĩ ĐŋОвĐŊŅŅŅŅ Đ˛Đ¸ĐŧĐēĐŊĐĩĐŊиК.", "authentication_settings_reenable": "ĐĐģŅ ĐŋОвŅĐžŅĐŊĐžĐŗĐž ввŅĐŧĐēĐŊĐĩĐŊĐŊŅ Đ˛Đ¸ĐēĐžŅиŅŅОвŅĐšŅĐĩ <link>ĐĐžĐŧаĐŊĐ´Ņ ŅĐĩŅвĐĩŅа</link>.", "background_task_job": "ФОĐŊĐžĐ˛Ņ ĐавдаĐŊĐŊŅ", - "backup_database": "Đ ĐĩСĐĩŅвĐŊа ĐēĐžĐŋŅŅ ĐąĐ°ĐˇĐ¸ даĐŊĐ¸Ņ ", - "backup_database_enable_description": "ĐŖĐ˛ŅĐŧĐēĐŊŅŅи ŅĐĩСĐĩŅвĐŊĐĩ ĐēĐžĐŋŅŅваĐŊĐŊŅ ĐąĐ°ĐˇĐ¸ даĐŊĐ¸Ņ ", - "backup_keep_last_amount": "ĐŅĐģŅĐēŅŅŅŅ ŅĐĩСĐĩŅвĐŊĐ¸Ņ ĐēĐžĐŋŅĐš Đ´ĐģŅ ĐˇĐąĐĩŅŅĐŗĐ°ĐŊĐŊŅ", - "backup_settings": "ĐаĐģаŅŅŅваĐŊĐŊŅ ŅĐĩСĐĩŅвĐŊĐžĐŗĐž ĐēĐžĐŋŅŅваĐŊĐŊŅ", - "backup_settings_description": "ĐĐĩŅŅваĐŊĐŊŅ ĐŊаĐģаŅŅŅваĐŊĐŊŅĐŧи ŅĐĩСĐĩŅвĐŊĐžĐŗĐž ĐēĐžĐŋŅŅваĐŊĐŊŅ ĐąĐ°ĐˇĐ¸ даĐŊĐ¸Ņ ", + "backup_database": "ĐĄŅвОŅиŅи даĐŧĐŋ йаСи даĐŊĐ¸Ņ ", + "backup_database_enable_description": "ĐŖĐ˛ŅĐŧĐēĐŊŅŅи даĐŧĐŋи йаСи даĐŊĐ¸Ņ ", + "backup_keep_last_amount": "ĐŅĐģŅĐēŅŅŅŅ ĐŋĐžĐŋĐĩŅĐĩĐ´ĐŊŅŅ Đ´Đ°ĐŧĐŋŅв, ŅĐēŅ ĐˇĐąĐĩŅŅĐŗĐ°Ņи", + "backup_settings": "ĐаĐģаŅŅŅваĐŊĐŊŅ Đ´Đ°ĐŧĐŋа йаСи даĐŊĐ¸Ņ ", + "backup_settings_description": "ĐĐĩŅŅваŅи ĐŊаĐģаŅŅŅваĐŊĐŊŅĐŧи даĐŧĐŋа йаСи даĐŊĐ¸Ņ . ĐŅиĐŧŅŅĐēа: ŅŅ ĐˇĐ°Đ˛Đ´Đ°ĐŊĐŊŅ ĐŊĐĩ ĐēĐžĐŊŅŅĐžĐģŅŅŅŅŅŅ, Ņ Đ˛Đ¸ ĐŊĐĩ ĐžŅŅиĐŧаŅŅĐĩ ŅĐŋОвŅŅĐĩĐŊĐŊŅ ĐŋŅĐž ĐŋĐžĐŧиĐģĐēи.", "check_all": "ĐĐĩŅĐĩвŅŅиŅи вŅĐĩ", "cleanup": "ĐŅиŅĐĩĐŊĐŊŅ", "cleared_jobs": "ĐŅиŅĐĩĐŊŅ ĐˇĐ°Đ˛Đ´Đ°ĐŊĐŊŅ Đ´ĐģŅ: {job}", @@ -371,13 +371,17 @@ "admin_password": "ĐаŅĐžĐģŅ Đ°Đ´ĐŧŅĐŊŅŅŅŅаŅĐžŅа", "administration": "ĐĐ´ĐŧŅĐŊŅŅŅŅŅваĐŊĐŊŅ", "advanced": "РОСŅиŅĐĩĐŊŅ", + "advanced_settings_enable_alternate_media_filter_subtitle": "ĐиĐēĐžŅиŅŅОвŅĐšŅĐĩ ŅĐĩĐš ваŅŅаĐŊŅ Đ´ĐģŅ ŅŅĐģŅŅŅаŅŅŅ ĐŧĐĩĐ´ŅаŅаКĐģŅв ĐŋŅĐ´ ŅĐ°Ņ ŅиĐŊŅ ŅĐžĐŊŅСаŅŅŅ ĐˇĐ° аĐģŅŅĐĩŅĐŊаŅивĐŊиĐŧи ĐēŅиŅĐĩŅŅŅĐŧи. ĐĄĐŋŅОйŅĐšŅĐĩ ŅĐĩ, ŅĐēŅĐž Ņ Đ˛Đ°Ņ Đ˛Đ¸ĐŊиĐēаŅŅŅ ĐŋŅОйĐģĐĩĐŧи С ŅиĐŧ, ŅĐž дОдаŅĐžĐē ĐŊĐĩ виŅвĐģŅŅ Đ˛ŅŅ Đ°ĐģŅйОĐŧи.", + "advanced_settings_enable_alternate_media_filter_title": "[ĐĐĐĄĐĐĐ ĐĐĐĐĐĸĐĐĐŦĐĐĐ] ĐиĐēĐžŅиŅŅОвŅĐšŅĐĩ аĐģŅŅĐĩŅĐŊаŅивĐŊиК ŅŅĐģŅŅŅ ŅиĐŊŅ ŅĐžĐŊŅСаŅŅŅ Đ°ĐģŅйОĐŧŅв ĐŋŅиŅŅŅĐžŅ", "advanced_settings_log_level_title": "Đ ŅвĐĩĐŊŅ ĐģĐžĐŗŅваĐŊĐŊŅ: {}", "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_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": "ĐŖŅŅĐŊĐĩĐŊĐŊŅ ĐŊĐĩŅĐŋŅавĐŊĐžŅŅĐĩĐš", @@ -498,18 +502,18 @@ "background_location_permission": "ĐОСвŅĐģ Đ´Đž ĐŧŅŅŅĐĩСĐŊĐ°Ņ ĐžĐ´ĐļĐĩĐŊĐŊŅ Ņ ŅĐžĐŊŅ", "background_location_permission_content": "ЊОй ĐŋĐĩŅĐĩĐŧиĐēаŅи ĐŧĐĩŅĐĩĐļŅ Ņ ŅĐžĐŊОвОĐŧŅ ŅĐĩĐļиĐŧŅ, Immich ĐŧĐ°Ņ *СавĐļди* ĐŧаŅи Đ´ĐžŅŅŅĐŋ Đ´Đž ŅĐžŅĐŊĐžŅ ĐŗĐĩĐžĐģĐžĐēаŅŅŅ, ŅОй СŅиŅŅваŅи ĐŊĐ°ĐˇĐ˛Ņ Wi-Fi ĐŧĐĩŅĐĩĐļŅ", "backup_album_selection_page_albums_device": "ĐĐģŅйОĐŧи ĐŊа ĐŋŅиŅŅŅĐžŅ ({})", - "backup_album_selection_page_albums_tap": "ĐĸĐžŅĐēĐŊŅŅŅŅŅ, ŅОй вĐēĐģŅŅиŅи,\nŅĐžŅĐēĐŊŅŅŅŅŅ Đ´Đ˛ŅŅŅ, ŅОй виĐēĐģŅŅиŅи", + "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_all": "ĐŖŅŅ", - "backup_background_service_backup_failed_message": "ĐĐĩ вдаĐģĐžŅŅ ĐˇŅОйиŅи ŅĐĩСĐĩŅвĐŊŅ ĐēĐžĐŋŅŅ ĐĩĐģĐĩĐŧĐĩĐŊŅŅв. ĐОвŅĐžŅŅŅ...", - "backup_background_service_connection_failed_message": "ĐĐĩ вдаĐģĐžŅŅ ĐˇĐ˛'ŅСаŅиŅŅ ŅС ŅĐĩŅвĐĩŅĐžĐŧ. ĐОвŅĐžŅŅŅ...", + "backup_background_service_backup_failed_message": "ĐĐĩ вдаĐģĐžŅŅ ĐˇŅОйиŅи ŅĐĩСĐĩŅвĐŊŅ ĐēĐžĐŋŅŅ ĐĩĐģĐĩĐŧĐĩĐŊŅŅв. ĐОвŅĐžŅŅŅâĻ", + "backup_background_service_connection_failed_message": "ĐĐĩ вдаĐģĐžŅŅ ĐˇĐ˛'ŅСаŅиŅŅ ŅС ŅĐĩŅвĐĩŅĐžĐŧ. ĐОвŅĐžŅŅŅâĻ", "backup_background_service_current_upload_notification": "ĐаваĐŊŅаĐļŅŅŅŅŅŅ {}", "backup_background_service_default_notification": "ĐĐĩŅĐĩвŅŅŅŅ ĐŊаŅвĐŊŅŅŅŅ ĐŊĐžĐ˛Đ¸Ņ ĐĩĐģĐĩĐŧĐĩĐŊŅŅвâĻ", "backup_background_service_error_title": "ĐĐžĐŧиĐģĐēа ŅĐĩСĐĩŅвĐŊĐžĐŗĐž ĐēĐžĐŋŅŅваĐŊĐŊŅ", - "backup_background_service_in_progress_notification": "Đ ĐĩСĐĩŅвĐŊĐĩ ĐēĐžĐŋŅŅваĐŊĐŊŅ Đ˛Đ°ŅĐ¸Ņ ĐĩĐģĐĩĐŧĐĩĐŊŅŅв...", + "backup_background_service_in_progress_notification": "Đ ĐĩСĐĩŅвĐŊĐĩ ĐēĐžĐŋŅŅваĐŊĐŊŅ Đ˛Đ°ŅĐ¸Ņ ĐĩĐģĐĩĐŧĐĩĐŊŅŅвâĻ", "backup_background_service_upload_failure_notification": "ĐĐĩ вдаĐģĐžŅŅ ĐˇĐ°Đ˛Đ°ĐŊŅаĐļиŅи {}", "backup_controller_page_albums": "Đ ĐĩСĐĩŅвĐŊĐĩ ĐēĐžĐŋŅŅваĐŊĐŊŅ Đ°ĐģŅйОĐŧŅв", "backup_controller_page_background_app_refresh_disabled_content": "ĐĐģŅ ŅĐžĐŊĐžĐ˛ĐžĐŗĐž ŅĐĩСĐĩŅвĐŊĐžĐŗĐž ĐēĐžĐŋŅŅваĐŊĐŊŅ ŅвŅĐŧĐēĐŊŅŅŅ ŅĐžĐŊОвĐĩ ĐžĐŊОвĐģĐĩĐŊĐŊŅ Đ˛ ĐŧĐĩĐŊŅ \"ĐаĐģаŅŅŅваĐŊĐŊŅ > ĐĐ°ĐŗĐ°ĐģŅĐŊŅ > ФОĐŊОвĐĩ ĐžĐŊОвĐģĐĩĐŊĐŊŅ ĐŋŅĐžĐŗŅаĐŧи\".", @@ -521,7 +525,7 @@ "backup_controller_page_background_battery_info_title": "ĐĐŋŅиĐŧŅСаŅŅŅ ĐąĐ°ŅаŅĐĩŅ", "backup_controller_page_background_charging": "ĐиŅĐĩ ĐŋŅĐ´ ŅĐ°Ņ ĐˇĐ°ŅŅĐ´ĐļаĐŊĐŊŅ", "backup_controller_page_background_configure_error": "ĐĐĩ вдаĐģĐžŅŅ ĐŊаĐģаŅŅŅваŅи ŅĐžĐŊОвиК ŅĐĩŅвŅŅ", - "backup_controller_page_background_delay": "ĐаŅŅиĐŧĐēа ĐŋĐĩŅĐĩĐ´ ŅĐĩСĐĩŅвĐŊиĐŧ ĐēĐžĐŋŅŅваĐŊĐŊŅĐŧ ĐŊĐžĐ˛Đ¸Ņ ĐĩĐģĐĩĐŧĐĩĐŊŅŅв: {}", + "backup_controller_page_background_delay": "ĐаŅŅиĐŧĐēа ŅĐĩСĐĩŅвĐŊĐžĐŗĐž ĐēĐžĐŋŅŅваĐŊĐŊŅ ĐŊĐžĐ˛Đ¸Ņ ĐĩĐģĐĩĐŧĐĩĐŊŅŅв: {}", "backup_controller_page_background_description": "ĐŖĐ˛ŅĐŧĐēĐŊŅŅŅ ŅĐžĐŊĐžĐ˛Ņ ŅĐģŅĐļĐąŅ, ŅОй авŅĐžĐŧаŅиŅĐŊĐž ŅŅвОŅŅваŅи ŅĐĩСĐĩŅвĐŊŅ ĐēĐžĐŋŅŅ ĐąŅĐ´Ņ-ŅĐēĐ¸Ņ ĐŊĐžĐ˛Đ¸Ņ ĐĩĐģĐĩĐŧĐĩĐŊŅŅв ĐąĐĩС ĐŊĐĩĐžĐąŅ ŅĐ´ĐŊĐžŅŅŅ Đ˛ŅĐ´ĐēŅиваŅи ĐŋŅĐžĐŗŅаĐŧŅ", "backup_controller_page_background_is_off": "ĐвŅĐžĐŧаŅиŅĐŊĐĩ ŅĐžĐŊОвĐĩ ŅĐĩСĐĩŅвĐŊĐĩ ĐēĐžĐŋŅŅваĐŊĐŊŅ Đ˛Đ¸ĐŧĐēĐŊĐĩĐŊĐž", "backup_controller_page_background_is_on": "ĐвŅĐžĐŧаŅиŅĐŊĐĩ ŅĐžĐŊОвĐĩ ŅĐĩСĐĩŅвĐŊĐĩ ĐēĐžĐŋŅŅваĐŊĐŊŅ Đ˛Đ˛ŅĐŧĐēĐŊĐĩĐŊĐž", @@ -529,11 +533,11 @@ "backup_controller_page_background_turn_on": "ĐŖĐ˛ŅĐŧĐēĐŊŅŅи ŅĐžĐŊОвиК ŅĐĩŅвŅŅ", "backup_controller_page_background_wifi": "ĐиŅĐĩ ĐŊа WiFi", "backup_controller_page_backup": "Đ ĐĩСĐĩŅвĐŊĐĩ ĐēĐžĐŋŅŅваĐŊĐŊŅ", - "backup_controller_page_backup_selected": "ĐĐąŅаĐŊĐž:", + "backup_controller_page_backup_selected": "ĐĐąŅаĐŊĐž: ", "backup_controller_page_backup_sub": "Đ ĐĩСĐĩŅвĐŊŅ ĐēĐžĐŋŅŅ ĐˇĐŊŅĐŧĐēŅв Ņа вŅĐ´ĐĩĐž", "backup_controller_page_created": "ĐĄŅвОŅĐĩĐŊĐž: {}", "backup_controller_page_desc_backup": "ĐŖĐ˛ŅĐŧĐēĐŊŅŅŅ ŅĐĩСĐĩŅвĐŊĐĩ ĐēĐžĐŋŅŅваĐŊĐŊŅ ĐŊа ĐŋĐĩŅĐĩĐ´ĐŊŅĐžĐŧŅ ĐŋĐģаĐŊŅ, ŅОй авŅĐžĐŧаŅиŅĐŊĐž СаваĐŊŅаĐļŅваŅи ĐŊĐžĐ˛Ņ ĐĩĐģĐĩĐŧĐĩĐŊŅи ĐŊа ŅĐĩŅвĐĩŅ ĐŋŅĐ´ ŅĐ°Ņ Đ˛ŅĐ´ĐēŅиŅŅŅ ĐŋŅĐžĐŗŅаĐŧи.", - "backup_controller_page_excluded": "ĐиĐģŅŅĐĩĐŊĐž:", + "backup_controller_page_excluded": "ĐиĐģŅŅĐĩĐŊĐž: ", "backup_controller_page_failed": "ĐĐĩвдаĐģŅ ({})", "backup_controller_page_filename": "ĐаСва ŅаКĐģŅ: {} [{}]", "backup_controller_page_id": "ID: {}", @@ -545,7 +549,7 @@ "backup_controller_page_start_backup": "ĐĐžŅаŅи ŅĐĩСĐĩŅвĐŊĐĩ ĐēĐžĐŋŅŅваĐŊĐŊŅ", "backup_controller_page_status_off": "ĐвŅĐžĐŧаŅиŅĐŊĐĩ ŅĐĩСĐĩŅвĐŊĐĩ ĐēĐžĐŋŅŅваĐŊĐŊŅ Đ˛ аĐēŅивĐŊĐžĐŧŅ ŅĐĩĐļиĐŧŅ Đ˛Đ¸ĐŧĐēĐŊĐĩĐŊĐž", "backup_controller_page_status_on": "ĐвŅĐžĐŧаŅиŅĐŊĐĩ ŅĐĩСĐĩŅвĐŊĐĩ ĐēĐžĐŋŅŅваĐŊĐŊŅ Đ˛ аĐēŅивĐŊĐžĐŧŅ ŅĐĩĐļиĐŧŅ Đ˛Đ˛ŅĐŧĐēĐŊĐĩĐŊĐž", - "backup_controller_page_storage_format": "{} ŅС {} ŅĐŋĐžĐļиŅĐž", + "backup_controller_page_storage_format": "ĐиĐēĐžŅиŅŅаĐŊĐž: {} С {}", "backup_controller_page_to_backup": "ĐĐģŅйОĐŧи Đ´Đž ŅĐĩСĐĩŅвĐŊĐžĐŗĐž ĐēĐžĐŋŅŅваĐŊĐŊŅ", "backup_controller_page_total_sub": "ĐŖŅŅ ŅĐŊŅĐēаĐģŅĐŊŅ ĐˇĐŊŅĐŧĐēи Ņа вŅĐ´ĐĩĐž С вийŅаĐŊĐ¸Ņ Đ°ĐģŅйОĐŧŅв", "backup_controller_page_turn_off": "ĐиĐŧĐēĐŊŅŅи ŅĐĩСĐĩŅвĐŊĐĩ ĐēĐžĐŋŅŅваĐŊĐŊŅ Đ˛ аĐēŅивĐŊĐžĐŧŅ ŅĐĩĐļиĐŧŅ", @@ -606,7 +610,7 @@ "change_password": "ĐĐŧŅĐŊиŅи ĐŋаŅĐžĐģŅ", "change_password_description": "ĐĻĐĩ айО ĐŋĐĩŅŅиК ŅаС, ĐēĐžĐģи ви ŅвŅĐšŅĐģи в ŅиŅŅĐĩĐŧŅ, айО ĐąŅĐģĐž СŅОйĐģĐĩĐŊĐž СаĐŋĐ¸Ņ ĐŊа СĐŧŅĐŊŅ Đ˛Đ°ŅĐžĐŗĐž ĐŋаŅĐžĐģŅ. ĐŅĐ´Ņ ĐģаŅĐēа, ввĐĩĐ´ŅŅŅ ĐŊОвиК ĐŋаŅĐžĐģŅ ĐŊиĐļŅĐĩ.", "change_password_form_confirm_password": "ĐŅĐ´ŅвĐĩŅдиŅи ĐŋаŅĐžĐģŅ", - "change_password_form_description": "ĐŅивŅŅ {name},\n\nĐи айО айО вĐŋĐĩŅŅĐĩ Đ˛Ņ ĐžĐ´Đ¸ŅĐĩ Ņ ŅиŅŅĐĩĐŧŅ, айО ĐąŅĐģĐž СŅОйĐģĐĩĐŊĐž СаĐŋĐ¸Ņ ĐŊа СĐŧŅĐŊŅ Đ˛Đ°ŅĐžĐŗĐž ĐŋаŅĐžĐģŅ. \nĐвĐĩĐ´ŅŅŅ Đ˛Đ°Ņ ĐŊОвиК ĐŋаŅĐžĐģŅ.", + "change_password_form_description": "ĐŅивŅŅ, {name},\n\nĐĻĐĩ айО Đ˛Đ°Ņ ĐŋĐĩŅŅиК Đ˛Ņ ŅĐ´ Ņ ŅиŅŅĐĩĐŧŅ, айО ĐąŅĐģĐž ĐŊадŅŅĐģаĐŊĐž СаĐŋĐ¸Ņ ĐŊа СĐŧŅĐŊŅ ĐŋаŅĐžĐģŅ. ĐŅĐ´Ņ ĐģаŅĐēа, ввĐĩĐ´ŅŅŅ ĐŊОвиК ĐŋаŅĐžĐģŅ ĐŊиĐļŅĐĩ.", "change_password_form_new_password": "ĐОвиК ĐŋаŅĐžĐģŅ", "change_password_form_password_mismatch": "ĐаŅĐžĐģŅ ĐŊĐĩ ŅĐŋŅвĐŋадаŅŅŅ", "change_password_form_reenter_new_password": "ĐОвŅĐžŅŅŅŅ ĐŊОвиК ĐŋаŅĐžĐģŅ", @@ -630,7 +634,7 @@ "client_cert_import_success_msg": "ĐĐģŅŅĐŊŅŅŅĐēиК ŅĐĩŅŅиŅŅĐēĐ°Ņ ŅĐŧĐŋĐžŅŅОваĐŊĐž", "client_cert_invalid_msg": "ĐĐĩĐ´ŅĐšŅĐŊиК ŅаКĐģ ŅĐĩŅŅиŅŅĐēаŅа айО ĐŊĐĩĐŋŅавиĐģŅĐŊиК ĐŋаŅĐžĐģŅ", "client_cert_remove_msg": "ĐĐģŅŅĐŊŅŅŅĐēиК ŅĐĩŅŅиŅŅĐēĐ°Ņ Đ˛Đ¸Đ´Đ°ĐģĐĩĐŊĐž", - "client_cert_subtitle": "ĐŅĐ´ŅŅиĐŧŅŅŅŅŅŅ ĐģиŅĐĩ ŅĐžŅĐŧĐ°Ņ PKCS12 (.p12, .pfx). ĐĐŧĐŋĐžŅŅ/видаĐģĐĩĐŊĐŊŅ ŅĐĩŅŅиŅŅĐēаŅа Đ´ĐžŅŅŅĐŋĐŊĐĩ ĐģиŅĐĩ ĐŋĐĩŅĐĩĐ´ Đ˛Ņ ĐžĐ´ĐžĐŧ Ņ ŅиŅŅĐĩĐŧŅ.", + "client_cert_subtitle": "ĐŅĐ´ŅŅиĐŧŅŅŅŅŅŅ ĐģиŅĐĩ ŅĐžŅĐŧĐ°Ņ PKCS12 (.p12, .pfx). ĐĐŧĐŋĐžŅŅ/видаĐģĐĩĐŊĐŊŅ ŅĐĩŅŅиŅŅĐēаŅа Đ´ĐžŅŅŅĐŋĐŊŅ ĐģиŅĐĩ Đ´Đž Đ˛Ņ ĐžĐ´Ņ Đ˛ ŅиŅŅĐĩĐŧŅ", "client_cert_title": "ĐĐģŅŅĐŊŅŅŅĐēиК SSL-ŅĐĩŅŅиŅŅĐēаŅ", "clockwise": "ĐĐž ĐŗĐžĐ´Đ¸ĐŊĐŊиĐēОвŅĐš ŅŅŅŅĐģŅŅ", "close": "ĐаĐēŅиŅи", @@ -719,8 +723,8 @@ "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_local": "ĐĻŅ ĐĩĐģĐĩĐŧĐĩĐŊŅи ĐąŅĐ´ŅŅŅ ĐžŅŅаŅĐžŅĐŊĐž видаĐģĐĩĐŊŅ Đˇ ваŅĐžĐŗĐž ĐŋŅиŅŅŅĐžŅ, аĐģĐĩ СаĐģиŅаŅŅŅŅ Đ´ĐžŅŅŅĐŋĐŊиĐŧи ĐŊа ŅĐĩŅвĐĩŅŅ Immich", + "delete_dialog_alert_local_non_backed_up": "ĐĐĩŅĐēŅ ĐĩĐģĐĩĐŧĐĩĐŊŅи ĐŊĐĩ ĐąŅĐģи СйĐĩŅĐĩĐļĐĩĐŊŅ ĐŊа ŅĐĩŅвĐĩŅŅ Immich Ņ ĐąŅĐ´ŅŅŅ ĐžŅŅаŅĐžŅĐŊĐž видаĐģĐĩĐŊŅ Đˇ ваŅĐžĐŗĐž ĐŋŅиŅŅŅĐžŅ", "delete_dialog_alert_remote": "ĐĻŅ ĐĩĐģĐĩĐŧĐĩĐŊŅи ĐąŅĐ´ŅŅŅ ĐŊаСавĐļди видаĐģĐĩĐŊŅ Đˇ ŅĐĩŅвĐĩŅŅ Immich", "delete_dialog_ok_force": "ĐŅĐĩ ОдĐŊĐž видаĐģиŅи", "delete_dialog_title": "ĐидаĐģиŅи ĐžŅŅаŅĐžŅĐŊĐž", @@ -992,6 +996,7 @@ "filetype": "ĐĸиĐŋ ŅаКĐģŅ", "filter": "ФŅĐģŅŅŅ", "filter_people": "ФŅĐģŅŅŅ ĐŋĐž ĐģŅĐ´ŅŅ ", + "filter_places": "ФŅĐģŅŅŅ ĐŋĐž ĐŧŅŅŅŅŅ ", "find_them_fast": "ШвидĐēĐž СĐŊĐ°Ņ ĐžĐ´ŅŅĐĩ ŅŅ ĐˇĐ° ĐŊĐ°ĐˇĐ˛ĐžŅ ĐˇĐ° Đ´ĐžĐŋĐžĐŧĐžĐŗĐžŅ ĐŋĐžŅŅĐēŅ", "fix_incorrect_match": "ĐиĐŋŅавиŅи ĐŊĐĩĐŋŅавиĐģŅĐŊиК СйŅĐŗ", "folder": "ĐаĐŋĐēа", @@ -1020,7 +1025,7 @@ "header_settings_field_validator_msg": "ĐĐŊаŅĐĩĐŊĐŊŅ ĐŊĐĩ ĐŧĐžĐļĐĩ ĐąŅŅи ĐŋĐžŅĐžĐļĐŊŅĐŧ", "header_settings_header_name_input": "ĐĐŧ'Ņ ĐˇĐ°ĐŗĐžĐģОвĐēŅ", "header_settings_header_value_input": "ĐĐŊаŅĐĩĐŊĐŊŅ ĐˇĐ°ĐŗĐžĐģОвĐēŅ", - "headers_settings_tile_subtitle": "ĐиСĐŊаŅŅĐĩ ĐˇĐ°ĐŗĐžĐģОвĐēи ĐŋŅĐžĐēŅŅ, ŅĐēŅ ĐŋŅĐžĐŗŅаĐŧа ĐŧĐ°Ņ ĐŊадŅиĐģаŅи С ĐēĐžĐļĐŊиĐŧ ĐŧĐĩŅĐĩĐļĐĩвиĐŧ СаĐŋиŅĐžĐŧ.", + "headers_settings_tile_subtitle": "ĐиСĐŊаŅŅĐĩ ĐˇĐ°ĐŗĐžĐģОвĐēи ĐŋŅĐžĐēŅŅ, ŅĐēŅ ĐŋŅĐžĐŗŅаĐŧа ĐŧĐ°Ņ ĐŊадŅиĐģаŅи С ĐēĐžĐļĐŊиĐŧ ĐŧĐĩŅĐĩĐļĐĩвиĐŧ СаĐŋиŅĐžĐŧ", "headers_settings_tile_title": "ĐĐžŅиŅŅŅваĐģŅĐŊиŅŅĐēŅ ĐˇĐ°ĐŗĐžĐģОвĐēи ĐŋŅĐžĐēŅŅ", "hi_user": "ĐŅивŅŅ {name} ({email})", "hide_all_people": "ĐĄŅ ĐžĐ˛Đ°Ņи вŅŅŅ ", @@ -1040,7 +1045,7 @@ "home_page_delete_remote_err_local": "ĐĐžĐēаĐģŅĐŊŅ ĐĩĐģĐĩĐŧĐĩĐŊŅ(и) вĐļĐĩ в ĐŋŅĐžŅĐĩŅŅ Đ˛Đ¸Đ´Đ°ĐģĐĩĐŊĐŊŅ Đˇ ŅĐĩŅвĐĩŅа, ĐŋŅĐžĐŋŅŅĐĩĐŊĐž", "home_page_favorite_err_local": "ĐĐžĐēи ŅĐž ĐŊĐĩ ĐŧĐžĐļĐŊа дОдаŅи Đ´Đž ŅĐģŅĐąĐģĐĩĐŊĐ¸Ņ ĐģĐžĐēаĐģŅĐŊŅ ĐĩĐģĐĩĐŧĐĩĐŊŅи, ĐŋŅĐžĐŋŅŅĐĩĐŊĐž", "home_page_favorite_err_partner": "ĐĐžĐēи ŅĐž ĐŊĐĩ ĐŧĐžĐļĐŊа дОдаŅи Đ´Đž ŅĐģŅĐąĐģĐĩĐŊĐ¸Ņ ĐĩĐģĐĩĐŧĐĩĐŊŅи ĐŋаŅŅĐŊĐĩŅа, ĐŋŅĐžĐŋŅŅĐĩĐŊĐž", - "home_page_first_time_notice": "Đ¯ĐēŅĐž ви вĐŋĐĩŅŅĐĩ ĐēĐžŅиŅŅŅŅŅĐĩŅŅ ĐŋŅĐžĐŗŅаĐŧĐžŅ, ĐŋĐĩŅĐĩĐēĐžĐŊаКŅĐĩŅŅ, ŅĐž ви вийŅаĐģи аĐģŅйОĐŧи Đ´ĐģŅ ŅĐĩСĐĩŅвŅваĐŊĐŊŅ, ŅОй ĐŧĐžĐŗŅи СаĐŋОвĐŊŅваŅи Ņ ŅĐžĐŊĐžĐģĐžĐŗŅŅ ĐˇĐŊŅĐŧĐēŅв Ņа вŅĐ´ĐĩĐž в аĐģŅйОĐŧĐ°Ņ .", + "home_page_first_time_notice": "Đ¯ĐēŅĐž ви ĐēĐžŅиŅŅŅŅŅĐĩŅŅ Đ´ĐžĐ´Đ°ŅĐēĐžĐŧ вĐŋĐĩŅŅĐĩ, ĐąŅĐ´Ņ ĐģаŅĐēа, ОйĐĩŅŅŅŅ Đ°ĐģŅйОĐŧ Đ´ĐģŅ ŅĐĩСĐĩŅвĐŊĐžĐŗĐž ĐēĐžĐŋŅŅваĐŊĐŊŅ, ŅОй ĐŊа ŅĐēаĐģŅ ŅаŅŅ ĐˇâŅвиĐģиŅŅ ŅĐžŅĐž Ņа вŅĐ´ĐĩĐž", "home_page_share_err_local": "ĐĐĩĐŧĐžĐļĐģивО ĐŋОдŅĐģиŅиŅŅ ĐģĐžĐēаĐģŅĐŊиĐŧи ĐĩĐģĐĩĐŧĐĩĐŊŅаĐŧи ŅĐĩŅĐĩС ĐŋĐžŅиĐģаĐŊĐŊŅ, ĐŋŅĐžĐŋŅŅĐĩĐŊĐž", "home_page_upload_err_limit": "ĐĐžĐļĐŊа ваĐŊŅаĐļиŅи ĐŊĐĩ ĐąŅĐģŅŅĐĩ 30 ĐĩĐģĐĩĐŧĐĩĐŊŅŅв вОдĐŊĐžŅаŅ, ĐŋŅĐžĐŋŅŅĐĩĐŊĐž", "host": "ĐĨĐžŅŅ", @@ -1132,7 +1137,7 @@ "logged_out_device": "ĐĐ¸Ņ ŅĐ´ С ĐŋŅиŅŅŅĐžŅ", "login": "ĐŅ ŅĐ´", "login_disabled": "ĐвŅĐžŅиСаŅŅŅ ĐąŅĐģа вŅĐ´ĐēĐģŅŅĐĩĐŊа", - "login_form_api_exception": "ĐĐžĐŧиĐģĐēа API. ĐĐĩŅĐĩвŅŅŅĐĩ адŅĐĩŅŅ ŅĐĩŅвĐĩŅа Ņ ŅĐŋŅОйŅĐšŅĐĩ СĐŊОвŅ", + "login_form_api_exception": "ĐĐžĐŧиĐģĐēа API. ĐĐĩŅĐĩвŅŅŅĐĩ адŅĐĩŅŅ ŅĐĩŅвĐĩŅа Ņ ŅĐŋŅОйŅĐšŅĐĩ СĐŊОвŅ.", "login_form_back_button_text": "ĐаСад", "login_form_email_hint": "youremail@email.com", "login_form_endpoint_hint": "http://your-server-ip:port", @@ -1149,7 +1154,7 @@ "login_form_password_hint": "ĐŋаŅĐžĐģŅ", "login_form_save_login": "ĐаĐŋаĐŧ'ŅŅаŅи Đ˛Ņ ŅĐ´", "login_form_server_empty": "ĐвĐĩĐ´ŅŅŅ URL-адŅĐĩŅŅ ŅĐĩŅвĐĩŅа.", - "login_form_server_error": "ĐĐĩĐŧĐžĐļĐģивО С'ŅĐ´ĐŊаŅиŅŅ ŅС ŅĐĩŅвĐĩŅĐžĐŧ", + "login_form_server_error": "ĐĐĩ вдаĐģĐžŅŅ ĐŋŅĐ´ĐēĐģŅŅиŅиŅŅ Đ´Đž ŅĐĩŅвĐĩŅа.", "login_has_been_disabled": "ĐŅ ŅĐ´ ĐąŅĐģĐž виĐŧĐēĐŊĐĩĐŊĐž.", "login_password_changed_error": "ĐĐžĐŧиĐģĐēа Ņ ĐžĐŊОвĐģĐĩĐŊŅ Đ˛Đ°ŅĐžĐŗĐž ĐŋаŅĐžĐģŅ", "login_password_changed_success": "ĐаŅĐžĐģŅ ĐžĐŊОвĐģĐĩĐŊĐž ŅŅĐŋŅŅĐŊĐž", @@ -1282,6 +1287,7 @@ "onboarding_welcome_user": "ĐаŅĐēавО ĐŋŅĐžŅиĐŧĐž, {user}", "online": "ĐĐžŅŅŅĐŋĐŊиК", "only_favorites": "ĐиŅĐĩ ОйŅаĐŊŅ", + "open": "ĐŅĐ´ĐēŅиŅи", "open_in_map_view": "ĐŅĐ´ĐēŅиŅи Ņ ĐŋĐĩŅĐĩĐŗĐģŅĐ´Ņ ĐŧаĐŋи", "open_in_openstreetmap": "ĐŅĐ´ĐēŅиŅи в OpenStreetMap", "open_the_search_filters": "ĐŅĐ´ĐēŅиКŅĐĩ ŅŅĐģŅŅŅи ĐŋĐžŅŅĐēŅ", @@ -1304,7 +1310,7 @@ "partner_page_no_more_users": "ĐŅĐģŅŅĐĩ ĐŊĐĩĐŧĐ°Ņ ĐēĐžĐŗĐž дОдаŅи", "partner_page_partner_add_failed": "ĐĐĩ вдаĐģĐžŅŅ Đ´ĐžĐ´Đ°Ņи ĐŋаŅŅĐŊĐĩŅа", "partner_page_select_partner": "ĐĐąŅаŅи ĐŋаŅŅĐŊĐĩŅа", - "partner_page_shared_to_title": "ĐĄĐŋŅĐģŅĐŊĐĩ ŅС ", + "partner_page_shared_to_title": "ĐĄĐŋŅĐģŅĐŊĐĩ ŅС", "partner_page_stop_sharing_content": "{} вŅŅаŅиŅŅ Đ´ĐžŅŅŅĐŋ Đ´Đž ваŅĐ¸Ņ ĐˇĐŊŅĐŧĐēŅв.", "partner_sharing": "ĐĄĐŋŅĐģŅĐŊĐĩ виĐēĐžŅиŅŅаĐŊĐŊŅ", "partners": "ĐаŅŅĐŊĐĩŅи", @@ -1338,9 +1344,9 @@ "permission_onboarding_continue_anyway": "ĐŅĐĩ ОдĐŊĐž ĐŋŅОдОвĐļиŅи", "permission_onboarding_get_started": "РОСĐŋĐžŅаŅи", "permission_onboarding_go_to_settings": "ĐĐĩŅĐĩĐšŅи Đ´Đž ĐŊаĐģаŅŅŅваĐŊŅ", - "permission_onboarding_permission_denied": "ĐĐžŅŅŅĐŋ СайОŅĐžĐŊĐĩĐŊĐž. Đйи ĐēĐžŅиŅŅŅваŅиŅŅ Immich, ĐŊадаКŅĐĩ Đ´ĐžŅŅŅĐŋ Đ´Đž СĐŊŅĐŧĐēŅв Ņа вŅĐ´ĐĩĐž Ņ ĐаĐģаŅŅŅваĐŊĐŊŅŅ .", + "permission_onboarding_permission_denied": "ĐĐžŅŅŅĐŋ СайОŅĐžĐŊĐĩĐŊĐž. ĐĐģŅ Đ˛Đ¸ĐēĐžŅиŅŅаĐŊĐŊŅ Immich ĐŊадаКŅĐĩ дОСвОĐģи Đ´Đž \"ФОŅĐž Ņа вŅĐ´ĐĩĐž\" в ĐŊаĐģаŅŅŅваĐŊĐŊŅŅ .", "permission_onboarding_permission_granted": "ĐĐžŅŅŅĐŋ ĐŊадаĐŊĐž! ĐŅĐĩ ĐŗĐžŅОвО.", - "permission_onboarding_permission_limited": "ĐĐąĐŧĐĩĐļĐĩĐŊиК Đ´ĐžŅŅŅĐŋ. Đйи дОСвОĐģиŅи Immich ŅĐĩСĐĩŅвĐŊĐĩ ĐēĐžĐŋŅŅваĐŊĐŊŅ Ņа ĐēĐĩŅŅваĐŊĐŊŅ Đ˛Đ°ŅĐžŅ ĐŗĐ°ĐģĐĩŅĐĩŅŅ, ĐŊадаКŅĐĩ Đ´ĐžŅŅŅĐŋ Đ´Đž СĐŊŅĐŧĐēŅв Ņа вŅĐ´ĐĩĐž Ņ ĐаĐģаŅŅŅваĐŊĐŊŅŅ ", + "permission_onboarding_permission_limited": "ĐĐąĐŧĐĩĐļĐĩĐŊиК Đ´ĐžŅŅŅĐŋ. Đйи дОСвОĐģиŅи Immich ŅĐĩСĐĩŅвĐŊĐĩ ĐēĐžĐŋŅŅваĐŊĐŊŅ Ņа ĐēĐĩŅŅваĐŊĐŊŅ Đ˛Đ°ŅĐžŅ ĐŗĐ°ĐģĐĩŅĐĩŅŅ, ĐŊадаКŅĐĩ Đ´ĐžŅŅŅĐŋ Đ´Đž СĐŊŅĐŧĐēŅв Ņа вŅĐ´ĐĩĐž Ņ ĐаĐģаŅŅŅваĐŊĐŊŅŅ .", "permission_onboarding_request": "Immich ĐŋĐžŅŅĐĩĐąŅŅ Đ´ĐžŅŅŅĐŋŅ Đ´Đž ваŅĐ¸Ņ ĐˇĐŊŅĐŧĐēŅв Ņа вŅĐ´ĐĩĐž.", "person": "ĐŅдиĐŊа", "person_birthdate": "ĐаŅОдивŅŅ {date}", @@ -1540,7 +1546,7 @@ "search_result_page_new_search_hint": "ĐОвиК ĐŋĐžŅŅĐē", "search_settings": "ĐаĐģаŅŅŅваĐŊĐŊŅ ĐŋĐžŅŅĐēŅ", "search_state": "ĐĐžŅŅĐē ŅĐĩĐŗŅĐžĐŊŅ...", - "search_suggestion_list_smart_search_hint_1": "ĐĐŊŅĐĩĐģĐĩĐēŅŅаĐģŅĐŊиК ĐŋĐžŅŅĐē ŅвŅĐŧĐēĐŊĐĩĐŊĐž Са СаĐŧОвŅŅваĐŊĐŊŅĐŧ, Đ´ĐģŅ ĐŋĐžŅŅĐēŅ ĐŧĐĩŅадаĐŊĐ¸Ņ Đ˛Đ¸ĐēĐžŅиŅŅОвŅĐšŅĐĩ ŅиĐŊŅаĐēŅиŅ", + "search_suggestion_list_smart_search_hint_1": "РОСŅĐŧĐŊиК ĐŋĐžŅŅĐē ŅвŅĐŧĐēĐŊĐĩĐŊĐž Са СаĐŧОвŅŅваĐŊĐŊŅĐŧ, Đ´ĐģŅ ĐŋĐžŅŅĐēŅ ĐˇĐ° ĐŧĐĩŅадаĐŊиĐŧи виĐēĐžŅиŅŅОвŅĐšŅĐĩ ŅиĐŊŅаĐēŅиŅ. ", "search_suggestion_list_smart_search_hint_2": "m:ваŅ-ĐŋĐžŅŅĐēОвиК-ŅĐĩŅĐŧŅĐŊ", "search_tags": "ĐĐžŅŅĐē ŅĐĩĐŗŅв...", "search_timezone": "ĐĐžŅŅĐē ŅаŅĐžĐ˛ĐžĐŗĐž ĐŋĐžŅŅŅ...", @@ -1582,9 +1588,9 @@ "set_profile_picture": "ĐŅŅаĐŊОвиŅи СОйŅаĐļĐĩĐŊĐŊŅ ĐŋŅĐžŅŅĐģŅ", "set_slideshow_to_fullscreen": "ĐŅŅаĐŊОвиŅи ŅĐģаКд-ŅĐžŅ ĐŊа вĐĩŅŅ ĐĩĐēŅаĐŊ", "setting_image_viewer_help": "ĐОвĐŊĐžĐĩĐēŅаĐŊĐŊиК ĐŋĐĩŅĐĩĐŗĐģŅĐ´Đ°Ņ ŅĐŋĐžŅаŅĐēŅ ĐˇĐ°Đ˛Đ°ĐŊŅаĐļŅŅ ĐˇĐžĐąŅаĐļĐĩĐŊĐŊŅ Đ´ĐģŅ ĐŋĐžĐŋĐĩŅĐĩĐ´ĐŊŅĐžĐŗĐž ĐŋĐĩŅĐĩĐŗĐģŅĐ´Ņ Đ˛ ĐŊиСŅĐēŅĐš ŅОСдŅĐģŅĐŊŅĐš СдаŅĐŊĐžŅŅŅ, ĐŋĐžŅŅĐŧ СаваĐŊŅаĐļŅŅ ĐˇĐžĐąŅаĐļĐĩĐŊĐŊŅ Đ˛ СĐŧĐĩĐŊŅĐĩĐŊŅĐš ŅОСдŅĐģŅĐŊŅĐš СдаŅĐŊĐžŅŅŅ Đ˛ŅĐ´ĐŊĐžŅĐŊĐž ĐžŅĐ¸ĐŗŅĐŊаĐģŅ (ŅĐēŅĐž вĐēĐģŅŅĐĩĐŊĐž) Ņ ĐˇŅĐĩŅŅĐžŅ ĐˇĐ°Đ˛Đ°ĐŊŅаĐļŅŅ ĐžŅĐ¸ĐŗŅĐŊаĐģ (ŅĐēŅĐž вĐēĐģŅŅĐĩĐŊĐž).", - "setting_image_viewer_original_subtitle": "ĐŖĐ˛ŅĐŧĐēĐŊŅŅŅ Đ´ĐģŅ ĐˇĐ°Đ˛Đ°ĐŊŅаĐļĐĩĐŊĐŊŅ ĐžŅĐ¸ĐŗŅĐŊаĐģŅĐŊĐžĐŗĐž СОйŅаĐļĐĩĐŊĐŊŅ Đˇ ĐŋОвĐŊĐžŅ ŅОСдŅĐģŅĐŊĐžŅ ĐˇĐ´Đ°ŅĐŊŅŅŅŅ (вĐĩĐģиĐēĐĩ!).\nĐиĐŧĐēĐŊŅŅŅ, ŅОй СĐŧĐĩĐŊŅиŅи виĐēĐžŅиŅŅаĐŊĐŊŅ Đ´Đ°ĐŊĐ¸Ņ (ĐŧĐĩŅĐĩĐļŅ Ņа ĐēĐĩŅŅ ĐŋŅиŅŅŅĐžŅ).", + "setting_image_viewer_original_subtitle": "ĐŖĐ˛ŅĐŧĐēĐŊŅŅи Đ´ĐģŅ ĐˇĐ°Đ˛Đ°ĐŊŅаĐļĐĩĐŊĐŊŅ ĐžŅĐ¸ĐŗŅĐŊаĐģŅĐŊĐžĐŗĐž СОйŅаĐļĐĩĐŊĐŊŅ Đˇ ĐŋОвĐŊĐžŅ ŅОСдŅĐģŅĐŊĐžŅ ĐˇĐ´Đ°ŅĐŊŅŅŅŅ (вĐĩĐģиĐēĐĩ!). ĐиĐŧĐēĐŊŅŅи, ŅОй СĐŧĐĩĐŊŅиŅи виĐēĐžŅиŅŅаĐŊĐŊŅ Đ´Đ°ĐŊĐ¸Ņ (ŅĐē ŅĐĩŅĐĩС ĐŧĐĩŅĐĩĐļŅ, ŅаĐē Ņ ĐŊа ĐēĐĩŅŅ ĐŋŅиŅŅŅĐžŅ).", "setting_image_viewer_original_title": "ĐаваĐŊŅаĐļŅваŅи ĐžŅĐ¸ĐŗŅĐŊаĐģŅĐŊĐĩ СОйŅаĐļĐĩĐŊĐŊŅ", - "setting_image_viewer_preview_subtitle": "ĐŖĐ˛ŅĐŧĐēĐŊŅŅŅ Đ´ĐģŅ ĐˇĐ°Đ˛Đ°ĐŊŅаĐļĐĩĐŊĐŊŅ ĐˇĐžĐąŅаĐļĐĩĐŊŅ ŅĐĩŅĐĩĐ´ĐŊŅĐžŅ ŅОСдŅĐģŅĐŊĐžŅ ĐˇĐ´Đ°ŅĐŊĐžŅŅŅ.\nĐиĐŧĐēĐŊŅŅŅ Đ´ĐģŅ ĐąĐĩСĐŋĐžŅĐĩŅĐĩĐ´ĐŊŅĐžĐŗĐž СаваĐŊŅаĐļĐĩĐŊĐŊŅ ĐžŅĐ¸ĐŗŅĐŊаĐģŅ Đ°ĐąĐž виĐēĐžŅиŅŅОвŅваŅи ĐģиŅĐĩ ĐŧŅĐŊŅаŅŅŅŅ.", + "setting_image_viewer_preview_subtitle": "ĐŖĐ˛ŅĐŧĐēĐŊŅŅи Đ´ĐģŅ ĐˇĐ°Đ˛Đ°ĐŊŅаĐļĐĩĐŊĐŊŅ ĐˇĐžĐąŅаĐļĐĩĐŊĐŊŅ ŅĐĩŅĐĩĐ´ĐŊŅĐžŅ ŅОСдŅĐģŅĐŊĐžŅ ĐˇĐ´Đ°ŅĐŊĐžŅŅŅ. ĐиĐŧĐēĐŊŅŅи, ŅОй СаваĐŊŅаĐļŅваŅи ĐžŅĐ¸ĐŗŅĐŊаĐģ айО виĐēĐžŅиŅŅОвŅваŅи ŅŅĐģŅĐēи ĐĩŅĐēŅС.", "setting_image_viewer_preview_title": "ĐаваĐŊŅаĐļŅваŅи СОйŅаĐļĐĩĐŊĐŊŅ ĐŋĐžĐŋĐĩŅĐĩĐ´ĐŊŅĐžĐŗĐž ĐŋĐĩŅĐĩĐŗĐģŅĐ´Ņ", "setting_image_viewer_title": "ĐОйŅаĐļĐĩĐŊĐŊŅ", "setting_languages_apply": "ĐаŅŅĐžŅŅваŅи", @@ -1783,7 +1789,7 @@ "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": "ĐĐžĐŧŅŅĐĩĐŊŅ Ņ ĐēĐžŅиĐē ĐĩĐģĐĩĐŧĐĩĐŊŅи ĐąŅĐ´Đĩ ĐžŅŅаŅĐžŅĐŊĐž видаĐģĐĩĐŊĐž ŅĐĩŅĐĩС {} Đ´ĐŊŅв", "trash_page_no_assets": "ĐŅддаĐģĐĩĐŊŅ ĐĩĐģĐĩĐŧĐĩĐŊŅи вŅĐ´ŅŅŅĐŊŅ", "trash_page_restore_all": "ĐŅĐ´ĐŊОвиŅи ŅŅŅ", @@ -1851,9 +1857,9 @@ "version_announcement_closing": "ĐĸвŅĐš Đ´ŅŅĐŗ, ĐĐģĐĩĐēŅ", "version_announcement_message": "ĐŅивŅŅ! ĐĐžŅŅŅĐŋĐŊа ĐŊОва вĐĩŅŅŅŅ Immich. ĐŅĐ´Ņ ĐģаŅĐēа, ĐŋŅидŅĐģŅŅŅ ŅŅĐžŅ Đ¸ ŅаŅŅ Đ´ĐģŅ ĐžĐˇĐŊаКОĐŧĐģĐĩĐŊĐŊŅ Đˇ <link>ĐŋŅиĐŧŅŅĐēаĐŧи Đ´Đž виĐŋŅŅĐēŅ</link>, ŅОй ĐŋĐĩŅĐĩĐēĐžĐŊаŅиŅŅ, ŅĐž ваŅа ŅŅŅаĐŊОвĐēа ĐžĐŊОвĐģĐĩĐŊа Ņ ŅĐŊиĐēĐŊŅŅи ĐŧĐžĐļĐģĐ¸Đ˛Đ¸Ņ ĐŋĐžĐŧиĐģĐžĐē Ņ ĐŊаĐģаŅŅŅваĐŊĐŊŅŅ , ĐžŅОйĐģивО ŅĐēŅĐž ви виĐēĐžŅиŅŅОвŅŅŅĐĩ WatchTower айО ĐąŅĐ´Ņ-ŅĐēиК ŅĐŊŅиК ĐŧĐĩŅ Đ°ĐŊŅСĐŧ, ŅĐēиК авŅĐžĐŧаŅиŅĐŊĐž ĐžĐŊОвĐģŅŅ Đ˛Đ°Ņ ĐĩĐēСĐĩĐŧĐŋĐģŅŅ Immich.", "version_announcement_overlay_release_notes": "ĐŋŅиĐŧŅŅĐēи Đ´Đž виĐŋŅŅĐēŅ", - "version_announcement_overlay_text_1": "ĐŅŅаŅĐŧĐž, Ņ ĐŊОвиК виĐŋŅŅĐē ", + "version_announcement_overlay_text_1": "ĐŅŅаŅĐŧĐž, Ņ ĐŊОвиК виĐŋŅŅĐē", "version_announcement_overlay_text_2": "СĐŊаКдŅŅŅ Ņ Đ˛Đ¸ĐģŅĐēŅ ĐŊавŅдаŅиŅŅ ĐŊа ", - "version_announcement_overlay_text_3": "Ņ ĐŋĐĩŅĐĩĐēĐžĐŊаКŅĐĩŅŅ, ŅĐž ваŅŅ ĐŊаĐģаŅŅŅваĐŊĐŊŅ docker-compose Ņа .env ĐžĐŊОвĐģĐĩĐŊŅ, айи СаĐŋОйŅĐŗŅи ĐąŅĐ´Ņ-ŅĐēŅĐš ĐŊĐĩĐŋŅавиĐģŅĐŊŅĐš ĐēĐžĐŊŅŅĐŗŅŅаŅŅŅ, ĐžŅОйĐģивО, ŅĐēŅĐž ви виĐēĐžŅиŅŅОвŅŅŅĐĩ WatchTower айО ŅĐŊŅиК ĐŧĐĩŅ Đ°ĐŊŅСĐŧ, Đ´ĐģŅ Đ°Đ˛ŅĐžĐŧаŅиŅĐŊĐ¸Ņ ĐžĐŊОвĐģĐĩĐŊŅ Đ˛Đ°ŅĐžŅ ŅĐĩŅвĐĩŅĐŊĐžŅ ŅаŅŅиĐŊи.", + "version_announcement_overlay_text_3": " Ņ ĐŋĐĩŅĐĩĐēĐžĐŊаКŅĐĩŅŅ, ŅĐž ваŅŅ ĐŊаĐģаŅŅŅваĐŊĐŊŅ docker-compose Ņа .env ĐžĐŊОвĐģĐĩĐŊŅ, айи СаĐŋОйŅĐŗŅи ĐąŅĐ´Ņ-ŅĐēŅĐš ĐŊĐĩĐŋŅавиĐģŅĐŊŅĐš ĐēĐžĐŊŅŅĐŗŅŅаŅŅŅ, ĐžŅОйĐģивО, ŅĐēŅĐž ви виĐēĐžŅиŅŅОвŅŅŅĐĩ WatchTower айО ŅĐŊŅиК ĐŧĐĩŅ Đ°ĐŊŅСĐŧ, Đ´ĐģŅ Đ°Đ˛ŅĐžĐŧаŅиŅĐŊĐ¸Ņ ĐžĐŊОвĐģĐĩĐŊŅ Đ˛Đ°ŅĐžŅ ŅĐĩŅвĐĩŅĐŊĐžŅ ŅаŅŅиĐŊи.", "version_announcement_overlay_title": "ĐĐžŅŅŅĐŋĐŊа ĐŊОва вĐĩŅŅŅŅ ŅĐĩŅвĐĩŅа đ", "version_history": "ĐŅŅĐžŅŅŅ Đ˛ĐĩŅŅŅĐš", "version_history_item": "ĐŅŅаĐŊОвĐģĐĩĐŊĐž {version} {date}", diff --git a/i18n/vi.json b/i18n/vi.json index fdcea7772c..9262d8a6b3 100644 --- a/i18n/vi.json +++ b/i18n/vi.json @@ -525,7 +525,7 @@ "backup_controller_page_backup_sub": "áēĸnh và video ÄÃŖ sao lưu", "backup_controller_page_created": "TáēĄo và o: {}", "backup_controller_page_desc_backup": "Báēt sao lưu khi áģŠng dáģĨng hoáēĄt Äáģng Äáģ táģą Äáģng sao lưu áēŖnh máģi lÃĒn mÃĄy cháģ§ khi máģ áģŠng dáģĨng.", - "backup_controller_page_excluded": "ÄÃŖ báģ qua:", + "backup_controller_page_excluded": "ÄÃŖ báģ qua: ", "backup_controller_page_failed": "TháēĨt báēĄi ({})", "backup_controller_page_filename": "TÃĒn táģp: {} [{}]", "backup_controller_page_id": "ID: {}", @@ -537,7 +537,7 @@ "backup_controller_page_start_backup": "Báē¯t Äáē§u sao lưu", "backup_controller_page_status_off": "Sao lưu táģą Äáģng khi áģŠng dáģĨng hoáēĄt Äáģng Äang táē¯t", "backup_controller_page_status_on": "Sao lưu táģą Äáģng khi áģŠng dáģĨng hoáēĄt Äáģng Äang báēt", - "backup_controller_page_storage_format": "ÄÃŖ sáģ dáģĨng {} cáģ§a {} ", + "backup_controller_page_storage_format": "ÄÃŖ sáģ dáģĨng {} cáģ§a {}", "backup_controller_page_to_backup": "CÃĄc album cáē§n ÄÆ°áģŖc sao lưu", "backup_controller_page_total_sub": "TáēĨt cáēŖ áēŖnh và video không trÚng láēp táģĢ cÃĄc album ÄÆ°áģŖc cháģn", "backup_controller_page_turn_off": "Táē¯t sao lưu khi áģŠng dáģĨng hoáēĄt Äáģng", diff --git a/i18n/zh_Hant.json b/i18n/zh_Hant.json index bb5094e352..7758052880 100644 --- a/i18n/zh_Hant.json +++ b/i18n/zh_Hant.json @@ -400,9 +400,9 @@ "album_remove_user_confirmation": "įĸēåŽčĻį§ģé¤ {user} åīŧ", "album_share_no_users": "įäžæ¨čææäŊŋį¨č å ąäēĢäēéæŦį¸į°ŋīŧææ˛æå ļäģäŊŋį¨č å¯äžåäēĢã", "album_thumbnail_card_item": "1 é ", - "album_thumbnail_card_items": "{} é ", + "album_thumbnail_card_items": "{} é ", "album_thumbnail_card_shared": " ¡ åˇ˛å ąäēĢ", - "album_thumbnail_shared_by": "įą {} å ąäēĢ", + "album_thumbnail_shared_by": "įą {} å ąäēĢ", "album_updated": "æ´æ°į¸į°ŋæ", "album_updated_setting_description": "įļå ąäēĢį¸į°ŋææ°æĒæĄæīŧį¨éģåéĩäģļéįĨæ", "album_user_left": "厞éĸé {album}", @@ -440,7 +440,7 @@ "archive": "å°å", "archive_or_unarchive_photo": "å°åæåæļå°åį §į", "archive_page_no_archived_assets": "æĒæžå°æ¸æĒé įŽ", - "archive_page_title": "æ¸æĒīŧ {} īŧ", + "archive_page_title": "å°å ({})", "archive_size": "å°åé", "archive_size_description": "č¨åŽčĻä¸čŧįå°åéīŧåŽäŊīŧGiBīŧ", "archived": "厞åæĒ", @@ -506,11 +506,11 @@ "backup_all": "å ¨é¨", "backup_background_service_backup_failed_message": "åäģŊå¤ąæīŧæŖå¨éčŠĻâĻ", "backup_background_service_connection_failed_message": "éŖæĨäŧēæå¨å¤ąæīŧæŖå¨éčŠĻâĻ", - "backup_background_service_current_upload_notification": "æŖå¨ä¸åŗ {} ", + "backup_background_service_current_upload_notification": "æŖå¨ä¸åŗ {}", "backup_background_service_default_notification": "æŖå¨æĒĸæĨæ°é įŽâĻ", "backup_background_service_error_title": "åäģŊå¤ąæ", "backup_background_service_in_progress_notification": "æŖå¨åäģŊâĻ", - "backup_background_service_upload_failure_notification": "ä¸åŗå¤ąæ {} ", + "backup_background_service_upload_failure_notification": "ä¸åŗå¤ąæ {}", "backup_controller_page_albums": "åäģŊį¸į°ŋ", "backup_controller_page_background_app_refresh_disabled_content": "čĻäŊŋį¨čæ¯åäģŊåčŊīŧčĢå¨ãč¨åŽã>ãåäģŊã>ã违åĨ፿´æ°ãä¸åį¨čæŦį¨åŧæ´æ°ã", "backup_controller_page_background_app_refresh_disabled_title": "违åĨ፿´æ°åˇ˛įĻį¨", @@ -531,12 +531,12 @@ "backup_controller_page_backup": "åäģŊ", "backup_controller_page_backup_selected": "厞é¸ä¸īŧ", "backup_controller_page_backup_sub": "厞åäģŊįį §įåįį", - "backup_controller_page_created": "æ°åĸæé: {} ", + "backup_controller_page_created": "æ°åĸæé: {}", "backup_controller_page_desc_backup": "æéåå°åäģŊīŧäģĨæŦį¨åŧéčĄæčĒååäģŊæ°é įŽã", "backup_controller_page_excluded": "厞æé¤īŧ", "backup_controller_page_failed": "å¤ąæīŧ {} īŧ", "backup_controller_page_filename": "æäģļåį¨ą: {} [ {} ]", - "backup_controller_page_id": "ID: {} ", + "backup_controller_page_id": "ID: {}", "backup_controller_page_info": "åäģŊčŗč¨", "backup_controller_page_none_selected": "æĒ鏿", "backup_controller_page_remainder": "åŠé¤", @@ -545,7 +545,7 @@ "backup_controller_page_start_backup": "éå§åäģŊ", "backup_controller_page_status_off": "åå°čĒååäģŊ厞éé", "backup_controller_page_status_on": "åå°čĒååäģŊ厞éå", - "backup_controller_page_storage_format": " {} / {} 厞äŊŋį¨", + "backup_controller_page_storage_format": "{} / {} 厞äŊŋį¨", "backup_controller_page_to_backup": "čĻåäģŊįį¸į°ŋ", "backup_controller_page_total_sub": "é¸ä¸į¸į°ŋ䏿æä¸éč¤įįįååį", "backup_controller_page_turn_off": "ééåå°åäģŊ", @@ -578,7 +578,7 @@ "cache_settings_duplicated_assets_title": "éč¤é įŽīŧ {} īŧ", "cache_settings_image_cache_size": "åįįˇŠå大å°īŧ {} é īŧ", "cache_settings_statistics_album": "ååēĢį¸Žå", - "cache_settings_statistics_assets": " {} é īŧ {} īŧ", + "cache_settings_statistics_assets": "{} é īŧ {} īŧ", "cache_settings_statistics_full": "åŽæ´åį", "cache_settings_statistics_shared": "å ąäēĢį¸į°ŋį¸Žå", "cache_settings_statistics_thumbnail": "į¸Žå", @@ -654,7 +654,7 @@ "contain": "å åĢ", "context": "æ åĸ", "continue": "įšŧįē", - "control_bottom_app_bar_album_info_shared": " {} é ¡ åˇ˛å ąäēĢ", + "control_bottom_app_bar_album_info_shared": "{} é ¡ åˇ˛å ąäēĢ", "control_bottom_app_bar_create_new_album": "æ°åĸį¸į°ŋ", "control_bottom_app_bar_delete_from_immich": "åžImmichäŧēæå¨ä¸åĒé¤", "control_bottom_app_bar_delete_from_local": "åžį§ģåčŖįŊŽä¸åĒé¤", @@ -763,7 +763,7 @@ "download_enqueue": "厞å å Ĩä¸čŧéå", "download_error": "ä¸čŧåēé¯", "download_failed": "ä¸čŧå¤ąæ", - "download_filename": "æäģļīŧ {} ", + "download_filename": "æäģļīŧ {}", "download_finished": "ä¸čŧåŽæ", "download_include_embedded_motion_videos": "åĩå ĨåŊąį", "download_include_embedded_motion_videos_description": "æåĩå Ĩåæ į §įįåŊąįäŊįēåŽį¨įæĒæĄå åĢå¨å §", @@ -819,7 +819,7 @@ "error_change_sort_album": "Failed to change album sort order", "error_delete_face": "åžé įŽä¸åĒé¤čåæįŧįé¯čǤ", "error_loading_image": "čŧå Ĩåįæåēé¯", - "error_saving_image": "é¯čǤīŧ {} ", + "error_saving_image": "é¯čǤīŧ {}", "error_title": "é¯čǤ - åēåéĄäē", "errors": { "cannot_navigate_next_asset": "įĄæŗįčĻŊä¸ä¸åæĒæĄ", @@ -1170,8 +1170,8 @@ "manage_your_devices": "įŽĄį厞įģå ĨįčŖįŊŽ", "manage_your_oauth_connection": "įŽĄįæ¨į OAuth éŖæĨ", "map": "å°å", - "map_assets_in_bound": " {} åŧĩį §į", - "map_assets_in_bounds": " {} åŧĩį §į", + "map_assets_in_bound": "{} åŧĩį §į", + "map_assets_in_bounds": "{} åŧĩį §į", "map_cannot_get_user_location": "įĄæŗį˛å፿ļäŊįŊŽ", "map_location_dialog_yes": "įĸēåŽ", "map_location_picker_page_use_location": "äŊŋ፿¤äŊįŊŽ", @@ -1185,9 +1185,9 @@ "map_settings": "å°åč¨åŽ", "map_settings_dark_mode": "æˇąč˛æ¨Ąåŧ", "map_settings_date_range_option_day": "éåģ24å°æ", - "map_settings_date_range_option_days": " {} 夊å", + "map_settings_date_range_option_days": "{} 夊å", "map_settings_date_range_option_year": "1åš´å", - "map_settings_date_range_option_years": " {} åš´å", + "map_settings_date_range_option_years": "{} åš´å", "map_settings_dialog_title": "å°åč¨åŽ", "map_settings_include_show_archived": "å æŦåˇ˛æ¸æĒé įŽ", "map_settings_include_show_partners": "å åĢå¤Ĩäŧ´", @@ -1203,7 +1203,7 @@ "memories_start_over": "åį䏿ŦĄ", "memories_swipe_to_close": "䏿ģéé", "memories_year_ago": "1åš´å", - "memories_years_ago": " {} åš´å", + "memories_years_ago": "{} åš´å", "memory": "åæļ", "memory_lane_title": "åæļéˇåģ{title}", "menu": "é¸åŽ", @@ -1305,7 +1305,7 @@ "partner_page_partner_add_failed": "æ°åĸåäŧ´å¤ąæ", "partner_page_select_partner": "鏿åäŧ´", "partner_page_shared_to_title": "å ąäēĢįĩĻ", - "partner_page_stop_sharing_content": " {} å°įĄæŗåå忍įį §įã", + "partner_page_stop_sharing_content": "{} å°įĄæŗåå忍įį §įã", "partner_sharing": "å¤Ĩäŧ´åäēĢ", "partners": "å¤Ĩäŧ´", "password": "å¯įĸŧ", @@ -1590,12 +1590,12 @@ "setting_languages_apply": "åĨį¨", "setting_languages_subtitle": "Change the app's language", "setting_languages_title": "čĒč¨", - "setting_notifications_notify_failures_grace_period": "违åäģŊå¤ąæéįĨīŧ {} ", - "setting_notifications_notify_hours": " {} å°æ", + "setting_notifications_notify_failures_grace_period": "违åäģŊå¤ąæéįĨīŧ {}", + "setting_notifications_notify_hours": "{} å°æ", "setting_notifications_notify_immediately": "įĢåŗ", - "setting_notifications_notify_minutes": " {} åé", + "setting_notifications_notify_minutes": "{} åé", "setting_notifications_notify_never": "åžä¸", - "setting_notifications_notify_seconds": " {} į§", + "setting_notifications_notify_seconds": "{} į§", "setting_notifications_single_progress_subtitle": "æ¯é įčŠŗį´°ä¸åŗé˛åēĻčŗč¨", "setting_notifications_single_progress_title": "éĄ¯į¤ē违åäģŊčŠŗį´°é˛åēĻ", "setting_notifications_subtitle": "čĒŋæ´éįĨé¸é ", @@ -1609,7 +1609,7 @@ "settings_saved": "č¨åŽåˇ˛å˛å", "share": "åäēĢ", "share_add_photos": "æ°åĸé įŽ", - "share_assets_selected": " {} 厞鏿", + "share_assets_selected": "{} 厞鏿", "share_dialog_preparing": "æŖå¨æēå...", "shared": "å ąäēĢ", "shared_album_activities_input_disable": "厞įĻį¨čŠčĢ", @@ -1626,28 +1626,28 @@ "shared_intent_upload_button_progress_text": "{} / {} Uploaded", "shared_link_app_bar_title": "å ąäēĢéæĨ", "shared_link_clipboard_copied_massage": "č¤čŖŊå°åĒč˛ŧæŋ", - "shared_link_clipboard_text": "éæĨīŧ {} \nå¯įĸŧīŧ {} ", + "shared_link_clipboard_text": "éæĨīŧ {} \nå¯įĸŧīŧ {}", "shared_link_create_error": "æ°åĸå ąäēĢéæĨåēé¯", "shared_link_edit_description_hint": "ᎍčŧ¯å ąäēĢæčŋ°", "shared_link_edit_expire_after_option_day": "1夊", - "shared_link_edit_expire_after_option_days": " {} 夊", + "shared_link_edit_expire_after_option_days": "{} 夊", "shared_link_edit_expire_after_option_hour": "1å°æ", - "shared_link_edit_expire_after_option_hours": " {} å°æ", + "shared_link_edit_expire_after_option_hours": "{} å°æ", "shared_link_edit_expire_after_option_minute": "1åé", - "shared_link_edit_expire_after_option_minutes": " {} åé", - "shared_link_edit_expire_after_option_months": " {} åæ", - "shared_link_edit_expire_after_option_year": " {} åš´", + "shared_link_edit_expire_after_option_minutes": "{} åé", + "shared_link_edit_expire_after_option_months": "{} åæ", + "shared_link_edit_expire_after_option_year": "{} åš´", "shared_link_edit_password_hint": "čŧ¸å Ĩå ąäēĢå¯įĸŧ", "shared_link_edit_submit_button": "æ´æ°éæĨ", "shared_link_error_server_url_fetch": "įĄæŗį˛åäŧēæå¨å°å", - "shared_link_expires_day": " {} 夊åžéæ", - "shared_link_expires_days": " {} 夊åžéæ", - "shared_link_expires_hour": " {} å°æåžéæ", - "shared_link_expires_hours": " {} å°æåžéæ", - "shared_link_expires_minute": " {} åéåžéæ", + "shared_link_expires_day": "{} 夊åžéæ", + "shared_link_expires_days": "{} 夊åžéæ", + "shared_link_expires_hour": "{} å°æåžéæ", + "shared_link_expires_hours": "{} å°æåžéæ", + "shared_link_expires_minute": "{} åéåžéæ", "shared_link_expires_minutes": "å°å¨ {} åéåžéæ", "shared_link_expires_never": "æ°¸ä¸éæ", - "shared_link_expires_second": " {} į§åžéæ", + "shared_link_expires_second": "{} į§åžéæ", "shared_link_expires_seconds": "å°å¨ {} į§åžéæ", "shared_link_individual_shared": "åäēēå ąäēĢ", "shared_link_info_chip_metadata": "EXIF", diff --git a/i18n/zh_SIMPLIFIED.json b/i18n/zh_SIMPLIFIED.json index 20b906ced3..573fbb7a24 100644 --- a/i18n/zh_SIMPLIFIED.json +++ b/i18n/zh_SIMPLIFIED.json @@ -39,11 +39,11 @@ "authentication_settings_disable_all": "įĄŽåŽčĻįĻ፿æįįģåŊæšåŧīŧč¯ĨæäŊå°åŽå ¨įĻæĸįģåŊã", "authentication_settings_reenable": "åĻéåæŦĄå¯į¨īŧäŊŋ፠<link>æåĄå¨æäģ¤</link>ã", "background_task_job": "åå°äģģåĄ", - "backup_database": "å¤äģŊæ°æŽåē", - "backup_database_enable_description": "å¯į¨æ°æŽåēå¤äģŊ", - "backup_keep_last_amount": "čĻäŋįįåå˛å¤äģŊæ°é", - "backup_settings": "å¤äģŊ莞įŊŽ", - "backup_settings_description": "įŽĄįæ°æŽåēå¤äģŊ莞įŊŽ", + "backup_database": "ååģēæ°æŽåēå¤äģŊ", + "backup_database_enable_description": "å¯į¨æ°æŽåēå¯ŧåēå¤äģŊ", + "backup_keep_last_amount": "čĻäŋįįåå˛å¯ŧåēæ°é", + "backup_settings": "æ°æŽåēå¯ŧåē莞įŊŽ", + "backup_settings_description": "įŽĄįæ°æŽåēå¤äģŊ莞įŊŽãæŗ¨æīŧčŋäēäģģåĄä¸äŧčĸĢįæ§īŧå¤ąč´Ĩäšä¸äŧéįĨæ¨ã", "check_all": "æŖæĨå ¨é¨", "cleanup": "æ¸ į", "cleared_jobs": "åˇ˛æ¸ įäģģåĄīŧ{job}", @@ -371,13 +371,17 @@ "admin_password": "įŽĄįåå¯į ", "administration": "įŗģįģįŽĄį", "advanced": "éĢįē§", - "advanced_settings_log_level_title": "æĨåŋįįē§īŧ{}", - "advanced_settings_prefer_remote_subtitle": "卿äē莞å¤ä¸īŧäģæŦå°į饚įŽå čŊŊįŧŠįĨåžįéåēĻé常æ ĸã\nå¯į¨æ¤é饚äģĨå čŊŊčŋį¨éĄšįŽã", + "advanced_settings_enable_alternate_media_filter_subtitle": "äŊŋ፿¤é饚å¯å¨åæĨčŋį¨ä¸æ šæŽå¤į¨æĄäģļįé饚įŽãäģ åŊæ¨å¨åēį¨į¨åēæŖæĩææį¸ååéå°éŽéĸæļæå°č¯æ¤åčŊã", + "advanced_settings_enable_alternate_media_filter_title": "[åŽéĒ] äŊŋį¨å¤į¨į莞å¤į¸ååæĨįéæĄäģļ", + "advanced_settings_log_level_title": "æĨåŋįįē§: {}", + "advanced_settings_prefer_remote_subtitle": "卿äē莞å¤ä¸īŧäģæŦå°į饚įŽå čŊŊįŧŠįĨåžįéåēĻé常æ ĸãå¯į¨æ¤é饚äģĨå čŊŊčŋį¨éĄšįŽã", "advanced_settings_prefer_remote_title": "äŧå čŋį¨éĄšįŽ", "advanced_settings_proxy_headers_subtitle": "åŽäšäģŖįæ 头īŧåēį¨äēImmich῝æŦĄįŊįģ蝎æą", "advanced_settings_proxy_headers_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": "æ éæé¤", @@ -477,18 +481,18 @@ "assets_added_to_album_count": "厞æˇģå {count, plural, one {#ä¸Ē饚įŽ} other {#ä¸Ē饚įŽ}}å°į¸å", "assets_added_to_name_count": "厞æˇģå {count, plural, one {#ä¸Ē饚įŽ} other {#ä¸Ē饚įŽ}}å°{hasName, select, true {<b>{name}</b>} other {æ°į¸å}}", "assets_count": "{count, plural, one {#ä¸Ē饚įŽ} other {#ä¸Ē饚įŽ}}", - "assets_deleted_permanently": "{}ä¸Ē饚įŽåˇ˛čĸĢæ°¸äš å é¤", - "assets_deleted_permanently_from_server": "厞äģæåĄå¨ä¸æ°¸äš į§ģé¤{}ä¸Ē饚įŽ", + "assets_deleted_permanently": "{} ä¸Ē饚įŽåˇ˛čĸĢæ°¸äš å é¤", + "assets_deleted_permanently_from_server": "åˇ˛æ°¸äš į§ģé¤ {} ä¸Ē饚įŽ", "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": "厞äģ莞å¤ä¸æ°¸äš į§ģé¤{}ä¸Ē饚įŽ", + "assets_removed_permanently_from_device": "厞äģ莞å¤ä¸æ°¸äš į§ģé¤ {} ä¸Ē饚įŽ", "assets_restore_confirmation": "įĄŽåŽčĻæĸå¤åæļįĢä¸įææéĄšįŽåīŧč¯ĨæäŊæ æŗæ¤æļīŧč¯ˇæŗ¨æīŧčąæēéĄšįŽæ æŗéčŋčŋį§æšåŧæĸå¤ã", "assets_restored_count": "厞æĸå¤{count, plural, one {#ä¸Ē饚įŽ} other {#ä¸Ē饚įŽ}}", "assets_restored_successfully": "厞æåæĸå¤{}ä¸Ē饚įŽ", - "assets_trashed": "{}ä¸ĒåæļįĢ饚įŽ", + "assets_trashed": "{} ä¸ĒéĄšįŽæžå ĨåæļįĢ", "assets_trashed_count": "{count, plural, one {#ä¸Ē饚įŽ} other {#ä¸Ē饚įŽ}}厞æžå ĨåæļįĢ", - "assets_trashed_from_server": "{}ä¸Ē饚įŽåˇ˛æžå ĨåæļįĢ", + "assets_trashed_from_server": "{} ä¸Ē饚įŽåˇ˛æžå ĨåæļįĢ", "assets_were_part_of_album_count": "{count, plural, one {饚įŽ} other {饚įŽ}}厞įģå¨į¸åä¸", "authorized_devices": "厞ææčŽžå¤", "automatic_endpoint_switching_subtitle": "å¨å¯į¨įæ åĩä¸īŧéčŋæåŽį Wi-Fi čŋčĄæŦå°čŋæĨīŧåšļå¨å ļåŽå°æšäŊŋ፿ŋäģŖčŋæĨ", @@ -521,7 +525,7 @@ "backup_controller_page_background_battery_info_title": "įĩæą äŧå", "backup_controller_page_background_charging": "äģ å įĩæļ", "backup_controller_page_background_configure_error": "é įŊŽåå°æåĄå¤ąč´Ĩ", - "backup_controller_page_background_delay": "åģļčŋ {} åå¤äģŊ", + "backup_controller_page_background_delay": "åģļčŋå¤äģŊįæ°éĄšįŽīŧ{}", "backup_controller_page_background_description": "æåŧåå°æåĄäģĨčĒå¨å¤äģŊäģģäŊæ°éĄšįŽīŧ䏿 éæåŧåēį¨", "backup_controller_page_background_is_off": "åå°čĒå¨å¤äģŊåˇ˛å ŗé", "backup_controller_page_background_is_on": "åå°čĒå¨å¤äģŊ厞åŧå¯", @@ -529,14 +533,14 @@ "backup_controller_page_background_turn_on": "åŧå¯åå°æåĄ", "backup_controller_page_background_wifi": "äģ WiFi", "backup_controller_page_backup": "å¤äģŊ", - "backup_controller_page_backup_selected": "厞éä¸īŧ", + "backup_controller_page_backup_selected": "厞éä¸īŧ ", "backup_controller_page_backup_sub": "厞å¤äģŊįį §įåč§éĸ", - "backup_controller_page_created": "ååģēæļé´: {}", + "backup_controller_page_created": "ååģēæļé´īŧ{}", "backup_controller_page_desc_backup": "æåŧåå°å¤äģŊīŧäģĨå¨į¨åēčŋčĄæļčĒå¨å¤äģŊæ°éĄšįŽã", - "backup_controller_page_excluded": "厞æé¤īŧ", + "backup_controller_page_excluded": "厞æé¤īŧ ", "backup_controller_page_failed": "å¤ąč´Ĩīŧ{}īŧ", - "backup_controller_page_filename": "æäģļåį§°: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_filename": "æäģļåį§°īŧ{} [{}]", + "backup_controller_page_id": "IDīŧ{}", "backup_controller_page_info": "å¤äģŊäŋĄæ¯", "backup_controller_page_none_selected": "æĒéæŠ", "backup_controller_page_remainder": "åŠäŊ", @@ -570,7 +574,7 @@ "bulk_keep_duplicates_confirmation": "æ¨įĄŽåŽčĻäŋį{count, plural, one {#ä¸Ēéå¤éĄšįŽ} other {#ä¸Ēéå¤éĄšįŽ}}åīŧčŋå°æ¸ įŠēææéå¤čްåŊīŧäŊä¸äŧå é¤äģģäŊå 厚ã", "bulk_trash_duplicates_confirmation": "æ¨įĄŽåŽčĻæšéå é¤{count, plural, one {#ä¸Ēéå¤éĄšįŽ} other {#ä¸Ēéå¤éĄšįŽ}}åīŧčŋå°äŋ῝įģ䏿大į饚įŽåšļå 餿æå ļåŽéå¤éĄšįŽã", "buy": "č´äš° Immich", - "cache_settings_album_thumbnails": "åžåēįŧŠįĨåžīŧ{} 饚īŧ", + "cache_settings_album_thumbnails": "åžåēéĄĩéĸįŧŠįĨåžīŧ{} 饚īŧ", "cache_settings_clear_cache_button": "æ¸ é¤įŧå", "cache_settings_clear_cache_button_title": "æ¸ é¤åēį¨įŧåãå¨éæ°įæįŧåäšåīŧå°æžčåŊąååēį¨įæ§čŊã", "cache_settings_duplicated_assets_clear_button": "æ¸ é¤", @@ -606,7 +610,7 @@ "change_password": "äŋŽæšå¯į ", "change_password_description": "čŋæ¯äŊ įįŦŦ䏿ŦĄįģåŊäēĻææäēēčĻæąæ´æšäŊ įå¯į ã蝎å¨ä¸éĸčžå Ĩæ°å¯į ã", "change_password_form_confirm_password": "įĄŽčŽ¤å¯į ", - "change_password_form_description": "{name} æ¨åĨŊīŧ\n\nčŋæ¯æ¨éĻæŦĄįģåŊįŗģįģīŧæčĸĢįŽĄįåčĻæąæ´æšå¯į ã\n蝎å¨ä¸æščžå Ĩæ°å¯į ã", + "change_password_form_description": "{name} æ¨åĨŊīŧ\n\nčŋæ¯æ¨éĻæŦĄįģåŊįŗģįģīŧæčĸĢįŽĄįåčĻæąæ´æšå¯į ã蝎å¨ä¸æščžå Ĩæ°å¯į ã", "change_password_form_new_password": "æ°å¯į ", "change_password_form_password_mismatch": "å¯į ä¸åšé ", "change_password_form_reenter_new_password": "åæŦĄčžå Ĩæ°å¯į ", @@ -654,13 +658,13 @@ "contain": "å åĢ", "context": "äģĨææåž", "continue": "įģ§įģ", - "control_bottom_app_bar_album_info_shared": "{} 饚 ¡ åˇ˛å ąäēĢ", + "control_bottom_app_bar_album_info_shared": "åˇ˛å ąäēĢ {} 饚", "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": "Share Link", + "control_bottom_app_bar_share_link": "åäēĢéžæĨ", "control_bottom_app_bar_share_to": "åéįģ", "control_bottom_app_bar_trash_from_immich": "æžå ĨåæļįĢ", "copied_image_to_clipboard": "厞å¤åļåžįčŗåĒåæŋã", @@ -953,10 +957,10 @@ "exif_bottom_sheet_location": "äŊįŊŽ", "exif_bottom_sheet_people": "äēēįŠ", "exif_bottom_sheet_person_add_person": "æˇģå å§å", - "exif_bottom_sheet_person_age": "åš´éž {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "{} å˛", + "exif_bottom_sheet_person_age_months": "{} æéž", + "exif_bottom_sheet_person_age_year_months": "1å˛ {} ä¸Ēæ", + "exif_bottom_sheet_person_age_years": "{} å˛", "exit_slideshow": "éåēåšģį¯įæžæ ", "expand_all": "å ¨é¨åąåŧ", "experimental_settings_new_asset_list_subtitle": "æŖå¨å¤į", @@ -992,6 +996,7 @@ "filetype": "æäģļįąģå", "filter": "įé", "filter_people": "čŋæģ¤äēēįŠ", + "filter_places": "įéå°įš", "find_them_fast": "æåį§°åŋĢéæį´ĸ", "fix_incorrect_match": "äŋŽå¤ä¸æŖįĄŽįåšé ", "folder": "æäģļ多", @@ -1029,9 +1034,9 @@ "hide_password": "éčå¯į ", "hide_person": "éčäēēįŠ", "hide_unnamed_people": "éčæĒåŊåįäēēįŠ", - "home_page_add_to_album_conflicts": "厞åį¸å {album} 䏿ˇģå {added} 饚ã\nå ļä¸ {failed} 饚å¨į¸åä¸åˇ˛åå¨ã", + "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_add_to_album_success": "厞åį¸å {album} 䏿ˇģå {added} 饚ã", "home_page_album_err_partner": "ææ æŗå°åäŧ´įéĄšįŽæˇģå å°į¸åīŧ莺čŋ", "home_page_archive_err_local": "ææ æŗåŊæĄŖæŦå°éĄšįŽīŧ莺čŋ", "home_page_archive_err_partner": "æ æŗåæĄŖåäŧ´į饚įŽīŧ莺čŋ", @@ -1151,7 +1156,7 @@ "login_form_server_empty": "čžå ĨæåĄå¨å°å", "login_form_server_error": "æ æŗčŋæĨå°æåĄå¨ã", "login_has_been_disabled": "įģåŊ厞įĻį¨ã", - "login_password_changed_error": "æ´æ°å¯į æļåēé\n", + "login_password_changed_error": "æ´æ°å¯į æļåēé", "login_password_changed_success": "å¯į æ´æ°æå", "logout_all_device_confirmation": "įĄŽåŽčĻäģææčŽžå¤æŗ¨éīŧ", "logout_this_device_confirmation": "įĄŽåŽčĻäģæŦčŽžå¤æŗ¨éīŧ", @@ -1170,8 +1175,8 @@ "manage_your_devices": "įŽĄį厞įģåŊ莞å¤", "manage_your_oauth_connection": "įŽĄįäŊ į OAuth įģåŽ", "map": "å°åž", - "map_assets_in_bound": "{}åŧ į §į", - "map_assets_in_bounds": "{}åŧ į §į", + "map_assets_in_bound": "{} åŧ į §į", + "map_assets_in_bounds": "{} åŧ į §į", "map_cannot_get_user_location": "æ æŗčˇå፿ˇäŊįŊŽ", "map_location_dialog_yes": "æ¯", "map_location_picker_page_use_location": "äŊŋ፿¤äŊįŊŽ", @@ -1185,9 +1190,9 @@ "map_settings": "å°åžčŽžįŊŽ", "map_settings_dark_mode": "æˇąč˛æ¨Ąåŧ", "map_settings_date_range_option_day": "čŋåģ24å°æļ", - "map_settings_date_range_option_days": "{}夊å", + "map_settings_date_range_option_days": "{} 夊å", "map_settings_date_range_option_year": "1åš´å", - "map_settings_date_range_option_years": "{}åš´å", + "map_settings_date_range_option_years": "{} åš´å", "map_settings_dialog_title": "å°åžčŽžįŊŽ", "map_settings_include_show_archived": "å æŦ厞åŊæĄŖéĄšįŽ", "map_settings_include_show_partners": "å åĢäŧäŧ´", @@ -1203,7 +1208,7 @@ "memories_start_over": "åį䏿ŦĄ", "memories_swipe_to_close": "ä¸åå ŗé", "memories_year_ago": "1åš´å", - "memories_years_ago": "{}åš´å", + "memories_years_ago": "{} åš´å", "memory": "ååŋ", "memory_lane_title": "莰åŋįēŋ{title}", "menu": "čå", @@ -1282,6 +1287,7 @@ "onboarding_welcome_user": "æŦĸčŋäŊ īŧ{user}", "online": "å¨įēŋ", "only_favorites": "äģ æžį¤ē厞æļč", + "open": "æåŧ", "open_in_map_view": "å¨å°åžč§åžä¸æåŧ", "open_in_openstreetmap": "å¨ OpenStreetMap 䏿åŧ", "open_the_search_filters": "æåŧæį´ĸčŋæģ¤å¨", @@ -1540,7 +1546,7 @@ "search_result_page_new_search_hint": "æį´ĸæ°į", "search_settings": "æį´ĸ莞įŊŽ", "search_state": "æįäģŊæĨæž...", - "search_suggestion_list_smart_search_hint_1": "éģ莤æ åĩä¸å¯į¨æēčŊæį´ĸīŧčĻæį´ĸå æ°æŽīŧ蝎äŊŋį¨į¸å ŗč¯æŗ", + "search_suggestion_list_smart_search_hint_1": "éģ莤æ åĩä¸å¯į¨æēčŊæį´ĸīŧčĻæį´ĸå æ°æŽīŧ蝎äŊŋį¨į¸å ŗč¯æŗ ", "search_suggestion_list_smart_search_hint_2": "m:æ¨įæį´ĸå ŗéŽč¯", "search_tags": "ææ įžæĨæžâĻ", "search_timezone": "ææļåēæĨæž...", @@ -1630,25 +1636,25 @@ "shared_link_create_error": "ååģēå ąäēĢéžæĨåēé", "shared_link_edit_description_hint": "įŧčžå ąäēĢæčŋ°", "shared_link_edit_expire_after_option_day": "1夊", - "shared_link_edit_expire_after_option_days": "{}夊", + "shared_link_edit_expire_after_option_days": "{} 夊", "shared_link_edit_expire_after_option_hour": "1å°æļ", - "shared_link_edit_expire_after_option_hours": "{}å°æļ", + "shared_link_edit_expire_after_option_hours": "{} å°æļ", "shared_link_edit_expire_after_option_minute": "1åé", - "shared_link_edit_expire_after_option_minutes": "{}åé", + "shared_link_edit_expire_after_option_minutes": "{} åé", "shared_link_edit_expire_after_option_months": "{} ä¸Ēæ", "shared_link_edit_expire_after_option_year": "{} åš´", "shared_link_edit_password_hint": "čžå Ĩå ąäēĢå¯į ", "shared_link_edit_submit_button": "æ´æ°éžæĨ", "shared_link_error_server_url_fetch": "æ æŗčˇåæåĄå¨å°å", - "shared_link_expires_day": "{}夊åčŋæ", - "shared_link_expires_days": "{}夊åčŋæ", - "shared_link_expires_hour": "{}å°æļåčŋæ", - "shared_link_expires_hours": "{}å°æļåčŋæ", - "shared_link_expires_minute": "{}åéåčŋæ", - "shared_link_expires_minutes": "å°å¨{}åéåčŋæ", + "shared_link_expires_day": "{} 夊åčŋæ", + "shared_link_expires_days": "{} 夊åčŋæ", + "shared_link_expires_hour": "{} å°æļåčŋæ", + "shared_link_expires_hours": "{} å°æļåčŋæ", + "shared_link_expires_minute": "{} åéåčŋæ", + "shared_link_expires_minutes": "{} åéåčŋæ", "shared_link_expires_never": "čŋææļé´ â", - "shared_link_expires_second": "{}į§åčŋæ", - "shared_link_expires_seconds": "å°å¨{}į§åčŋæ", + "shared_link_expires_second": "{} į§åčŋæ", + "shared_link_expires_seconds": "{} į§åčŋæ", "shared_link_individual_shared": "ä¸Ēäēēå ąäēĢ", "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "įŽĄįå ąäēĢéžæĨ", @@ -1784,7 +1790,7 @@ "trash_no_results_message": "å é¤įį §įåč§éĸå°å¨æ¤å¤åąį¤ēã", "trash_page_delete_all": "å é¤å ¨é¨", "trash_page_empty_trash_dialog_content": "æ¯åĻæ¸ įŠēåæļįĢīŧčŋäē饚įŽå°čĸĢäģImmich䏿°¸äš å é¤", - "trash_page_info": "åæļįĢä¸éĄšįŽå°å¨{}å¤Šåæ°¸äš å é¤", + "trash_page_info": "åæļįĢä¸éĄšįŽå°å¨ {} å¤Šåæ°¸äš å é¤", "trash_page_no_assets": "ææ åˇ˛å é¤éĄšįŽ", "trash_page_restore_all": "æĸå¤å ¨é¨", "trash_page_select_assets_btn": "éæŠéĄšįŽ", @@ -1826,7 +1832,7 @@ "upload_status_errors": "é蝝", "upload_status_uploaded": "厞ä¸äŧ ", "upload_success": "ä¸äŧ æåīŧ县°éĄĩéĸæĨįæ°ä¸äŧ į饚įŽã", - "upload_to_immich": "ä¸äŧ čŗImmich ({})", + "upload_to_immich": "ä¸äŧ čŗImmichīŧ{}īŧ", "uploading": "æŖå¨ä¸äŧ ", "url": "URL", "usage": "į¨é", @@ -1852,8 +1858,8 @@ "version_announcement_message": "äŊ åĨŊīŧ厞įģæŖæĩå°Immichææ°įæŦã蝎æŊįŠēé č¯ģä¸ä¸<link>åčĄč¯´æ</link>īŧäģĨįĄŽäŋæ¨įé įŊŽæäģļæ¯ææ°įīŧéŋå åå¨é įŊŽé蝝īŧįšåĢæ¯åŊäŊ æ¯äŊŋį¨WatchToweræå ļåŽįąģäŧŧįčĒå¨åįē§åˇĨå ˇæļã", "version_announcement_overlay_release_notes": "åčĄč¯´æ", "version_announcement_overlay_text_1": "åˇå¤åˇå¤īŧææ°įæŦį", - "version_announcement_overlay_text_2": "蝎čąįšæļé´čŽŋéŽ", - "version_announcement_overlay_text_3": "åšļæŖæĨæ¨į docker-compose å .env æ¯åĻä¸ēææ°ä¸æŖįĄŽįé įŊŽīŧįšåĢæ¯æ¨å¨äŊŋ፠WatchTower æč å ļäģčĒ卿´æ°įį¨åēæļīŧæ¨éčĻæ´å įģč´įæŖæĨã", + "version_announcement_overlay_text_2": "蝎čąįšæļé´čŽŋéŽ ", + "version_announcement_overlay_text_3": " åšļæŖæĨæ¨į docker-compose å .env æ¯åĻä¸ēææ°ä¸æŖįĄŽįé įŊŽīŧįšåĢæ¯æ¨å¨äŊŋ፠WatchTower æč å ļäģčĒ卿´æ°įį¨åēæļīŧæ¨éčĻæ´å įģč´įæŖæĨã", "version_announcement_overlay_title": "æåĄįĢ¯ææ°įæŦåĻ đ", "version_history": "įæŦæ´æ°åå˛čްåŊ", "version_history_item": "å¨ {date} åŽčŖ {version} įæŦ", diff --git a/machine-learning/Dockerfile b/machine-learning/Dockerfile index 25f3c44d9e..270e743291 100644 --- a/machine-learning/Dockerfile +++ b/machine-learning/Dockerfile @@ -1,6 +1,6 @@ ARG DEVICE=cpu -FROM python:3.11-bookworm@sha256:0a9d314ae6e976351bd37b702bf6b0a89bb58e6304e5df35b960059b12531419 AS builder-cpu +FROM python:3.11-bookworm@sha256:ab60e444e04215a62671149f24c59cc2893b49cb5dad26f9d139077a86be760e AS builder-cpu FROM builder-cpu AS builder-openvino @@ -54,7 +54,7 @@ ENV PYTHONDONTWRITEBYTECODE=1 \ RUN apt-get update && apt-get install -y --no-install-recommends g++ -COPY --from=ghcr.io/astral-sh/uv:latest@sha256:0b6dc79013b689f3bc0cbf12807cb1c901beaafe80f2ee10a1d76aa3842afb92 /uv /uvx /bin/ +COPY --from=ghcr.io/astral-sh/uv:latest@sha256:4a6c9444b126bd325fba904bff796bf91fb777bf6148d60109c4cb1de2ffc497 /uv /uvx /bin/ RUN --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=uv.lock,target=uv.lock \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ @@ -63,11 +63,11 @@ RUN if [ "$DEVICE" = "rocm" ]; then \ uv pip install /opt/onnxruntime_rocm-*.whl; \ fi -FROM python:3.11-slim-bookworm@sha256:49d73c49616929b0a4f37c50fee0056eb4b0f15de624591e8d9bf84b4dfdd3ce AS prod-cpu +FROM python:3.11-slim-bookworm@sha256:97ef3198ec8c78690587167bb6a4905d00ffe053900687bdae93ad667e507cbb AS prod-cpu ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 -FROM python:3.11-slim-bookworm@sha256:49d73c49616929b0a4f37c50fee0056eb4b0f15de624591e8d9bf84b4dfdd3ce AS prod-openvino +FROM python:3.11-slim-bookworm@sha256:97ef3198ec8c78690587167bb6a4905d00ffe053900687bdae93ad667e507cbb AS prod-openvino RUN apt-get update && \ apt-get install --no-install-recommends -yqq ocl-icd-libopencl1 wget && \ diff --git a/machine-learning/uv.lock b/machine-learning/uv.lock index 65011f9cab..1930e80661 100644 --- a/machine-learning/uv.lock +++ b/machine-learning/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 1 +revision = 2 requires-python = ">=3.10, <4.0" resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'darwin'", @@ -23,9 +23,9 @@ resolution-markers = [ name = "aiocache" version = "0.12.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7a/64/b945b8025a9d1e6e2138845f4022165d3b337f55f50984fbc6a4c0a1e355/aiocache-0.12.3.tar.gz", hash = "sha256:f528b27bf4d436b497a1d0d1a8f59a542c153ab1e37c3621713cb376d44c4713", size = 132196 } +sdist = { url = "https://files.pythonhosted.org/packages/7a/64/b945b8025a9d1e6e2138845f4022165d3b337f55f50984fbc6a4c0a1e355/aiocache-0.12.3.tar.gz", hash = "sha256:f528b27bf4d436b497a1d0d1a8f59a542c153ab1e37c3621713cb376d44c4713", size = 132196, upload_time = "2024-09-25T13:20:23.823Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/37/d7/15d67e05b235d1ed8c3ce61688fe4d84130e72af1657acadfaac3479f4cf/aiocache-0.12.3-py2.py3-none-any.whl", hash = "sha256:889086fc24710f431937b87ad3720a289f7fc31c4fd8b68e9f918b9bacd8270d", size = 28199 }, + { url = "https://files.pythonhosted.org/packages/37/d7/15d67e05b235d1ed8c3ce61688fe4d84130e72af1657acadfaac3479f4cf/aiocache-0.12.3-py2.py3-none-any.whl", hash = "sha256:889086fc24710f431937b87ad3720a289f7fc31c4fd8b68e9f918b9bacd8270d", size = 28199, upload_time = "2024-09-25T13:20:22.688Z" }, ] [[package]] @@ -40,18 +40,18 @@ dependencies = [ { name = "scikit-image" }, { name = "scipy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/14/d6/8dd5b690d28a332a0b2c3179a345808b5d4c7ad5ddc079b7e116098dff35/albumentations-1.3.1.tar.gz", hash = "sha256:a6a38388fe546c568071e8c82f414498e86c9ed03c08b58e7a88b31cf7a244c6", size = 176371 } +sdist = { url = "https://files.pythonhosted.org/packages/14/d6/8dd5b690d28a332a0b2c3179a345808b5d4c7ad5ddc079b7e116098dff35/albumentations-1.3.1.tar.gz", hash = "sha256:a6a38388fe546c568071e8c82f414498e86c9ed03c08b58e7a88b31cf7a244c6", size = 176371, upload_time = "2023-06-10T07:44:32.36Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/f6/c486cedb4f75147232f32ec4c97026714cfef7c7e247a1f0427bc5489f66/albumentations-1.3.1-py3-none-any.whl", hash = "sha256:6b641d13733181d9ecdc29550e6ad580d1bfa9d25e2213a66940062f25e291bd", size = 125706 }, + { url = "https://files.pythonhosted.org/packages/9b/f6/c486cedb4f75147232f32ec4c97026714cfef7c7e247a1f0427bc5489f66/albumentations-1.3.1-py3-none-any.whl", hash = "sha256:6b641d13733181d9ecdc29550e6ad580d1bfa9d25e2213a66940062f25e291bd", size = 125706, upload_time = "2023-06-10T07:44:30.373Z" }, ] [[package]] name = "annotated-types" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload_time = "2024-05-20T21:33:25.928Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload_time = "2024-05-20T21:33:24.1Z" }, ] [[package]] @@ -64,9 +64,18 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2d/b8/7333d87d5f03247215d86a86362fd3e324111788c6cdd8d2e6196a6ba833/anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f", size = 158770 } +sdist = { url = "https://files.pythonhosted.org/packages/2d/b8/7333d87d5f03247215d86a86362fd3e324111788c6cdd8d2e6196a6ba833/anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f", size = 158770, upload_time = "2023-12-16T17:06:57.709Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/cd/d6d9bb1dadf73e7af02d18225cbd2c93f8552e13130484f1c8dcfece292b/anyio-4.2.0-py3-none-any.whl", hash = "sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee", size = 85481 }, + { url = "https://files.pythonhosted.org/packages/bf/cd/d6d9bb1dadf73e7af02d18225cbd2c93f8552e13130484f1c8dcfece292b/anyio-4.2.0-py3-none-any.whl", hash = "sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee", size = 85481, upload_time = "2023-12-16T17:06:55.989Z" }, +] + +[[package]] +name = "bidict" +version = "0.23.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/6e/026678aa5a830e07cd9498a05d3e7e650a4f56a42f267a53d22bcda1bdc9/bidict-0.23.1.tar.gz", hash = "sha256:03069d763bc387bbd20e7d49914e75fc4132a41937fa3405417e1a5a2d006d71", size = 29093, upload_time = "2024-02-18T19:09:05.748Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/37/e8730c3587a65eb5645d4aba2d27aae48e8003614d6aaf15dda67f702f1f/bidict-0.23.1-py3-none-any.whl", hash = "sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5", size = 32764, upload_time = "2024-02-18T19:09:04.156Z" }, ] [[package]] @@ -82,113 +91,113 @@ dependencies = [ { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449 } +sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449, upload_time = "2025-01-29T04:15:40.373Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/3b/4ba3f93ac8d90410423fdd31d7541ada9bcee1df32fb90d26de41ed40e1d/black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32", size = 1629419 }, - { url = "https://files.pythonhosted.org/packages/b4/02/0bde0485146a8a5e694daed47561785e8b77a0466ccc1f3e485d5ef2925e/black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da", size = 1461080 }, - { url = "https://files.pythonhosted.org/packages/52/0e/abdf75183c830eaca7589144ff96d49bce73d7ec6ad12ef62185cc0f79a2/black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7", size = 1766886 }, - { url = "https://files.pythonhosted.org/packages/dc/a6/97d8bb65b1d8a41f8a6736222ba0a334db7b7b77b8023ab4568288f23973/black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9", size = 1419404 }, - { url = "https://files.pythonhosted.org/packages/7e/4f/87f596aca05c3ce5b94b8663dbfe242a12843caaa82dd3f85f1ffdc3f177/black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0", size = 1614372 }, - { url = "https://files.pythonhosted.org/packages/e7/d0/2c34c36190b741c59c901e56ab7f6e54dad8df05a6272a9747ecef7c6036/black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299", size = 1442865 }, - { url = "https://files.pythonhosted.org/packages/21/d4/7518c72262468430ead45cf22bd86c883a6448b9eb43672765d69a8f1248/black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096", size = 1749699 }, - { url = "https://files.pythonhosted.org/packages/58/db/4f5beb989b547f79096e035c4981ceb36ac2b552d0ac5f2620e941501c99/black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2", size = 1428028 }, - { url = "https://files.pythonhosted.org/packages/83/71/3fe4741df7adf015ad8dfa082dd36c94ca86bb21f25608eb247b4afb15b2/black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", size = 1650988 }, - { url = "https://files.pythonhosted.org/packages/13/f3/89aac8a83d73937ccd39bbe8fc6ac8860c11cfa0af5b1c96d081facac844/black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", size = 1453985 }, - { url = "https://files.pythonhosted.org/packages/6f/22/b99efca33f1f3a1d2552c714b1e1b5ae92efac6c43e790ad539a163d1754/black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", size = 1783816 }, - { url = "https://files.pythonhosted.org/packages/18/7e/a27c3ad3822b6f2e0e00d63d58ff6299a99a5b3aee69fa77cd4b0076b261/black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", size = 1440860 }, - { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673 }, - { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190 }, - { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926 }, - { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613 }, - { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646 }, + { url = "https://files.pythonhosted.org/packages/4d/3b/4ba3f93ac8d90410423fdd31d7541ada9bcee1df32fb90d26de41ed40e1d/black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32", size = 1629419, upload_time = "2025-01-29T05:37:06.642Z" }, + { url = "https://files.pythonhosted.org/packages/b4/02/0bde0485146a8a5e694daed47561785e8b77a0466ccc1f3e485d5ef2925e/black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da", size = 1461080, upload_time = "2025-01-29T05:37:09.321Z" }, + { url = "https://files.pythonhosted.org/packages/52/0e/abdf75183c830eaca7589144ff96d49bce73d7ec6ad12ef62185cc0f79a2/black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7", size = 1766886, upload_time = "2025-01-29T04:18:24.432Z" }, + { url = "https://files.pythonhosted.org/packages/dc/a6/97d8bb65b1d8a41f8a6736222ba0a334db7b7b77b8023ab4568288f23973/black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9", size = 1419404, upload_time = "2025-01-29T04:19:04.296Z" }, + { url = "https://files.pythonhosted.org/packages/7e/4f/87f596aca05c3ce5b94b8663dbfe242a12843caaa82dd3f85f1ffdc3f177/black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0", size = 1614372, upload_time = "2025-01-29T05:37:11.71Z" }, + { url = "https://files.pythonhosted.org/packages/e7/d0/2c34c36190b741c59c901e56ab7f6e54dad8df05a6272a9747ecef7c6036/black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299", size = 1442865, upload_time = "2025-01-29T05:37:14.309Z" }, + { url = "https://files.pythonhosted.org/packages/21/d4/7518c72262468430ead45cf22bd86c883a6448b9eb43672765d69a8f1248/black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096", size = 1749699, upload_time = "2025-01-29T04:18:17.688Z" }, + { url = "https://files.pythonhosted.org/packages/58/db/4f5beb989b547f79096e035c4981ceb36ac2b552d0ac5f2620e941501c99/black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2", size = 1428028, upload_time = "2025-01-29T04:18:51.711Z" }, + { url = "https://files.pythonhosted.org/packages/83/71/3fe4741df7adf015ad8dfa082dd36c94ca86bb21f25608eb247b4afb15b2/black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", size = 1650988, upload_time = "2025-01-29T05:37:16.707Z" }, + { url = "https://files.pythonhosted.org/packages/13/f3/89aac8a83d73937ccd39bbe8fc6ac8860c11cfa0af5b1c96d081facac844/black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", size = 1453985, upload_time = "2025-01-29T05:37:18.273Z" }, + { url = "https://files.pythonhosted.org/packages/6f/22/b99efca33f1f3a1d2552c714b1e1b5ae92efac6c43e790ad539a163d1754/black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", size = 1783816, upload_time = "2025-01-29T04:18:33.823Z" }, + { url = "https://files.pythonhosted.org/packages/18/7e/a27c3ad3822b6f2e0e00d63d58ff6299a99a5b3aee69fa77cd4b0076b261/black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", size = 1440860, upload_time = "2025-01-29T04:19:12.944Z" }, + { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673, upload_time = "2025-01-29T05:37:20.574Z" }, + { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190, upload_time = "2025-01-29T05:37:22.106Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926, upload_time = "2025-01-29T04:18:58.564Z" }, + { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613, upload_time = "2025-01-29T04:19:27.63Z" }, + { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646, upload_time = "2025-01-29T04:15:38.082Z" }, ] [[package]] name = "blinker" version = "1.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/13/6df5fc090ff4e5d246baf1f45fe9e5623aa8565757dfa5bd243f6a545f9e/blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182", size = 28134 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/13/6df5fc090ff4e5d246baf1f45fe9e5623aa8565757dfa5bd243f6a545f9e/blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182", size = 28134, upload_time = "2023-11-01T22:06:01.588Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/2a/7f3714cbc6356a0efec525ce7a0613d581072ed6eb53eb7b9754f33db807/blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", size = 13068 }, + { url = "https://files.pythonhosted.org/packages/fa/2a/7f3714cbc6356a0efec525ce7a0613d581072ed6eb53eb7b9754f33db807/blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", size = 13068, upload_time = "2023-11-01T22:06:00.162Z" }, ] [[package]] name = "brotli" version = "1.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/c2/f9e977608bdf958650638c3f1e28f85a1b075f075ebbe77db8555463787b/Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724", size = 7372270 } +sdist = { url = "https://files.pythonhosted.org/packages/2f/c2/f9e977608bdf958650638c3f1e28f85a1b075f075ebbe77db8555463787b/Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724", size = 7372270, upload_time = "2023-09-07T14:05:41.643Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/3a/dbf4fb970c1019a57b5e492e1e0eae745d32e59ba4d6161ab5422b08eefe/Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752", size = 873045 }, - { url = "https://files.pythonhosted.org/packages/dd/11/afc14026ea7f44bd6eb9316d800d439d092c8d508752055ce8d03086079a/Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9", size = 446218 }, - { url = "https://files.pythonhosted.org/packages/36/83/7545a6e7729db43cb36c4287ae388d6885c85a86dd251768a47015dfde32/Brotli-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3", size = 2903872 }, - { url = "https://files.pythonhosted.org/packages/32/23/35331c4d9391fcc0f29fd9bec2c76e4b4eeab769afbc4b11dd2e1098fb13/Brotli-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d", size = 2941254 }, - { url = "https://files.pythonhosted.org/packages/3b/24/1671acb450c902edb64bd765d73603797c6c7280a9ada85a195f6b78c6e5/Brotli-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e", size = 2857293 }, - { url = "https://files.pythonhosted.org/packages/d5/00/40f760cc27007912b327fe15bf6bfd8eaecbe451687f72a8abc587d503b3/Brotli-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da", size = 3002385 }, - { url = "https://files.pythonhosted.org/packages/b8/cb/8aaa83f7a4caa131757668c0fb0c4b6384b09ffa77f2fba9570d87ab587d/Brotli-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80", size = 2911104 }, - { url = "https://files.pythonhosted.org/packages/bc/c4/65456561d89d3c49f46b7fbeb8fe6e449f13bdc8ea7791832c5d476b2faf/Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d", size = 2809981 }, - { url = "https://files.pythonhosted.org/packages/05/1b/cf49528437bae28abce5f6e059f0d0be6fecdcc1d3e33e7c54b3ca498425/Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0", size = 2935297 }, - { url = "https://files.pythonhosted.org/packages/81/ff/190d4af610680bf0c5a09eb5d1eac6e99c7c8e216440f9c7cfd42b7adab5/Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e", size = 2930735 }, - { url = "https://files.pythonhosted.org/packages/80/7d/f1abbc0c98f6e09abd3cad63ec34af17abc4c44f308a7a539010f79aae7a/Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c", size = 2933107 }, - { url = "https://files.pythonhosted.org/packages/34/ce/5a5020ba48f2b5a4ad1c0522d095ad5847a0be508e7d7569c8630ce25062/Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1", size = 2845400 }, - { url = "https://files.pythonhosted.org/packages/44/89/fa2c4355ab1eecf3994e5a0a7f5492c6ff81dfcb5f9ba7859bd534bb5c1a/Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2", size = 3031985 }, - { url = "https://files.pythonhosted.org/packages/af/a4/79196b4a1674143d19dca400866b1a4d1a089040df7b93b88ebae81f3447/Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec", size = 2927099 }, - { url = "https://files.pythonhosted.org/packages/e9/54/1c0278556a097f9651e657b873ab08f01b9a9ae4cac128ceb66427d7cd20/Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2", size = 333172 }, - { url = "https://files.pythonhosted.org/packages/f7/65/b785722e941193fd8b571afd9edbec2a9b838ddec4375d8af33a50b8dab9/Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128", size = 357255 }, - { url = "https://files.pythonhosted.org/packages/96/12/ad41e7fadd5db55459c4c401842b47f7fee51068f86dd2894dd0dcfc2d2a/Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc", size = 873068 }, - { url = "https://files.pythonhosted.org/packages/95/4e/5afab7b2b4b61a84e9c75b17814198ce515343a44e2ed4488fac314cd0a9/Brotli-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6", size = 446244 }, - { url = "https://files.pythonhosted.org/packages/9d/e6/f305eb61fb9a8580c525478a4a34c5ae1a9bcb12c3aee619114940bc513d/Brotli-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd", size = 2906500 }, - { url = "https://files.pythonhosted.org/packages/3e/4f/af6846cfbc1550a3024e5d3775ede1e00474c40882c7bf5b37a43ca35e91/Brotli-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf", size = 2943950 }, - { url = "https://files.pythonhosted.org/packages/b3/e7/ca2993c7682d8629b62630ebf0d1f3bb3d579e667ce8e7ca03a0a0576a2d/Brotli-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61", size = 2918527 }, - { url = "https://files.pythonhosted.org/packages/b3/96/da98e7bedc4c51104d29cc61e5f449a502dd3dbc211944546a4cc65500d3/Brotli-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327", size = 2845489 }, - { url = "https://files.pythonhosted.org/packages/e8/ef/ccbc16947d6ce943a7f57e1a40596c75859eeb6d279c6994eddd69615265/Brotli-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd", size = 2914080 }, - { url = "https://files.pythonhosted.org/packages/80/d6/0bd38d758d1afa62a5524172f0b18626bb2392d717ff94806f741fcd5ee9/Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9", size = 2813051 }, - { url = "https://files.pythonhosted.org/packages/14/56/48859dd5d129d7519e001f06dcfbb6e2cf6db92b2702c0c2ce7d97e086c1/Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265", size = 2938172 }, - { url = "https://files.pythonhosted.org/packages/3d/77/a236d5f8cd9e9f4348da5acc75ab032ab1ab2c03cc8f430d24eea2672888/Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8", size = 2933023 }, - { url = "https://files.pythonhosted.org/packages/f1/87/3b283efc0f5cb35f7f84c0c240b1e1a1003a5e47141a4881bf87c86d0ce2/Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f", size = 2935871 }, - { url = "https://files.pythonhosted.org/packages/f3/eb/2be4cc3e2141dc1a43ad4ca1875a72088229de38c68e842746b342667b2a/Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757", size = 2847784 }, - { url = "https://files.pythonhosted.org/packages/66/13/b58ddebfd35edde572ccefe6890cf7c493f0c319aad2a5badee134b4d8ec/Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0", size = 3034905 }, - { url = "https://files.pythonhosted.org/packages/84/9c/bc96b6c7db824998a49ed3b38e441a2cae9234da6fa11f6ed17e8cf4f147/Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b", size = 2929467 }, - { url = "https://files.pythonhosted.org/packages/e7/71/8f161dee223c7ff7fea9d44893fba953ce97cf2c3c33f78ba260a91bcff5/Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50", size = 333169 }, - { url = "https://files.pythonhosted.org/packages/02/8a/fece0ee1057643cb2a5bbf59682de13f1725f8482b2c057d4e799d7ade75/Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1", size = 357253 }, - { url = "https://files.pythonhosted.org/packages/5c/d0/5373ae13b93fe00095a58efcbce837fd470ca39f703a235d2a999baadfbc/Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28", size = 815693 }, - { url = "https://files.pythonhosted.org/packages/8e/48/f6e1cdf86751300c288c1459724bfa6917a80e30dbfc326f92cea5d3683a/Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f", size = 422489 }, - { url = "https://files.pythonhosted.org/packages/06/88/564958cedce636d0f1bed313381dfc4b4e3d3f6015a63dae6146e1b8c65c/Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409", size = 873081 }, - { url = "https://files.pythonhosted.org/packages/58/79/b7026a8bb65da9a6bb7d14329fd2bd48d2b7f86d7329d5cc8ddc6a90526f/Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2", size = 446244 }, - { url = "https://files.pythonhosted.org/packages/e5/18/c18c32ecea41b6c0004e15606e274006366fe19436b6adccc1ae7b2e50c2/Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451", size = 2906505 }, - { url = "https://files.pythonhosted.org/packages/08/c8/69ec0496b1ada7569b62d85893d928e865df29b90736558d6c98c2031208/Brotli-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91", size = 2944152 }, - { url = "https://files.pythonhosted.org/packages/ab/fb/0517cea182219d6768113a38167ef6d4eb157a033178cc938033a552ed6d/Brotli-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408", size = 2919252 }, - { url = "https://files.pythonhosted.org/packages/c7/53/73a3431662e33ae61a5c80b1b9d2d18f58dfa910ae8dd696e57d39f1a2f5/Brotli-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0", size = 2845955 }, - { url = "https://files.pythonhosted.org/packages/55/ac/bd280708d9c5ebdbf9de01459e625a3e3803cce0784f47d633562cf40e83/Brotli-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc", size = 2914304 }, - { url = "https://files.pythonhosted.org/packages/76/58/5c391b41ecfc4527d2cc3350719b02e87cb424ef8ba2023fb662f9bf743c/Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180", size = 2814452 }, - { url = "https://files.pythonhosted.org/packages/c7/4e/91b8256dfe99c407f174924b65a01f5305e303f486cc7a2e8a5d43c8bec3/Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248", size = 2938751 }, - { url = "https://files.pythonhosted.org/packages/5a/a6/e2a39a5d3b412938362bbbeba5af904092bf3f95b867b4a3eb856104074e/Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966", size = 2933757 }, - { url = "https://files.pythonhosted.org/packages/13/f0/358354786280a509482e0e77c1a5459e439766597d280f28cb097642fc26/Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9", size = 2936146 }, - { url = "https://files.pythonhosted.org/packages/80/f7/daf538c1060d3a88266b80ecc1d1c98b79553b3f117a485653f17070ea2a/Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb", size = 2848055 }, - { url = "https://files.pythonhosted.org/packages/ad/cf/0eaa0585c4077d3c2d1edf322d8e97aabf317941d3a72d7b3ad8bce004b0/Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111", size = 3035102 }, - { url = "https://files.pythonhosted.org/packages/d8/63/1c1585b2aa554fe6dbce30f0c18bdbc877fa9a1bf5ff17677d9cca0ac122/Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839", size = 2930029 }, - { url = "https://files.pythonhosted.org/packages/5f/3b/4e3fd1893eb3bbfef8e5a80d4508bec17a57bb92d586c85c12d28666bb13/Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0", size = 333276 }, - { url = "https://files.pythonhosted.org/packages/3d/d5/942051b45a9e883b5b6e98c041698b1eb2012d25e5948c58d6bf85b1bb43/Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951", size = 357255 }, - { url = "https://files.pythonhosted.org/packages/0a/9f/fb37bb8ffc52a8da37b1c03c459a8cd55df7a57bdccd8831d500e994a0ca/Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5", size = 815681 }, - { url = "https://files.pythonhosted.org/packages/06/b3/dbd332a988586fefb0aa49c779f59f47cae76855c2d00f450364bb574cac/Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8", size = 422475 }, - { url = "https://files.pythonhosted.org/packages/bb/80/6aaddc2f63dbcf2d93c2d204e49c11a9ec93a8c7c63261e2b4bd35198283/Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f", size = 2906173 }, - { url = "https://files.pythonhosted.org/packages/ea/1d/e6ca79c96ff5b641df6097d299347507d39a9604bde8915e76bf026d6c77/Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648", size = 2943803 }, - { url = "https://files.pythonhosted.org/packages/ac/a3/d98d2472e0130b7dd3acdbb7f390d478123dbf62b7d32bda5c830a96116d/Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0", size = 2918946 }, - { url = "https://files.pythonhosted.org/packages/c4/a5/c69e6d272aee3e1423ed005d8915a7eaa0384c7de503da987f2d224d0721/Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089", size = 2845707 }, - { url = "https://files.pythonhosted.org/packages/58/9f/4149d38b52725afa39067350696c09526de0125ebfbaab5acc5af28b42ea/Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368", size = 2936231 }, - { url = "https://files.pythonhosted.org/packages/5a/5a/145de884285611838a16bebfdb060c231c52b8f84dfbe52b852a15780386/Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c", size = 2848157 }, - { url = "https://files.pythonhosted.org/packages/50/ae/408b6bfb8525dadebd3b3dd5b19d631da4f7d46420321db44cd99dcf2f2c/Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284", size = 3035122 }, - { url = "https://files.pythonhosted.org/packages/af/85/a94e5cfaa0ca449d8f91c3d6f78313ebf919a0dbd55a100c711c6e9655bc/Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7", size = 2930206 }, - { url = "https://files.pythonhosted.org/packages/c2/f0/a61d9262cd01351df22e57ad7c34f66794709acab13f34be2675f45bf89d/Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0", size = 333804 }, - { url = "https://files.pythonhosted.org/packages/7e/c1/ec214e9c94000d1c1974ec67ced1c970c148aa6b8d8373066123fc3dbf06/Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b", size = 358517 }, + { url = "https://files.pythonhosted.org/packages/6d/3a/dbf4fb970c1019a57b5e492e1e0eae745d32e59ba4d6161ab5422b08eefe/Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752", size = 873045, upload_time = "2023-09-07T14:03:16.894Z" }, + { url = "https://files.pythonhosted.org/packages/dd/11/afc14026ea7f44bd6eb9316d800d439d092c8d508752055ce8d03086079a/Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9", size = 446218, upload_time = "2023-09-07T14:03:18.917Z" }, + { url = "https://files.pythonhosted.org/packages/36/83/7545a6e7729db43cb36c4287ae388d6885c85a86dd251768a47015dfde32/Brotli-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3", size = 2903872, upload_time = "2023-09-07T14:03:20.398Z" }, + { url = "https://files.pythonhosted.org/packages/32/23/35331c4d9391fcc0f29fd9bec2c76e4b4eeab769afbc4b11dd2e1098fb13/Brotli-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d", size = 2941254, upload_time = "2023-09-07T14:03:21.914Z" }, + { url = "https://files.pythonhosted.org/packages/3b/24/1671acb450c902edb64bd765d73603797c6c7280a9ada85a195f6b78c6e5/Brotli-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e", size = 2857293, upload_time = "2023-09-07T14:03:24Z" }, + { url = "https://files.pythonhosted.org/packages/d5/00/40f760cc27007912b327fe15bf6bfd8eaecbe451687f72a8abc587d503b3/Brotli-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da", size = 3002385, upload_time = "2023-09-07T14:03:26.248Z" }, + { url = "https://files.pythonhosted.org/packages/b8/cb/8aaa83f7a4caa131757668c0fb0c4b6384b09ffa77f2fba9570d87ab587d/Brotli-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80", size = 2911104, upload_time = "2023-09-07T14:03:27.849Z" }, + { url = "https://files.pythonhosted.org/packages/bc/c4/65456561d89d3c49f46b7fbeb8fe6e449f13bdc8ea7791832c5d476b2faf/Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d", size = 2809981, upload_time = "2023-09-07T14:03:29.92Z" }, + { url = "https://files.pythonhosted.org/packages/05/1b/cf49528437bae28abce5f6e059f0d0be6fecdcc1d3e33e7c54b3ca498425/Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0", size = 2935297, upload_time = "2023-09-07T14:03:32.035Z" }, + { url = "https://files.pythonhosted.org/packages/81/ff/190d4af610680bf0c5a09eb5d1eac6e99c7c8e216440f9c7cfd42b7adab5/Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e", size = 2930735, upload_time = "2023-09-07T14:03:33.801Z" }, + { url = "https://files.pythonhosted.org/packages/80/7d/f1abbc0c98f6e09abd3cad63ec34af17abc4c44f308a7a539010f79aae7a/Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c", size = 2933107, upload_time = "2024-10-18T12:32:09.016Z" }, + { url = "https://files.pythonhosted.org/packages/34/ce/5a5020ba48f2b5a4ad1c0522d095ad5847a0be508e7d7569c8630ce25062/Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1", size = 2845400, upload_time = "2024-10-18T12:32:11.134Z" }, + { url = "https://files.pythonhosted.org/packages/44/89/fa2c4355ab1eecf3994e5a0a7f5492c6ff81dfcb5f9ba7859bd534bb5c1a/Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2", size = 3031985, upload_time = "2024-10-18T12:32:12.813Z" }, + { url = "https://files.pythonhosted.org/packages/af/a4/79196b4a1674143d19dca400866b1a4d1a089040df7b93b88ebae81f3447/Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec", size = 2927099, upload_time = "2024-10-18T12:32:14.733Z" }, + { url = "https://files.pythonhosted.org/packages/e9/54/1c0278556a097f9651e657b873ab08f01b9a9ae4cac128ceb66427d7cd20/Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2", size = 333172, upload_time = "2023-09-07T14:03:35.212Z" }, + { url = "https://files.pythonhosted.org/packages/f7/65/b785722e941193fd8b571afd9edbec2a9b838ddec4375d8af33a50b8dab9/Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128", size = 357255, upload_time = "2023-09-07T14:03:36.447Z" }, + { url = "https://files.pythonhosted.org/packages/96/12/ad41e7fadd5db55459c4c401842b47f7fee51068f86dd2894dd0dcfc2d2a/Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc", size = 873068, upload_time = "2023-09-07T14:03:37.779Z" }, + { url = "https://files.pythonhosted.org/packages/95/4e/5afab7b2b4b61a84e9c75b17814198ce515343a44e2ed4488fac314cd0a9/Brotli-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6", size = 446244, upload_time = "2023-09-07T14:03:39.223Z" }, + { url = "https://files.pythonhosted.org/packages/9d/e6/f305eb61fb9a8580c525478a4a34c5ae1a9bcb12c3aee619114940bc513d/Brotli-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd", size = 2906500, upload_time = "2023-09-07T14:03:40.858Z" }, + { url = "https://files.pythonhosted.org/packages/3e/4f/af6846cfbc1550a3024e5d3775ede1e00474c40882c7bf5b37a43ca35e91/Brotli-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf", size = 2943950, upload_time = "2023-09-07T14:03:42.896Z" }, + { url = "https://files.pythonhosted.org/packages/b3/e7/ca2993c7682d8629b62630ebf0d1f3bb3d579e667ce8e7ca03a0a0576a2d/Brotli-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61", size = 2918527, upload_time = "2023-09-07T14:03:44.552Z" }, + { url = "https://files.pythonhosted.org/packages/b3/96/da98e7bedc4c51104d29cc61e5f449a502dd3dbc211944546a4cc65500d3/Brotli-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327", size = 2845489, upload_time = "2023-09-07T14:03:46.594Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ef/ccbc16947d6ce943a7f57e1a40596c75859eeb6d279c6994eddd69615265/Brotli-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd", size = 2914080, upload_time = "2023-09-07T14:03:48.204Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/0bd38d758d1afa62a5524172f0b18626bb2392d717ff94806f741fcd5ee9/Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9", size = 2813051, upload_time = "2023-09-07T14:03:50.348Z" }, + { url = "https://files.pythonhosted.org/packages/14/56/48859dd5d129d7519e001f06dcfbb6e2cf6db92b2702c0c2ce7d97e086c1/Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265", size = 2938172, upload_time = "2023-09-07T14:03:52.395Z" }, + { url = "https://files.pythonhosted.org/packages/3d/77/a236d5f8cd9e9f4348da5acc75ab032ab1ab2c03cc8f430d24eea2672888/Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8", size = 2933023, upload_time = "2023-09-07T14:03:53.96Z" }, + { url = "https://files.pythonhosted.org/packages/f1/87/3b283efc0f5cb35f7f84c0c240b1e1a1003a5e47141a4881bf87c86d0ce2/Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f", size = 2935871, upload_time = "2024-10-18T12:32:16.688Z" }, + { url = "https://files.pythonhosted.org/packages/f3/eb/2be4cc3e2141dc1a43ad4ca1875a72088229de38c68e842746b342667b2a/Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757", size = 2847784, upload_time = "2024-10-18T12:32:18.459Z" }, + { url = "https://files.pythonhosted.org/packages/66/13/b58ddebfd35edde572ccefe6890cf7c493f0c319aad2a5badee134b4d8ec/Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0", size = 3034905, upload_time = "2024-10-18T12:32:20.192Z" }, + { url = "https://files.pythonhosted.org/packages/84/9c/bc96b6c7db824998a49ed3b38e441a2cae9234da6fa11f6ed17e8cf4f147/Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b", size = 2929467, upload_time = "2024-10-18T12:32:21.774Z" }, + { url = "https://files.pythonhosted.org/packages/e7/71/8f161dee223c7ff7fea9d44893fba953ce97cf2c3c33f78ba260a91bcff5/Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50", size = 333169, upload_time = "2023-09-07T14:03:55.404Z" }, + { url = "https://files.pythonhosted.org/packages/02/8a/fece0ee1057643cb2a5bbf59682de13f1725f8482b2c057d4e799d7ade75/Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1", size = 357253, upload_time = "2023-09-07T14:03:56.643Z" }, + { url = "https://files.pythonhosted.org/packages/5c/d0/5373ae13b93fe00095a58efcbce837fd470ca39f703a235d2a999baadfbc/Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28", size = 815693, upload_time = "2024-10-18T12:32:23.824Z" }, + { url = "https://files.pythonhosted.org/packages/8e/48/f6e1cdf86751300c288c1459724bfa6917a80e30dbfc326f92cea5d3683a/Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f", size = 422489, upload_time = "2024-10-18T12:32:25.641Z" }, + { url = "https://files.pythonhosted.org/packages/06/88/564958cedce636d0f1bed313381dfc4b4e3d3f6015a63dae6146e1b8c65c/Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409", size = 873081, upload_time = "2023-09-07T14:03:57.967Z" }, + { url = "https://files.pythonhosted.org/packages/58/79/b7026a8bb65da9a6bb7d14329fd2bd48d2b7f86d7329d5cc8ddc6a90526f/Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2", size = 446244, upload_time = "2023-09-07T14:03:59.319Z" }, + { url = "https://files.pythonhosted.org/packages/e5/18/c18c32ecea41b6c0004e15606e274006366fe19436b6adccc1ae7b2e50c2/Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451", size = 2906505, upload_time = "2023-09-07T14:04:01.327Z" }, + { url = "https://files.pythonhosted.org/packages/08/c8/69ec0496b1ada7569b62d85893d928e865df29b90736558d6c98c2031208/Brotli-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91", size = 2944152, upload_time = "2023-09-07T14:04:03.033Z" }, + { url = "https://files.pythonhosted.org/packages/ab/fb/0517cea182219d6768113a38167ef6d4eb157a033178cc938033a552ed6d/Brotli-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408", size = 2919252, upload_time = "2023-09-07T14:04:04.675Z" }, + { url = "https://files.pythonhosted.org/packages/c7/53/73a3431662e33ae61a5c80b1b9d2d18f58dfa910ae8dd696e57d39f1a2f5/Brotli-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0", size = 2845955, upload_time = "2023-09-07T14:04:06.585Z" }, + { url = "https://files.pythonhosted.org/packages/55/ac/bd280708d9c5ebdbf9de01459e625a3e3803cce0784f47d633562cf40e83/Brotli-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc", size = 2914304, upload_time = "2023-09-07T14:04:08.668Z" }, + { url = "https://files.pythonhosted.org/packages/76/58/5c391b41ecfc4527d2cc3350719b02e87cb424ef8ba2023fb662f9bf743c/Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180", size = 2814452, upload_time = "2023-09-07T14:04:10.736Z" }, + { url = "https://files.pythonhosted.org/packages/c7/4e/91b8256dfe99c407f174924b65a01f5305e303f486cc7a2e8a5d43c8bec3/Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248", size = 2938751, upload_time = "2023-09-07T14:04:12.875Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a6/e2a39a5d3b412938362bbbeba5af904092bf3f95b867b4a3eb856104074e/Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966", size = 2933757, upload_time = "2023-09-07T14:04:14.551Z" }, + { url = "https://files.pythonhosted.org/packages/13/f0/358354786280a509482e0e77c1a5459e439766597d280f28cb097642fc26/Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9", size = 2936146, upload_time = "2024-10-18T12:32:27.257Z" }, + { url = "https://files.pythonhosted.org/packages/80/f7/daf538c1060d3a88266b80ecc1d1c98b79553b3f117a485653f17070ea2a/Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb", size = 2848055, upload_time = "2024-10-18T12:32:29.376Z" }, + { url = "https://files.pythonhosted.org/packages/ad/cf/0eaa0585c4077d3c2d1edf322d8e97aabf317941d3a72d7b3ad8bce004b0/Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111", size = 3035102, upload_time = "2024-10-18T12:32:31.371Z" }, + { url = "https://files.pythonhosted.org/packages/d8/63/1c1585b2aa554fe6dbce30f0c18bdbc877fa9a1bf5ff17677d9cca0ac122/Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839", size = 2930029, upload_time = "2024-10-18T12:32:33.293Z" }, + { url = "https://files.pythonhosted.org/packages/5f/3b/4e3fd1893eb3bbfef8e5a80d4508bec17a57bb92d586c85c12d28666bb13/Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0", size = 333276, upload_time = "2023-09-07T14:04:16.49Z" }, + { url = "https://files.pythonhosted.org/packages/3d/d5/942051b45a9e883b5b6e98c041698b1eb2012d25e5948c58d6bf85b1bb43/Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951", size = 357255, upload_time = "2023-09-07T14:04:17.83Z" }, + { url = "https://files.pythonhosted.org/packages/0a/9f/fb37bb8ffc52a8da37b1c03c459a8cd55df7a57bdccd8831d500e994a0ca/Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5", size = 815681, upload_time = "2024-10-18T12:32:34.942Z" }, + { url = "https://files.pythonhosted.org/packages/06/b3/dbd332a988586fefb0aa49c779f59f47cae76855c2d00f450364bb574cac/Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8", size = 422475, upload_time = "2024-10-18T12:32:36.485Z" }, + { url = "https://files.pythonhosted.org/packages/bb/80/6aaddc2f63dbcf2d93c2d204e49c11a9ec93a8c7c63261e2b4bd35198283/Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f", size = 2906173, upload_time = "2024-10-18T12:32:37.978Z" }, + { url = "https://files.pythonhosted.org/packages/ea/1d/e6ca79c96ff5b641df6097d299347507d39a9604bde8915e76bf026d6c77/Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648", size = 2943803, upload_time = "2024-10-18T12:32:39.606Z" }, + { url = "https://files.pythonhosted.org/packages/ac/a3/d98d2472e0130b7dd3acdbb7f390d478123dbf62b7d32bda5c830a96116d/Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0", size = 2918946, upload_time = "2024-10-18T12:32:41.679Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a5/c69e6d272aee3e1423ed005d8915a7eaa0384c7de503da987f2d224d0721/Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089", size = 2845707, upload_time = "2024-10-18T12:32:43.478Z" }, + { url = "https://files.pythonhosted.org/packages/58/9f/4149d38b52725afa39067350696c09526de0125ebfbaab5acc5af28b42ea/Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368", size = 2936231, upload_time = "2024-10-18T12:32:45.224Z" }, + { url = "https://files.pythonhosted.org/packages/5a/5a/145de884285611838a16bebfdb060c231c52b8f84dfbe52b852a15780386/Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c", size = 2848157, upload_time = "2024-10-18T12:32:46.894Z" }, + { url = "https://files.pythonhosted.org/packages/50/ae/408b6bfb8525dadebd3b3dd5b19d631da4f7d46420321db44cd99dcf2f2c/Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284", size = 3035122, upload_time = "2024-10-18T12:32:48.844Z" }, + { url = "https://files.pythonhosted.org/packages/af/85/a94e5cfaa0ca449d8f91c3d6f78313ebf919a0dbd55a100c711c6e9655bc/Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7", size = 2930206, upload_time = "2024-10-18T12:32:51.198Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f0/a61d9262cd01351df22e57ad7c34f66794709acab13f34be2675f45bf89d/Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0", size = 333804, upload_time = "2024-10-18T12:32:52.661Z" }, + { url = "https://files.pythonhosted.org/packages/7e/c1/ec214e9c94000d1c1974ec67ced1c970c148aa6b8d8373066123fc3dbf06/Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b", size = 358517, upload_time = "2024-10-18T12:32:54.066Z" }, ] [[package]] name = "certifi" version = "2023.11.17" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d4/91/c89518dd4fe1f3a4e3f6ab7ff23cb00ef2e8c9adf99dacc618ad5e068e28/certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", size = 163637 } +sdist = { url = "https://files.pythonhosted.org/packages/d4/91/c89518dd4fe1f3a4e3f6ab7ff23cb00ef2e8c9adf99dacc618ad5e068e28/certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", size = 163637, upload_time = "2023-11-18T02:54:02.397Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/64/62/428ef076be88fa93716b576e4a01f919d25968913e817077a386fcbe4f42/certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474", size = 162530 }, + { url = "https://files.pythonhosted.org/packages/64/62/428ef076be88fa93716b576e4a01f919d25968913e817077a386fcbe4f42/certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474", size = 162530, upload_time = "2023-11-18T02:54:00.083Z" }, ] [[package]] @@ -198,108 +207,108 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } +sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload_time = "2024-09-04T20:45:21.852Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", size = 182191 }, - { url = "https://files.pythonhosted.org/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", size = 178592 }, - { url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024 }, - { url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188 }, - { url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571 }, - { url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687 }, - { url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211 }, - { url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325 }, - { url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784 }, - { url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564 }, - { url = "https://files.pythonhosted.org/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", size = 171804 }, - { url = "https://files.pythonhosted.org/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", size = 181299 }, - { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264 }, - { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651 }, - { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259 }, - { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200 }, - { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235 }, - { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721 }, - { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242 }, - { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999 }, - { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242 }, - { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604 }, - { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727 }, - { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400 }, - { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178 }, - { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840 }, - { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803 }, - { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850 }, - { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729 }, - { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256 }, - { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424 }, - { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568 }, - { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736 }, - { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 }, - { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 }, - { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 }, - { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 }, - { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 }, - { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 }, - { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 }, - { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 }, - { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 }, - { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 }, - { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 }, - { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 }, - { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 }, + { url = "https://files.pythonhosted.org/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", size = 182191, upload_time = "2024-09-04T20:43:30.027Z" }, + { url = "https://files.pythonhosted.org/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", size = 178592, upload_time = "2024-09-04T20:43:32.108Z" }, + { url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024, upload_time = "2024-09-04T20:43:34.186Z" }, + { url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188, upload_time = "2024-09-04T20:43:36.286Z" }, + { url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571, upload_time = "2024-09-04T20:43:38.586Z" }, + { url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687, upload_time = "2024-09-04T20:43:40.084Z" }, + { url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211, upload_time = "2024-09-04T20:43:41.526Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325, upload_time = "2024-09-04T20:43:43.117Z" }, + { url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784, upload_time = "2024-09-04T20:43:45.256Z" }, + { url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564, upload_time = "2024-09-04T20:43:46.779Z" }, + { url = "https://files.pythonhosted.org/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", size = 171804, upload_time = "2024-09-04T20:43:48.186Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", size = 181299, upload_time = "2024-09-04T20:43:49.812Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264, upload_time = "2024-09-04T20:43:51.124Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651, upload_time = "2024-09-04T20:43:52.872Z" }, + { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259, upload_time = "2024-09-04T20:43:56.123Z" }, + { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200, upload_time = "2024-09-04T20:43:57.891Z" }, + { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235, upload_time = "2024-09-04T20:44:00.18Z" }, + { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721, upload_time = "2024-09-04T20:44:01.585Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242, upload_time = "2024-09-04T20:44:03.467Z" }, + { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999, upload_time = "2024-09-04T20:44:05.023Z" }, + { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242, upload_time = "2024-09-04T20:44:06.444Z" }, + { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604, upload_time = "2024-09-04T20:44:08.206Z" }, + { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727, upload_time = "2024-09-04T20:44:09.481Z" }, + { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400, upload_time = "2024-09-04T20:44:10.873Z" }, + { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178, upload_time = "2024-09-04T20:44:12.232Z" }, + { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840, upload_time = "2024-09-04T20:44:13.739Z" }, + { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803, upload_time = "2024-09-04T20:44:15.231Z" }, + { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload_time = "2024-09-04T20:44:17.188Z" }, + { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload_time = "2024-09-04T20:44:18.688Z" }, + { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload_time = "2024-09-04T20:44:20.248Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload_time = "2024-09-04T20:44:21.673Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload_time = "2024-09-04T20:44:23.245Z" }, + { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload_time = "2024-09-04T20:44:24.757Z" }, + { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload_time = "2024-09-04T20:44:26.208Z" }, + { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload_time = "2024-09-04T20:44:27.578Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989, upload_time = "2024-09-04T20:44:28.956Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802, upload_time = "2024-09-04T20:44:30.289Z" }, + { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792, upload_time = "2024-09-04T20:44:32.01Z" }, + { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893, upload_time = "2024-09-04T20:44:33.606Z" }, + { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810, upload_time = "2024-09-04T20:44:35.191Z" }, + { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200, upload_time = "2024-09-04T20:44:36.743Z" }, + { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447, upload_time = "2024-09-04T20:44:38.492Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358, upload_time = "2024-09-04T20:44:40.046Z" }, + { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469, upload_time = "2024-09-04T20:44:41.616Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475, upload_time = "2024-09-04T20:44:43.733Z" }, + { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload_time = "2024-09-04T20:44:45.309Z" }, ] [[package]] name = "charset-normalizer" version = "3.3.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 } +sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809, upload_time = "2023-11-01T04:04:59.997Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/61/095a0aa1a84d1481998b534177c8566fdc50bb1233ea9a0478cd3cc075bd/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", size = 194219 }, - { url = "https://files.pythonhosted.org/packages/cc/94/f7cf5e5134175de79ad2059edf2adce18e0685ebdb9227ff0139975d0e93/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", size = 122521 }, - { url = "https://files.pythonhosted.org/packages/46/6a/d5c26c41c49b546860cc1acabdddf48b0b3fb2685f4f5617ac59261b44ae/charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", size = 120383 }, - { url = "https://files.pythonhosted.org/packages/b8/60/e2f67915a51be59d4539ed189eb0a2b0d292bf79270410746becb32bc2c3/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", size = 138223 }, - { url = "https://files.pythonhosted.org/packages/05/8c/eb854996d5fef5e4f33ad56927ad053d04dc820e4a3d39023f35cad72617/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", size = 148101 }, - { url = "https://files.pythonhosted.org/packages/f6/93/bb6cbeec3bf9da9b2eba458c15966658d1daa8b982c642f81c93ad9b40e1/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", size = 140699 }, - { url = "https://files.pythonhosted.org/packages/da/f1/3702ba2a7470666a62fd81c58a4c40be00670e5006a67f4d626e57f013ae/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", size = 142065 }, - { url = "https://files.pythonhosted.org/packages/3f/ba/3f5e7be00b215fa10e13d64b1f6237eb6ebea66676a41b2bcdd09fe74323/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", size = 144505 }, - { url = "https://files.pythonhosted.org/packages/33/c3/3b96a435c5109dd5b6adc8a59ba1d678b302a97938f032e3770cc84cd354/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", size = 139425 }, - { url = "https://files.pythonhosted.org/packages/43/05/3bf613e719efe68fb3a77f9c536a389f35b95d75424b96b426a47a45ef1d/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", size = 145287 }, - { url = "https://files.pythonhosted.org/packages/58/78/a0bc646900994df12e07b4ae5c713f2b3e5998f58b9d3720cce2aa45652f/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", size = 149929 }, - { url = "https://files.pythonhosted.org/packages/eb/5c/97d97248af4920bc68687d9c3b3c0f47c910e21a8ff80af4565a576bd2f0/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", size = 141605 }, - { url = "https://files.pythonhosted.org/packages/a8/31/47d018ef89f95b8aded95c589a77c072c55e94b50a41aa99c0a2008a45a4/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", size = 142646 }, - { url = "https://files.pythonhosted.org/packages/ae/d5/4fecf1d58bedb1340a50f165ba1c7ddc0400252d6832ff619c4568b36cc0/charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", size = 92846 }, - { url = "https://files.pythonhosted.org/packages/a2/a0/4af29e22cb5942488cf45630cbdd7cefd908768e69bdd90280842e4e8529/charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", size = 100343 }, - { url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647 }, - { url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434 }, - { url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979 }, - { url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582 }, - { url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645 }, - { url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398 }, - { url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273 }, - { url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577 }, - { url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747 }, - { url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375 }, - { url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474 }, - { url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232 }, - { url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859 }, - { url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509 }, - { url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870 }, - { url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892 }, - { url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213 }, - { url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404 }, - { url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 }, - { url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 }, - { url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 }, - { url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869 }, - { url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042 }, - { url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275 }, - { url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819 }, - { url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 }, - { url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 }, - { url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 }, - { url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 }, - { url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 }, - { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 }, + { url = "https://files.pythonhosted.org/packages/2b/61/095a0aa1a84d1481998b534177c8566fdc50bb1233ea9a0478cd3cc075bd/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", size = 194219, upload_time = "2023-11-01T04:02:29.048Z" }, + { url = "https://files.pythonhosted.org/packages/cc/94/f7cf5e5134175de79ad2059edf2adce18e0685ebdb9227ff0139975d0e93/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", size = 122521, upload_time = "2023-11-01T04:02:32.452Z" }, + { url = "https://files.pythonhosted.org/packages/46/6a/d5c26c41c49b546860cc1acabdddf48b0b3fb2685f4f5617ac59261b44ae/charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", size = 120383, upload_time = "2023-11-01T04:02:34.11Z" }, + { url = "https://files.pythonhosted.org/packages/b8/60/e2f67915a51be59d4539ed189eb0a2b0d292bf79270410746becb32bc2c3/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", size = 138223, upload_time = "2023-11-01T04:02:36.213Z" }, + { url = "https://files.pythonhosted.org/packages/05/8c/eb854996d5fef5e4f33ad56927ad053d04dc820e4a3d39023f35cad72617/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", size = 148101, upload_time = "2023-11-01T04:02:38.067Z" }, + { url = "https://files.pythonhosted.org/packages/f6/93/bb6cbeec3bf9da9b2eba458c15966658d1daa8b982c642f81c93ad9b40e1/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", size = 140699, upload_time = "2023-11-01T04:02:39.436Z" }, + { url = "https://files.pythonhosted.org/packages/da/f1/3702ba2a7470666a62fd81c58a4c40be00670e5006a67f4d626e57f013ae/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", size = 142065, upload_time = "2023-11-01T04:02:41.357Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ba/3f5e7be00b215fa10e13d64b1f6237eb6ebea66676a41b2bcdd09fe74323/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", size = 144505, upload_time = "2023-11-01T04:02:43.108Z" }, + { url = "https://files.pythonhosted.org/packages/33/c3/3b96a435c5109dd5b6adc8a59ba1d678b302a97938f032e3770cc84cd354/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", size = 139425, upload_time = "2023-11-01T04:02:45.427Z" }, + { url = "https://files.pythonhosted.org/packages/43/05/3bf613e719efe68fb3a77f9c536a389f35b95d75424b96b426a47a45ef1d/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", size = 145287, upload_time = "2023-11-01T04:02:46.705Z" }, + { url = "https://files.pythonhosted.org/packages/58/78/a0bc646900994df12e07b4ae5c713f2b3e5998f58b9d3720cce2aa45652f/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", size = 149929, upload_time = "2023-11-01T04:02:48.098Z" }, + { url = "https://files.pythonhosted.org/packages/eb/5c/97d97248af4920bc68687d9c3b3c0f47c910e21a8ff80af4565a576bd2f0/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", size = 141605, upload_time = "2023-11-01T04:02:49.605Z" }, + { url = "https://files.pythonhosted.org/packages/a8/31/47d018ef89f95b8aded95c589a77c072c55e94b50a41aa99c0a2008a45a4/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", size = 142646, upload_time = "2023-11-01T04:02:51.35Z" }, + { url = "https://files.pythonhosted.org/packages/ae/d5/4fecf1d58bedb1340a50f165ba1c7ddc0400252d6832ff619c4568b36cc0/charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", size = 92846, upload_time = "2023-11-01T04:02:52.679Z" }, + { url = "https://files.pythonhosted.org/packages/a2/a0/4af29e22cb5942488cf45630cbdd7cefd908768e69bdd90280842e4e8529/charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", size = 100343, upload_time = "2023-11-01T04:02:53.915Z" }, + { url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647, upload_time = "2023-11-01T04:02:55.329Z" }, + { url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434, upload_time = "2023-11-01T04:02:57.173Z" }, + { url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979, upload_time = "2023-11-01T04:02:58.442Z" }, + { url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582, upload_time = "2023-11-01T04:02:59.776Z" }, + { url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645, upload_time = "2023-11-01T04:03:02.186Z" }, + { url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398, upload_time = "2023-11-01T04:03:04.255Z" }, + { url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273, upload_time = "2023-11-01T04:03:05.983Z" }, + { url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577, upload_time = "2023-11-01T04:03:07.567Z" }, + { url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747, upload_time = "2023-11-01T04:03:08.886Z" }, + { url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375, upload_time = "2023-11-01T04:03:10.613Z" }, + { url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474, upload_time = "2023-11-01T04:03:11.973Z" }, + { url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232, upload_time = "2023-11-01T04:03:13.505Z" }, + { url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859, upload_time = "2023-11-01T04:03:17.362Z" }, + { url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509, upload_time = "2023-11-01T04:03:21.453Z" }, + { url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870, upload_time = "2023-11-01T04:03:22.723Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892, upload_time = "2023-11-01T04:03:24.135Z" }, + { url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213, upload_time = "2023-11-01T04:03:25.66Z" }, + { url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404, upload_time = "2023-11-01T04:03:27.04Z" }, + { url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275, upload_time = "2023-11-01T04:03:28.466Z" }, + { url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518, upload_time = "2023-11-01T04:03:29.82Z" }, + { url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182, upload_time = "2023-11-01T04:03:31.511Z" }, + { url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869, upload_time = "2023-11-01T04:03:32.887Z" }, + { url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042, upload_time = "2023-11-01T04:03:34.412Z" }, + { url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275, upload_time = "2023-11-01T04:03:35.759Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819, upload_time = "2023-11-01T04:03:37.216Z" }, + { url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415, upload_time = "2023-11-01T04:03:38.694Z" }, + { url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212, upload_time = "2023-11-01T04:03:40.07Z" }, + { url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167, upload_time = "2023-11-01T04:03:41.491Z" }, + { url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041, upload_time = "2023-11-01T04:03:42.836Z" }, + { url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397, upload_time = "2023-11-01T04:03:44.467Z" }, + { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543, upload_time = "2023-11-01T04:04:58.622Z" }, ] [[package]] @@ -309,18 +318,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } +sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121, upload_time = "2023-08-17T17:29:11.868Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 }, + { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941, upload_time = "2023-08-17T17:29:10.08Z" }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload_time = "2022-10-25T02:36:22.414Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload_time = "2022-10-25T02:36:20.889Z" }, ] [[package]] @@ -330,18 +339,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "humanfriendly" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520 } +sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520, upload_time = "2021-06-11T10:22:45.202Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018 }, + { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018, upload_time = "2021-06-11T10:22:42.561Z" }, ] [[package]] name = "configargparse" version = "1.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/70/8a/73f1008adfad01cb923255b924b1528727b8270e67cb4ef41eabdc7d783e/ConfigArgParse-1.7.tar.gz", hash = "sha256:e7067471884de5478c58a511e529f0f9bd1c66bfef1dea90935438d6c23306d1", size = 43817 } +sdist = { url = "https://files.pythonhosted.org/packages/70/8a/73f1008adfad01cb923255b924b1528727b8270e67cb4ef41eabdc7d783e/ConfigArgParse-1.7.tar.gz", hash = "sha256:e7067471884de5478c58a511e529f0f9bd1c66bfef1dea90935438d6c23306d1", size = 43817, upload_time = "2023-07-23T16:20:04.95Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/b3/b4ac838711fd74a2b4e6f746703cf9dd2cf5462d17dac07e349234e21b97/ConfigArgParse-1.7-py3-none-any.whl", hash = "sha256:d249da6591465c6c26df64a9f73d2536e743be2f244eb3ebe61114af2f94f86b", size = 25489 }, + { url = "https://files.pythonhosted.org/packages/6f/b3/b4ac838711fd74a2b4e6f746703cf9dd2cf5462d17dac07e349234e21b97/ConfigArgParse-1.7-py3-none-any.whl", hash = "sha256:d249da6591465c6c26df64a9f73d2536e743be2f244eb3ebe61114af2f94f86b", size = 25489, upload_time = "2023-07-23T16:20:03.27Z" }, ] [[package]] @@ -351,97 +360,97 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/11/a3/48ddc7ae832b000952cf4be64452381d150a41a2299c2eb19237168528d1/contourpy-1.2.0.tar.gz", hash = "sha256:171f311cb758de7da13fc53af221ae47a5877be5a0843a9fe150818c51ed276a", size = 13455881 } +sdist = { url = "https://files.pythonhosted.org/packages/11/a3/48ddc7ae832b000952cf4be64452381d150a41a2299c2eb19237168528d1/contourpy-1.2.0.tar.gz", hash = "sha256:171f311cb758de7da13fc53af221ae47a5877be5a0843a9fe150818c51ed276a", size = 13455881, upload_time = "2023-11-03T17:01:03.144Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/ea/f6e90933d82cc5aacf52f886a1c01f47f96eba99108ca2929c7b3ef45f82/contourpy-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0274c1cb63625972c0c007ab14dd9ba9e199c36ae1a231ce45d725cbcbfd10a8", size = 256873 }, - { url = "https://files.pythonhosted.org/packages/fe/26/43821d61b7ee62c1809ec852bc572aaf4c27f101ebcebbbcce29a5ee0445/contourpy-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab459a1cbbf18e8698399c595a01f6dcc5c138220ca3ea9e7e6126232d102bb4", size = 242211 }, - { url = "https://files.pythonhosted.org/packages/9b/99/c8fb63072a7573fe7682e1786a021f29f9c5f660a3aafcdce80b9ee8348d/contourpy-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fdd887f17c2f4572ce548461e4f96396681212d858cae7bd52ba3310bc6f00f", size = 293195 }, - { url = "https://files.pythonhosted.org/packages/c7/a7/ae0b4bb8e0c865270d02ee619981413996dc10ddf1fd2689c938173ff62f/contourpy-1.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d16edfc3fc09968e09ddffada434b3bf989bf4911535e04eada58469873e28e", size = 332279 }, - { url = "https://files.pythonhosted.org/packages/94/7c/682228b9085ff323fb7e946fe139072e5f21b71360cf91f36ea079d4ea95/contourpy-1.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c203f617abc0dde5792beb586f827021069fb6d403d7f4d5c2b543d87edceb9", size = 305326 }, - { url = "https://files.pythonhosted.org/packages/58/56/e2c43dcfa1f9c7db4d5e3d6f5134b24ed953f4e2133a4b12f0062148db58/contourpy-1.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b69303ceb2e4d4f146bf82fda78891ef7bcd80c41bf16bfca3d0d7eb545448aa", size = 310732 }, - { url = "https://files.pythonhosted.org/packages/94/0b/8495c4582057abc8377f945f6e11a86f1c07ad7b32fd4fdc968478cd0324/contourpy-1.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:884c3f9d42d7218304bc74a8a7693d172685c84bd7ab2bab1ee567b769696df9", size = 803420 }, - { url = "https://files.pythonhosted.org/packages/d5/1f/40399c7da649297147d404aedaa675cc60018f48ad284630c0d1406133e3/contourpy-1.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4a1b1208102be6e851f20066bf0e7a96b7d48a07c9b0cfe6d0d4545c2f6cadab", size = 829204 }, - { url = "https://files.pythonhosted.org/packages/8b/01/4be433b60dce7cbce8315cbcdfc016e7d25430a8b94e272355dff79cc3a8/contourpy-1.2.0-cp310-cp310-win32.whl", hash = "sha256:34b9071c040d6fe45d9826cbbe3727d20d83f1b6110d219b83eb0e2a01d79488", size = 165434 }, - { url = "https://files.pythonhosted.org/packages/fd/7c/168f8343f33d861305e18c56901ef1bb675d3c7f977f435ec72751a71a54/contourpy-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:bd2f1ae63998da104f16a8b788f685e55d65760cd1929518fd94cd682bf03e41", size = 186652 }, - { url = "https://files.pythonhosted.org/packages/9b/54/1dafec3c84df1d29119037330f7289db84a679cb2d5283af4ef24d89f532/contourpy-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd10c26b4eadae44783c45ad6655220426f971c61d9b239e6f7b16d5cdaaa727", size = 258243 }, - { url = "https://files.pythonhosted.org/packages/5b/ac/26fa1057f62beaa2af4c55c6ac733b114a403b746cfe0ce3dc6e4aec921a/contourpy-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c6b28956b7b232ae801406e529ad7b350d3f09a4fde958dfdf3c0520cdde0dd", size = 243408 }, - { url = "https://files.pythonhosted.org/packages/b7/33/cd0ecc80123f499d76d2fe2807cb4d5638ef8730735c580c8a8a03e1928e/contourpy-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebeac59e9e1eb4b84940d076d9f9a6cec0064e241818bcb6e32124cc5c3e377a", size = 294142 }, - { url = "https://files.pythonhosted.org/packages/6d/75/1b7bf20bf6394e01df2c4b4b3d44d3dc280c16ddaff72724639100bd4314/contourpy-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:139d8d2e1c1dd52d78682f505e980f592ba53c9f73bd6be102233e358b401063", size = 333129 }, - { url = "https://files.pythonhosted.org/packages/22/5b/fedd961dff1877e5d3b83c5201295cfdcdc2438884c2851aa7ecf6cec045/contourpy-1.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e9dc350fb4c58adc64df3e0703ab076f60aac06e67d48b3848c23647ae4310e", size = 307461 }, - { url = "https://files.pythonhosted.org/packages/e2/83/29a63bbc72839cc6b24b5a0e3d004d4ed4e8439f26460ad9a34e39251904/contourpy-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18fc2b4ed8e4a8fe849d18dce4bd3c7ea637758c6343a1f2bae1e9bd4c9f4686", size = 313352 }, - { url = "https://files.pythonhosted.org/packages/4b/c7/4bac0fc4f1e802ab47e75076d83d2e1448e0668ba6cc9000cf4e9d5bd94a/contourpy-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:16a7380e943a6d52472096cb7ad5264ecee36ed60888e2a3d3814991a0107286", size = 804127 }, - { url = "https://files.pythonhosted.org/packages/e3/47/b3fd5bdc2f6ec13502d57a5bc390ffe62648605ed1689c93b0015150a784/contourpy-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8d8faf05be5ec8e02a4d86f616fc2a0322ff4a4ce26c0f09d9f7fb5330a35c95", size = 829561 }, - { url = "https://files.pythonhosted.org/packages/5c/04/be16038e754169caea4d02d82f8e5cd97dece593e5ac9e05735da0afd0c5/contourpy-1.2.0-cp311-cp311-win32.whl", hash = "sha256:67b7f17679fa62ec82b7e3e611c43a016b887bd64fb933b3ae8638583006c6d6", size = 166197 }, - { url = "https://files.pythonhosted.org/packages/ca/2a/d197a412ec474391ee878b1218cf2fe9c6e963903755887fc5654c06636a/contourpy-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:99ad97258985328b4f207a5e777c1b44a83bfe7cf1f87b99f9c11d4ee477c4de", size = 187556 }, - { url = "https://files.pythonhosted.org/packages/4f/03/839da46999173226bead08794cbd7b4d37c9e6b02686ca74c93556b43258/contourpy-1.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:575bcaf957a25d1194903a10bc9f316c136c19f24e0985a2b9b5608bdf5dbfe0", size = 259253 }, - { url = "https://files.pythonhosted.org/packages/f3/9e/8fb3f53144269d3fecdd8786d3a4686eeff55b9b35a3c0772a3f62f71e36/contourpy-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9e6c93b5b2dbcedad20a2f18ec22cae47da0d705d454308063421a3b290d9ea4", size = 242555 }, - { url = "https://files.pythonhosted.org/packages/a6/85/9815ccb5a18ee8c9a46bd5ef20d02b292cd4a99c62553f38c87015f16d59/contourpy-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:464b423bc2a009088f19bdf1f232299e8b6917963e2b7e1d277da5041f33a779", size = 288108 }, - { url = "https://files.pythonhosted.org/packages/5a/d9/4df5c26bd0f496c8cd7940fd53db95d07deeb98518f02f805ce570590da8/contourpy-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68ce4788b7d93e47f84edd3f1f95acdcd142ae60bc0e5493bfd120683d2d4316", size = 330810 }, - { url = "https://files.pythonhosted.org/packages/67/d4/8aae9793a0cfde72959312521ebd3aa635c260c3d580448e8db6bdcdd1aa/contourpy-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d7d1f8871998cdff5d2ff6a087e5e1780139abe2838e85b0b46b7ae6cc25399", size = 305290 }, - { url = "https://files.pythonhosted.org/packages/20/84/ffddcdcc579cbf7213fd92a3578ca08a931a3bf879a22deb5a83ffc5002c/contourpy-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e739530c662a8d6d42c37c2ed52a6f0932c2d4a3e8c1f90692ad0ce1274abe0", size = 303937 }, - { url = "https://files.pythonhosted.org/packages/d8/ad/6e570cf525f909da94559ed716189f92f529bc7b5f78645733c44619a0e2/contourpy-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:247b9d16535acaa766d03037d8e8fb20866d054d3c7fbf6fd1f993f11fc60ca0", size = 801977 }, - { url = "https://files.pythonhosted.org/packages/36/b4/55f23482c596eca36d16fc668b147865c56fcf90353f4c57f073d8d5e532/contourpy-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:461e3ae84cd90b30f8d533f07d87c00379644205b1d33a5ea03381edc4b69431", size = 827442 }, - { url = "https://files.pythonhosted.org/packages/e9/47/9c081b1f11d6053cb0aa4c46b7de2ea2849a4a8d40de81c7bc3f99773b02/contourpy-1.2.0-cp312-cp312-win32.whl", hash = "sha256:1c2559d6cffc94890b0529ea7eeecc20d6fadc1539273aa27faf503eb4656d8f", size = 165363 }, - { url = "https://files.pythonhosted.org/packages/8e/ae/a6353db548bff1a592b85ae6bb80275f0a51dc25a0410d059e5b33183e36/contourpy-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:491b1917afdd8638a05b611a56d46587d5a632cabead889a5440f7c638bc6ed9", size = 187731 }, + { url = "https://files.pythonhosted.org/packages/e8/ea/f6e90933d82cc5aacf52f886a1c01f47f96eba99108ca2929c7b3ef45f82/contourpy-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0274c1cb63625972c0c007ab14dd9ba9e199c36ae1a231ce45d725cbcbfd10a8", size = 256873, upload_time = "2023-11-03T16:56:34.548Z" }, + { url = "https://files.pythonhosted.org/packages/fe/26/43821d61b7ee62c1809ec852bc572aaf4c27f101ebcebbbcce29a5ee0445/contourpy-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab459a1cbbf18e8698399c595a01f6dcc5c138220ca3ea9e7e6126232d102bb4", size = 242211, upload_time = "2023-11-03T16:56:38.028Z" }, + { url = "https://files.pythonhosted.org/packages/9b/99/c8fb63072a7573fe7682e1786a021f29f9c5f660a3aafcdce80b9ee8348d/contourpy-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fdd887f17c2f4572ce548461e4f96396681212d858cae7bd52ba3310bc6f00f", size = 293195, upload_time = "2023-11-03T16:56:41.598Z" }, + { url = "https://files.pythonhosted.org/packages/c7/a7/ae0b4bb8e0c865270d02ee619981413996dc10ddf1fd2689c938173ff62f/contourpy-1.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d16edfc3fc09968e09ddffada434b3bf989bf4911535e04eada58469873e28e", size = 332279, upload_time = "2023-11-03T16:56:46.08Z" }, + { url = "https://files.pythonhosted.org/packages/94/7c/682228b9085ff323fb7e946fe139072e5f21b71360cf91f36ea079d4ea95/contourpy-1.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c203f617abc0dde5792beb586f827021069fb6d403d7f4d5c2b543d87edceb9", size = 305326, upload_time = "2023-11-03T16:56:49.647Z" }, + { url = "https://files.pythonhosted.org/packages/58/56/e2c43dcfa1f9c7db4d5e3d6f5134b24ed953f4e2133a4b12f0062148db58/contourpy-1.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b69303ceb2e4d4f146bf82fda78891ef7bcd80c41bf16bfca3d0d7eb545448aa", size = 310732, upload_time = "2023-11-03T16:56:53.773Z" }, + { url = "https://files.pythonhosted.org/packages/94/0b/8495c4582057abc8377f945f6e11a86f1c07ad7b32fd4fdc968478cd0324/contourpy-1.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:884c3f9d42d7218304bc74a8a7693d172685c84bd7ab2bab1ee567b769696df9", size = 803420, upload_time = "2023-11-03T16:57:00.669Z" }, + { url = "https://files.pythonhosted.org/packages/d5/1f/40399c7da649297147d404aedaa675cc60018f48ad284630c0d1406133e3/contourpy-1.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4a1b1208102be6e851f20066bf0e7a96b7d48a07c9b0cfe6d0d4545c2f6cadab", size = 829204, upload_time = "2023-11-03T16:57:07.813Z" }, + { url = "https://files.pythonhosted.org/packages/8b/01/4be433b60dce7cbce8315cbcdfc016e7d25430a8b94e272355dff79cc3a8/contourpy-1.2.0-cp310-cp310-win32.whl", hash = "sha256:34b9071c040d6fe45d9826cbbe3727d20d83f1b6110d219b83eb0e2a01d79488", size = 165434, upload_time = "2023-11-03T16:57:10.601Z" }, + { url = "https://files.pythonhosted.org/packages/fd/7c/168f8343f33d861305e18c56901ef1bb675d3c7f977f435ec72751a71a54/contourpy-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:bd2f1ae63998da104f16a8b788f685e55d65760cd1929518fd94cd682bf03e41", size = 186652, upload_time = "2023-11-03T16:57:13.57Z" }, + { url = "https://files.pythonhosted.org/packages/9b/54/1dafec3c84df1d29119037330f7289db84a679cb2d5283af4ef24d89f532/contourpy-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd10c26b4eadae44783c45ad6655220426f971c61d9b239e6f7b16d5cdaaa727", size = 258243, upload_time = "2023-11-03T16:57:16.604Z" }, + { url = "https://files.pythonhosted.org/packages/5b/ac/26fa1057f62beaa2af4c55c6ac733b114a403b746cfe0ce3dc6e4aec921a/contourpy-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c6b28956b7b232ae801406e529ad7b350d3f09a4fde958dfdf3c0520cdde0dd", size = 243408, upload_time = "2023-11-03T16:57:20.021Z" }, + { url = "https://files.pythonhosted.org/packages/b7/33/cd0ecc80123f499d76d2fe2807cb4d5638ef8730735c580c8a8a03e1928e/contourpy-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebeac59e9e1eb4b84940d076d9f9a6cec0064e241818bcb6e32124cc5c3e377a", size = 294142, upload_time = "2023-11-03T16:57:23.48Z" }, + { url = "https://files.pythonhosted.org/packages/6d/75/1b7bf20bf6394e01df2c4b4b3d44d3dc280c16ddaff72724639100bd4314/contourpy-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:139d8d2e1c1dd52d78682f505e980f592ba53c9f73bd6be102233e358b401063", size = 333129, upload_time = "2023-11-03T16:57:27.141Z" }, + { url = "https://files.pythonhosted.org/packages/22/5b/fedd961dff1877e5d3b83c5201295cfdcdc2438884c2851aa7ecf6cec045/contourpy-1.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e9dc350fb4c58adc64df3e0703ab076f60aac06e67d48b3848c23647ae4310e", size = 307461, upload_time = "2023-11-03T16:57:30.537Z" }, + { url = "https://files.pythonhosted.org/packages/e2/83/29a63bbc72839cc6b24b5a0e3d004d4ed4e8439f26460ad9a34e39251904/contourpy-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18fc2b4ed8e4a8fe849d18dce4bd3c7ea637758c6343a1f2bae1e9bd4c9f4686", size = 313352, upload_time = "2023-11-03T16:57:34.937Z" }, + { url = "https://files.pythonhosted.org/packages/4b/c7/4bac0fc4f1e802ab47e75076d83d2e1448e0668ba6cc9000cf4e9d5bd94a/contourpy-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:16a7380e943a6d52472096cb7ad5264ecee36ed60888e2a3d3814991a0107286", size = 804127, upload_time = "2023-11-03T16:57:42.201Z" }, + { url = "https://files.pythonhosted.org/packages/e3/47/b3fd5bdc2f6ec13502d57a5bc390ffe62648605ed1689c93b0015150a784/contourpy-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8d8faf05be5ec8e02a4d86f616fc2a0322ff4a4ce26c0f09d9f7fb5330a35c95", size = 829561, upload_time = "2023-11-03T16:57:49.667Z" }, + { url = "https://files.pythonhosted.org/packages/5c/04/be16038e754169caea4d02d82f8e5cd97dece593e5ac9e05735da0afd0c5/contourpy-1.2.0-cp311-cp311-win32.whl", hash = "sha256:67b7f17679fa62ec82b7e3e611c43a016b887bd64fb933b3ae8638583006c6d6", size = 166197, upload_time = "2023-11-03T16:57:52.682Z" }, + { url = "https://files.pythonhosted.org/packages/ca/2a/d197a412ec474391ee878b1218cf2fe9c6e963903755887fc5654c06636a/contourpy-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:99ad97258985328b4f207a5e777c1b44a83bfe7cf1f87b99f9c11d4ee477c4de", size = 187556, upload_time = "2023-11-03T16:57:55.286Z" }, + { url = "https://files.pythonhosted.org/packages/4f/03/839da46999173226bead08794cbd7b4d37c9e6b02686ca74c93556b43258/contourpy-1.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:575bcaf957a25d1194903a10bc9f316c136c19f24e0985a2b9b5608bdf5dbfe0", size = 259253, upload_time = "2023-11-03T16:57:58.572Z" }, + { url = "https://files.pythonhosted.org/packages/f3/9e/8fb3f53144269d3fecdd8786d3a4686eeff55b9b35a3c0772a3f62f71e36/contourpy-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9e6c93b5b2dbcedad20a2f18ec22cae47da0d705d454308063421a3b290d9ea4", size = 242555, upload_time = "2023-11-03T16:58:01.48Z" }, + { url = "https://files.pythonhosted.org/packages/a6/85/9815ccb5a18ee8c9a46bd5ef20d02b292cd4a99c62553f38c87015f16d59/contourpy-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:464b423bc2a009088f19bdf1f232299e8b6917963e2b7e1d277da5041f33a779", size = 288108, upload_time = "2023-11-03T16:58:05.546Z" }, + { url = "https://files.pythonhosted.org/packages/5a/d9/4df5c26bd0f496c8cd7940fd53db95d07deeb98518f02f805ce570590da8/contourpy-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68ce4788b7d93e47f84edd3f1f95acdcd142ae60bc0e5493bfd120683d2d4316", size = 330810, upload_time = "2023-11-03T16:58:09.568Z" }, + { url = "https://files.pythonhosted.org/packages/67/d4/8aae9793a0cfde72959312521ebd3aa635c260c3d580448e8db6bdcdd1aa/contourpy-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d7d1f8871998cdff5d2ff6a087e5e1780139abe2838e85b0b46b7ae6cc25399", size = 305290, upload_time = "2023-11-03T16:58:13.017Z" }, + { url = "https://files.pythonhosted.org/packages/20/84/ffddcdcc579cbf7213fd92a3578ca08a931a3bf879a22deb5a83ffc5002c/contourpy-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e739530c662a8d6d42c37c2ed52a6f0932c2d4a3e8c1f90692ad0ce1274abe0", size = 303937, upload_time = "2023-11-03T16:58:16.426Z" }, + { url = "https://files.pythonhosted.org/packages/d8/ad/6e570cf525f909da94559ed716189f92f529bc7b5f78645733c44619a0e2/contourpy-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:247b9d16535acaa766d03037d8e8fb20866d054d3c7fbf6fd1f993f11fc60ca0", size = 801977, upload_time = "2023-11-03T16:58:23.539Z" }, + { url = "https://files.pythonhosted.org/packages/36/b4/55f23482c596eca36d16fc668b147865c56fcf90353f4c57f073d8d5e532/contourpy-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:461e3ae84cd90b30f8d533f07d87c00379644205b1d33a5ea03381edc4b69431", size = 827442, upload_time = "2023-11-03T16:58:30.724Z" }, + { url = "https://files.pythonhosted.org/packages/e9/47/9c081b1f11d6053cb0aa4c46b7de2ea2849a4a8d40de81c7bc3f99773b02/contourpy-1.2.0-cp312-cp312-win32.whl", hash = "sha256:1c2559d6cffc94890b0529ea7eeecc20d6fadc1539273aa27faf503eb4656d8f", size = 165363, upload_time = "2023-11-03T16:58:33.54Z" }, + { url = "https://files.pythonhosted.org/packages/8e/ae/a6353db548bff1a592b85ae6bb80275f0a51dc25a0410d059e5b33183e36/contourpy-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:491b1917afdd8638a05b611a56d46587d5a632cabead889a5440f7c638bc6ed9", size = 187731, upload_time = "2023-11-03T16:58:36.585Z" }, ] [[package]] name = "coverage" version = "7.6.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/12/3669b6382792783e92046730ad3327f53b2726f0603f4c311c4da4824222/coverage-7.6.4.tar.gz", hash = "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73", size = 798716 } +sdist = { url = "https://files.pythonhosted.org/packages/52/12/3669b6382792783e92046730ad3327f53b2726f0603f4c311c4da4824222/coverage-7.6.4.tar.gz", hash = "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73", size = 798716, upload_time = "2024-10-20T22:57:39.682Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/93/4ad92f71e28ece5c0326e5f4a6630aa4928a8846654a65cfff69b49b95b9/coverage-7.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07", size = 206713 }, - { url = "https://files.pythonhosted.org/packages/01/ae/747a580b1eda3f2e431d87de48f0604bd7bc92e52a1a95185a4aa585bc47/coverage-7.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0", size = 207149 }, - { url = "https://files.pythonhosted.org/packages/07/1a/1f573f8a6145f6d4c9130bbc120e0024daf1b24cf2a78d7393fa6eb6aba7/coverage-7.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72", size = 235584 }, - { url = "https://files.pythonhosted.org/packages/40/42/c8523f2e4db34aa9389caee0d3688b6ada7a84fcc782e943a868a7f302bd/coverage-7.6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51", size = 233486 }, - { url = "https://files.pythonhosted.org/packages/8d/95/565c310fffa16ede1a042e9ea1ca3962af0d8eb5543bc72df6b91dc0c3d5/coverage-7.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491", size = 234649 }, - { url = "https://files.pythonhosted.org/packages/d5/81/3b550674d98968ec29c92e3e8650682be6c8b1fa7581a059e7e12e74c431/coverage-7.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b", size = 233744 }, - { url = "https://files.pythonhosted.org/packages/0d/70/d66c7f51b3e33aabc5ea9f9624c1c9d9655472962270eb5e7b0d32707224/coverage-7.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea", size = 232204 }, - { url = "https://files.pythonhosted.org/packages/23/2d/2b3a2dbed7a5f40693404c8a09e779d7c1a5fbed089d3e7224c002129ec8/coverage-7.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a", size = 233335 }, - { url = "https://files.pythonhosted.org/packages/5a/4f/92d1d2ad720d698a4e71c176eacf531bfb8e0721d5ad560556f2c484a513/coverage-7.6.4-cp310-cp310-win32.whl", hash = "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa", size = 209435 }, - { url = "https://files.pythonhosted.org/packages/c7/b9/cdf158e7991e2287bcf9082670928badb73d310047facac203ff8dcd5ff3/coverage-7.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172", size = 210243 }, - { url = "https://files.pythonhosted.org/packages/87/31/9c0cf84f0dfcbe4215b7eb95c31777cdc0483c13390e69584c8150c85175/coverage-7.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b", size = 206819 }, - { url = "https://files.pythonhosted.org/packages/53/ed/a38401079ad320ad6e054a01ec2b61d270511aeb3c201c80e99c841229d5/coverage-7.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25", size = 207263 }, - { url = "https://files.pythonhosted.org/packages/20/e7/c3ad33b179ab4213f0d70da25a9c214d52464efa11caeab438592eb1d837/coverage-7.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546", size = 239205 }, - { url = "https://files.pythonhosted.org/packages/36/91/fc02e8d8e694f557752120487fd982f654ba1421bbaa5560debf96ddceda/coverage-7.6.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b", size = 236612 }, - { url = "https://files.pythonhosted.org/packages/cc/57/cb08f0eda0389a9a8aaa4fc1f9fec7ac361c3e2d68efd5890d7042c18aa3/coverage-7.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e", size = 238479 }, - { url = "https://files.pythonhosted.org/packages/d5/c9/2c7681a9b3ca6e6f43d489c2e6653a53278ed857fd6e7010490c307b0a47/coverage-7.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718", size = 237405 }, - { url = "https://files.pythonhosted.org/packages/b5/4e/ebfc6944b96317df8b537ae875d2e57c27b84eb98820bc0a1055f358f056/coverage-7.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db", size = 236038 }, - { url = "https://files.pythonhosted.org/packages/13/f2/3a0bf1841a97c0654905e2ef531170f02c89fad2555879db8fe41a097871/coverage-7.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522", size = 236812 }, - { url = "https://files.pythonhosted.org/packages/b9/9c/66bf59226b52ce6ed9541b02d33e80a6e816a832558fbdc1111a7bd3abd4/coverage-7.6.4-cp311-cp311-win32.whl", hash = "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf", size = 209400 }, - { url = "https://files.pythonhosted.org/packages/2a/a0/b0790934c04dfc8d658d4a62acb8f7ca0efdf3818456fcad757b11c6479d/coverage-7.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19", size = 210243 }, - { url = "https://files.pythonhosted.org/packages/7d/e7/9291de916d084f41adddfd4b82246e68d61d6a75747f075f7e64628998d2/coverage-7.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2", size = 207013 }, - { url = "https://files.pythonhosted.org/packages/27/03/932c2c5717a7fa80cd43c6a07d3177076d97b79f12f40f882f9916db0063/coverage-7.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117", size = 207251 }, - { url = "https://files.pythonhosted.org/packages/d5/3f/0af47dcb9327f65a45455fbca846fe96eb57c153af46c4754a3ba678938a/coverage-7.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613", size = 240268 }, - { url = "https://files.pythonhosted.org/packages/8a/3c/37a9d81bbd4b23bc7d46ca820e16174c613579c66342faa390a271d2e18b/coverage-7.6.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27", size = 237298 }, - { url = "https://files.pythonhosted.org/packages/c0/70/6b0627e5bd68204ee580126ed3513140b2298995c1233bd67404b4e44d0e/coverage-7.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52", size = 239367 }, - { url = "https://files.pythonhosted.org/packages/3c/eb/634d7dfab24ac3b790bebaf9da0f4a5352cbc125ce6a9d5c6cf4c6cae3c7/coverage-7.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2", size = 238853 }, - { url = "https://files.pythonhosted.org/packages/d9/0d/8e3ed00f1266ef7472a4e33458f42e39492e01a64281084fb3043553d3f1/coverage-7.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1", size = 237160 }, - { url = "https://files.pythonhosted.org/packages/ce/9c/4337f468ef0ab7a2e0887a9c9da0e58e2eada6fc6cbee637a4acd5dfd8a9/coverage-7.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5", size = 238824 }, - { url = "https://files.pythonhosted.org/packages/5e/09/3e94912b8dd37251377bb02727a33a67ee96b84bbbe092f132b401ca5dd9/coverage-7.6.4-cp312-cp312-win32.whl", hash = "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17", size = 209639 }, - { url = "https://files.pythonhosted.org/packages/01/69/d4f3a4101171f32bc5b3caec8ff94c2c60f700107a6aaef7244b2c166793/coverage-7.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08", size = 210428 }, - { url = "https://files.pythonhosted.org/packages/c2/4d/2dede4f7cb5a70fb0bb40a57627fddf1dbdc6b9c1db81f7c4dcdcb19e2f4/coverage-7.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9", size = 207039 }, - { url = "https://files.pythonhosted.org/packages/3f/f9/d86368ae8c79e28f1fb458ebc76ae9ff3e8bd8069adc24e8f2fed03c58b7/coverage-7.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba", size = 207298 }, - { url = "https://files.pythonhosted.org/packages/64/c5/b4cc3c3f64622c58fbfd4d8b9a7a8ce9d355f172f91fcabbba1f026852f6/coverage-7.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c", size = 239813 }, - { url = "https://files.pythonhosted.org/packages/8a/86/14c42e60b70a79b26099e4d289ccdfefbc68624d096f4481163085aa614c/coverage-7.6.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06", size = 236959 }, - { url = "https://files.pythonhosted.org/packages/7f/f8/4436a643631a2fbab4b44d54f515028f6099bfb1cd95b13cfbf701e7f2f2/coverage-7.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f", size = 238950 }, - { url = "https://files.pythonhosted.org/packages/49/50/1571810ddd01f99a0a8be464a4ac8b147f322cd1e8e296a1528984fc560b/coverage-7.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b", size = 238610 }, - { url = "https://files.pythonhosted.org/packages/f3/8c/6312d241fe7cbd1f0cade34a62fea6f333d1a261255d76b9a87074d8703c/coverage-7.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21", size = 236697 }, - { url = "https://files.pythonhosted.org/packages/ce/5f/fef33dfd05d87ee9030f614c857deb6df6556b8f6a1c51bbbb41e24ee5ac/coverage-7.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a", size = 238541 }, - { url = "https://files.pythonhosted.org/packages/a9/64/6a984b6e92e1ea1353b7ffa08e27f707a5e29b044622445859200f541e8c/coverage-7.6.4-cp313-cp313-win32.whl", hash = "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e", size = 209707 }, - { url = "https://files.pythonhosted.org/packages/5c/60/ce5a9e942e9543783b3db5d942e0578b391c25cdd5e7f342d854ea83d6b7/coverage-7.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963", size = 210439 }, - { url = "https://files.pythonhosted.org/packages/78/53/6719677e92c308207e7f10561a1b16ab8b5c00e9328efc9af7cfd6fb703e/coverage-7.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f", size = 207784 }, - { url = "https://files.pythonhosted.org/packages/fa/dd/7054928930671fcb39ae6a83bb71d9ab5f0afb733172543ced4b09a115ca/coverage-7.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806", size = 208058 }, - { url = "https://files.pythonhosted.org/packages/b5/7d/fd656ddc2b38301927b9eb3aae3fe827e7aa82e691923ed43721fd9423c9/coverage-7.6.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11", size = 250772 }, - { url = "https://files.pythonhosted.org/packages/90/d0/eb9a3cc2100b83064bb086f18aedde3afffd7de6ead28f69736c00b7f302/coverage-7.6.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3", size = 246490 }, - { url = "https://files.pythonhosted.org/packages/45/44/3f64f38f6faab8a0cfd2c6bc6eb4c6daead246b97cf5f8fc23bf3788f841/coverage-7.6.4-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a", size = 248848 }, - { url = "https://files.pythonhosted.org/packages/5d/11/4c465a5f98656821e499f4b4619929bd5a34639c466021740ecdca42aa30/coverage-7.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc", size = 248340 }, - { url = "https://files.pythonhosted.org/packages/f1/96/ebecda2d016cce9da812f404f720ca5df83c6b29f65dc80d2000d0078741/coverage-7.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70", size = 246229 }, - { url = "https://files.pythonhosted.org/packages/16/d9/3d820c00066ae55d69e6d0eae11d6149a5ca7546de469ba9d597f01bf2d7/coverage-7.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef", size = 247510 }, - { url = "https://files.pythonhosted.org/packages/8f/c3/4fa1eb412bb288ff6bfcc163c11700ff06e02c5fad8513817186e460ed43/coverage-7.6.4-cp313-cp313t-win32.whl", hash = "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e", size = 210353 }, - { url = "https://files.pythonhosted.org/packages/7e/77/03fc2979d1538884d921c2013075917fc927f41cd8526909852fe4494112/coverage-7.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1", size = 211502 }, - { url = "https://files.pythonhosted.org/packages/cc/56/e1d75e8981a2a92c2a777e67c26efa96c66da59d645423146eb9ff3a851b/coverage-7.6.4-pp39.pp310-none-any.whl", hash = "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e", size = 198954 }, + { url = "https://files.pythonhosted.org/packages/a5/93/4ad92f71e28ece5c0326e5f4a6630aa4928a8846654a65cfff69b49b95b9/coverage-7.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07", size = 206713, upload_time = "2024-10-20T22:56:03.877Z" }, + { url = "https://files.pythonhosted.org/packages/01/ae/747a580b1eda3f2e431d87de48f0604bd7bc92e52a1a95185a4aa585bc47/coverage-7.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0", size = 207149, upload_time = "2024-10-20T22:56:06.511Z" }, + { url = "https://files.pythonhosted.org/packages/07/1a/1f573f8a6145f6d4c9130bbc120e0024daf1b24cf2a78d7393fa6eb6aba7/coverage-7.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72", size = 235584, upload_time = "2024-10-20T22:56:07.678Z" }, + { url = "https://files.pythonhosted.org/packages/40/42/c8523f2e4db34aa9389caee0d3688b6ada7a84fcc782e943a868a7f302bd/coverage-7.6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51", size = 233486, upload_time = "2024-10-20T22:56:09.496Z" }, + { url = "https://files.pythonhosted.org/packages/8d/95/565c310fffa16ede1a042e9ea1ca3962af0d8eb5543bc72df6b91dc0c3d5/coverage-7.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491", size = 234649, upload_time = "2024-10-20T22:56:11.326Z" }, + { url = "https://files.pythonhosted.org/packages/d5/81/3b550674d98968ec29c92e3e8650682be6c8b1fa7581a059e7e12e74c431/coverage-7.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b", size = 233744, upload_time = "2024-10-20T22:56:12.481Z" }, + { url = "https://files.pythonhosted.org/packages/0d/70/d66c7f51b3e33aabc5ea9f9624c1c9d9655472962270eb5e7b0d32707224/coverage-7.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea", size = 232204, upload_time = "2024-10-20T22:56:14.236Z" }, + { url = "https://files.pythonhosted.org/packages/23/2d/2b3a2dbed7a5f40693404c8a09e779d7c1a5fbed089d3e7224c002129ec8/coverage-7.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a", size = 233335, upload_time = "2024-10-20T22:56:15.521Z" }, + { url = "https://files.pythonhosted.org/packages/5a/4f/92d1d2ad720d698a4e71c176eacf531bfb8e0721d5ad560556f2c484a513/coverage-7.6.4-cp310-cp310-win32.whl", hash = "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa", size = 209435, upload_time = "2024-10-20T22:56:17.309Z" }, + { url = "https://files.pythonhosted.org/packages/c7/b9/cdf158e7991e2287bcf9082670928badb73d310047facac203ff8dcd5ff3/coverage-7.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172", size = 210243, upload_time = "2024-10-20T22:56:18.366Z" }, + { url = "https://files.pythonhosted.org/packages/87/31/9c0cf84f0dfcbe4215b7eb95c31777cdc0483c13390e69584c8150c85175/coverage-7.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b", size = 206819, upload_time = "2024-10-20T22:56:20.132Z" }, + { url = "https://files.pythonhosted.org/packages/53/ed/a38401079ad320ad6e054a01ec2b61d270511aeb3c201c80e99c841229d5/coverage-7.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25", size = 207263, upload_time = "2024-10-20T22:56:21.88Z" }, + { url = "https://files.pythonhosted.org/packages/20/e7/c3ad33b179ab4213f0d70da25a9c214d52464efa11caeab438592eb1d837/coverage-7.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546", size = 239205, upload_time = "2024-10-20T22:56:23.03Z" }, + { url = "https://files.pythonhosted.org/packages/36/91/fc02e8d8e694f557752120487fd982f654ba1421bbaa5560debf96ddceda/coverage-7.6.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b", size = 236612, upload_time = "2024-10-20T22:56:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/cc/57/cb08f0eda0389a9a8aaa4fc1f9fec7ac361c3e2d68efd5890d7042c18aa3/coverage-7.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e", size = 238479, upload_time = "2024-10-20T22:56:26.749Z" }, + { url = "https://files.pythonhosted.org/packages/d5/c9/2c7681a9b3ca6e6f43d489c2e6653a53278ed857fd6e7010490c307b0a47/coverage-7.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718", size = 237405, upload_time = "2024-10-20T22:56:27.958Z" }, + { url = "https://files.pythonhosted.org/packages/b5/4e/ebfc6944b96317df8b537ae875d2e57c27b84eb98820bc0a1055f358f056/coverage-7.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db", size = 236038, upload_time = "2024-10-20T22:56:29.816Z" }, + { url = "https://files.pythonhosted.org/packages/13/f2/3a0bf1841a97c0654905e2ef531170f02c89fad2555879db8fe41a097871/coverage-7.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522", size = 236812, upload_time = "2024-10-20T22:56:31.654Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9c/66bf59226b52ce6ed9541b02d33e80a6e816a832558fbdc1111a7bd3abd4/coverage-7.6.4-cp311-cp311-win32.whl", hash = "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf", size = 209400, upload_time = "2024-10-20T22:56:33.569Z" }, + { url = "https://files.pythonhosted.org/packages/2a/a0/b0790934c04dfc8d658d4a62acb8f7ca0efdf3818456fcad757b11c6479d/coverage-7.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19", size = 210243, upload_time = "2024-10-20T22:56:34.863Z" }, + { url = "https://files.pythonhosted.org/packages/7d/e7/9291de916d084f41adddfd4b82246e68d61d6a75747f075f7e64628998d2/coverage-7.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2", size = 207013, upload_time = "2024-10-20T22:56:36.034Z" }, + { url = "https://files.pythonhosted.org/packages/27/03/932c2c5717a7fa80cd43c6a07d3177076d97b79f12f40f882f9916db0063/coverage-7.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117", size = 207251, upload_time = "2024-10-20T22:56:38.054Z" }, + { url = "https://files.pythonhosted.org/packages/d5/3f/0af47dcb9327f65a45455fbca846fe96eb57c153af46c4754a3ba678938a/coverage-7.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613", size = 240268, upload_time = "2024-10-20T22:56:40.051Z" }, + { url = "https://files.pythonhosted.org/packages/8a/3c/37a9d81bbd4b23bc7d46ca820e16174c613579c66342faa390a271d2e18b/coverage-7.6.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27", size = 237298, upload_time = "2024-10-20T22:56:41.929Z" }, + { url = "https://files.pythonhosted.org/packages/c0/70/6b0627e5bd68204ee580126ed3513140b2298995c1233bd67404b4e44d0e/coverage-7.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52", size = 239367, upload_time = "2024-10-20T22:56:43.141Z" }, + { url = "https://files.pythonhosted.org/packages/3c/eb/634d7dfab24ac3b790bebaf9da0f4a5352cbc125ce6a9d5c6cf4c6cae3c7/coverage-7.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2", size = 238853, upload_time = "2024-10-20T22:56:44.33Z" }, + { url = "https://files.pythonhosted.org/packages/d9/0d/8e3ed00f1266ef7472a4e33458f42e39492e01a64281084fb3043553d3f1/coverage-7.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1", size = 237160, upload_time = "2024-10-20T22:56:46.258Z" }, + { url = "https://files.pythonhosted.org/packages/ce/9c/4337f468ef0ab7a2e0887a9c9da0e58e2eada6fc6cbee637a4acd5dfd8a9/coverage-7.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5", size = 238824, upload_time = "2024-10-20T22:56:48.666Z" }, + { url = "https://files.pythonhosted.org/packages/5e/09/3e94912b8dd37251377bb02727a33a67ee96b84bbbe092f132b401ca5dd9/coverage-7.6.4-cp312-cp312-win32.whl", hash = "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17", size = 209639, upload_time = "2024-10-20T22:56:50.664Z" }, + { url = "https://files.pythonhosted.org/packages/01/69/d4f3a4101171f32bc5b3caec8ff94c2c60f700107a6aaef7244b2c166793/coverage-7.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08", size = 210428, upload_time = "2024-10-20T22:56:52.468Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4d/2dede4f7cb5a70fb0bb40a57627fddf1dbdc6b9c1db81f7c4dcdcb19e2f4/coverage-7.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9", size = 207039, upload_time = "2024-10-20T22:56:53.656Z" }, + { url = "https://files.pythonhosted.org/packages/3f/f9/d86368ae8c79e28f1fb458ebc76ae9ff3e8bd8069adc24e8f2fed03c58b7/coverage-7.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba", size = 207298, upload_time = "2024-10-20T22:56:54.979Z" }, + { url = "https://files.pythonhosted.org/packages/64/c5/b4cc3c3f64622c58fbfd4d8b9a7a8ce9d355f172f91fcabbba1f026852f6/coverage-7.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c", size = 239813, upload_time = "2024-10-20T22:56:56.209Z" }, + { url = "https://files.pythonhosted.org/packages/8a/86/14c42e60b70a79b26099e4d289ccdfefbc68624d096f4481163085aa614c/coverage-7.6.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06", size = 236959, upload_time = "2024-10-20T22:56:58.06Z" }, + { url = "https://files.pythonhosted.org/packages/7f/f8/4436a643631a2fbab4b44d54f515028f6099bfb1cd95b13cfbf701e7f2f2/coverage-7.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f", size = 238950, upload_time = "2024-10-20T22:56:59.329Z" }, + { url = "https://files.pythonhosted.org/packages/49/50/1571810ddd01f99a0a8be464a4ac8b147f322cd1e8e296a1528984fc560b/coverage-7.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b", size = 238610, upload_time = "2024-10-20T22:57:00.645Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8c/6312d241fe7cbd1f0cade34a62fea6f333d1a261255d76b9a87074d8703c/coverage-7.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21", size = 236697, upload_time = "2024-10-20T22:57:01.944Z" }, + { url = "https://files.pythonhosted.org/packages/ce/5f/fef33dfd05d87ee9030f614c857deb6df6556b8f6a1c51bbbb41e24ee5ac/coverage-7.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a", size = 238541, upload_time = "2024-10-20T22:57:03.848Z" }, + { url = "https://files.pythonhosted.org/packages/a9/64/6a984b6e92e1ea1353b7ffa08e27f707a5e29b044622445859200f541e8c/coverage-7.6.4-cp313-cp313-win32.whl", hash = "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e", size = 209707, upload_time = "2024-10-20T22:57:05.123Z" }, + { url = "https://files.pythonhosted.org/packages/5c/60/ce5a9e942e9543783b3db5d942e0578b391c25cdd5e7f342d854ea83d6b7/coverage-7.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963", size = 210439, upload_time = "2024-10-20T22:57:06.35Z" }, + { url = "https://files.pythonhosted.org/packages/78/53/6719677e92c308207e7f10561a1b16ab8b5c00e9328efc9af7cfd6fb703e/coverage-7.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f", size = 207784, upload_time = "2024-10-20T22:57:07.857Z" }, + { url = "https://files.pythonhosted.org/packages/fa/dd/7054928930671fcb39ae6a83bb71d9ab5f0afb733172543ced4b09a115ca/coverage-7.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806", size = 208058, upload_time = "2024-10-20T22:57:09.845Z" }, + { url = "https://files.pythonhosted.org/packages/b5/7d/fd656ddc2b38301927b9eb3aae3fe827e7aa82e691923ed43721fd9423c9/coverage-7.6.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11", size = 250772, upload_time = "2024-10-20T22:57:11.147Z" }, + { url = "https://files.pythonhosted.org/packages/90/d0/eb9a3cc2100b83064bb086f18aedde3afffd7de6ead28f69736c00b7f302/coverage-7.6.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3", size = 246490, upload_time = "2024-10-20T22:57:13.02Z" }, + { url = "https://files.pythonhosted.org/packages/45/44/3f64f38f6faab8a0cfd2c6bc6eb4c6daead246b97cf5f8fc23bf3788f841/coverage-7.6.4-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a", size = 248848, upload_time = "2024-10-20T22:57:14.927Z" }, + { url = "https://files.pythonhosted.org/packages/5d/11/4c465a5f98656821e499f4b4619929bd5a34639c466021740ecdca42aa30/coverage-7.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc", size = 248340, upload_time = "2024-10-20T22:57:16.246Z" }, + { url = "https://files.pythonhosted.org/packages/f1/96/ebecda2d016cce9da812f404f720ca5df83c6b29f65dc80d2000d0078741/coverage-7.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70", size = 246229, upload_time = "2024-10-20T22:57:17.546Z" }, + { url = "https://files.pythonhosted.org/packages/16/d9/3d820c00066ae55d69e6d0eae11d6149a5ca7546de469ba9d597f01bf2d7/coverage-7.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef", size = 247510, upload_time = "2024-10-20T22:57:18.925Z" }, + { url = "https://files.pythonhosted.org/packages/8f/c3/4fa1eb412bb288ff6bfcc163c11700ff06e02c5fad8513817186e460ed43/coverage-7.6.4-cp313-cp313t-win32.whl", hash = "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e", size = 210353, upload_time = "2024-10-20T22:57:20.891Z" }, + { url = "https://files.pythonhosted.org/packages/7e/77/03fc2979d1538884d921c2013075917fc927f41cd8526909852fe4494112/coverage-7.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1", size = 211502, upload_time = "2024-10-20T22:57:22.21Z" }, + { url = "https://files.pythonhosted.org/packages/cc/56/e1d75e8981a2a92c2a777e67c26efa96c66da59d645423146eb9ff3a851b/coverage-7.6.4-pp39.pp310-none-any.whl", hash = "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e", size = 198954, upload_time = "2024-10-20T22:57:38.28Z" }, ] [package.optional-dependencies] @@ -453,57 +462,57 @@ toml = [ name = "cycler" version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615 } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload_time = "2023-10-07T05:32:18.335Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload_time = "2023-10-07T05:32:16.783Z" }, ] [[package]] name = "cython" version = "3.0.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/68/09/ffb61f29b8e3d207c444032b21328327d753e274ea081bc74e009827cc81/Cython-3.0.8.tar.gz", hash = "sha256:8333423d8fd5765e7cceea3a9985dd1e0a5dfeb2734629e1a2ed2d6233d39de6", size = 2744096 } +sdist = { url = "https://files.pythonhosted.org/packages/68/09/ffb61f29b8e3d207c444032b21328327d753e274ea081bc74e009827cc81/Cython-3.0.8.tar.gz", hash = "sha256:8333423d8fd5765e7cceea3a9985dd1e0a5dfeb2734629e1a2ed2d6233d39de6", size = 2744096, upload_time = "2024-01-10T11:01:02.155Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/63/f4/d2542e186fe33ec1cc542770fb17466421ed54f4ffe04d00fe9549d0a467/Cython-3.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a846e0a38e2b24e9a5c5dc74b0e54c6e29420d88d1dafabc99e0fc0f3e338636", size = 3100459 }, - { url = "https://files.pythonhosted.org/packages/fc/27/2652f395aa708fb3081148e0df3ab700bd7288636c65332ef7febad6a380/Cython-3.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45523fdc2b78d79b32834cc1cc12dc2ca8967af87e22a3ee1bff20e77c7f5520", size = 3456626 }, - { url = "https://files.pythonhosted.org/packages/f9/bd/e8a1d26d04c08a67bcc383f2ea5493a4e77f37a8770ead00a238b08ad729/Cython-3.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa0b7f3f841fe087410cab66778e2d3fb20ae2d2078a2be3dffe66c6574be39", size = 3621379 }, - { url = "https://files.pythonhosted.org/packages/03/ae/ead7ec03d0062d439879d41b7830e4f2480213f7beabf2f7052a191cc6f7/Cython-3.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e87294e33e40c289c77a135f491cd721bd089f193f956f7b8ed5aa2d0b8c558f", size = 3671873 }, - { url = "https://files.pythonhosted.org/packages/63/b0/81dad725604d7b529c492f873a7fa1b5800704a9f26e100ed25e9fd8d057/Cython-3.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a1df7a129344b1215c20096d33c00193437df1a8fcca25b71f17c23b1a44f782", size = 3463832 }, - { url = "https://files.pythonhosted.org/packages/13/cd/72b8e0af597ac1b376421847acf6d6fa252e60059a2a00dcf05ceb16d28f/Cython-3.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:13c2a5e57a0358da467d97667297bf820b62a1a87ae47c5f87938b9bb593acbd", size = 3618325 }, - { url = "https://files.pythonhosted.org/packages/ef/73/11a4355d8b8966504c751e5bcb25916c4140de27bb2ba1b54ff21994d7fe/Cython-3.0.8-cp310-cp310-win32.whl", hash = "sha256:96b028f044f5880e3cb18ecdcfc6c8d3ce9d0af28418d5ab464509f26d8adf12", size = 2571305 }, - { url = "https://files.pythonhosted.org/packages/18/15/fdc0c3552d20f9337b134a36d786da24e47998fc39f62cb61c1534f26123/Cython-3.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:8140597a8b5cc4f119a1190f5a2228a84f5ca6d8d9ec386cfce24663f48b2539", size = 2776113 }, - { url = "https://files.pythonhosted.org/packages/db/a7/f4a0bc9a80e23b380daa2ebb4879bf434aaa0b3b91f7ad8a7f9762b4bd1b/Cython-3.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aae26f9663e50caf9657148403d9874eea41770ecdd6caf381d177c2b1bb82ba", size = 3113615 }, - { url = "https://files.pythonhosted.org/packages/e9/e9/e9295df74246c165b91253a473bfa179debf739c9bee961cbb3ae56c2b79/Cython-3.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:547eb3cdb2f8c6f48e6865d5a741d9dd051c25b3ce076fbca571727977b28ac3", size = 3436320 }, - { url = "https://files.pythonhosted.org/packages/26/2c/6a887c957aa53e44f928119dea628a5dfacc8e875424034f5fecac9daba4/Cython-3.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a567d4b9ba70b26db89d75b243529de9e649a2f56384287533cf91512705bee", size = 3591755 }, - { url = "https://files.pythonhosted.org/packages/ba/b8/f9c97bae6281da50b3ecb1f7fef0f7f7851eae084609b364717a2b366bf1/Cython-3.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51d1426263b0e82fb22bda8ea60dc77a428581cc19e97741011b938445d383f1", size = 3636099 }, - { url = "https://files.pythonhosted.org/packages/17/ae/cd055c2c081c67a6fcad1d8d17d82bd6395b14c6741e3a938f40318c8bc5/Cython-3.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c26daaeccda072459b48d211415fd1e5507c06bcd976fa0d5b8b9f1063467d7b", size = 3458119 }, - { url = "https://files.pythonhosted.org/packages/72/ab/ac6f5548d6194f4bb2fc8c6c996aa7369f0fa1403e4d4de787d9e9309b27/Cython-3.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:289ce7838208211cd166e975865fd73b0649bf118170b6cebaedfbdaf4a37795", size = 3614418 }, - { url = "https://files.pythonhosted.org/packages/70/e2/3e3e448b7a94887bec3235bcb71957b6681dc42b4536459f8f54d46fa936/Cython-3.0.8-cp311-cp311-win32.whl", hash = "sha256:c8aa05f5e17f8042a3be052c24f2edc013fb8af874b0bf76907d16c51b4e7871", size = 2572819 }, - { url = "https://files.pythonhosted.org/packages/85/7d/58635941dfbb5b4e197adb88080b9cbfb230dc3b75683698a530a1989bdb/Cython-3.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:000dc9e135d0eec6ecb2b40a5b02d0868a2f8d2e027a41b0fe16a908a9e6de02", size = 2784167 }, - { url = "https://files.pythonhosted.org/packages/3d/8e/28f8c6109990eef7317ab7e43644092b49a88a39f9373dcd19318946df09/Cython-3.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:90d3fe31db55685d8cb97d43b0ec39ef614fcf660f83c77ed06aa670cb0e164f", size = 3135638 }, - { url = "https://files.pythonhosted.org/packages/83/1f/4720cb682b8ed1ab9749dea35351a66dd29b6a022628cce038415660c384/Cython-3.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e24791ddae2324e88e3c902a765595c738f19ae34ee66bfb1a6dac54b1833419", size = 3340052 }, - { url = "https://files.pythonhosted.org/packages/8a/47/ec3fceb9e8f7d6fa130216b8740038e1df7c8e5f215bba363fcf1272a6c1/Cython-3.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f020fa1c0552052e0660790b8153b79e3fc9a15dbd8f1d0b841fe5d204a6ae6", size = 3510079 }, - { url = "https://files.pythonhosted.org/packages/71/31/b458127851e248effb909e2791b55870914863cde7c60b94db5ee65d7867/Cython-3.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18bfa387d7a7f77d7b2526af69a65dbd0b731b8d941aaff5becff8e21f6d7717", size = 3573972 }, - { url = "https://files.pythonhosted.org/packages/6b/d5/ca6513844d0634abd05ba12304053a454bb70441a9520afa9897d4300156/Cython-3.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fe81b339cffd87c0069c6049b4d33e28bdd1874625ee515785bf42c9fdff3658", size = 3356158 }, - { url = "https://files.pythonhosted.org/packages/33/59/98a87b6264f4ad45c820db13c4ec657567476efde020c49443cc842a86af/Cython-3.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:80fd94c076e1e1b1ee40a309be03080b75f413e8997cddcf401a118879863388", size = 3522312 }, - { url = "https://files.pythonhosted.org/packages/2b/cb/132115d07a0b9d4f075e0741db70a5416b424dcd875b2bb0dd805e818222/Cython-3.0.8-cp312-cp312-win32.whl", hash = "sha256:85077915a93e359a9b920280d214dc0cf8a62773e1f3d7d30fab8ea4daed670c", size = 2602579 }, - { url = "https://files.pythonhosted.org/packages/b4/69/cb4620287cd9ef461103e122c0a2ae7f7ecf183e02510676fb5a15c95b05/Cython-3.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:0cb2dcc565c7851f75d496f724a384a790fab12d1b82461b663e66605bec429a", size = 2791268 }, - { url = "https://files.pythonhosted.org/packages/e3/7f/f584f5d15323feb897d42ef0e9d910649e2150d7a30cf7e7a8cc1d236e6f/Cython-3.0.8-py2.py3-none-any.whl", hash = "sha256:171b27051253d3f9108e9759e504ba59ff06e7f7ba944457f94deaf9c21bf0b6", size = 1168213 }, + { url = "https://files.pythonhosted.org/packages/63/f4/d2542e186fe33ec1cc542770fb17466421ed54f4ffe04d00fe9549d0a467/Cython-3.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a846e0a38e2b24e9a5c5dc74b0e54c6e29420d88d1dafabc99e0fc0f3e338636", size = 3100459, upload_time = "2024-01-10T11:33:49.545Z" }, + { url = "https://files.pythonhosted.org/packages/fc/27/2652f395aa708fb3081148e0df3ab700bd7288636c65332ef7febad6a380/Cython-3.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45523fdc2b78d79b32834cc1cc12dc2ca8967af87e22a3ee1bff20e77c7f5520", size = 3456626, upload_time = "2024-01-10T11:01:44.897Z" }, + { url = "https://files.pythonhosted.org/packages/f9/bd/e8a1d26d04c08a67bcc383f2ea5493a4e77f37a8770ead00a238b08ad729/Cython-3.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa0b7f3f841fe087410cab66778e2d3fb20ae2d2078a2be3dffe66c6574be39", size = 3621379, upload_time = "2024-01-10T11:01:48.777Z" }, + { url = "https://files.pythonhosted.org/packages/03/ae/ead7ec03d0062d439879d41b7830e4f2480213f7beabf2f7052a191cc6f7/Cython-3.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e87294e33e40c289c77a135f491cd721bd089f193f956f7b8ed5aa2d0b8c558f", size = 3671873, upload_time = "2024-01-10T11:01:51.858Z" }, + { url = "https://files.pythonhosted.org/packages/63/b0/81dad725604d7b529c492f873a7fa1b5800704a9f26e100ed25e9fd8d057/Cython-3.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a1df7a129344b1215c20096d33c00193437df1a8fcca25b71f17c23b1a44f782", size = 3463832, upload_time = "2024-01-10T11:01:55.364Z" }, + { url = "https://files.pythonhosted.org/packages/13/cd/72b8e0af597ac1b376421847acf6d6fa252e60059a2a00dcf05ceb16d28f/Cython-3.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:13c2a5e57a0358da467d97667297bf820b62a1a87ae47c5f87938b9bb593acbd", size = 3618325, upload_time = "2024-01-10T11:01:59.03Z" }, + { url = "https://files.pythonhosted.org/packages/ef/73/11a4355d8b8966504c751e5bcb25916c4140de27bb2ba1b54ff21994d7fe/Cython-3.0.8-cp310-cp310-win32.whl", hash = "sha256:96b028f044f5880e3cb18ecdcfc6c8d3ce9d0af28418d5ab464509f26d8adf12", size = 2571305, upload_time = "2024-01-10T11:02:02.589Z" }, + { url = "https://files.pythonhosted.org/packages/18/15/fdc0c3552d20f9337b134a36d786da24e47998fc39f62cb61c1534f26123/Cython-3.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:8140597a8b5cc4f119a1190f5a2228a84f5ca6d8d9ec386cfce24663f48b2539", size = 2776113, upload_time = "2024-01-10T11:02:05.581Z" }, + { url = "https://files.pythonhosted.org/packages/db/a7/f4a0bc9a80e23b380daa2ebb4879bf434aaa0b3b91f7ad8a7f9762b4bd1b/Cython-3.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aae26f9663e50caf9657148403d9874eea41770ecdd6caf381d177c2b1bb82ba", size = 3113615, upload_time = "2024-01-10T11:34:05.899Z" }, + { url = "https://files.pythonhosted.org/packages/e9/e9/e9295df74246c165b91253a473bfa179debf739c9bee961cbb3ae56c2b79/Cython-3.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:547eb3cdb2f8c6f48e6865d5a741d9dd051c25b3ce076fbca571727977b28ac3", size = 3436320, upload_time = "2024-01-10T11:02:08.689Z" }, + { url = "https://files.pythonhosted.org/packages/26/2c/6a887c957aa53e44f928119dea628a5dfacc8e875424034f5fecac9daba4/Cython-3.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a567d4b9ba70b26db89d75b243529de9e649a2f56384287533cf91512705bee", size = 3591755, upload_time = "2024-01-10T11:02:11.773Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b8/f9c97bae6281da50b3ecb1f7fef0f7f7851eae084609b364717a2b366bf1/Cython-3.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51d1426263b0e82fb22bda8ea60dc77a428581cc19e97741011b938445d383f1", size = 3636099, upload_time = "2024-01-10T11:02:15.191Z" }, + { url = "https://files.pythonhosted.org/packages/17/ae/cd055c2c081c67a6fcad1d8d17d82bd6395b14c6741e3a938f40318c8bc5/Cython-3.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c26daaeccda072459b48d211415fd1e5507c06bcd976fa0d5b8b9f1063467d7b", size = 3458119, upload_time = "2024-01-10T11:02:19.103Z" }, + { url = "https://files.pythonhosted.org/packages/72/ab/ac6f5548d6194f4bb2fc8c6c996aa7369f0fa1403e4d4de787d9e9309b27/Cython-3.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:289ce7838208211cd166e975865fd73b0649bf118170b6cebaedfbdaf4a37795", size = 3614418, upload_time = "2024-01-10T11:02:22.732Z" }, + { url = "https://files.pythonhosted.org/packages/70/e2/3e3e448b7a94887bec3235bcb71957b6681dc42b4536459f8f54d46fa936/Cython-3.0.8-cp311-cp311-win32.whl", hash = "sha256:c8aa05f5e17f8042a3be052c24f2edc013fb8af874b0bf76907d16c51b4e7871", size = 2572819, upload_time = "2024-01-10T11:02:25.976Z" }, + { url = "https://files.pythonhosted.org/packages/85/7d/58635941dfbb5b4e197adb88080b9cbfb230dc3b75683698a530a1989bdb/Cython-3.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:000dc9e135d0eec6ecb2b40a5b02d0868a2f8d2e027a41b0fe16a908a9e6de02", size = 2784167, upload_time = "2024-01-10T11:02:28.808Z" }, + { url = "https://files.pythonhosted.org/packages/3d/8e/28f8c6109990eef7317ab7e43644092b49a88a39f9373dcd19318946df09/Cython-3.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:90d3fe31db55685d8cb97d43b0ec39ef614fcf660f83c77ed06aa670cb0e164f", size = 3135638, upload_time = "2024-01-10T11:34:22.889Z" }, + { url = "https://files.pythonhosted.org/packages/83/1f/4720cb682b8ed1ab9749dea35351a66dd29b6a022628cce038415660c384/Cython-3.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e24791ddae2324e88e3c902a765595c738f19ae34ee66bfb1a6dac54b1833419", size = 3340052, upload_time = "2024-01-10T11:02:32.471Z" }, + { url = "https://files.pythonhosted.org/packages/8a/47/ec3fceb9e8f7d6fa130216b8740038e1df7c8e5f215bba363fcf1272a6c1/Cython-3.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f020fa1c0552052e0660790b8153b79e3fc9a15dbd8f1d0b841fe5d204a6ae6", size = 3510079, upload_time = "2024-01-10T11:02:35.312Z" }, + { url = "https://files.pythonhosted.org/packages/71/31/b458127851e248effb909e2791b55870914863cde7c60b94db5ee65d7867/Cython-3.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18bfa387d7a7f77d7b2526af69a65dbd0b731b8d941aaff5becff8e21f6d7717", size = 3573972, upload_time = "2024-01-10T11:02:39.044Z" }, + { url = "https://files.pythonhosted.org/packages/6b/d5/ca6513844d0634abd05ba12304053a454bb70441a9520afa9897d4300156/Cython-3.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fe81b339cffd87c0069c6049b4d33e28bdd1874625ee515785bf42c9fdff3658", size = 3356158, upload_time = "2024-01-10T11:02:42.125Z" }, + { url = "https://files.pythonhosted.org/packages/33/59/98a87b6264f4ad45c820db13c4ec657567476efde020c49443cc842a86af/Cython-3.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:80fd94c076e1e1b1ee40a309be03080b75f413e8997cddcf401a118879863388", size = 3522312, upload_time = "2024-01-10T11:02:45.056Z" }, + { url = "https://files.pythonhosted.org/packages/2b/cb/132115d07a0b9d4f075e0741db70a5416b424dcd875b2bb0dd805e818222/Cython-3.0.8-cp312-cp312-win32.whl", hash = "sha256:85077915a93e359a9b920280d214dc0cf8a62773e1f3d7d30fab8ea4daed670c", size = 2602579, upload_time = "2024-01-10T11:02:48.368Z" }, + { url = "https://files.pythonhosted.org/packages/b4/69/cb4620287cd9ef461103e122c0a2ae7f7ecf183e02510676fb5a15c95b05/Cython-3.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:0cb2dcc565c7851f75d496f724a384a790fab12d1b82461b663e66605bec429a", size = 2791268, upload_time = "2024-01-10T11:02:51.483Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7f/f584f5d15323feb897d42ef0e9d910649e2150d7a30cf7e7a8cc1d236e6f/Cython-3.0.8-py2.py3-none-any.whl", hash = "sha256:171b27051253d3f9108e9759e504ba59ff06e7f7ba944457f94deaf9c21bf0b6", size = 1168213, upload_time = "2024-01-10T11:00:56.857Z" }, ] [[package]] name = "easydict" version = "1.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/d2/deb3296d08097fedd622d423c0ec8b68b78c1704b3f1545326f6ce05c75c/easydict-1.11.tar.gz", hash = "sha256:dcb1d2ed28eb300c8e46cd371340373abc62f7c14d6dea74fdfc6f1069061c78", size = 6644 } +sdist = { url = "https://files.pythonhosted.org/packages/0a/d2/deb3296d08097fedd622d423c0ec8b68b78c1704b3f1545326f6ce05c75c/easydict-1.11.tar.gz", hash = "sha256:dcb1d2ed28eb300c8e46cd371340373abc62f7c14d6dea74fdfc6f1069061c78", size = 6644, upload_time = "2023-10-23T23:01:37.686Z" } [[package]] name = "exceptiongroup" version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/1c/beef724eaf5b01bb44b6338c8c3494eff7cab376fab4904cfbbc3585dc79/exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68", size = 26264 } +sdist = { url = "https://files.pythonhosted.org/packages/8e/1c/beef724eaf5b01bb44b6338c8c3494eff7cab376fab4904cfbbc3585dc79/exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68", size = 26264, upload_time = "2023-11-21T08:42:17.407Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/9a/5028fd52db10e600f1c4674441b968cf2ea4959085bfb5b99fb1250e5f68/exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", size = 16210 }, + { url = "https://files.pythonhosted.org/packages/b8/9a/5028fd52db10e600f1c4674441b968cf2ea4959085bfb5b99fb1250e5f68/exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", size = 16210, upload_time = "2023-11-21T08:42:15.525Z" }, ] [[package]] @@ -515,18 +524,18 @@ dependencies = [ { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/55/ae499352d82338331ca1e28c7f4a63bfd09479b16395dce38cf50a39e2c2/fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681", size = 295236 } +sdist = { url = "https://files.pythonhosted.org/packages/f4/55/ae499352d82338331ca1e28c7f4a63bfd09479b16395dce38cf50a39e2c2/fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681", size = 295236, upload_time = "2025-03-23T22:55:43.822Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/b3/b51f09c2ba432a576fe63758bddc81f78f0c6309d9e5c10d194313bf021e/fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d", size = 95164 }, + { url = "https://files.pythonhosted.org/packages/50/b3/b51f09c2ba432a576fe63758bddc81f78f0c6309d9e5c10d194313bf021e/fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d", size = 95164, upload_time = "2025-03-23T22:55:42.101Z" }, ] [[package]] name = "filelock" version = "3.13.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/70/70/41905c80dcfe71b22fb06827b8eae65781783d4a14194bce79d16a013263/filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e", size = 14553 } +sdist = { url = "https://files.pythonhosted.org/packages/70/70/41905c80dcfe71b22fb06827b8eae65781783d4a14194bce79d16a013263/filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e", size = 14553, upload_time = "2023-10-30T18:29:39.035Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/54/84d42a0bee35edba99dee7b59a8d4970eccdd44b99fe728ed912106fc781/filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c", size = 11740 }, + { url = "https://files.pythonhosted.org/packages/81/54/84d42a0bee35edba99dee7b59a8d4970eccdd44b99fe728ed912106fc781/filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c", size = 11740, upload_time = "2023-10-30T18:29:37.267Z" }, ] [[package]] @@ -540,9 +549,9 @@ dependencies = [ { name = "jinja2" }, { name = "werkzeug" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d8/09/c1a7354d3925a3c6c8cfdebf4245bae67d633ffda1ba415add06ffc839c5/flask-3.0.0.tar.gz", hash = "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58", size = 674171 } +sdist = { url = "https://files.pythonhosted.org/packages/d8/09/c1a7354d3925a3c6c8cfdebf4245bae67d633ffda1ba415add06ffc839c5/flask-3.0.0.tar.gz", hash = "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58", size = 674171, upload_time = "2023-09-30T14:36:12.918Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl", hash = "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638", size = 99724 }, + { url = "https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl", hash = "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638", size = 99724, upload_time = "2023-09-30T14:36:10.961Z" }, ] [[package]] @@ -552,9 +561,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "flask" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/6a/a8d56d60bcfa1ec3e4fdad81b45aafd508c3bd5c244a16526fa29139d7d4/flask_cors-4.0.1.tar.gz", hash = "sha256:eeb69b342142fdbf4766ad99357a7f3876a2ceb77689dc10ff912aac06c389e4", size = 30306 } +sdist = { url = "https://files.pythonhosted.org/packages/40/6a/a8d56d60bcfa1ec3e4fdad81b45aafd508c3bd5c244a16526fa29139d7d4/flask_cors-4.0.1.tar.gz", hash = "sha256:eeb69b342142fdbf4766ad99357a7f3876a2ceb77689dc10ff912aac06c389e4", size = 30306, upload_time = "2024-05-04T19:49:43.538Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/52/2aa6285f104616f73ee1ad7905a16b2b35af0143034ad0cf7b64bcba715c/Flask_Cors-4.0.1-py2.py3-none-any.whl", hash = "sha256:f2a704e4458665580c074b714c4627dd5a306b333deb9074d0b1794dfa2fb677", size = 14290 }, + { url = "https://files.pythonhosted.org/packages/8b/52/2aa6285f104616f73ee1ad7905a16b2b35af0143034ad0cf7b64bcba715c/Flask_Cors-4.0.1-py2.py3-none-any.whl", hash = "sha256:f2a704e4458665580c074b714c4627dd5a306b333deb9074d0b1794dfa2fb677", size = 14290, upload_time = "2024-05-04T19:49:41.721Z" }, ] [[package]] @@ -565,60 +574,60 @@ dependencies = [ { name = "flask" }, { name = "werkzeug" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/6e/2f4e13e373bb49e68c02c51ceadd22d172715a06716f9299d9df01b6ddb2/Flask-Login-0.6.3.tar.gz", hash = "sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333", size = 48834 } +sdist = { url = "https://files.pythonhosted.org/packages/c3/6e/2f4e13e373bb49e68c02c51ceadd22d172715a06716f9299d9df01b6ddb2/Flask-Login-0.6.3.tar.gz", hash = "sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333", size = 48834, upload_time = "2023-10-30T14:53:21.151Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/59/f5/67e9cc5c2036f58115f9fe0f00d203cf6780c3ff8ae0e705e7a9d9e8ff9e/Flask_Login-0.6.3-py3-none-any.whl", hash = "sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d", size = 17303 }, + { url = "https://files.pythonhosted.org/packages/59/f5/67e9cc5c2036f58115f9fe0f00d203cf6780c3ff8ae0e705e7a9d9e8ff9e/Flask_Login-0.6.3-py3-none-any.whl", hash = "sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d", size = 17303, upload_time = "2023-10-30T14:53:19.636Z" }, ] [[package]] name = "flatbuffers" version = "23.5.26" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0c/6e/3e52cd294d8e7a61e010973cce076a0cb2c6c0dfd4d0b7a13648c1b98329/flatbuffers-23.5.26.tar.gz", hash = "sha256:9ea1144cac05ce5d86e2859f431c6cd5e66cd9c78c558317c7955fb8d4c78d89", size = 22114 } +sdist = { url = "https://files.pythonhosted.org/packages/0c/6e/3e52cd294d8e7a61e010973cce076a0cb2c6c0dfd4d0b7a13648c1b98329/flatbuffers-23.5.26.tar.gz", hash = "sha256:9ea1144cac05ce5d86e2859f431c6cd5e66cd9c78c558317c7955fb8d4c78d89", size = 22114, upload_time = "2023-05-26T17:35:16.034Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/12/d5c79ee252793ffe845d58a913197bfa02ae9a0b5c9bc3dc4b58d477b9e7/flatbuffers-23.5.26-py2.py3-none-any.whl", hash = "sha256:c0ff356da363087b915fde4b8b45bdda73432fc17cddb3c8157472eab1422ad1", size = 26744 }, + { url = "https://files.pythonhosted.org/packages/6f/12/d5c79ee252793ffe845d58a913197bfa02ae9a0b5c9bc3dc4b58d477b9e7/flatbuffers-23.5.26-py2.py3-none-any.whl", hash = "sha256:c0ff356da363087b915fde4b8b45bdda73432fc17cddb3c8157472eab1422ad1", size = 26744, upload_time = "2023-05-26T17:35:14.269Z" }, ] [[package]] name = "fonttools" version = "4.47.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/cd/75d24afa673edf92fd04657fad7d3b5e20c4abc3cad5bc14e5e30051c1f0/fonttools-4.47.2.tar.gz", hash = "sha256:7df26dd3650e98ca45f1e29883c96a0b9f5bb6af8d632a6a108bc744fa0bd9b3", size = 3410067 } +sdist = { url = "https://files.pythonhosted.org/packages/e5/cd/75d24afa673edf92fd04657fad7d3b5e20c4abc3cad5bc14e5e30051c1f0/fonttools-4.47.2.tar.gz", hash = "sha256:7df26dd3650e98ca45f1e29883c96a0b9f5bb6af8d632a6a108bc744fa0bd9b3", size = 3410067, upload_time = "2024-01-11T11:22:45.293Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/19/30/02de0b7f3d72f2c4fce3e512b166c1bdbe5a687408474b61eb0114be921c/fonttools-4.47.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3b629108351d25512d4ea1a8393a2dba325b7b7d7308116b605ea3f8e1be88df", size = 2779949 }, - { url = "https://files.pythonhosted.org/packages/9a/52/1a5e1373afb78a040ea0c371ab8a79da121060a8e518968bb8f41457ca90/fonttools-4.47.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c19044256c44fe299d9a73456aabee4b4d06c6b930287be93b533b4737d70aa1", size = 2281336 }, - { url = "https://files.pythonhosted.org/packages/c5/ce/9d3b5bf51aafee024566ebb374f5b040381d92660cb04647af3c5860c611/fonttools-4.47.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8be28c036b9f186e8c7eaf8a11b42373e7e4949f9e9f370202b9da4c4c3f56c", size = 4541692 }, - { url = "https://files.pythonhosted.org/packages/e8/68/af41b7cfd35c7418e17b6a43bb106be4b0f0e5feb405a88dee29b186f2a7/fonttools-4.47.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f83a4daef6d2a202acb9bf572958f91cfde5b10c8ee7fb1d09a4c81e5d851fd8", size = 4600529 }, - { url = "https://files.pythonhosted.org/packages/ab/7e/428dbb4cfc342b7a05cbc9d349e134e7fad6588f4ce2a7128e8e3e58ad3b/fonttools-4.47.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5a5318ba5365d992666ac4fe35365f93004109d18858a3e18ae46f67907670", size = 4524215 }, - { url = "https://files.pythonhosted.org/packages/a6/61/762fad1cc1debc4626f2eb373fa999591c63c231fce53d5073574a639531/fonttools-4.47.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8f57ecd742545362a0f7186774b2d1c53423ed9ece67689c93a1055b236f638c", size = 4584778 }, - { url = "https://files.pythonhosted.org/packages/04/30/170ca22284c1d825470e8b5871d6b25d3a70e2f5b185ffb1647d5e11ee4d/fonttools-4.47.2-cp310-cp310-win32.whl", hash = "sha256:a1c154bb85dc9a4cf145250c88d112d88eb414bad81d4cb524d06258dea1bdc0", size = 2131876 }, - { url = "https://files.pythonhosted.org/packages/df/07/4a30437bed355b838b8ce31d14c5983334c31adc97e70c6ecff90c60d6d2/fonttools-4.47.2-cp310-cp310-win_amd64.whl", hash = "sha256:3e2b95dce2ead58fb12524d0ca7d63a63459dd489e7e5838c3cd53557f8933e1", size = 2177937 }, - { url = "https://files.pythonhosted.org/packages/dd/1d/670372323642eada0f7743cfcdd156de6a28d37769c916421fec2f32c814/fonttools-4.47.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:29495d6d109cdbabe73cfb6f419ce67080c3ef9ea1e08d5750240fd4b0c4763b", size = 2782908 }, - { url = "https://files.pythonhosted.org/packages/c1/36/5f0bb863a6575db4c4b67fa9be7f98e4c551dd87638ef327bc180b988998/fonttools-4.47.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0a1d313a415eaaba2b35d6cd33536560deeebd2ed758b9bfb89ab5d97dc5deac", size = 2283501 }, - { url = "https://files.pythonhosted.org/packages/bd/1e/95de682a86567426bcc40a56c9b118ffa97de6cbfcc293addf20994e329d/fonttools-4.47.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90f898cdd67f52f18049250a6474185ef6544c91f27a7bee70d87d77a8daf89c", size = 4848039 }, - { url = "https://files.pythonhosted.org/packages/ef/95/92a0b5fc844c1db734752f8a51431de519cd6b02e7e561efa9e9fd415544/fonttools-4.47.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3480eeb52770ff75140fe7d9a2ec33fb67b07efea0ab5129c7e0c6a639c40c70", size = 4893166 }, - { url = "https://files.pythonhosted.org/packages/ff/e6/ed9dd7ee1afd6cd70eb7237688118fe489dbde962e3765c91c86c095f84b/fonttools-4.47.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0255dbc128fee75fb9be364806b940ed450dd6838672a150d501ee86523ac61e", size = 4815529 }, - { url = "https://files.pythonhosted.org/packages/6b/67/cdffa0b3cd8f863b45125c335bbd3d9dc16ec42f5a8d5b64dd1244c5ce6b/fonttools-4.47.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f791446ff297fd5f1e2247c188de53c1bfb9dd7f0549eba55b73a3c2087a2703", size = 4875414 }, - { url = "https://files.pythonhosted.org/packages/b8/fb/41638e748c8f20f5483987afcf9be746d3ccb9e9600ca62128a27c791a82/fonttools-4.47.2-cp311-cp311-win32.whl", hash = "sha256:740947906590a878a4bde7dd748e85fefa4d470a268b964748403b3ab2aeed6c", size = 2130073 }, - { url = "https://files.pythonhosted.org/packages/a0/ef/93321cf55180a778b4d97919b28739874c0afab90e7b9f5b232db70f47c2/fonttools-4.47.2-cp311-cp311-win_amd64.whl", hash = "sha256:63fbed184979f09a65aa9c88b395ca539c94287ba3a364517698462e13e457c9", size = 2178744 }, - { url = "https://files.pythonhosted.org/packages/c0/bd/4dd1e8a9e632f325d9203ce543402f912f26efd213c8d9efec0180fbac64/fonttools-4.47.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4ec558c543609e71b2275c4894e93493f65d2f41c15fe1d089080c1d0bb4d635", size = 2754076 }, - { url = "https://files.pythonhosted.org/packages/e6/4d/c2ebaac81dadbc3fc3c3c2fa5fe7b16429dc713b1b8ace49e11e92904d78/fonttools-4.47.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e040f905d542362e07e72e03612a6270c33d38281fd573160e1003e43718d68d", size = 2263784 }, - { url = "https://files.pythonhosted.org/packages/d3/f6/9d484cd275845c7e503a8669a5952a7fa089c7a881babb4dce5ebe6fc5d1/fonttools-4.47.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6dd58cc03016b281bd2c74c84cdaa6bd3ce54c5a7f47478b7657b930ac3ed8eb", size = 4769142 }, - { url = "https://files.pythonhosted.org/packages/7a/bf/c6ae0768a531b38245aac0bb8d30bc05d53d499e09fccdc5d72e7c8d28b6/fonttools-4.47.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32ab2e9702dff0dd4510c7bb958f265a8d3dd5c0e2547e7b5f7a3df4979abb07", size = 4853241 }, - { url = "https://files.pythonhosted.org/packages/2b/f0/c06709666cb7722447efb70ea456c302bd6eb3b997d30076401fb32bca4b/fonttools-4.47.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a808f3c1d1df1f5bf39be869b6e0c263570cdafb5bdb2df66087733f566ea71", size = 4730447 }, - { url = "https://files.pythonhosted.org/packages/3e/71/4c758ae5f4f8047904fc1c6bbbb828248c94cc7aa6406af3a62ede766f25/fonttools-4.47.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ac71e2e201df041a2891067dc36256755b1229ae167edbdc419b16da78732c2f", size = 4809265 }, - { url = "https://files.pythonhosted.org/packages/81/f6/a6912c11280607d48947341e2167502605a3917925c835afcd7dfcabc289/fonttools-4.47.2-cp312-cp312-win32.whl", hash = "sha256:69731e8bea0578b3c28fdb43dbf95b9386e2d49a399e9a4ad736b8e479b08085", size = 2118363 }, - { url = "https://files.pythonhosted.org/packages/81/4b/42d0488765ea5aa308b4e8197cb75366b2124240a73e86f98b6107ccf282/fonttools-4.47.2-cp312-cp312-win_amd64.whl", hash = "sha256:b3e1304e5f19ca861d86a72218ecce68f391646d85c851742d265787f55457a4", size = 2165866 }, - { url = "https://files.pythonhosted.org/packages/af/2f/c34b0f99d46766cf49566d1ee2ee3606e4c9880b5a7d734257dc61c804e9/fonttools-4.47.2-py3-none-any.whl", hash = "sha256:7eb7ad665258fba68fd22228a09f347469d95a97fb88198e133595947a20a184", size = 1063011 }, + { url = "https://files.pythonhosted.org/packages/19/30/02de0b7f3d72f2c4fce3e512b166c1bdbe5a687408474b61eb0114be921c/fonttools-4.47.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3b629108351d25512d4ea1a8393a2dba325b7b7d7308116b605ea3f8e1be88df", size = 2779949, upload_time = "2024-01-11T11:19:56.276Z" }, + { url = "https://files.pythonhosted.org/packages/9a/52/1a5e1373afb78a040ea0c371ab8a79da121060a8e518968bb8f41457ca90/fonttools-4.47.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c19044256c44fe299d9a73456aabee4b4d06c6b930287be93b533b4737d70aa1", size = 2281336, upload_time = "2024-01-11T11:20:08.835Z" }, + { url = "https://files.pythonhosted.org/packages/c5/ce/9d3b5bf51aafee024566ebb374f5b040381d92660cb04647af3c5860c611/fonttools-4.47.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8be28c036b9f186e8c7eaf8a11b42373e7e4949f9e9f370202b9da4c4c3f56c", size = 4541692, upload_time = "2024-01-11T11:20:13.378Z" }, + { url = "https://files.pythonhosted.org/packages/e8/68/af41b7cfd35c7418e17b6a43bb106be4b0f0e5feb405a88dee29b186f2a7/fonttools-4.47.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f83a4daef6d2a202acb9bf572958f91cfde5b10c8ee7fb1d09a4c81e5d851fd8", size = 4600529, upload_time = "2024-01-11T11:20:17.27Z" }, + { url = "https://files.pythonhosted.org/packages/ab/7e/428dbb4cfc342b7a05cbc9d349e134e7fad6588f4ce2a7128e8e3e58ad3b/fonttools-4.47.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5a5318ba5365d992666ac4fe35365f93004109d18858a3e18ae46f67907670", size = 4524215, upload_time = "2024-01-11T11:20:21.061Z" }, + { url = "https://files.pythonhosted.org/packages/a6/61/762fad1cc1debc4626f2eb373fa999591c63c231fce53d5073574a639531/fonttools-4.47.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8f57ecd742545362a0f7186774b2d1c53423ed9ece67689c93a1055b236f638c", size = 4584778, upload_time = "2024-01-11T11:20:25.815Z" }, + { url = "https://files.pythonhosted.org/packages/04/30/170ca22284c1d825470e8b5871d6b25d3a70e2f5b185ffb1647d5e11ee4d/fonttools-4.47.2-cp310-cp310-win32.whl", hash = "sha256:a1c154bb85dc9a4cf145250c88d112d88eb414bad81d4cb524d06258dea1bdc0", size = 2131876, upload_time = "2024-01-11T11:20:30.261Z" }, + { url = "https://files.pythonhosted.org/packages/df/07/4a30437bed355b838b8ce31d14c5983334c31adc97e70c6ecff90c60d6d2/fonttools-4.47.2-cp310-cp310-win_amd64.whl", hash = "sha256:3e2b95dce2ead58fb12524d0ca7d63a63459dd489e7e5838c3cd53557f8933e1", size = 2177937, upload_time = "2024-01-11T11:20:33.814Z" }, + { url = "https://files.pythonhosted.org/packages/dd/1d/670372323642eada0f7743cfcdd156de6a28d37769c916421fec2f32c814/fonttools-4.47.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:29495d6d109cdbabe73cfb6f419ce67080c3ef9ea1e08d5750240fd4b0c4763b", size = 2782908, upload_time = "2024-01-11T11:20:37.495Z" }, + { url = "https://files.pythonhosted.org/packages/c1/36/5f0bb863a6575db4c4b67fa9be7f98e4c551dd87638ef327bc180b988998/fonttools-4.47.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0a1d313a415eaaba2b35d6cd33536560deeebd2ed758b9bfb89ab5d97dc5deac", size = 2283501, upload_time = "2024-01-11T11:20:42.027Z" }, + { url = "https://files.pythonhosted.org/packages/bd/1e/95de682a86567426bcc40a56c9b118ffa97de6cbfcc293addf20994e329d/fonttools-4.47.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90f898cdd67f52f18049250a6474185ef6544c91f27a7bee70d87d77a8daf89c", size = 4848039, upload_time = "2024-01-11T11:20:47.038Z" }, + { url = "https://files.pythonhosted.org/packages/ef/95/92a0b5fc844c1db734752f8a51431de519cd6b02e7e561efa9e9fd415544/fonttools-4.47.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3480eeb52770ff75140fe7d9a2ec33fb67b07efea0ab5129c7e0c6a639c40c70", size = 4893166, upload_time = "2024-01-11T11:20:50.855Z" }, + { url = "https://files.pythonhosted.org/packages/ff/e6/ed9dd7ee1afd6cd70eb7237688118fe489dbde962e3765c91c86c095f84b/fonttools-4.47.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0255dbc128fee75fb9be364806b940ed450dd6838672a150d501ee86523ac61e", size = 4815529, upload_time = "2024-01-11T11:20:54.696Z" }, + { url = "https://files.pythonhosted.org/packages/6b/67/cdffa0b3cd8f863b45125c335bbd3d9dc16ec42f5a8d5b64dd1244c5ce6b/fonttools-4.47.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f791446ff297fd5f1e2247c188de53c1bfb9dd7f0549eba55b73a3c2087a2703", size = 4875414, upload_time = "2024-01-11T11:20:58.435Z" }, + { url = "https://files.pythonhosted.org/packages/b8/fb/41638e748c8f20f5483987afcf9be746d3ccb9e9600ca62128a27c791a82/fonttools-4.47.2-cp311-cp311-win32.whl", hash = "sha256:740947906590a878a4bde7dd748e85fefa4d470a268b964748403b3ab2aeed6c", size = 2130073, upload_time = "2024-01-11T11:21:02.056Z" }, + { url = "https://files.pythonhosted.org/packages/a0/ef/93321cf55180a778b4d97919b28739874c0afab90e7b9f5b232db70f47c2/fonttools-4.47.2-cp311-cp311-win_amd64.whl", hash = "sha256:63fbed184979f09a65aa9c88b395ca539c94287ba3a364517698462e13e457c9", size = 2178744, upload_time = "2024-01-11T11:21:05.88Z" }, + { url = "https://files.pythonhosted.org/packages/c0/bd/4dd1e8a9e632f325d9203ce543402f912f26efd213c8d9efec0180fbac64/fonttools-4.47.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4ec558c543609e71b2275c4894e93493f65d2f41c15fe1d089080c1d0bb4d635", size = 2754076, upload_time = "2024-01-11T11:21:09.745Z" }, + { url = "https://files.pythonhosted.org/packages/e6/4d/c2ebaac81dadbc3fc3c3c2fa5fe7b16429dc713b1b8ace49e11e92904d78/fonttools-4.47.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e040f905d542362e07e72e03612a6270c33d38281fd573160e1003e43718d68d", size = 2263784, upload_time = "2024-01-11T11:21:13.367Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f6/9d484cd275845c7e503a8669a5952a7fa089c7a881babb4dce5ebe6fc5d1/fonttools-4.47.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6dd58cc03016b281bd2c74c84cdaa6bd3ce54c5a7f47478b7657b930ac3ed8eb", size = 4769142, upload_time = "2024-01-11T11:21:17.615Z" }, + { url = "https://files.pythonhosted.org/packages/7a/bf/c6ae0768a531b38245aac0bb8d30bc05d53d499e09fccdc5d72e7c8d28b6/fonttools-4.47.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32ab2e9702dff0dd4510c7bb958f265a8d3dd5c0e2547e7b5f7a3df4979abb07", size = 4853241, upload_time = "2024-01-11T11:21:21.16Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f0/c06709666cb7722447efb70ea456c302bd6eb3b997d30076401fb32bca4b/fonttools-4.47.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a808f3c1d1df1f5bf39be869b6e0c263570cdafb5bdb2df66087733f566ea71", size = 4730447, upload_time = "2024-01-11T11:21:24.755Z" }, + { url = "https://files.pythonhosted.org/packages/3e/71/4c758ae5f4f8047904fc1c6bbbb828248c94cc7aa6406af3a62ede766f25/fonttools-4.47.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ac71e2e201df041a2891067dc36256755b1229ae167edbdc419b16da78732c2f", size = 4809265, upload_time = "2024-01-11T11:21:28.586Z" }, + { url = "https://files.pythonhosted.org/packages/81/f6/a6912c11280607d48947341e2167502605a3917925c835afcd7dfcabc289/fonttools-4.47.2-cp312-cp312-win32.whl", hash = "sha256:69731e8bea0578b3c28fdb43dbf95b9386e2d49a399e9a4ad736b8e479b08085", size = 2118363, upload_time = "2024-01-11T11:21:33.245Z" }, + { url = "https://files.pythonhosted.org/packages/81/4b/42d0488765ea5aa308b4e8197cb75366b2124240a73e86f98b6107ccf282/fonttools-4.47.2-cp312-cp312-win_amd64.whl", hash = "sha256:b3e1304e5f19ca861d86a72218ecce68f391646d85c851742d265787f55457a4", size = 2165866, upload_time = "2024-01-11T11:21:37.23Z" }, + { url = "https://files.pythonhosted.org/packages/af/2f/c34b0f99d46766cf49566d1ee2ee3606e4c9880b5a7d734257dc61c804e9/fonttools-4.47.2-py3-none-any.whl", hash = "sha256:7eb7ad665258fba68fd22228a09f347469d95a97fb88198e133595947a20a184", size = 1063011, upload_time = "2024-01-11T11:22:41.676Z" }, ] [[package]] name = "fsspec" version = "2023.12.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/08/cac914ff6ff46c4500fc4323a939dbe7a0f528cca04e7fd3e859611dea41/fsspec-2023.12.2.tar.gz", hash = "sha256:8548d39e8810b59c38014934f6b31e57f40c1b20f911f4cc2b85389c7e9bf0cb", size = 167507 } +sdist = { url = "https://files.pythonhosted.org/packages/fa/08/cac914ff6ff46c4500fc4323a939dbe7a0f528cca04e7fd3e859611dea41/fsspec-2023.12.2.tar.gz", hash = "sha256:8548d39e8810b59c38014934f6b31e57f40c1b20f911f4cc2b85389c7e9bf0cb", size = 167507, upload_time = "2023-12-11T21:19:54.832Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/25/fab23259a52ece5670dcb8452e1af34b89e6135ecc17cd4b54b4b479eac6/fsspec-2023.12.2-py3-none-any.whl", hash = "sha256:d800d87f72189a745fa3d6b033b9dc4a34ad069f60ca60b943a63599f5501960", size = 168979 }, + { url = "https://files.pythonhosted.org/packages/70/25/fab23259a52ece5670dcb8452e1af34b89e6135ecc17cd4b54b4b479eac6/fsspec-2023.12.2-py3-none-any.whl", hash = "sha256:d800d87f72189a745fa3d6b033b9dc4a34ad069f60ca60b943a63599f5501960", size = 168979, upload_time = "2023-12-11T21:19:52.446Z" }, ] [[package]] @@ -628,9 +637,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a5/d3/8650919bc3c7c6e90ee3fa7fd618bf373cbbe55dff043bd67353dbb20cd8/ftfy-6.3.1.tar.gz", hash = "sha256:9b3c3d90f84fb267fe64d375a07b7f8912d817cf86009ae134aa03e1819506ec", size = 308927 } +sdist = { url = "https://files.pythonhosted.org/packages/a5/d3/8650919bc3c7c6e90ee3fa7fd618bf373cbbe55dff043bd67353dbb20cd8/ftfy-6.3.1.tar.gz", hash = "sha256:9b3c3d90f84fb267fe64d375a07b7f8912d817cf86009ae134aa03e1819506ec", size = 308927, upload_time = "2024-10-26T00:50:35.149Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/6e/81d47999aebc1b155f81eca4477a616a70f238a2549848c38983f3c22a82/ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083", size = 44821 }, + { url = "https://files.pythonhosted.org/packages/ab/6e/81d47999aebc1b155f81eca4477a616a70f238a2549848c38983f3c22a82/ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083", size = 44821, upload_time = "2024-10-26T00:50:33.425Z" }, ] [[package]] @@ -643,41 +652,41 @@ dependencies = [ { name = "zope-event" }, { name = "zope-interface" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/70/f0/be10ed5d7721ed2317d7feb59e167603217156c2a6d57f128523e24e673d/gevent-24.10.3.tar.gz", hash = "sha256:aa7ee1bd5cabb2b7ef35105f863b386c8d5e332f754b60cfc354148bd70d35d1", size = 6108837 } +sdist = { url = "https://files.pythonhosted.org/packages/70/f0/be10ed5d7721ed2317d7feb59e167603217156c2a6d57f128523e24e673d/gevent-24.10.3.tar.gz", hash = "sha256:aa7ee1bd5cabb2b7ef35105f863b386c8d5e332f754b60cfc354148bd70d35d1", size = 6108837, upload_time = "2024-10-18T16:06:25.867Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/6f/a2100e7883c7bdfc2b45cb60b310ca748762a21596258b9dd01c5c093dbc/gevent-24.10.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:d7a1ad0f2da582f5bd238bca067e1c6c482c30c15a6e4d14aaa3215cbb2232f3", size = 3014382 }, - { url = "https://files.pythonhosted.org/packages/7a/b1/460e4884ed6185d9eb9c4c2e9639d2b254197e46513301c0f63dec22dc90/gevent-24.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4e526fdc279c655c1e809b0c34b45844182c2a6b219802da5e411bd2cf5a8ad", size = 4853460 }, - { url = "https://files.pythonhosted.org/packages/ca/f6/7ded98760d381229183ecce8db2edcce96f13e23807d31a90c66dae85304/gevent-24.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57a5c4e0bdac482c5f02f240d0354e61362df73501ef6ebafce8ef635cad7527", size = 4977636 }, - { url = "https://files.pythonhosted.org/packages/7d/21/7b928e6029eedb93ef94fc0aee701f497af2e601f0ec00aac0e72e3f450e/gevent-24.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d67daed8383326dc8b5e58d88e148d29b6b52274a489e383530b0969ae7b9cb9", size = 5058031 }, - { url = "https://files.pythonhosted.org/packages/00/98/12c03fd004fbeeca01276ffc589f5a368fd741d02582ab7006d1bdef57e7/gevent-24.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e24ffea72e27987979c009536fd0868e52239b44afe6cf7135ce8aafd0f108e", size = 6683694 }, - { url = "https://files.pythonhosted.org/packages/64/4c/ea14d971452d3da09e49267e052d8312f112c7835120aed78d22ef14efee/gevent-24.10.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c1d80090485da1ea3d99205fe97908b31188c1f4857f08b333ffaf2de2e89d18", size = 5286063 }, - { url = "https://files.pythonhosted.org/packages/39/3f/397efff27e637d7306caa00d1560512c44028c25c70be1e72c46b79b1b66/gevent-24.10.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f0c129f81d60cda614acb4b0c5731997ca05b031fb406fcb58ad53a7ade53b13", size = 6817462 }, - { url = "https://files.pythonhosted.org/packages/aa/5d/19939eaa7c5b7c0f37e0a0665a911ddfe1e35c25c512446fc356a065c16e/gevent-24.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:26ca7a6b42d35129617025ac801135118333cad75856ffc3217b38e707383eba", size = 1566631 }, - { url = "https://files.pythonhosted.org/packages/6e/01/1be5cf013826d8baae235976d6a94f3628014fd2db7c071aeec13f82b4d1/gevent-24.10.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:68c3a0d8402755eba7f69022e42e8021192a721ca8341908acc222ea597029b6", size = 2966909 }, - { url = "https://files.pythonhosted.org/packages/fe/3e/7fa9ab023f24d8689e2c77951981f8ea1f25089e0349a0bf8b35ee9b9277/gevent-24.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d850a453d66336272be4f1d3a8126777f3efdaea62d053b4829857f91e09755", size = 4913247 }, - { url = "https://files.pythonhosted.org/packages/db/63/6e40eaaa3c2abd1561faff11dc3e6781f8c25e975354b8835762834415af/gevent-24.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8e58ee3723f1fbe07d66892f1caa7481c306f653a6829b6fd16cb23d618a5915", size = 5049036 }, - { url = "https://files.pythonhosted.org/packages/94/89/158bc32cdc898dda0481040ac18650022e73133d93460c5af56ca622fe9a/gevent-24.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b52382124eca13135a3abe4f65c6bd428656975980a48e51b17aeab68bdb14db", size = 5107299 }, - { url = "https://files.pythonhosted.org/packages/64/91/1abe62ee350fdfac186d33f615d0d3a0b3b140e7ccf23c73547aa0deec44/gevent-24.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ca2266e08f43c0e22c028801dff7d92a0b102ef20e4caeb6a46abfb95f6a328", size = 6819625 }, - { url = "https://files.pythonhosted.org/packages/92/8b/0b2fe0d36b7c4d463e46cc68eaf6c14488bd7d86cc37e995c64a0ff7d02f/gevent-24.10.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d758f0d4dbf32502ec87bb9b536ca8055090a16f8305f0ada3ce6f34e70f2fd7", size = 5474079 }, - { url = "https://files.pythonhosted.org/packages/12/7b/9f5abbf0021a50321314f850697e0f46d2e5081168223af2d8544af9d19f/gevent-24.10.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0de6eb3d55c03138fda567d9bfed28487ce5d0928c5107549767a93efdf2be26", size = 6901323 }, - { url = "https://files.pythonhosted.org/packages/8a/63/607715c621ae78ed581b7ba36d076df63feeb352993d521327f865056771/gevent-24.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:385710355eadecdb70428a5ae3e7e5a45dcf888baa1426884588be9d25ac4290", size = 1549468 }, - { url = "https://files.pythonhosted.org/packages/d9/e4/4edbe17001bb3e6fade4ad2d85ca8f9e4eabcbde4aa29aa6889281616e3e/gevent-24.10.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ad8fb70aa0ebc935729c9699ac31b210a49b689a7b27b7ac9f91676475f3f53", size = 2970952 }, - { url = "https://files.pythonhosted.org/packages/3c/a6/ce0824fe9398ba6b00028a74840f12be1165d5feaacdc028ea953db3d6c3/gevent-24.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f18689f7a70d2ed0e75bad5036ec3c89690a493d4cfac8d7cdb258ac04b132bd", size = 5172230 }, - { url = "https://files.pythonhosted.org/packages/25/d4/9002cfb585bfa52c860ed4b1349d1a6400bdf2df9f1bd21df5ff33eea33c/gevent-24.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f4f171d4d2018170454d84c934842e1b5f6ce7468ba298f6e7f7cff15000a3", size = 5338394 }, - { url = "https://files.pythonhosted.org/packages/0c/98/222f1a14f22ad2d1cbcc37edb74095264c1f9c7ab49e6423693383462b8a/gevent-24.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7021e26d70189b33c27173d4173f27bf4685d6b6f1c0ea50e5335f8491cb110c", size = 5437989 }, - { url = "https://files.pythonhosted.org/packages/bf/e8/cbb46afea3c7ecdc7289e15cb4a6f89903f4f9754a27ca320d3e465abc78/gevent-24.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34aea15f9c79f27a8faeaa361bc1e72c773a9b54a1996a2ec4eefc8bcd59a824", size = 6838539 }, - { url = "https://files.pythonhosted.org/packages/69/c3/e43e348f23da404a6d4368a14453ed097cdfca97d5212eaceb987d04a0e1/gevent-24.10.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8af65a4d4feaec6042c666d22c322a310fba3b47e841ad52f724b9c3ce5da48e", size = 5513842 }, - { url = "https://files.pythonhosted.org/packages/c2/76/84b7c19c072a80900118717a85236859127d630cdf8b079fe42f19649f12/gevent-24.10.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:89c4115e3f5ada55f92b61701a46043fe42f702b5af863b029e4c1a76f6cc2d4", size = 6927374 }, - { url = "https://files.pythonhosted.org/packages/5e/69/0ab1b04c363547058fb5035275c144957b80b36cb6aee715fe6181b0cee9/gevent-24.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:1ce6dab94c0b0d24425ba55712de2f8c9cb21267150ca63f5bb3a0e1f165da99", size = 1546701 }, - { url = "https://files.pythonhosted.org/packages/f7/2d/c783583d7999cd2f2e7aa2d6a1c333d663003ca61255a89ff6a891be95f4/gevent-24.10.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:f147e38423fbe96e8731f60a63475b3d2cab2f3d10578d8ee9d10c507c58a2ff", size = 2962857 }, - { url = "https://files.pythonhosted.org/packages/f3/77/d3ce96fd49406f61976e9a3b6c742b97bb274d3b30c68ff190c5b5f81afd/gevent-24.10.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18e6984ec96fc95fd67488555c38ece3015be1f38b1bcceb27b7d6c36b343008", size = 5141676 }, - { url = "https://files.pythonhosted.org/packages/49/f4/f99f893770c316b9d2f03bd684947126cbed0321b89fe5423838974c2025/gevent-24.10.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:051b22e2758accfddb0457728bfc9abf8c3f2ce6bca43f1ff6e07b5ed9e49bf4", size = 5310248 }, - { url = "https://files.pythonhosted.org/packages/e3/0c/67257ba906f76ed82e8f0bd8c00c2a0687b360a1050b70db7e58dff749ab/gevent-24.10.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb5edb6433764119a664bbb148d2aea9990950aa89cc3498f475c2408d523ea3", size = 5407304 }, - { url = "https://files.pythonhosted.org/packages/35/6c/3a72da7c224b0111728130c0f1abc3ee07feff91b37e0ea83db98f4a3eaf/gevent-24.10.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce417bcaaab496bc9c77f75566531e9d93816262037b8b2dbb88b0fdcd66587c", size = 6818624 }, - { url = "https://files.pythonhosted.org/packages/a3/96/cc5f6ecba032a45fc312fe0db2908a893057fd81361eea93845d6c325556/gevent-24.10.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:1c3a828b033fb02b7c31da4d75014a1f82e6c072fc0523456569a57f8b025861", size = 5484356 }, - { url = "https://files.pythonhosted.org/packages/7c/97/e680b2b2f0c291ae4db9813ffbf02c22c2a0f14c8f1a613971385e29ef67/gevent-24.10.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f2ae3efbbd120cdf4a68b7abc27a37e61e6f443c5a06ec2c6ad94c37cd8471ec", size = 6903191 }, - { url = "https://files.pythonhosted.org/packages/1b/1c/b4181957da062d1c060974ec6cb798cc24aeeb28e8cd2ece84eb4b4991f7/gevent-24.10.3-cp313-cp313-win_amd64.whl", hash = "sha256:9e1210334a9bc9f76c3d008e0785ca62214f8a54e1325f6c2ecab3b6a572a015", size = 1545117 }, - { url = "https://files.pythonhosted.org/packages/89/2b/bf4af9950b8f9abd5b4025858f6311930de550e3498bbfeb47c914701a1d/gevent-24.10.3-pp310-pypy310_pp73-macosx_11_0_universal2.whl", hash = "sha256:e534e6a968d74463b11de6c9c67f4b4bf61775fb00f2e6e0f7fcdd412ceade18", size = 1271541 }, + { url = "https://files.pythonhosted.org/packages/6b/6f/a2100e7883c7bdfc2b45cb60b310ca748762a21596258b9dd01c5c093dbc/gevent-24.10.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:d7a1ad0f2da582f5bd238bca067e1c6c482c30c15a6e4d14aaa3215cbb2232f3", size = 3014382, upload_time = "2024-10-18T15:37:34.041Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b1/460e4884ed6185d9eb9c4c2e9639d2b254197e46513301c0f63dec22dc90/gevent-24.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4e526fdc279c655c1e809b0c34b45844182c2a6b219802da5e411bd2cf5a8ad", size = 4853460, upload_time = "2024-10-18T16:19:39.515Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f6/7ded98760d381229183ecce8db2edcce96f13e23807d31a90c66dae85304/gevent-24.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57a5c4e0bdac482c5f02f240d0354e61362df73501ef6ebafce8ef635cad7527", size = 4977636, upload_time = "2024-10-18T16:18:45.464Z" }, + { url = "https://files.pythonhosted.org/packages/7d/21/7b928e6029eedb93ef94fc0aee701f497af2e601f0ec00aac0e72e3f450e/gevent-24.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d67daed8383326dc8b5e58d88e148d29b6b52274a489e383530b0969ae7b9cb9", size = 5058031, upload_time = "2024-10-18T16:23:10.719Z" }, + { url = "https://files.pythonhosted.org/packages/00/98/12c03fd004fbeeca01276ffc589f5a368fd741d02582ab7006d1bdef57e7/gevent-24.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e24ffea72e27987979c009536fd0868e52239b44afe6cf7135ce8aafd0f108e", size = 6683694, upload_time = "2024-10-18T15:59:35.475Z" }, + { url = "https://files.pythonhosted.org/packages/64/4c/ea14d971452d3da09e49267e052d8312f112c7835120aed78d22ef14efee/gevent-24.10.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c1d80090485da1ea3d99205fe97908b31188c1f4857f08b333ffaf2de2e89d18", size = 5286063, upload_time = "2024-10-18T16:38:24.113Z" }, + { url = "https://files.pythonhosted.org/packages/39/3f/397efff27e637d7306caa00d1560512c44028c25c70be1e72c46b79b1b66/gevent-24.10.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f0c129f81d60cda614acb4b0c5731997ca05b031fb406fcb58ad53a7ade53b13", size = 6817462, upload_time = "2024-10-18T16:02:48.427Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5d/19939eaa7c5b7c0f37e0a0665a911ddfe1e35c25c512446fc356a065c16e/gevent-24.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:26ca7a6b42d35129617025ac801135118333cad75856ffc3217b38e707383eba", size = 1566631, upload_time = "2024-10-18T16:08:38.489Z" }, + { url = "https://files.pythonhosted.org/packages/6e/01/1be5cf013826d8baae235976d6a94f3628014fd2db7c071aeec13f82b4d1/gevent-24.10.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:68c3a0d8402755eba7f69022e42e8021192a721ca8341908acc222ea597029b6", size = 2966909, upload_time = "2024-10-18T15:37:31.43Z" }, + { url = "https://files.pythonhosted.org/packages/fe/3e/7fa9ab023f24d8689e2c77951981f8ea1f25089e0349a0bf8b35ee9b9277/gevent-24.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d850a453d66336272be4f1d3a8126777f3efdaea62d053b4829857f91e09755", size = 4913247, upload_time = "2024-10-18T16:19:41.792Z" }, + { url = "https://files.pythonhosted.org/packages/db/63/6e40eaaa3c2abd1561faff11dc3e6781f8c25e975354b8835762834415af/gevent-24.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8e58ee3723f1fbe07d66892f1caa7481c306f653a6829b6fd16cb23d618a5915", size = 5049036, upload_time = "2024-10-18T16:18:47.419Z" }, + { url = "https://files.pythonhosted.org/packages/94/89/158bc32cdc898dda0481040ac18650022e73133d93460c5af56ca622fe9a/gevent-24.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b52382124eca13135a3abe4f65c6bd428656975980a48e51b17aeab68bdb14db", size = 5107299, upload_time = "2024-10-18T16:23:12.296Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/1abe62ee350fdfac186d33f615d0d3a0b3b140e7ccf23c73547aa0deec44/gevent-24.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ca2266e08f43c0e22c028801dff7d92a0b102ef20e4caeb6a46abfb95f6a328", size = 6819625, upload_time = "2024-10-18T15:59:38.226Z" }, + { url = "https://files.pythonhosted.org/packages/92/8b/0b2fe0d36b7c4d463e46cc68eaf6c14488bd7d86cc37e995c64a0ff7d02f/gevent-24.10.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d758f0d4dbf32502ec87bb9b536ca8055090a16f8305f0ada3ce6f34e70f2fd7", size = 5474079, upload_time = "2024-10-18T16:38:26.866Z" }, + { url = "https://files.pythonhosted.org/packages/12/7b/9f5abbf0021a50321314f850697e0f46d2e5081168223af2d8544af9d19f/gevent-24.10.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0de6eb3d55c03138fda567d9bfed28487ce5d0928c5107549767a93efdf2be26", size = 6901323, upload_time = "2024-10-18T16:02:50.066Z" }, + { url = "https://files.pythonhosted.org/packages/8a/63/607715c621ae78ed581b7ba36d076df63feeb352993d521327f865056771/gevent-24.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:385710355eadecdb70428a5ae3e7e5a45dcf888baa1426884588be9d25ac4290", size = 1549468, upload_time = "2024-10-18T16:01:30.331Z" }, + { url = "https://files.pythonhosted.org/packages/d9/e4/4edbe17001bb3e6fade4ad2d85ca8f9e4eabcbde4aa29aa6889281616e3e/gevent-24.10.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ad8fb70aa0ebc935729c9699ac31b210a49b689a7b27b7ac9f91676475f3f53", size = 2970952, upload_time = "2024-10-18T15:37:31.389Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a6/ce0824fe9398ba6b00028a74840f12be1165d5feaacdc028ea953db3d6c3/gevent-24.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f18689f7a70d2ed0e75bad5036ec3c89690a493d4cfac8d7cdb258ac04b132bd", size = 5172230, upload_time = "2024-10-18T16:19:43.661Z" }, + { url = "https://files.pythonhosted.org/packages/25/d4/9002cfb585bfa52c860ed4b1349d1a6400bdf2df9f1bd21df5ff33eea33c/gevent-24.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f4f171d4d2018170454d84c934842e1b5f6ce7468ba298f6e7f7cff15000a3", size = 5338394, upload_time = "2024-10-18T16:18:49.371Z" }, + { url = "https://files.pythonhosted.org/packages/0c/98/222f1a14f22ad2d1cbcc37edb74095264c1f9c7ab49e6423693383462b8a/gevent-24.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7021e26d70189b33c27173d4173f27bf4685d6b6f1c0ea50e5335f8491cb110c", size = 5437989, upload_time = "2024-10-18T16:23:13.851Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e8/cbb46afea3c7ecdc7289e15cb4a6f89903f4f9754a27ca320d3e465abc78/gevent-24.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34aea15f9c79f27a8faeaa361bc1e72c773a9b54a1996a2ec4eefc8bcd59a824", size = 6838539, upload_time = "2024-10-18T15:59:40.489Z" }, + { url = "https://files.pythonhosted.org/packages/69/c3/e43e348f23da404a6d4368a14453ed097cdfca97d5212eaceb987d04a0e1/gevent-24.10.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8af65a4d4feaec6042c666d22c322a310fba3b47e841ad52f724b9c3ce5da48e", size = 5513842, upload_time = "2024-10-18T16:38:29.538Z" }, + { url = "https://files.pythonhosted.org/packages/c2/76/84b7c19c072a80900118717a85236859127d630cdf8b079fe42f19649f12/gevent-24.10.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:89c4115e3f5ada55f92b61701a46043fe42f702b5af863b029e4c1a76f6cc2d4", size = 6927374, upload_time = "2024-10-18T16:02:51.669Z" }, + { url = "https://files.pythonhosted.org/packages/5e/69/0ab1b04c363547058fb5035275c144957b80b36cb6aee715fe6181b0cee9/gevent-24.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:1ce6dab94c0b0d24425ba55712de2f8c9cb21267150ca63f5bb3a0e1f165da99", size = 1546701, upload_time = "2024-10-18T15:54:53.562Z" }, + { url = "https://files.pythonhosted.org/packages/f7/2d/c783583d7999cd2f2e7aa2d6a1c333d663003ca61255a89ff6a891be95f4/gevent-24.10.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:f147e38423fbe96e8731f60a63475b3d2cab2f3d10578d8ee9d10c507c58a2ff", size = 2962857, upload_time = "2024-10-18T15:37:33.098Z" }, + { url = "https://files.pythonhosted.org/packages/f3/77/d3ce96fd49406f61976e9a3b6c742b97bb274d3b30c68ff190c5b5f81afd/gevent-24.10.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18e6984ec96fc95fd67488555c38ece3015be1f38b1bcceb27b7d6c36b343008", size = 5141676, upload_time = "2024-10-18T16:19:45.484Z" }, + { url = "https://files.pythonhosted.org/packages/49/f4/f99f893770c316b9d2f03bd684947126cbed0321b89fe5423838974c2025/gevent-24.10.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:051b22e2758accfddb0457728bfc9abf8c3f2ce6bca43f1ff6e07b5ed9e49bf4", size = 5310248, upload_time = "2024-10-18T16:18:51.175Z" }, + { url = "https://files.pythonhosted.org/packages/e3/0c/67257ba906f76ed82e8f0bd8c00c2a0687b360a1050b70db7e58dff749ab/gevent-24.10.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb5edb6433764119a664bbb148d2aea9990950aa89cc3498f475c2408d523ea3", size = 5407304, upload_time = "2024-10-18T16:23:15.348Z" }, + { url = "https://files.pythonhosted.org/packages/35/6c/3a72da7c224b0111728130c0f1abc3ee07feff91b37e0ea83db98f4a3eaf/gevent-24.10.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce417bcaaab496bc9c77f75566531e9d93816262037b8b2dbb88b0fdcd66587c", size = 6818624, upload_time = "2024-10-18T15:59:42.068Z" }, + { url = "https://files.pythonhosted.org/packages/a3/96/cc5f6ecba032a45fc312fe0db2908a893057fd81361eea93845d6c325556/gevent-24.10.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:1c3a828b033fb02b7c31da4d75014a1f82e6c072fc0523456569a57f8b025861", size = 5484356, upload_time = "2024-10-18T16:38:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/7c/97/e680b2b2f0c291ae4db9813ffbf02c22c2a0f14c8f1a613971385e29ef67/gevent-24.10.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f2ae3efbbd120cdf4a68b7abc27a37e61e6f443c5a06ec2c6ad94c37cd8471ec", size = 6903191, upload_time = "2024-10-18T16:02:53.888Z" }, + { url = "https://files.pythonhosted.org/packages/1b/1c/b4181957da062d1c060974ec6cb798cc24aeeb28e8cd2ece84eb4b4991f7/gevent-24.10.3-cp313-cp313-win_amd64.whl", hash = "sha256:9e1210334a9bc9f76c3d008e0785ca62214f8a54e1325f6c2ecab3b6a572a015", size = 1545117, upload_time = "2024-10-18T15:45:47.375Z" }, + { url = "https://files.pythonhosted.org/packages/89/2b/bf4af9950b8f9abd5b4025858f6311930de550e3498bbfeb47c914701a1d/gevent-24.10.3-pp310-pypy310_pp73-macosx_11_0_universal2.whl", hash = "sha256:e534e6a968d74463b11de6c9c67f4b4bf61775fb00f2e6e0f7fcdd412ceade18", size = 1271541, upload_time = "2024-10-18T15:37:53.146Z" }, ] [[package]] @@ -690,103 +699,103 @@ dependencies = [ { name = "gevent" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8c/14/d4eddae757de44985718a9e38d9e6f2a923d764ed97d0f1cbc1a8aa2b0ef/geventhttpclient-2.3.1.tar.gz", hash = "sha256:b40ddac8517c456818942c7812f555f84702105c82783238c9fcb8dc12675185", size = 69345 } +sdist = { url = "https://files.pythonhosted.org/packages/8c/14/d4eddae757de44985718a9e38d9e6f2a923d764ed97d0f1cbc1a8aa2b0ef/geventhttpclient-2.3.1.tar.gz", hash = "sha256:b40ddac8517c456818942c7812f555f84702105c82783238c9fcb8dc12675185", size = 69345, upload_time = "2024-04-18T21:39:50.83Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/a5/5e49d6a581b3f1399425e22961c6e341e90c12fa2193ed0adee9afbd864c/geventhttpclient-2.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:da22ab7bf5af4ba3d07cffee6de448b42696e53e7ac1fe97ed289037733bf1c2", size = 71729 }, - { url = "https://files.pythonhosted.org/packages/eb/23/4ff584e5f344dae64b5bc588b65c4ea81083f9d662b9f64cf5f28e5ae9cc/geventhttpclient-2.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2399e3d4e2fae8bbd91756189da6e9d84adf8f3eaace5eef0667874a705a29f8", size = 52062 }, - { url = "https://files.pythonhosted.org/packages/bb/60/6bd8badb97b31a49f4c2b79466abce208a97dad95d447893c7546063fc8a/geventhttpclient-2.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3e33e87d0d5b9f5782c4e6d3cb7e3592fea41af52713137d04776df7646d71b", size = 51645 }, - { url = "https://files.pythonhosted.org/packages/e1/62/47d431bf05f74aa683d63163a11432bda8f576c86dec8c3bc9d6a156ee03/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c071db313866c3d0510feb6c0f40ec086ccf7e4a845701b6316c82c06e8b9b29", size = 117838 }, - { url = "https://files.pythonhosted.org/packages/6c/8b/e7c9ae813bb41883a96ad9afcf86465219c3bb682daa8b09448481edef8a/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f36f0c6ef88a27e60af8369d9c2189fe372c6f2943182a7568e0f2ad33bb69f1", size = 123272 }, - { url = "https://files.pythonhosted.org/packages/4d/26/71e9b2526009faadda9f588dac04f8bf837a5b97628ab44145efc3fa796e/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4624843c03a5337282a42247d987c2531193e57255ee307b36eeb4f243a0c21", size = 114319 }, - { url = "https://files.pythonhosted.org/packages/34/8c/1da2960293c42b7a6b01dbe3204b569e4cdb55b8289cb1c7154826500f19/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d614573621ba827c417786057e1e20e9f96c4f6b3878c55b1b7b54e1026693bc", size = 112705 }, - { url = "https://files.pythonhosted.org/packages/a7/a1/4d08ecf0f213fdc63f78a217f87c07c1cb9891e68cdf74c8cbca76298bdb/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5d51330a40ac9762879d0e296c279c1beae8cfa6484bb196ac829242c416b709", size = 121236 }, - { url = "https://files.pythonhosted.org/packages/4f/f7/42ece3e1f54602c518d74364a214da3b35b6be267b335564b7e9f0d37705/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bc9f2162d4e8cb86bb5322d99bfd552088a3eacd540a841298f06bb8bc1f1f03", size = 117859 }, - { url = "https://files.pythonhosted.org/packages/1f/8e/de026b3697bffe5fa1a4938a3882107e378eea826905acf8e46c69b71ffd/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:06e59d3397e63c65ecc7a7561a5289f0cf2e2c2252e29632741e792f57f5d124", size = 127268 }, - { url = "https://files.pythonhosted.org/packages/54/bf/1ee99a322467e6825a24612d306a46ca94b51088170d1b5de0df1c82ab2a/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4436eef515b3e0c1d4a453ae32e047290e780a623c1eddb11026ae9d5fb03d42", size = 116426 }, - { url = "https://files.pythonhosted.org/packages/72/54/10c8ec745b3dcbfd52af62977fec85829749c0325e1a5429d050a4b45e75/geventhttpclient-2.3.1-cp310-cp310-win32.whl", hash = "sha256:5d1cf7d8a4f8e15cc8fd7d88ac4cdb058d6274203a42587e594cc9f0850ac862", size = 47599 }, - { url = "https://files.pythonhosted.org/packages/da/0d/36a47cdeaa83c3b4efdbd18d77720fa27dc40600998f4dedd7c4a1259862/geventhttpclient-2.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:4deaebc121036f7ea95430c2d0f80ab085b15280e6ab677a6360b70e57020e7f", size = 48302 }, - { url = "https://files.pythonhosted.org/packages/56/ad/1fcbbea0465f04d4425960e3737d4d8ae6407043cfc88688fb17b9064160/geventhttpclient-2.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0ae055b9ce1704f2ce72c0847df28f4e14dbb3eea79256cda6c909d82688ea3", size = 71733 }, - { url = "https://files.pythonhosted.org/packages/06/1a/10e547adb675beea407ff7117ecb4e5063534569ac14bb4360279d2888dd/geventhttpclient-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f087af2ac439495b5388841d6f3c4de8d2573ca9870593d78f7b554aa5cfa7f5", size = 52060 }, - { url = "https://files.pythonhosted.org/packages/e0/c0/9960ac6e8818a00702743cd2a9637d6f26909ac7ac59ca231f446e367b20/geventhttpclient-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:76c367d175810facfe56281e516c9a5a4a191eff76641faaa30aa33882ed4b2f", size = 51649 }, - { url = "https://files.pythonhosted.org/packages/58/3a/b032cd8f885dafdfa002a8a0e4e21b633713798ec08e19010b815fbfead6/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a58376d0d461fe0322ff2ad362553b437daee1eeb92b4c0e3b1ffef9e77defbe", size = 117987 }, - { url = "https://files.pythonhosted.org/packages/94/36/6493a5cbc20c269a51186946947f3ca2eae687e05831289891027bd038c3/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f440cc704f8a9869848a109b2c401805c17c070539b2014e7b884ecfc8591e33", size = 123356 }, - { url = "https://files.pythonhosted.org/packages/2f/07/b66d9a13b97a7e59d84b4faf704113aa963aaf3a0f71c9138c8740d57d5c/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f10c62994f9052f23948c19de930b2d1f063240462c8bd7077c2b3290e61f4fa", size = 114460 }, - { url = "https://files.pythonhosted.org/packages/4e/72/1467b9e1ef63aecfe3b42333fb7607f66129dffaeca231f97e4be6f71803/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52c45d9f3dd9627844c12e9ca347258c7be585bed54046336220e25ea6eac155", size = 112808 }, - { url = "https://files.pythonhosted.org/packages/ce/ef/64894efd67cb3459074c734736ecacff398cd841a5538dc70e3e77d35500/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:77c1a2c6e3854bf87cd5588b95174640c8a881716bd07fa0d131d082270a6795", size = 122049 }, - { url = "https://files.pythonhosted.org/packages/c5/c8/1b13b4ea4bb88d7c2db56d070a52daf4757b3139afd83885e81455cb422f/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ce649d4e25c2d56023471df0bf1e8e2ab67dfe4ff12ce3e8fe7e6fae30cd672a", size = 118755 }, - { url = "https://files.pythonhosted.org/packages/d1/06/95ac63fa1ee118a4d5824aa0a6b0dc3a2934a2f4ce695bf6747e1744d813/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:265d9f31b4ac8f688eebef0bd4c814ffb37a16f769ad0c8c8b8c24a84db8eab5", size = 128053 }, - { url = "https://files.pythonhosted.org/packages/8a/27/3d6dbbd128e1b965bae198bffa4b5552cd635397e3d2bbcc7d9592890ca9/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2de436a9d61dae877e4e811fb3e2594e2a1df1b18f4280878f318aef48a562b9", size = 117316 }, - { url = "https://files.pythonhosted.org/packages/ed/9a/8b65daf417ff982fa1928ebc6ebdfb081750d426f877f0056288aaa689e8/geventhttpclient-2.3.1-cp311-cp311-win32.whl", hash = "sha256:83e22178b9480b0a95edf0053d4f30b717d0b696b3c262beabe6964d9c5224b1", size = 47598 }, - { url = "https://files.pythonhosted.org/packages/ab/83/ed0d14787861cf30beddd3aadc29ad07d75555de43c629ba514ddd2978d0/geventhttpclient-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:97b072a282233384c1302a7dee88ad8bfedc916f06b1bc1da54f84980f1406a9", size = 48301 }, - { url = "https://files.pythonhosted.org/packages/82/ee/bf3d26170a518d2b1254f44202f2fa4490496b476ee24046ff6c34e79c08/geventhttpclient-2.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e1c90abcc2735cd8dd2d2572a13da32f6625392dc04862decb5c6476a3ddee22", size = 71742 }, - { url = "https://files.pythonhosted.org/packages/77/72/bd64b2a491094a3fbf7f3c314bb3c3918afb652783a8a9db07b86072da35/geventhttpclient-2.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5deb41c2f51247b4e568c14964f59d7b8e537eff51900564c88af3200004e678", size = 52070 }, - { url = "https://files.pythonhosted.org/packages/85/96/e25becfde16c5551ba04ed2beac1f018e2efc70275ec19ae3765ff634ff2/geventhttpclient-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c6f1a56a66a90c4beae2f009b5e9d42db9a58ced165aa35441ace04d69cb7b37", size = 51650 }, - { url = "https://files.pythonhosted.org/packages/5d/b8/fe6e938a369b3742103d04e5771e1ec7b18c047ac30b06a8e9704e2d34fc/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ee6e741849c29e3129b1ec3828ac3a5e5dcb043402f852ea92c52334fb8cabf", size = 118507 }, - { url = "https://files.pythonhosted.org/packages/68/0b/381d01de049b02dc70addbcc1c8e24d15500bff6a9e89103c4aa8eb352c3/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d0972096a63b1ddaa73fa3dab2c7a136e3ab8bf7999a2f85a5dee851fa77cdd", size = 124061 }, - { url = "https://files.pythonhosted.org/packages/c6/e6/7c97b5bf41cc403b2936a0887a85550b3153aa4b60c0c5062c49cd6286f2/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00675ba682fb7d19d659c14686fa8a52a65e3f301b56c2a4ee6333b380dd9467", size = 115060 }, - { url = "https://files.pythonhosted.org/packages/45/1f/3e02464449c74a8146f27218471578c1dfabf18731cf047520b76e1b6331/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea77b67c186df90473416f4403839728f70ef6cf1689cec97b4f6bbde392a8a8", size = 113762 }, - { url = "https://files.pythonhosted.org/packages/4f/a4/08551776f7d6b219d6f73ca25be88806007b16af51a1dbfed7192528e1c3/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ddcc3f0fdffd9a3801e1005b73026202cffed8199863fdef9315bea9a860a032", size = 122018 }, - { url = "https://files.pythonhosted.org/packages/70/14/ba91417ac7cbce8d553f72c885a19c6b9d7f9dc7de81b7814551cf020a57/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:c9f1ef4ec048563cc621a47ff01a4f10048ff8b676d7a4d75e5433ed8e703e56", size = 118884 }, - { url = "https://files.pythonhosted.org/packages/7c/78/e1f2c30e11bda8347a74b3a7254f727ff53ea260244da77d76b96779a006/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:a364b30bec7a0a00dbe256e2b6807e4dc866bead7ac84aaa51ca5e2c3d15c258", size = 128224 }, - { url = "https://files.pythonhosted.org/packages/ac/2f/b7fd96e9cfa9d9719b0c9feb50b4cbb341d1940e34fd3305006efa8c3e33/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:25d255383d3d6a6fbd643bb51ae1a7e4f6f7b0dbd5f3225b537d0bd0432eaf39", size = 117758 }, - { url = "https://files.pythonhosted.org/packages/fb/e0/1384c9a76379ab257b75df92283797861dcae592dd98e471df254f87c635/geventhttpclient-2.3.1-cp312-cp312-win32.whl", hash = "sha256:ad0b507e354d2f398186dcb12fe526d0594e7c9387b514fb843f7a14fdf1729a", size = 47595 }, - { url = "https://files.pythonhosted.org/packages/54/e3/6b8dbb24e3941e20abbe7736e59290c5d4182057ea1d984d46c853208bcd/geventhttpclient-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:7924e0883bc2b177cfe27aa65af6bb9dd57f3e26905c7675a2d1f3ef69df7cca", size = 48271 }, - { url = "https://files.pythonhosted.org/packages/ee/9f/251b1b7e665523137a8711f0f0029196cf18b57741135f01aea80a56f16c/geventhttpclient-2.3.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c31431e38df45b3c79bf3c9427c796adb8263d622bc6fa25e2f6ba916c2aad93", size = 49827 }, - { url = "https://files.pythonhosted.org/packages/74/c7/ad4c23de669191e1c83cfa28c51d3b50fc246d72e1ee40d4d5b330532492/geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:855ab1e145575769b180b57accb0573a77cd6a7392f40a6ef7bc9a4926ebd77b", size = 54017 }, - { url = "https://files.pythonhosted.org/packages/04/7b/59fc8c8fbd10596abfc46dc103654e3d9676de64229d8eee4b0a4ac2e890/geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a374aad77c01539e786d0c7829bec2eba034ccd45733c1bf9811ad18d2a8ecd", size = 58359 }, - { url = "https://files.pythonhosted.org/packages/94/b7/743552b0ecda75458c83d55d62937e29c9ee9a42598f57d4025d5de70004/geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c1e97460608304f400485ac099736fff3566d3d8db2038533d466f8cf5de5a", size = 54262 }, - { url = "https://files.pythonhosted.org/packages/18/60/10f6215b6cc76b5845a7f4b9c3d1f47d7ecd84ce8769b1e27e0482d605d7/geventhttpclient-2.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4f843f81ee44ba4c553a1b3f73115e0ad8f00044023c24db29f5b1df3da08465", size = 48343 }, + { url = "https://files.pythonhosted.org/packages/a2/a5/5e49d6a581b3f1399425e22961c6e341e90c12fa2193ed0adee9afbd864c/geventhttpclient-2.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:da22ab7bf5af4ba3d07cffee6de448b42696e53e7ac1fe97ed289037733bf1c2", size = 71729, upload_time = "2024-04-18T21:38:06.866Z" }, + { url = "https://files.pythonhosted.org/packages/eb/23/4ff584e5f344dae64b5bc588b65c4ea81083f9d662b9f64cf5f28e5ae9cc/geventhttpclient-2.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2399e3d4e2fae8bbd91756189da6e9d84adf8f3eaace5eef0667874a705a29f8", size = 52062, upload_time = "2024-04-18T21:38:08.433Z" }, + { url = "https://files.pythonhosted.org/packages/bb/60/6bd8badb97b31a49f4c2b79466abce208a97dad95d447893c7546063fc8a/geventhttpclient-2.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3e33e87d0d5b9f5782c4e6d3cb7e3592fea41af52713137d04776df7646d71b", size = 51645, upload_time = "2024-04-18T21:38:10.139Z" }, + { url = "https://files.pythonhosted.org/packages/e1/62/47d431bf05f74aa683d63163a11432bda8f576c86dec8c3bc9d6a156ee03/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c071db313866c3d0510feb6c0f40ec086ccf7e4a845701b6316c82c06e8b9b29", size = 117838, upload_time = "2024-04-18T21:38:12.036Z" }, + { url = "https://files.pythonhosted.org/packages/6c/8b/e7c9ae813bb41883a96ad9afcf86465219c3bb682daa8b09448481edef8a/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f36f0c6ef88a27e60af8369d9c2189fe372c6f2943182a7568e0f2ad33bb69f1", size = 123272, upload_time = "2024-04-18T21:38:13.704Z" }, + { url = "https://files.pythonhosted.org/packages/4d/26/71e9b2526009faadda9f588dac04f8bf837a5b97628ab44145efc3fa796e/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4624843c03a5337282a42247d987c2531193e57255ee307b36eeb4f243a0c21", size = 114319, upload_time = "2024-04-18T21:38:15.097Z" }, + { url = "https://files.pythonhosted.org/packages/34/8c/1da2960293c42b7a6b01dbe3204b569e4cdb55b8289cb1c7154826500f19/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d614573621ba827c417786057e1e20e9f96c4f6b3878c55b1b7b54e1026693bc", size = 112705, upload_time = "2024-04-18T21:38:17.005Z" }, + { url = "https://files.pythonhosted.org/packages/a7/a1/4d08ecf0f213fdc63f78a217f87c07c1cb9891e68cdf74c8cbca76298bdb/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5d51330a40ac9762879d0e296c279c1beae8cfa6484bb196ac829242c416b709", size = 121236, upload_time = "2024-04-18T21:38:18.831Z" }, + { url = "https://files.pythonhosted.org/packages/4f/f7/42ece3e1f54602c518d74364a214da3b35b6be267b335564b7e9f0d37705/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bc9f2162d4e8cb86bb5322d99bfd552088a3eacd540a841298f06bb8bc1f1f03", size = 117859, upload_time = "2024-04-18T21:38:20.917Z" }, + { url = "https://files.pythonhosted.org/packages/1f/8e/de026b3697bffe5fa1a4938a3882107e378eea826905acf8e46c69b71ffd/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:06e59d3397e63c65ecc7a7561a5289f0cf2e2c2252e29632741e792f57f5d124", size = 127268, upload_time = "2024-04-18T21:38:22.676Z" }, + { url = "https://files.pythonhosted.org/packages/54/bf/1ee99a322467e6825a24612d306a46ca94b51088170d1b5de0df1c82ab2a/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4436eef515b3e0c1d4a453ae32e047290e780a623c1eddb11026ae9d5fb03d42", size = 116426, upload_time = "2024-04-18T21:38:24.228Z" }, + { url = "https://files.pythonhosted.org/packages/72/54/10c8ec745b3dcbfd52af62977fec85829749c0325e1a5429d050a4b45e75/geventhttpclient-2.3.1-cp310-cp310-win32.whl", hash = "sha256:5d1cf7d8a4f8e15cc8fd7d88ac4cdb058d6274203a42587e594cc9f0850ac862", size = 47599, upload_time = "2024-04-18T21:38:26.385Z" }, + { url = "https://files.pythonhosted.org/packages/da/0d/36a47cdeaa83c3b4efdbd18d77720fa27dc40600998f4dedd7c4a1259862/geventhttpclient-2.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:4deaebc121036f7ea95430c2d0f80ab085b15280e6ab677a6360b70e57020e7f", size = 48302, upload_time = "2024-04-18T21:38:28.297Z" }, + { url = "https://files.pythonhosted.org/packages/56/ad/1fcbbea0465f04d4425960e3737d4d8ae6407043cfc88688fb17b9064160/geventhttpclient-2.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0ae055b9ce1704f2ce72c0847df28f4e14dbb3eea79256cda6c909d82688ea3", size = 71733, upload_time = "2024-04-18T21:38:30.357Z" }, + { url = "https://files.pythonhosted.org/packages/06/1a/10e547adb675beea407ff7117ecb4e5063534569ac14bb4360279d2888dd/geventhttpclient-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f087af2ac439495b5388841d6f3c4de8d2573ca9870593d78f7b554aa5cfa7f5", size = 52060, upload_time = "2024-04-18T21:38:32.561Z" }, + { url = "https://files.pythonhosted.org/packages/e0/c0/9960ac6e8818a00702743cd2a9637d6f26909ac7ac59ca231f446e367b20/geventhttpclient-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:76c367d175810facfe56281e516c9a5a4a191eff76641faaa30aa33882ed4b2f", size = 51649, upload_time = "2024-04-18T21:38:34.265Z" }, + { url = "https://files.pythonhosted.org/packages/58/3a/b032cd8f885dafdfa002a8a0e4e21b633713798ec08e19010b815fbfead6/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a58376d0d461fe0322ff2ad362553b437daee1eeb92b4c0e3b1ffef9e77defbe", size = 117987, upload_time = "2024-04-18T21:38:37.661Z" }, + { url = "https://files.pythonhosted.org/packages/94/36/6493a5cbc20c269a51186946947f3ca2eae687e05831289891027bd038c3/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f440cc704f8a9869848a109b2c401805c17c070539b2014e7b884ecfc8591e33", size = 123356, upload_time = "2024-04-18T21:38:39.705Z" }, + { url = "https://files.pythonhosted.org/packages/2f/07/b66d9a13b97a7e59d84b4faf704113aa963aaf3a0f71c9138c8740d57d5c/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f10c62994f9052f23948c19de930b2d1f063240462c8bd7077c2b3290e61f4fa", size = 114460, upload_time = "2024-04-18T21:38:40.947Z" }, + { url = "https://files.pythonhosted.org/packages/4e/72/1467b9e1ef63aecfe3b42333fb7607f66129dffaeca231f97e4be6f71803/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52c45d9f3dd9627844c12e9ca347258c7be585bed54046336220e25ea6eac155", size = 112808, upload_time = "2024-04-18T21:38:42.684Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ef/64894efd67cb3459074c734736ecacff398cd841a5538dc70e3e77d35500/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:77c1a2c6e3854bf87cd5588b95174640c8a881716bd07fa0d131d082270a6795", size = 122049, upload_time = "2024-04-18T21:38:44.184Z" }, + { url = "https://files.pythonhosted.org/packages/c5/c8/1b13b4ea4bb88d7c2db56d070a52daf4757b3139afd83885e81455cb422f/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ce649d4e25c2d56023471df0bf1e8e2ab67dfe4ff12ce3e8fe7e6fae30cd672a", size = 118755, upload_time = "2024-04-18T21:38:45.654Z" }, + { url = "https://files.pythonhosted.org/packages/d1/06/95ac63fa1ee118a4d5824aa0a6b0dc3a2934a2f4ce695bf6747e1744d813/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:265d9f31b4ac8f688eebef0bd4c814ffb37a16f769ad0c8c8b8c24a84db8eab5", size = 128053, upload_time = "2024-04-18T21:38:47.247Z" }, + { url = "https://files.pythonhosted.org/packages/8a/27/3d6dbbd128e1b965bae198bffa4b5552cd635397e3d2bbcc7d9592890ca9/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2de436a9d61dae877e4e811fb3e2594e2a1df1b18f4280878f318aef48a562b9", size = 117316, upload_time = "2024-04-18T21:38:49.086Z" }, + { url = "https://files.pythonhosted.org/packages/ed/9a/8b65daf417ff982fa1928ebc6ebdfb081750d426f877f0056288aaa689e8/geventhttpclient-2.3.1-cp311-cp311-win32.whl", hash = "sha256:83e22178b9480b0a95edf0053d4f30b717d0b696b3c262beabe6964d9c5224b1", size = 47598, upload_time = "2024-04-18T21:38:50.919Z" }, + { url = "https://files.pythonhosted.org/packages/ab/83/ed0d14787861cf30beddd3aadc29ad07d75555de43c629ba514ddd2978d0/geventhttpclient-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:97b072a282233384c1302a7dee88ad8bfedc916f06b1bc1da54f84980f1406a9", size = 48301, upload_time = "2024-04-18T21:38:52.14Z" }, + { url = "https://files.pythonhosted.org/packages/82/ee/bf3d26170a518d2b1254f44202f2fa4490496b476ee24046ff6c34e79c08/geventhttpclient-2.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e1c90abcc2735cd8dd2d2572a13da32f6625392dc04862decb5c6476a3ddee22", size = 71742, upload_time = "2024-04-18T21:38:54.167Z" }, + { url = "https://files.pythonhosted.org/packages/77/72/bd64b2a491094a3fbf7f3c314bb3c3918afb652783a8a9db07b86072da35/geventhttpclient-2.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5deb41c2f51247b4e568c14964f59d7b8e537eff51900564c88af3200004e678", size = 52070, upload_time = "2024-04-18T21:38:55.484Z" }, + { url = "https://files.pythonhosted.org/packages/85/96/e25becfde16c5551ba04ed2beac1f018e2efc70275ec19ae3765ff634ff2/geventhttpclient-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c6f1a56a66a90c4beae2f009b5e9d42db9a58ced165aa35441ace04d69cb7b37", size = 51650, upload_time = "2024-04-18T21:38:57.022Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b8/fe6e938a369b3742103d04e5771e1ec7b18c047ac30b06a8e9704e2d34fc/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ee6e741849c29e3129b1ec3828ac3a5e5dcb043402f852ea92c52334fb8cabf", size = 118507, upload_time = "2024-04-18T21:38:58.936Z" }, + { url = "https://files.pythonhosted.org/packages/68/0b/381d01de049b02dc70addbcc1c8e24d15500bff6a9e89103c4aa8eb352c3/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d0972096a63b1ddaa73fa3dab2c7a136e3ab8bf7999a2f85a5dee851fa77cdd", size = 124061, upload_time = "2024-04-18T21:39:00.473Z" }, + { url = "https://files.pythonhosted.org/packages/c6/e6/7c97b5bf41cc403b2936a0887a85550b3153aa4b60c0c5062c49cd6286f2/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00675ba682fb7d19d659c14686fa8a52a65e3f301b56c2a4ee6333b380dd9467", size = 115060, upload_time = "2024-04-18T21:39:02.323Z" }, + { url = "https://files.pythonhosted.org/packages/45/1f/3e02464449c74a8146f27218471578c1dfabf18731cf047520b76e1b6331/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea77b67c186df90473416f4403839728f70ef6cf1689cec97b4f6bbde392a8a8", size = 113762, upload_time = "2024-04-18T21:39:03.769Z" }, + { url = "https://files.pythonhosted.org/packages/4f/a4/08551776f7d6b219d6f73ca25be88806007b16af51a1dbfed7192528e1c3/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ddcc3f0fdffd9a3801e1005b73026202cffed8199863fdef9315bea9a860a032", size = 122018, upload_time = "2024-04-18T21:39:05.781Z" }, + { url = "https://files.pythonhosted.org/packages/70/14/ba91417ac7cbce8d553f72c885a19c6b9d7f9dc7de81b7814551cf020a57/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:c9f1ef4ec048563cc621a47ff01a4f10048ff8b676d7a4d75e5433ed8e703e56", size = 118884, upload_time = "2024-04-18T21:39:08.001Z" }, + { url = "https://files.pythonhosted.org/packages/7c/78/e1f2c30e11bda8347a74b3a7254f727ff53ea260244da77d76b96779a006/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:a364b30bec7a0a00dbe256e2b6807e4dc866bead7ac84aaa51ca5e2c3d15c258", size = 128224, upload_time = "2024-04-18T21:39:09.31Z" }, + { url = "https://files.pythonhosted.org/packages/ac/2f/b7fd96e9cfa9d9719b0c9feb50b4cbb341d1940e34fd3305006efa8c3e33/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:25d255383d3d6a6fbd643bb51ae1a7e4f6f7b0dbd5f3225b537d0bd0432eaf39", size = 117758, upload_time = "2024-04-18T21:39:11.287Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e0/1384c9a76379ab257b75df92283797861dcae592dd98e471df254f87c635/geventhttpclient-2.3.1-cp312-cp312-win32.whl", hash = "sha256:ad0b507e354d2f398186dcb12fe526d0594e7c9387b514fb843f7a14fdf1729a", size = 47595, upload_time = "2024-04-18T21:39:12.535Z" }, + { url = "https://files.pythonhosted.org/packages/54/e3/6b8dbb24e3941e20abbe7736e59290c5d4182057ea1d984d46c853208bcd/geventhttpclient-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:7924e0883bc2b177cfe27aa65af6bb9dd57f3e26905c7675a2d1f3ef69df7cca", size = 48271, upload_time = "2024-04-18T21:39:14.479Z" }, + { url = "https://files.pythonhosted.org/packages/ee/9f/251b1b7e665523137a8711f0f0029196cf18b57741135f01aea80a56f16c/geventhttpclient-2.3.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c31431e38df45b3c79bf3c9427c796adb8263d622bc6fa25e2f6ba916c2aad93", size = 49827, upload_time = "2024-04-18T21:39:36.14Z" }, + { url = "https://files.pythonhosted.org/packages/74/c7/ad4c23de669191e1c83cfa28c51d3b50fc246d72e1ee40d4d5b330532492/geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:855ab1e145575769b180b57accb0573a77cd6a7392f40a6ef7bc9a4926ebd77b", size = 54017, upload_time = "2024-04-18T21:39:37.577Z" }, + { url = "https://files.pythonhosted.org/packages/04/7b/59fc8c8fbd10596abfc46dc103654e3d9676de64229d8eee4b0a4ac2e890/geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a374aad77c01539e786d0c7829bec2eba034ccd45733c1bf9811ad18d2a8ecd", size = 58359, upload_time = "2024-04-18T21:39:39.437Z" }, + { url = "https://files.pythonhosted.org/packages/94/b7/743552b0ecda75458c83d55d62937e29c9ee9a42598f57d4025d5de70004/geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c1e97460608304f400485ac099736fff3566d3d8db2038533d466f8cf5de5a", size = 54262, upload_time = "2024-04-18T21:39:40.866Z" }, + { url = "https://files.pythonhosted.org/packages/18/60/10f6215b6cc76b5845a7f4b9c3d1f47d7ecd84ce8769b1e27e0482d605d7/geventhttpclient-2.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4f843f81ee44ba4c553a1b3f73115e0ad8f00044023c24db29f5b1df3da08465", size = 48343, upload_time = "2024-04-18T21:39:42.173Z" }, ] [[package]] name = "greenlet" version = "3.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/ff/df5fede753cc10f6a5be0931204ea30c35fa2f2ea7a35b25bdaf4fe40e46/greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", size = 186022 } +sdist = { url = "https://files.pythonhosted.org/packages/2f/ff/df5fede753cc10f6a5be0931204ea30c35fa2f2ea7a35b25bdaf4fe40e46/greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", size = 186022, upload_time = "2024-09-20T18:21:04.506Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/90/5234a78dc0ef6496a6eb97b67a42a8e96742a56f7dc808cb954a85390448/greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563", size = 271235 }, - { url = "https://files.pythonhosted.org/packages/7c/16/cd631fa0ab7d06ef06387135b7549fdcc77d8d859ed770a0d28e47b20972/greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83", size = 637168 }, - { url = "https://files.pythonhosted.org/packages/2f/b1/aed39043a6fec33c284a2c9abd63ce191f4f1a07319340ffc04d2ed3256f/greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0", size = 648826 }, - { url = "https://files.pythonhosted.org/packages/76/25/40e0112f7f3ebe54e8e8ed91b2b9f970805143efef16d043dfc15e70f44b/greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120", size = 644443 }, - { url = "https://files.pythonhosted.org/packages/fb/2f/3850b867a9af519794784a7eeed1dd5bc68ffbcc5b28cef703711025fd0a/greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc", size = 643295 }, - { url = "https://files.pythonhosted.org/packages/cf/69/79e4d63b9387b48939096e25115b8af7cd8a90397a304f92436bcb21f5b2/greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617", size = 599544 }, - { url = "https://files.pythonhosted.org/packages/46/1d/44dbcb0e6c323bd6f71b8c2f4233766a5faf4b8948873225d34a0b7efa71/greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7", size = 1125456 }, - { url = "https://files.pythonhosted.org/packages/e0/1d/a305dce121838d0278cee39d5bb268c657f10a5363ae4b726848f833f1bb/greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6", size = 1149111 }, - { url = "https://files.pythonhosted.org/packages/96/28/d62835fb33fb5652f2e98d34c44ad1a0feacc8b1d3f1aecab035f51f267d/greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80", size = 298392 }, - { url = "https://files.pythonhosted.org/packages/28/62/1c2665558618553c42922ed47a4e6d6527e2fa3516a8256c2f431c5d0441/greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70", size = 272479 }, - { url = "https://files.pythonhosted.org/packages/76/9d/421e2d5f07285b6e4e3a676b016ca781f63cfe4a0cd8eaecf3fd6f7a71ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159", size = 640404 }, - { url = "https://files.pythonhosted.org/packages/e5/de/6e05f5c59262a584e502dd3d261bbdd2c97ab5416cc9c0b91ea38932a901/greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e", size = 652813 }, - { url = "https://files.pythonhosted.org/packages/49/93/d5f93c84241acdea15a8fd329362c2c71c79e1a507c3f142a5d67ea435ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1", size = 648517 }, - { url = "https://files.pythonhosted.org/packages/15/85/72f77fc02d00470c86a5c982b8daafdf65d38aefbbe441cebff3bf7037fc/greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383", size = 647831 }, - { url = "https://files.pythonhosted.org/packages/f7/4b/1c9695aa24f808e156c8f4813f685d975ca73c000c2a5056c514c64980f6/greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a", size = 602413 }, - { url = "https://files.pythonhosted.org/packages/76/70/ad6e5b31ef330f03b12559d19fda2606a522d3849cde46b24f223d6d1619/greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511", size = 1129619 }, - { url = "https://files.pythonhosted.org/packages/f4/fb/201e1b932e584066e0f0658b538e73c459b34d44b4bd4034f682423bc801/greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395", size = 1155198 }, - { url = "https://files.pythonhosted.org/packages/12/da/b9ed5e310bb8b89661b80cbcd4db5a067903bbcd7fc854923f5ebb4144f0/greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39", size = 298930 }, - { url = "https://files.pythonhosted.org/packages/7d/ec/bad1ac26764d26aa1353216fcbfa4670050f66d445448aafa227f8b16e80/greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d", size = 274260 }, - { url = "https://files.pythonhosted.org/packages/66/d4/c8c04958870f482459ab5956c2942c4ec35cac7fe245527f1039837c17a9/greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", size = 649064 }, - { url = "https://files.pythonhosted.org/packages/51/41/467b12a8c7c1303d20abcca145db2be4e6cd50a951fa30af48b6ec607581/greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa", size = 663420 }, - { url = "https://files.pythonhosted.org/packages/27/8f/2a93cd9b1e7107d5c7b3b7816eeadcac2ebcaf6d6513df9abaf0334777f6/greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441", size = 658035 }, - { url = "https://files.pythonhosted.org/packages/57/5c/7c6f50cb12be092e1dccb2599be5a942c3416dbcfb76efcf54b3f8be4d8d/greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36", size = 660105 }, - { url = "https://files.pythonhosted.org/packages/f1/66/033e58a50fd9ec9df00a8671c74f1f3a320564c6415a4ed82a1c651654ba/greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9", size = 613077 }, - { url = "https://files.pythonhosted.org/packages/19/c5/36384a06f748044d06bdd8776e231fadf92fc896bd12cb1c9f5a1bda9578/greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0", size = 1135975 }, - { url = "https://files.pythonhosted.org/packages/38/f9/c0a0eb61bdf808d23266ecf1d63309f0e1471f284300ce6dac0ae1231881/greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942", size = 1163955 }, - { url = "https://files.pythonhosted.org/packages/43/21/a5d9df1d21514883333fc86584c07c2b49ba7c602e670b174bd73cfc9c7f/greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", size = 299655 }, - { url = "https://files.pythonhosted.org/packages/f3/57/0db4940cd7bb461365ca8d6fd53e68254c9dbbcc2b452e69d0d41f10a85e/greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", size = 272990 }, - { url = "https://files.pythonhosted.org/packages/1c/ec/423d113c9f74e5e402e175b157203e9102feeb7088cee844d735b28ef963/greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", size = 649175 }, - { url = "https://files.pythonhosted.org/packages/a9/46/ddbd2db9ff209186b7b7c621d1432e2f21714adc988703dbdd0e65155c77/greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", size = 663425 }, - { url = "https://files.pythonhosted.org/packages/bc/f9/9c82d6b2b04aa37e38e74f0c429aece5eeb02bab6e3b98e7db89b23d94c6/greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", size = 657736 }, - { url = "https://files.pythonhosted.org/packages/d9/42/b87bc2a81e3a62c3de2b0d550bf91a86939442b7ff85abb94eec3fc0e6aa/greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", size = 660347 }, - { url = "https://files.pythonhosted.org/packages/37/fa/71599c3fd06336cdc3eac52e6871cfebab4d9d70674a9a9e7a482c318e99/greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", size = 615583 }, - { url = "https://files.pythonhosted.org/packages/4e/96/e9ef85de031703ee7a4483489b40cf307f93c1824a02e903106f2ea315fe/greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", size = 1133039 }, - { url = "https://files.pythonhosted.org/packages/87/76/b2b6362accd69f2d1889db61a18c94bc743e961e3cab344c2effaa4b4a25/greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", size = 1160716 }, - { url = "https://files.pythonhosted.org/packages/1f/1b/54336d876186920e185066d8c3024ad55f21d7cc3683c856127ddb7b13ce/greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", size = 299490 }, - { url = "https://files.pythonhosted.org/packages/5f/17/bea55bf36990e1638a2af5ba10c1640273ef20f627962cf97107f1e5d637/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", size = 643731 }, - { url = "https://files.pythonhosted.org/packages/78/d2/aa3d2157f9ab742a08e0fd8f77d4699f37c22adfbfeb0c610a186b5f75e0/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", size = 649304 }, - { url = "https://files.pythonhosted.org/packages/f1/8e/d0aeffe69e53ccff5a28fa86f07ad1d2d2d6537a9506229431a2a02e2f15/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", size = 646537 }, - { url = "https://files.pythonhosted.org/packages/05/79/e15408220bbb989469c8871062c97c6c9136770657ba779711b90870d867/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", size = 642506 }, - { url = "https://files.pythonhosted.org/packages/18/87/470e01a940307796f1d25f8167b551a968540fbe0551c0ebb853cb527dd6/greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", size = 602753 }, - { url = "https://files.pythonhosted.org/packages/e2/72/576815ba674eddc3c25028238f74d7b8068902b3968cbe456771b166455e/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", size = 1122731 }, - { url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112 }, + { url = "https://files.pythonhosted.org/packages/25/90/5234a78dc0ef6496a6eb97b67a42a8e96742a56f7dc808cb954a85390448/greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563", size = 271235, upload_time = "2024-09-20T17:07:18.761Z" }, + { url = "https://files.pythonhosted.org/packages/7c/16/cd631fa0ab7d06ef06387135b7549fdcc77d8d859ed770a0d28e47b20972/greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83", size = 637168, upload_time = "2024-09-20T17:36:43.774Z" }, + { url = "https://files.pythonhosted.org/packages/2f/b1/aed39043a6fec33c284a2c9abd63ce191f4f1a07319340ffc04d2ed3256f/greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0", size = 648826, upload_time = "2024-09-20T17:39:16.921Z" }, + { url = "https://files.pythonhosted.org/packages/76/25/40e0112f7f3ebe54e8e8ed91b2b9f970805143efef16d043dfc15e70f44b/greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120", size = 644443, upload_time = "2024-09-20T17:44:21.896Z" }, + { url = "https://files.pythonhosted.org/packages/fb/2f/3850b867a9af519794784a7eeed1dd5bc68ffbcc5b28cef703711025fd0a/greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc", size = 643295, upload_time = "2024-09-20T17:08:37.951Z" }, + { url = "https://files.pythonhosted.org/packages/cf/69/79e4d63b9387b48939096e25115b8af7cd8a90397a304f92436bcb21f5b2/greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617", size = 599544, upload_time = "2024-09-20T17:08:27.894Z" }, + { url = "https://files.pythonhosted.org/packages/46/1d/44dbcb0e6c323bd6f71b8c2f4233766a5faf4b8948873225d34a0b7efa71/greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7", size = 1125456, upload_time = "2024-09-20T17:44:11.755Z" }, + { url = "https://files.pythonhosted.org/packages/e0/1d/a305dce121838d0278cee39d5bb268c657f10a5363ae4b726848f833f1bb/greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6", size = 1149111, upload_time = "2024-09-20T17:09:22.104Z" }, + { url = "https://files.pythonhosted.org/packages/96/28/d62835fb33fb5652f2e98d34c44ad1a0feacc8b1d3f1aecab035f51f267d/greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80", size = 298392, upload_time = "2024-09-20T17:28:51.988Z" }, + { url = "https://files.pythonhosted.org/packages/28/62/1c2665558618553c42922ed47a4e6d6527e2fa3516a8256c2f431c5d0441/greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70", size = 272479, upload_time = "2024-09-20T17:07:22.332Z" }, + { url = "https://files.pythonhosted.org/packages/76/9d/421e2d5f07285b6e4e3a676b016ca781f63cfe4a0cd8eaecf3fd6f7a71ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159", size = 640404, upload_time = "2024-09-20T17:36:45.588Z" }, + { url = "https://files.pythonhosted.org/packages/e5/de/6e05f5c59262a584e502dd3d261bbdd2c97ab5416cc9c0b91ea38932a901/greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e", size = 652813, upload_time = "2024-09-20T17:39:19.052Z" }, + { url = "https://files.pythonhosted.org/packages/49/93/d5f93c84241acdea15a8fd329362c2c71c79e1a507c3f142a5d67ea435ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1", size = 648517, upload_time = "2024-09-20T17:44:24.101Z" }, + { url = "https://files.pythonhosted.org/packages/15/85/72f77fc02d00470c86a5c982b8daafdf65d38aefbbe441cebff3bf7037fc/greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383", size = 647831, upload_time = "2024-09-20T17:08:40.577Z" }, + { url = "https://files.pythonhosted.org/packages/f7/4b/1c9695aa24f808e156c8f4813f685d975ca73c000c2a5056c514c64980f6/greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a", size = 602413, upload_time = "2024-09-20T17:08:31.728Z" }, + { url = "https://files.pythonhosted.org/packages/76/70/ad6e5b31ef330f03b12559d19fda2606a522d3849cde46b24f223d6d1619/greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511", size = 1129619, upload_time = "2024-09-20T17:44:14.222Z" }, + { url = "https://files.pythonhosted.org/packages/f4/fb/201e1b932e584066e0f0658b538e73c459b34d44b4bd4034f682423bc801/greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395", size = 1155198, upload_time = "2024-09-20T17:09:23.903Z" }, + { url = "https://files.pythonhosted.org/packages/12/da/b9ed5e310bb8b89661b80cbcd4db5a067903bbcd7fc854923f5ebb4144f0/greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39", size = 298930, upload_time = "2024-09-20T17:25:18.656Z" }, + { url = "https://files.pythonhosted.org/packages/7d/ec/bad1ac26764d26aa1353216fcbfa4670050f66d445448aafa227f8b16e80/greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d", size = 274260, upload_time = "2024-09-20T17:08:07.301Z" }, + { url = "https://files.pythonhosted.org/packages/66/d4/c8c04958870f482459ab5956c2942c4ec35cac7fe245527f1039837c17a9/greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", size = 649064, upload_time = "2024-09-20T17:36:47.628Z" }, + { url = "https://files.pythonhosted.org/packages/51/41/467b12a8c7c1303d20abcca145db2be4e6cd50a951fa30af48b6ec607581/greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa", size = 663420, upload_time = "2024-09-20T17:39:21.258Z" }, + { url = "https://files.pythonhosted.org/packages/27/8f/2a93cd9b1e7107d5c7b3b7816eeadcac2ebcaf6d6513df9abaf0334777f6/greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441", size = 658035, upload_time = "2024-09-20T17:44:26.501Z" }, + { url = "https://files.pythonhosted.org/packages/57/5c/7c6f50cb12be092e1dccb2599be5a942c3416dbcfb76efcf54b3f8be4d8d/greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36", size = 660105, upload_time = "2024-09-20T17:08:42.048Z" }, + { url = "https://files.pythonhosted.org/packages/f1/66/033e58a50fd9ec9df00a8671c74f1f3a320564c6415a4ed82a1c651654ba/greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9", size = 613077, upload_time = "2024-09-20T17:08:33.707Z" }, + { url = "https://files.pythonhosted.org/packages/19/c5/36384a06f748044d06bdd8776e231fadf92fc896bd12cb1c9f5a1bda9578/greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0", size = 1135975, upload_time = "2024-09-20T17:44:15.989Z" }, + { url = "https://files.pythonhosted.org/packages/38/f9/c0a0eb61bdf808d23266ecf1d63309f0e1471f284300ce6dac0ae1231881/greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942", size = 1163955, upload_time = "2024-09-20T17:09:25.539Z" }, + { url = "https://files.pythonhosted.org/packages/43/21/a5d9df1d21514883333fc86584c07c2b49ba7c602e670b174bd73cfc9c7f/greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", size = 299655, upload_time = "2024-09-20T17:21:22.427Z" }, + { url = "https://files.pythonhosted.org/packages/f3/57/0db4940cd7bb461365ca8d6fd53e68254c9dbbcc2b452e69d0d41f10a85e/greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", size = 272990, upload_time = "2024-09-20T17:08:26.312Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ec/423d113c9f74e5e402e175b157203e9102feeb7088cee844d735b28ef963/greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", size = 649175, upload_time = "2024-09-20T17:36:48.983Z" }, + { url = "https://files.pythonhosted.org/packages/a9/46/ddbd2db9ff209186b7b7c621d1432e2f21714adc988703dbdd0e65155c77/greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", size = 663425, upload_time = "2024-09-20T17:39:22.705Z" }, + { url = "https://files.pythonhosted.org/packages/bc/f9/9c82d6b2b04aa37e38e74f0c429aece5eeb02bab6e3b98e7db89b23d94c6/greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", size = 657736, upload_time = "2024-09-20T17:44:28.544Z" }, + { url = "https://files.pythonhosted.org/packages/d9/42/b87bc2a81e3a62c3de2b0d550bf91a86939442b7ff85abb94eec3fc0e6aa/greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", size = 660347, upload_time = "2024-09-20T17:08:45.56Z" }, + { url = "https://files.pythonhosted.org/packages/37/fa/71599c3fd06336cdc3eac52e6871cfebab4d9d70674a9a9e7a482c318e99/greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", size = 615583, upload_time = "2024-09-20T17:08:36.85Z" }, + { url = "https://files.pythonhosted.org/packages/4e/96/e9ef85de031703ee7a4483489b40cf307f93c1824a02e903106f2ea315fe/greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", size = 1133039, upload_time = "2024-09-20T17:44:18.287Z" }, + { url = "https://files.pythonhosted.org/packages/87/76/b2b6362accd69f2d1889db61a18c94bc743e961e3cab344c2effaa4b4a25/greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", size = 1160716, upload_time = "2024-09-20T17:09:27.112Z" }, + { url = "https://files.pythonhosted.org/packages/1f/1b/54336d876186920e185066d8c3024ad55f21d7cc3683c856127ddb7b13ce/greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", size = 299490, upload_time = "2024-09-20T17:17:09.501Z" }, + { url = "https://files.pythonhosted.org/packages/5f/17/bea55bf36990e1638a2af5ba10c1640273ef20f627962cf97107f1e5d637/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", size = 643731, upload_time = "2024-09-20T17:36:50.376Z" }, + { url = "https://files.pythonhosted.org/packages/78/d2/aa3d2157f9ab742a08e0fd8f77d4699f37c22adfbfeb0c610a186b5f75e0/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", size = 649304, upload_time = "2024-09-20T17:39:24.55Z" }, + { url = "https://files.pythonhosted.org/packages/f1/8e/d0aeffe69e53ccff5a28fa86f07ad1d2d2d6537a9506229431a2a02e2f15/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", size = 646537, upload_time = "2024-09-20T17:44:31.102Z" }, + { url = "https://files.pythonhosted.org/packages/05/79/e15408220bbb989469c8871062c97c6c9136770657ba779711b90870d867/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", size = 642506, upload_time = "2024-09-20T17:08:47.852Z" }, + { url = "https://files.pythonhosted.org/packages/18/87/470e01a940307796f1d25f8167b551a968540fbe0551c0ebb853cb527dd6/greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", size = 602753, upload_time = "2024-09-20T17:08:38.079Z" }, + { url = "https://files.pythonhosted.org/packages/e2/72/576815ba674eddc3c25028238f74d7b8068902b3968cbe456771b166455e/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", size = 1122731, upload_time = "2024-09-20T17:44:20.556Z" }, + { url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112, upload_time = "2024-09-20T17:09:28.753Z" }, ] [[package]] @@ -796,18 +805,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031 } +sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031, upload_time = "2024-08-10T20:25:27.378Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029 }, + { url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029, upload_time = "2024-08-10T20:25:24.996Z" }, ] [[package]] name = "h11" version = "0.14.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } +sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418, upload_time = "2022-09-25T15:40:01.519Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, + { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259, upload_time = "2022-09-25T15:39:59.68Z" }, ] [[package]] @@ -818,45 +827,45 @@ dependencies = [ { name = "certifi" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/18/56/78a38490b834fa0942cbe6d39bd8a7fd76316e8940319305a98d2b320366/httpcore-1.0.2.tar.gz", hash = "sha256:9fc092e4799b26174648e54b74ed5f683132a464e95643b226e00c2ed2fa6535", size = 81036 } +sdist = { url = "https://files.pythonhosted.org/packages/18/56/78a38490b834fa0942cbe6d39bd8a7fd76316e8940319305a98d2b320366/httpcore-1.0.2.tar.gz", hash = "sha256:9fc092e4799b26174648e54b74ed5f683132a464e95643b226e00c2ed2fa6535", size = 81036, upload_time = "2023-11-10T13:37:42.496Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/ba/78b0a99c4da0ff8b0f59defa2f13ca4668189b134bd9840b6202a93d9a0f/httpcore-1.0.2-py3-none-any.whl", hash = "sha256:096cc05bca73b8e459a1fc3dcf585148f63e534eae4339559c9b8a8d6399acc7", size = 76943 }, + { url = "https://files.pythonhosted.org/packages/56/ba/78b0a99c4da0ff8b0f59defa2f13ca4668189b134bd9840b6202a93d9a0f/httpcore-1.0.2-py3-none-any.whl", hash = "sha256:096cc05bca73b8e459a1fc3dcf585148f63e534eae4339559c9b8a8d6399acc7", size = 76943, upload_time = "2023-11-10T13:37:40.937Z" }, ] [[package]] name = "httptools" version = "0.6.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/9a/ce5e1f7e131522e6d3426e8e7a490b3a01f39a6696602e1c4f33f9e94277/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c", size = 240639 } +sdist = { url = "https://files.pythonhosted.org/packages/a7/9a/ce5e1f7e131522e6d3426e8e7a490b3a01f39a6696602e1c4f33f9e94277/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c", size = 240639, upload_time = "2024-10-16T19:45:08.902Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/6f/972f8eb0ea7d98a1c6be436e2142d51ad2a64ee18e02b0e7ff1f62171ab1/httptools-0.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c73ce323711a6ffb0d247dcd5a550b8babf0f757e86a52558fe5b86d6fefcc0", size = 198780 }, - { url = "https://files.pythonhosted.org/packages/6a/b0/17c672b4bc5c7ba7f201eada4e96c71d0a59fbc185e60e42580093a86f21/httptools-0.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:345c288418f0944a6fe67be8e6afa9262b18c7626c3ef3c28adc5eabc06a68da", size = 103297 }, - { url = "https://files.pythonhosted.org/packages/92/5e/b4a826fe91971a0b68e8c2bd4e7db3e7519882f5a8ccdb1194be2b3ab98f/httptools-0.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deee0e3343f98ee8047e9f4c5bc7cedbf69f5734454a94c38ee829fb2d5fa3c1", size = 443130 }, - { url = "https://files.pythonhosted.org/packages/b0/51/ce61e531e40289a681a463e1258fa1e05e0be54540e40d91d065a264cd8f/httptools-0.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca80b7485c76f768a3bc83ea58373f8db7b015551117375e4918e2aa77ea9b50", size = 442148 }, - { url = "https://files.pythonhosted.org/packages/ea/9e/270b7d767849b0c96f275c695d27ca76c30671f8eb8cc1bab6ced5c5e1d0/httptools-0.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:90d96a385fa941283ebd231464045187a31ad932ebfa541be8edf5b3c2328959", size = 415949 }, - { url = "https://files.pythonhosted.org/packages/81/86/ced96e3179c48c6f656354e106934e65c8963d48b69be78f355797f0e1b3/httptools-0.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:59e724f8b332319e2875efd360e61ac07f33b492889284a3e05e6d13746876f4", size = 417591 }, - { url = "https://files.pythonhosted.org/packages/75/73/187a3f620ed3175364ddb56847d7a608a6fc42d551e133197098c0143eca/httptools-0.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:c26f313951f6e26147833fc923f78f95604bbec812a43e5ee37f26dc9e5a686c", size = 88344 }, - { url = "https://files.pythonhosted.org/packages/7b/26/bb526d4d14c2774fe07113ca1db7255737ffbb119315839af2065abfdac3/httptools-0.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f47f8ed67cc0ff862b84a1189831d1d33c963fb3ce1ee0c65d3b0cbe7b711069", size = 199029 }, - { url = "https://files.pythonhosted.org/packages/a6/17/3e0d3e9b901c732987a45f4f94d4e2c62b89a041d93db89eafb262afd8d5/httptools-0.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0614154d5454c21b6410fdf5262b4a3ddb0f53f1e1721cfd59d55f32138c578a", size = 103492 }, - { url = "https://files.pythonhosted.org/packages/b7/24/0fe235d7b69c42423c7698d086d4db96475f9b50b6ad26a718ef27a0bce6/httptools-0.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8787367fbdfccae38e35abf7641dafc5310310a5987b689f4c32cc8cc3ee975", size = 462891 }, - { url = "https://files.pythonhosted.org/packages/b1/2f/205d1f2a190b72da6ffb5f41a3736c26d6fa7871101212b15e9b5cd8f61d/httptools-0.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b0f7fe4fd38e6a507bdb751db0379df1e99120c65fbdc8ee6c1d044897a636", size = 459788 }, - { url = "https://files.pythonhosted.org/packages/6e/4c/d09ce0eff09057a206a74575ae8f1e1e2f0364d20e2442224f9e6612c8b9/httptools-0.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40a5ec98d3f49904b9fe36827dcf1aadfef3b89e2bd05b0e35e94f97c2b14721", size = 433214 }, - { url = "https://files.pythonhosted.org/packages/3e/d2/84c9e23edbccc4a4c6f96a1b8d99dfd2350289e94f00e9ccc7aadde26fb5/httptools-0.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dacdd3d10ea1b4ca9df97a0a303cbacafc04b5cd375fa98732678151643d4988", size = 434120 }, - { url = "https://files.pythonhosted.org/packages/d0/46/4d8e7ba9581416de1c425b8264e2cadd201eb709ec1584c381f3e98f51c1/httptools-0.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:288cd628406cc53f9a541cfaf06041b4c71d751856bab45e3702191f931ccd17", size = 88565 }, - { url = "https://files.pythonhosted.org/packages/bb/0e/d0b71465c66b9185f90a091ab36389a7352985fe857e352801c39d6127c8/httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2", size = 200683 }, - { url = "https://files.pythonhosted.org/packages/e2/b8/412a9bb28d0a8988de3296e01efa0bd62068b33856cdda47fe1b5e890954/httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44", size = 104337 }, - { url = "https://files.pythonhosted.org/packages/9b/01/6fb20be3196ffdc8eeec4e653bc2a275eca7f36634c86302242c4fbb2760/httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1", size = 508796 }, - { url = "https://files.pythonhosted.org/packages/f7/d8/b644c44acc1368938317d76ac991c9bba1166311880bcc0ac297cb9d6bd7/httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2", size = 510837 }, - { url = "https://files.pythonhosted.org/packages/52/d8/254d16a31d543073a0e57f1c329ca7378d8924e7e292eda72d0064987486/httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81", size = 485289 }, - { url = "https://files.pythonhosted.org/packages/5f/3c/4aee161b4b7a971660b8be71a92c24d6c64372c1ab3ae7f366b3680df20f/httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f", size = 489779 }, - { url = "https://files.pythonhosted.org/packages/12/b7/5cae71a8868e555f3f67a50ee7f673ce36eac970f029c0c5e9d584352961/httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970", size = 88634 }, - { url = "https://files.pythonhosted.org/packages/94/a3/9fe9ad23fd35f7de6b91eeb60848986058bd8b5a5c1e256f5860a160cc3e/httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660", size = 197214 }, - { url = "https://files.pythonhosted.org/packages/ea/d9/82d5e68bab783b632023f2fa31db20bebb4e89dfc4d2293945fd68484ee4/httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083", size = 102431 }, - { url = "https://files.pythonhosted.org/packages/96/c1/cb499655cbdbfb57b577734fde02f6fa0bbc3fe9fb4d87b742b512908dff/httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3", size = 473121 }, - { url = "https://files.pythonhosted.org/packages/af/71/ee32fd358f8a3bb199b03261f10921716990808a675d8160b5383487a317/httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071", size = 473805 }, - { url = "https://files.pythonhosted.org/packages/8a/0a/0d4df132bfca1507114198b766f1737d57580c9ad1cf93c1ff673e3387be/httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5", size = 448858 }, - { url = "https://files.pythonhosted.org/packages/1e/6a/787004fdef2cabea27bad1073bf6a33f2437b4dbd3b6fb4a9d71172b1c7c/httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0", size = 452042 }, - { url = "https://files.pythonhosted.org/packages/4d/dc/7decab5c404d1d2cdc1bb330b1bf70e83d6af0396fd4fc76fc60c0d522bf/httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8", size = 87682 }, + { url = "https://files.pythonhosted.org/packages/3b/6f/972f8eb0ea7d98a1c6be436e2142d51ad2a64ee18e02b0e7ff1f62171ab1/httptools-0.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c73ce323711a6ffb0d247dcd5a550b8babf0f757e86a52558fe5b86d6fefcc0", size = 198780, upload_time = "2024-10-16T19:44:06.882Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b0/17c672b4bc5c7ba7f201eada4e96c71d0a59fbc185e60e42580093a86f21/httptools-0.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:345c288418f0944a6fe67be8e6afa9262b18c7626c3ef3c28adc5eabc06a68da", size = 103297, upload_time = "2024-10-16T19:44:08.129Z" }, + { url = "https://files.pythonhosted.org/packages/92/5e/b4a826fe91971a0b68e8c2bd4e7db3e7519882f5a8ccdb1194be2b3ab98f/httptools-0.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deee0e3343f98ee8047e9f4c5bc7cedbf69f5734454a94c38ee829fb2d5fa3c1", size = 443130, upload_time = "2024-10-16T19:44:09.45Z" }, + { url = "https://files.pythonhosted.org/packages/b0/51/ce61e531e40289a681a463e1258fa1e05e0be54540e40d91d065a264cd8f/httptools-0.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca80b7485c76f768a3bc83ea58373f8db7b015551117375e4918e2aa77ea9b50", size = 442148, upload_time = "2024-10-16T19:44:11.539Z" }, + { url = "https://files.pythonhosted.org/packages/ea/9e/270b7d767849b0c96f275c695d27ca76c30671f8eb8cc1bab6ced5c5e1d0/httptools-0.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:90d96a385fa941283ebd231464045187a31ad932ebfa541be8edf5b3c2328959", size = 415949, upload_time = "2024-10-16T19:44:13.388Z" }, + { url = "https://files.pythonhosted.org/packages/81/86/ced96e3179c48c6f656354e106934e65c8963d48b69be78f355797f0e1b3/httptools-0.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:59e724f8b332319e2875efd360e61ac07f33b492889284a3e05e6d13746876f4", size = 417591, upload_time = "2024-10-16T19:44:15.258Z" }, + { url = "https://files.pythonhosted.org/packages/75/73/187a3f620ed3175364ddb56847d7a608a6fc42d551e133197098c0143eca/httptools-0.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:c26f313951f6e26147833fc923f78f95604bbec812a43e5ee37f26dc9e5a686c", size = 88344, upload_time = "2024-10-16T19:44:16.54Z" }, + { url = "https://files.pythonhosted.org/packages/7b/26/bb526d4d14c2774fe07113ca1db7255737ffbb119315839af2065abfdac3/httptools-0.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f47f8ed67cc0ff862b84a1189831d1d33c963fb3ce1ee0c65d3b0cbe7b711069", size = 199029, upload_time = "2024-10-16T19:44:18.427Z" }, + { url = "https://files.pythonhosted.org/packages/a6/17/3e0d3e9b901c732987a45f4f94d4e2c62b89a041d93db89eafb262afd8d5/httptools-0.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0614154d5454c21b6410fdf5262b4a3ddb0f53f1e1721cfd59d55f32138c578a", size = 103492, upload_time = "2024-10-16T19:44:19.515Z" }, + { url = "https://files.pythonhosted.org/packages/b7/24/0fe235d7b69c42423c7698d086d4db96475f9b50b6ad26a718ef27a0bce6/httptools-0.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8787367fbdfccae38e35abf7641dafc5310310a5987b689f4c32cc8cc3ee975", size = 462891, upload_time = "2024-10-16T19:44:21.067Z" }, + { url = "https://files.pythonhosted.org/packages/b1/2f/205d1f2a190b72da6ffb5f41a3736c26d6fa7871101212b15e9b5cd8f61d/httptools-0.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b0f7fe4fd38e6a507bdb751db0379df1e99120c65fbdc8ee6c1d044897a636", size = 459788, upload_time = "2024-10-16T19:44:22.958Z" }, + { url = "https://files.pythonhosted.org/packages/6e/4c/d09ce0eff09057a206a74575ae8f1e1e2f0364d20e2442224f9e6612c8b9/httptools-0.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40a5ec98d3f49904b9fe36827dcf1aadfef3b89e2bd05b0e35e94f97c2b14721", size = 433214, upload_time = "2024-10-16T19:44:24.513Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/84c9e23edbccc4a4c6f96a1b8d99dfd2350289e94f00e9ccc7aadde26fb5/httptools-0.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dacdd3d10ea1b4ca9df97a0a303cbacafc04b5cd375fa98732678151643d4988", size = 434120, upload_time = "2024-10-16T19:44:26.295Z" }, + { url = "https://files.pythonhosted.org/packages/d0/46/4d8e7ba9581416de1c425b8264e2cadd201eb709ec1584c381f3e98f51c1/httptools-0.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:288cd628406cc53f9a541cfaf06041b4c71d751856bab45e3702191f931ccd17", size = 88565, upload_time = "2024-10-16T19:44:29.188Z" }, + { url = "https://files.pythonhosted.org/packages/bb/0e/d0b71465c66b9185f90a091ab36389a7352985fe857e352801c39d6127c8/httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2", size = 200683, upload_time = "2024-10-16T19:44:30.175Z" }, + { url = "https://files.pythonhosted.org/packages/e2/b8/412a9bb28d0a8988de3296e01efa0bd62068b33856cdda47fe1b5e890954/httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44", size = 104337, upload_time = "2024-10-16T19:44:31.786Z" }, + { url = "https://files.pythonhosted.org/packages/9b/01/6fb20be3196ffdc8eeec4e653bc2a275eca7f36634c86302242c4fbb2760/httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1", size = 508796, upload_time = "2024-10-16T19:44:32.825Z" }, + { url = "https://files.pythonhosted.org/packages/f7/d8/b644c44acc1368938317d76ac991c9bba1166311880bcc0ac297cb9d6bd7/httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2", size = 510837, upload_time = "2024-10-16T19:44:33.974Z" }, + { url = "https://files.pythonhosted.org/packages/52/d8/254d16a31d543073a0e57f1c329ca7378d8924e7e292eda72d0064987486/httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81", size = 485289, upload_time = "2024-10-16T19:44:35.111Z" }, + { url = "https://files.pythonhosted.org/packages/5f/3c/4aee161b4b7a971660b8be71a92c24d6c64372c1ab3ae7f366b3680df20f/httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f", size = 489779, upload_time = "2024-10-16T19:44:36.253Z" }, + { url = "https://files.pythonhosted.org/packages/12/b7/5cae71a8868e555f3f67a50ee7f673ce36eac970f029c0c5e9d584352961/httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970", size = 88634, upload_time = "2024-10-16T19:44:37.357Z" }, + { url = "https://files.pythonhosted.org/packages/94/a3/9fe9ad23fd35f7de6b91eeb60848986058bd8b5a5c1e256f5860a160cc3e/httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660", size = 197214, upload_time = "2024-10-16T19:44:38.738Z" }, + { url = "https://files.pythonhosted.org/packages/ea/d9/82d5e68bab783b632023f2fa31db20bebb4e89dfc4d2293945fd68484ee4/httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083", size = 102431, upload_time = "2024-10-16T19:44:39.818Z" }, + { url = "https://files.pythonhosted.org/packages/96/c1/cb499655cbdbfb57b577734fde02f6fa0bbc3fe9fb4d87b742b512908dff/httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3", size = 473121, upload_time = "2024-10-16T19:44:41.189Z" }, + { url = "https://files.pythonhosted.org/packages/af/71/ee32fd358f8a3bb199b03261f10921716990808a675d8160b5383487a317/httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071", size = 473805, upload_time = "2024-10-16T19:44:42.384Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0a/0d4df132bfca1507114198b766f1737d57580c9ad1cf93c1ff673e3387be/httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5", size = 448858, upload_time = "2024-10-16T19:44:43.959Z" }, + { url = "https://files.pythonhosted.org/packages/1e/6a/787004fdef2cabea27bad1073bf6a33f2437b4dbd3b6fb4a9d71172b1c7c/httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0", size = 452042, upload_time = "2024-10-16T19:44:45.071Z" }, + { url = "https://files.pythonhosted.org/packages/4d/dc/7decab5c404d1d2cdc1bb330b1bf70e83d6af0396fd4fc76fc60c0d522bf/httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8", size = 87682, upload_time = "2024-10-16T19:44:46.46Z" }, ] [[package]] @@ -869,9 +878,9 @@ dependencies = [ { name = "httpcore" }, { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload_time = "2024-12-06T15:37:23.222Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload_time = "2024-12-06T15:37:21.509Z" }, ] [[package]] @@ -887,9 +896,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/22/8eb91736b1dcb83d879bd49050a09df29a57cc5cd9f38e48a4b1c45ee890/huggingface_hub-0.30.2.tar.gz", hash = "sha256:9a7897c5b6fd9dad3168a794a8998d6378210f5b9688d0dfc180b1a228dc2466", size = 400868 } +sdist = { url = "https://files.pythonhosted.org/packages/df/22/8eb91736b1dcb83d879bd49050a09df29a57cc5cd9f38e48a4b1c45ee890/huggingface_hub-0.30.2.tar.gz", hash = "sha256:9a7897c5b6fd9dad3168a794a8998d6378210f5b9688d0dfc180b1a228dc2466", size = 400868, upload_time = "2025-04-08T08:32:45.26Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/27/1fb384a841e9661faad1c31cbfa62864f59632e876df5d795234da51c395/huggingface_hub-0.30.2-py3-none-any.whl", hash = "sha256:68ff05969927058cfa41df4f2155d4bb48f5f54f719dd0390103eefa9b191e28", size = 481433 }, + { url = "https://files.pythonhosted.org/packages/93/27/1fb384a841e9661faad1c31cbfa62864f59632e876df5d795234da51c395/huggingface_hub-0.30.2-py3-none-any.whl", hash = "sha256:68ff05969927058cfa41df4f2155d4bb48f5f54f719dd0390103eefa9b191e28", size = 481433, upload_time = "2025-04-08T08:32:43.305Z" }, ] [[package]] @@ -899,18 +908,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyreadline3", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702 } +sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload_time = "2021-09-17T21:40:43.31Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794 }, + { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload_time = "2021-09-17T21:40:39.897Z" }, ] [[package]] name = "idna" version = "3.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426 } +sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426, upload_time = "2023-11-25T15:40:54.902Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567 }, + { url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567, upload_time = "2023-11-25T15:40:52.604Z" }, ] [[package]] @@ -921,9 +930,9 @@ dependencies = [ { name = "numpy" }, { name = "pillow" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/38/f4c568318c656352d211eec6954460dc3af0b7583a6682308f8a66e4c19b/imageio-2.33.1.tar.gz", hash = "sha256:78722d40b137bd98f5ec7312119f8aea9ad2049f76f434748eb306b6937cc1ce", size = 387374 } +sdist = { url = "https://files.pythonhosted.org/packages/25/38/f4c568318c656352d211eec6954460dc3af0b7583a6682308f8a66e4c19b/imageio-2.33.1.tar.gz", hash = "sha256:78722d40b137bd98f5ec7312119f8aea9ad2049f76f434748eb306b6937cc1ce", size = 387374, upload_time = "2023-12-11T02:26:44.715Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/69/3aaa69cb0748e33e644fda114c9abd3186ce369edd4fca11107e9f39c6a7/imageio-2.33.1-py3-none-any.whl", hash = "sha256:c5094c48ccf6b2e6da8b4061cd95e1209380afafcbeae4a4e280938cce227e1d", size = 313345 }, + { url = "https://files.pythonhosted.org/packages/c0/69/3aaa69cb0748e33e644fda114c9abd3186ce369edd4fca11107e9f39c6a7/imageio-2.33.1-py3-none-any.whl", hash = "sha256:c5094c48ccf6b2e6da8b4061cd95e1209380afafcbeae4a4e280938cce227e1d", size = 313345, upload_time = "2023-12-11T02:26:42.724Z" }, ] [[package]] @@ -1080,9 +1089,9 @@ types = [ name = "iniconfig" version = "2.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646, upload_time = "2023-01-07T11:08:11.254Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892, upload_time = "2023-01-07T11:08:09.864Z" }, ] [[package]] @@ -1104,15 +1113,15 @@ dependencies = [ { name = "scipy" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0b/8d/0f4af90999ca96cf8cb846eb5ae27c5ef5b390f9c090dd19e4fa76364c13/insightface-0.7.3.tar.gz", hash = "sha256:f191f719612ebb37018f41936814500544cd0f86e6fcd676c023f354c668ddf7", size = 439490 } +sdist = { url = "https://files.pythonhosted.org/packages/0b/8d/0f4af90999ca96cf8cb846eb5ae27c5ef5b390f9c090dd19e4fa76364c13/insightface-0.7.3.tar.gz", hash = "sha256:f191f719612ebb37018f41936814500544cd0f86e6fcd676c023f354c668ddf7", size = 439490, upload_time = "2023-04-02T08:01:54.541Z" } [[package]] name = "itsdangerous" version = "2.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7f/a1/d3fb83e7a61fa0c0d3d08ad0a94ddbeff3731c05212617dff3a94e097f08/itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a", size = 56143 } +sdist = { url = "https://files.pythonhosted.org/packages/7f/a1/d3fb83e7a61fa0c0d3d08ad0a94ddbeff3731c05212617dff3a94e097f08/itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a", size = 56143, upload_time = "2022-03-24T15:12:15.102Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", size = 15749 }, + { url = "https://files.pythonhosted.org/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", size = 15749, upload_time = "2022-03-24T15:12:13.2Z" }, ] [[package]] @@ -1122,93 +1131,94 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 } +sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245, upload_time = "2024-05-05T23:42:02.455Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 }, + { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271, upload_time = "2024-05-05T23:41:59.928Z" }, ] [[package]] name = "joblib" version = "1.3.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/0f/d3b33b9f106dddef461f6df1872b7881321b247f3d255b87f61a7636f7fe/joblib-1.3.2.tar.gz", hash = "sha256:92f865e621e17784e7955080b6d042489e3b8e294949cc44c6eac304f59772b1", size = 1987720 } +sdist = { url = "https://files.pythonhosted.org/packages/15/0f/d3b33b9f106dddef461f6df1872b7881321b247f3d255b87f61a7636f7fe/joblib-1.3.2.tar.gz", hash = "sha256:92f865e621e17784e7955080b6d042489e3b8e294949cc44c6eac304f59772b1", size = 1987720, upload_time = "2023-08-09T09:23:40.503Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/40/d551139c85db202f1f384ba8bcf96aca2f329440a844f924c8a0040b6d02/joblib-1.3.2-py3-none-any.whl", hash = "sha256:ef4331c65f239985f3f2220ecc87db222f08fd22097a3dd5698f693875f8cbb9", size = 302207 }, + { url = "https://files.pythonhosted.org/packages/10/40/d551139c85db202f1f384ba8bcf96aca2f329440a844f924c8a0040b6d02/joblib-1.3.2-py3-none-any.whl", hash = "sha256:ef4331c65f239985f3f2220ecc87db222f08fd22097a3dd5698f693875f8cbb9", size = 302207, upload_time = "2023-08-09T09:23:34.583Z" }, ] [[package]] name = "kiwisolver" version = "1.4.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b9/2d/226779e405724344fc678fcc025b812587617ea1a48b9442628b688e85ea/kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec", size = 97552 } +sdist = { url = "https://files.pythonhosted.org/packages/b9/2d/226779e405724344fc678fcc025b812587617ea1a48b9442628b688e85ea/kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec", size = 97552, upload_time = "2023-08-24T09:30:39.861Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/56/cb02dcefdaab40df636b91e703b172966b444605a0ea313549f3ffc05bd3/kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af", size = 127397 }, - { url = "https://files.pythonhosted.org/packages/0e/c1/d084f8edb26533a191415d5173157080837341f9a06af9dd1a75f727abb4/kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3", size = 68125 }, - { url = "https://files.pythonhosted.org/packages/23/11/6fb190bae4b279d712a834e7b1da89f6dcff6791132f7399aa28a57c3565/kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4", size = 66211 }, - { url = "https://files.pythonhosted.org/packages/b3/13/5e9e52feb33e9e063f76b2c5eb09cb977f5bba622df3210081bfb26ec9a3/kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1", size = 1637145 }, - { url = "https://files.pythonhosted.org/packages/6f/40/4ab1fdb57fced80ce5903f04ae1aed7c1d5939dda4fd0c0aa526c12fe28a/kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff", size = 1617849 }, - { url = "https://files.pythonhosted.org/packages/49/ca/61ef43bd0832c7253b370735b0c38972c140c8774889b884372a629a8189/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a", size = 1400921 }, - { url = "https://files.pythonhosted.org/packages/68/6f/854f6a845c00b4257482468e08d8bc386f4929ee499206142378ba234419/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa", size = 1513009 }, - { url = "https://files.pythonhosted.org/packages/50/65/76f303377167d12eb7a9b423d6771b39fe5c4373e4a42f075805b1f581ae/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c", size = 1444819 }, - { url = "https://files.pythonhosted.org/packages/7e/ee/98cdf9dde129551467138b6e18cc1cc901e75ecc7ffb898c6f49609f33b1/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b", size = 1817054 }, - { url = "https://files.pythonhosted.org/packages/e6/5b/ab569016ec4abc7b496f6cb8a3ab511372c99feb6a23d948cda97e0db6da/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770", size = 1918613 }, - { url = "https://files.pythonhosted.org/packages/93/ac/39b9f99d2474b1ac7af1ddfe5756ddf9b6a8f24c5f3a32cd4c010317fc6b/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0", size = 1872650 }, - { url = "https://files.pythonhosted.org/packages/40/5b/be568548266516b114d1776120281ea9236c732fb6032a1f8f3b1e5e921c/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525", size = 1827415 }, - { url = "https://files.pythonhosted.org/packages/d4/80/c0c13d2a17a12937a19ef378bf35e94399fd171ed6ec05bcee0f038e1eaf/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b", size = 1838094 }, - { url = "https://files.pythonhosted.org/packages/70/d1/5ab93ee00ca5af708929cc12fbe665b6f1ed4ad58088e70dc00e87e0d107/kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238", size = 46585 }, - { url = "https://files.pythonhosted.org/packages/4a/a1/8a9c9be45c642fa12954855d8b3a02d9fd8551165a558835a19508fec2e6/kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276", size = 56095 }, - { url = "https://files.pythonhosted.org/packages/2a/eb/9e099ad7c47c279995d2d20474e1821100a5f10f847739bd65b1c1f02442/kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5", size = 127403 }, - { url = "https://files.pythonhosted.org/packages/a6/94/695922e71288855fc7cace3bdb52edda9d7e50edba77abb0c9d7abb51e96/kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90", size = 68156 }, - { url = "https://files.pythonhosted.org/packages/4a/fe/23d7fa78f7c66086d196406beb1fb2eaf629dd7adc01c3453033303d17fa/kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797", size = 66166 }, - { url = "https://files.pythonhosted.org/packages/f1/68/f472bf16c9141bb1bea5c0b8c66c68fc1ccb048efdbd8f0872b92125724e/kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9", size = 1334300 }, - { url = "https://files.pythonhosted.org/packages/8d/26/b4569d1f29751fca22ee915b4ebfef5974f4ef239b3335fc072882bd62d9/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437", size = 1426579 }, - { url = "https://files.pythonhosted.org/packages/f3/a3/804fc7c8bf233806ec0321c9da35971578620f2ab4fafe67d76100b3ce52/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9", size = 1541360 }, - { url = "https://files.pythonhosted.org/packages/07/ef/286e1d26524854f6fbd6540e8364d67a8857d61038ac743e11edc42fe217/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da", size = 1470091 }, - { url = "https://files.pythonhosted.org/packages/17/ba/17a706b232308e65f57deeccae503c268292e6a091313f6ce833a23093ea/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e", size = 1426259 }, - { url = "https://files.pythonhosted.org/packages/d0/f3/a0925611c9d6c2f37c5935a39203cadec6883aa914e013b46c84c4c2e641/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8", size = 1847516 }, - { url = "https://files.pythonhosted.org/packages/da/85/82d59bb8f7c4c9bb2785138b72462cb1b161668f8230c58bbb28c0403cd5/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d", size = 1946228 }, - { url = "https://files.pythonhosted.org/packages/34/3c/6a37f444c0233993881e5db3a6a1775925d4d9d2f2609bb325bb1348ed94/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0", size = 1901716 }, - { url = "https://files.pythonhosted.org/packages/cd/7e/180425790efc00adfd47db14e1e341cb4826516982334129012b971121a6/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f", size = 1852871 }, - { url = "https://files.pythonhosted.org/packages/1b/9a/13c68b2edb1fa74321e60893a9a5829788e135138e68060cf44e2d92d2c3/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f", size = 1870265 }, - { url = "https://files.pythonhosted.org/packages/9f/0a/fa56a0fdee5da2b4c79899c0f6bd1aefb29d9438c2d66430e78793571c6b/kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac", size = 46649 }, - { url = "https://files.pythonhosted.org/packages/1e/37/d3c2d4ba2719059a0f12730947bbe1ad5ee8bff89e8c35319dcb2c9ddb4c/kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355", size = 56116 }, - { url = "https://files.pythonhosted.org/packages/f3/7a/debbce859be1a2711eb8437818107137192007b88d17b5cfdb556f457b42/kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a", size = 125484 }, - { url = "https://files.pythonhosted.org/packages/2d/e0/bf8df75ba93b9e035cc6757dd5dcaf63084fdc1c846ae134e818bd7e0f03/kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192", size = 67332 }, - { url = "https://files.pythonhosted.org/packages/26/61/58bb691f6880588be3a4801d199bd776032ece07203faf3e4a8b377f7d9b/kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45", size = 64987 }, - { url = "https://files.pythonhosted.org/packages/8e/a3/96ac5413068b237c006f54dd8d70114e8756d70e3da7613c5aef20627e22/kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7", size = 1370613 }, - { url = "https://files.pythonhosted.org/packages/4d/12/f48539e6e17068b59c7f12f4d6214b973431b8e3ac83af525cafd27cebec/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db", size = 1463183 }, - { url = "https://files.pythonhosted.org/packages/f3/70/26c99be8eb034cc8e3f62e0760af1fbdc97a842a7cbc252f7978507d41c2/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff", size = 1581248 }, - { url = "https://files.pythonhosted.org/packages/17/f6/f75f20e543639b09b2de7fc864274a5a9b96cda167a6210a1d9d19306b9d/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228", size = 1508815 }, - { url = "https://files.pythonhosted.org/packages/e3/d5/bc0f22ac108743062ab703f8d6d71c9c7b077b8839fa358700bfb81770b8/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16", size = 1466042 }, - { url = "https://files.pythonhosted.org/packages/75/18/98142500f21d6838bcab49ec919414a1f0c6d049d21ddadf139124db6a70/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9", size = 1885159 }, - { url = "https://files.pythonhosted.org/packages/21/49/a241eff9e0ee013368c1d17957f9d345b0957493c3a43d82ebb558c90b0a/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162", size = 1981694 }, - { url = "https://files.pythonhosted.org/packages/90/90/9490c3de4788123041b1d600d64434f1eed809a2ce9f688075a22166b289/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4", size = 1941579 }, - { url = "https://files.pythonhosted.org/packages/b7/bb/a0cc488ef2aa92d7d304318c8549d3ec8dfe6dd3c2c67a44e3922b77bc4f/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3", size = 1888168 }, - { url = "https://files.pythonhosted.org/packages/4f/e9/9c0de8e45fef3d63f85eed3b1757f9aa511065942866331ef8b99421f433/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a", size = 1908464 }, - { url = "https://files.pythonhosted.org/packages/a3/60/4f0fd50b08f5be536ea0cef518ac7255d9dab43ca40f3b93b60e3ddf80dd/kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20", size = 46473 }, - { url = "https://files.pythonhosted.org/packages/63/50/2746566bdf4a6a842d117367d05c90cfb87ac04e9e2845aa1fa21f071362/kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9", size = 56004 }, + { url = "https://files.pythonhosted.org/packages/f1/56/cb02dcefdaab40df636b91e703b172966b444605a0ea313549f3ffc05bd3/kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af", size = 127397, upload_time = "2023-08-24T09:28:18.105Z" }, + { url = "https://files.pythonhosted.org/packages/0e/c1/d084f8edb26533a191415d5173157080837341f9a06af9dd1a75f727abb4/kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3", size = 68125, upload_time = "2023-08-24T09:28:19.218Z" }, + { url = "https://files.pythonhosted.org/packages/23/11/6fb190bae4b279d712a834e7b1da89f6dcff6791132f7399aa28a57c3565/kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4", size = 66211, upload_time = "2023-08-24T09:28:20.241Z" }, + { url = "https://files.pythonhosted.org/packages/b3/13/5e9e52feb33e9e063f76b2c5eb09cb977f5bba622df3210081bfb26ec9a3/kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1", size = 1637145, upload_time = "2023-08-24T09:28:21.439Z" }, + { url = "https://files.pythonhosted.org/packages/6f/40/4ab1fdb57fced80ce5903f04ae1aed7c1d5939dda4fd0c0aa526c12fe28a/kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff", size = 1617849, upload_time = "2023-08-24T09:28:23.004Z" }, + { url = "https://files.pythonhosted.org/packages/49/ca/61ef43bd0832c7253b370735b0c38972c140c8774889b884372a629a8189/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a", size = 1400921, upload_time = "2023-08-24T09:28:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/68/6f/854f6a845c00b4257482468e08d8bc386f4929ee499206142378ba234419/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa", size = 1513009, upload_time = "2023-08-24T09:28:25.636Z" }, + { url = "https://files.pythonhosted.org/packages/50/65/76f303377167d12eb7a9b423d6771b39fe5c4373e4a42f075805b1f581ae/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c", size = 1444819, upload_time = "2023-08-24T09:28:27.547Z" }, + { url = "https://files.pythonhosted.org/packages/7e/ee/98cdf9dde129551467138b6e18cc1cc901e75ecc7ffb898c6f49609f33b1/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b", size = 1817054, upload_time = "2023-08-24T09:28:28.839Z" }, + { url = "https://files.pythonhosted.org/packages/e6/5b/ab569016ec4abc7b496f6cb8a3ab511372c99feb6a23d948cda97e0db6da/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770", size = 1918613, upload_time = "2023-08-24T09:28:30.351Z" }, + { url = "https://files.pythonhosted.org/packages/93/ac/39b9f99d2474b1ac7af1ddfe5756ddf9b6a8f24c5f3a32cd4c010317fc6b/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0", size = 1872650, upload_time = "2023-08-24T09:28:32.303Z" }, + { url = "https://files.pythonhosted.org/packages/40/5b/be568548266516b114d1776120281ea9236c732fb6032a1f8f3b1e5e921c/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525", size = 1827415, upload_time = "2023-08-24T09:28:34.141Z" }, + { url = "https://files.pythonhosted.org/packages/d4/80/c0c13d2a17a12937a19ef378bf35e94399fd171ed6ec05bcee0f038e1eaf/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b", size = 1838094, upload_time = "2023-08-24T09:28:35.97Z" }, + { url = "https://files.pythonhosted.org/packages/70/d1/5ab93ee00ca5af708929cc12fbe665b6f1ed4ad58088e70dc00e87e0d107/kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238", size = 46585, upload_time = "2023-08-24T09:28:37.326Z" }, + { url = "https://files.pythonhosted.org/packages/4a/a1/8a9c9be45c642fa12954855d8b3a02d9fd8551165a558835a19508fec2e6/kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276", size = 56095, upload_time = "2023-08-24T09:28:38.325Z" }, + { url = "https://files.pythonhosted.org/packages/2a/eb/9e099ad7c47c279995d2d20474e1821100a5f10f847739bd65b1c1f02442/kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5", size = 127403, upload_time = "2023-08-24T09:28:39.3Z" }, + { url = "https://files.pythonhosted.org/packages/a6/94/695922e71288855fc7cace3bdb52edda9d7e50edba77abb0c9d7abb51e96/kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90", size = 68156, upload_time = "2023-08-24T09:28:40.301Z" }, + { url = "https://files.pythonhosted.org/packages/4a/fe/23d7fa78f7c66086d196406beb1fb2eaf629dd7adc01c3453033303d17fa/kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797", size = 66166, upload_time = "2023-08-24T09:28:41.235Z" }, + { url = "https://files.pythonhosted.org/packages/f1/68/f472bf16c9141bb1bea5c0b8c66c68fc1ccb048efdbd8f0872b92125724e/kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9", size = 1334300, upload_time = "2023-08-24T09:28:42.409Z" }, + { url = "https://files.pythonhosted.org/packages/8d/26/b4569d1f29751fca22ee915b4ebfef5974f4ef239b3335fc072882bd62d9/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437", size = 1426579, upload_time = "2023-08-24T09:28:43.677Z" }, + { url = "https://files.pythonhosted.org/packages/f3/a3/804fc7c8bf233806ec0321c9da35971578620f2ab4fafe67d76100b3ce52/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9", size = 1541360, upload_time = "2023-08-24T09:28:45.939Z" }, + { url = "https://files.pythonhosted.org/packages/07/ef/286e1d26524854f6fbd6540e8364d67a8857d61038ac743e11edc42fe217/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da", size = 1470091, upload_time = "2023-08-24T09:28:47.959Z" }, + { url = "https://files.pythonhosted.org/packages/17/ba/17a706b232308e65f57deeccae503c268292e6a091313f6ce833a23093ea/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e", size = 1426259, upload_time = "2023-08-24T09:28:49.224Z" }, + { url = "https://files.pythonhosted.org/packages/d0/f3/a0925611c9d6c2f37c5935a39203cadec6883aa914e013b46c84c4c2e641/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8", size = 1847516, upload_time = "2023-08-24T09:28:50.979Z" }, + { url = "https://files.pythonhosted.org/packages/da/85/82d59bb8f7c4c9bb2785138b72462cb1b161668f8230c58bbb28c0403cd5/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d", size = 1946228, upload_time = "2023-08-24T09:28:52.812Z" }, + { url = "https://files.pythonhosted.org/packages/34/3c/6a37f444c0233993881e5db3a6a1775925d4d9d2f2609bb325bb1348ed94/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0", size = 1901716, upload_time = "2023-08-24T09:28:54.115Z" }, + { url = "https://files.pythonhosted.org/packages/cd/7e/180425790efc00adfd47db14e1e341cb4826516982334129012b971121a6/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f", size = 1852871, upload_time = "2023-08-24T09:28:55.433Z" }, + { url = "https://files.pythonhosted.org/packages/1b/9a/13c68b2edb1fa74321e60893a9a5829788e135138e68060cf44e2d92d2c3/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f", size = 1870265, upload_time = "2023-08-24T09:28:56.855Z" }, + { url = "https://files.pythonhosted.org/packages/9f/0a/fa56a0fdee5da2b4c79899c0f6bd1aefb29d9438c2d66430e78793571c6b/kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac", size = 46649, upload_time = "2023-08-24T09:28:58.021Z" }, + { url = "https://files.pythonhosted.org/packages/1e/37/d3c2d4ba2719059a0f12730947bbe1ad5ee8bff89e8c35319dcb2c9ddb4c/kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355", size = 56116, upload_time = "2023-08-24T09:28:58.994Z" }, + { url = "https://files.pythonhosted.org/packages/f3/7a/debbce859be1a2711eb8437818107137192007b88d17b5cfdb556f457b42/kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a", size = 125484, upload_time = "2023-08-24T09:28:59.975Z" }, + { url = "https://files.pythonhosted.org/packages/2d/e0/bf8df75ba93b9e035cc6757dd5dcaf63084fdc1c846ae134e818bd7e0f03/kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192", size = 67332, upload_time = "2023-08-24T09:29:01.733Z" }, + { url = "https://files.pythonhosted.org/packages/26/61/58bb691f6880588be3a4801d199bd776032ece07203faf3e4a8b377f7d9b/kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45", size = 64987, upload_time = "2023-08-24T09:29:02.789Z" }, + { url = "https://files.pythonhosted.org/packages/8e/a3/96ac5413068b237c006f54dd8d70114e8756d70e3da7613c5aef20627e22/kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7", size = 1370613, upload_time = "2023-08-24T09:29:03.912Z" }, + { url = "https://files.pythonhosted.org/packages/4d/12/f48539e6e17068b59c7f12f4d6214b973431b8e3ac83af525cafd27cebec/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db", size = 1463183, upload_time = "2023-08-24T09:29:05.244Z" }, + { url = "https://files.pythonhosted.org/packages/f3/70/26c99be8eb034cc8e3f62e0760af1fbdc97a842a7cbc252f7978507d41c2/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff", size = 1581248, upload_time = "2023-08-24T09:29:06.531Z" }, + { url = "https://files.pythonhosted.org/packages/17/f6/f75f20e543639b09b2de7fc864274a5a9b96cda167a6210a1d9d19306b9d/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228", size = 1508815, upload_time = "2023-08-24T09:29:07.867Z" }, + { url = "https://files.pythonhosted.org/packages/e3/d5/bc0f22ac108743062ab703f8d6d71c9c7b077b8839fa358700bfb81770b8/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16", size = 1466042, upload_time = "2023-08-24T09:29:09.403Z" }, + { url = "https://files.pythonhosted.org/packages/75/18/98142500f21d6838bcab49ec919414a1f0c6d049d21ddadf139124db6a70/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9", size = 1885159, upload_time = "2023-08-24T09:29:10.66Z" }, + { url = "https://files.pythonhosted.org/packages/21/49/a241eff9e0ee013368c1d17957f9d345b0957493c3a43d82ebb558c90b0a/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162", size = 1981694, upload_time = "2023-08-24T09:29:12.469Z" }, + { url = "https://files.pythonhosted.org/packages/90/90/9490c3de4788123041b1d600d64434f1eed809a2ce9f688075a22166b289/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4", size = 1941579, upload_time = "2023-08-24T09:29:13.743Z" }, + { url = "https://files.pythonhosted.org/packages/b7/bb/a0cc488ef2aa92d7d304318c8549d3ec8dfe6dd3c2c67a44e3922b77bc4f/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3", size = 1888168, upload_time = "2023-08-24T09:29:15.097Z" }, + { url = "https://files.pythonhosted.org/packages/4f/e9/9c0de8e45fef3d63f85eed3b1757f9aa511065942866331ef8b99421f433/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a", size = 1908464, upload_time = "2023-08-24T09:29:16.539Z" }, + { url = "https://files.pythonhosted.org/packages/a3/60/4f0fd50b08f5be536ea0cef518ac7255d9dab43ca40f3b93b60e3ddf80dd/kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20", size = 46473, upload_time = "2023-08-24T09:29:17.956Z" }, + { url = "https://files.pythonhosted.org/packages/63/50/2746566bdf4a6a842d117367d05c90cfb87ac04e9e2845aa1fa21f071362/kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9", size = 56004, upload_time = "2023-08-24T09:29:19.329Z" }, ] [[package]] name = "lazy-loader" version = "0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0e/3a/1630a735bfdf9eb857a3b9a53317a1e1658ea97a1b4b39dcb0f71dae81f8/lazy_loader-0.3.tar.gz", hash = "sha256:3b68898e34f5b2a29daaaac172c6555512d0f32074f147e2254e4a6d9d838f37", size = 12268 } +sdist = { url = "https://files.pythonhosted.org/packages/0e/3a/1630a735bfdf9eb857a3b9a53317a1e1658ea97a1b4b39dcb0f71dae81f8/lazy_loader-0.3.tar.gz", hash = "sha256:3b68898e34f5b2a29daaaac172c6555512d0f32074f147e2254e4a6d9d838f37", size = 12268, upload_time = "2023-06-30T21:12:55.362Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/c3/65b3814e155836acacf720e5be3b5757130346670ac454fee29d3eda1381/lazy_loader-0.3-py3-none-any.whl", hash = "sha256:1e9e76ee8631e264c62ce10006718e80b2cfc74340d17d1031e0f84af7478554", size = 9087 }, + { url = "https://files.pythonhosted.org/packages/a1/c3/65b3814e155836acacf720e5be3b5757130346670ac454fee29d3eda1381/lazy_loader-0.3-py3-none-any.whl", hash = "sha256:1e9e76ee8631e264c62ce10006718e80b2cfc74340d17d1031e0f84af7478554", size = 9087, upload_time = "2023-06-30T21:12:51.09Z" }, ] [[package]] name = "locust" -version = "2.34.1" +version = "2.36.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "configargparse" }, { name = "flask" }, { name = "flask-cors" }, { name = "flask-login" }, - { name = "gevent", marker = "python_full_version != '3.13.*'" }, + { name = "gevent" }, { name = "geventhttpclient" }, + { name = "locust-cloud" }, { name = "msgpack" }, { name = "psutil" }, { name = "pywin32", marker = "sys_platform == 'win32'" }, @@ -1219,9 +1229,25 @@ dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, { name = "werkzeug" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/56/21/c2bfe4f9482f8754e9a1ff2b1840a1abe63640576fc918a67a02fff7d961/locust-2.34.1.tar.gz", hash = "sha256:184a6ffcb0d6c543bbeae4de65cbb198c7e0739d569d48a2b8bf5db962077733", size = 2240533 } +sdist = { url = "https://files.pythonhosted.org/packages/6d/90/55d4fbc8911e5e6ec4072caaca9e8b7b2b11279435c0d1330c9966b0c898/locust-2.36.2.tar.gz", hash = "sha256:604aff7535f5a83b7f666d32373b2dc74ad260c7c3d1dc274f4c82844be72eb6", size = 2251110, upload_time = "2025-04-25T14:03:35.919Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/e4/0944fbfb1ce0bf09cb400ed9349d4cbaed1230114e4018ac28805097f1c6/locust-2.34.1-py3-none-any.whl", hash = "sha256:487bfadd584e3320f9862adf5aa1cfa1023e030a6af414f4e0a92e62617ce451", size = 2257910 }, + { url = "https://files.pythonhosted.org/packages/ab/f5/99dab104be69122eee3513dcdc6e0b32d59ca1f4cfd8715470c5f3aa7643/locust-2.36.2-py3-none-any.whl", hash = "sha256:74239f493f44035b25a87a0665deadf41d213b3dcd45774398e511dec15e26eb", size = 2267937, upload_time = "2025-04-25T14:03:33.671Z" }, +] + +[[package]] +name = "locust-cloud" +version = "1.20.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "configargparse" }, + { name = "gevent" }, + { name = "platformdirs" }, + { name = "python-socketio", extra = ["client"] }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/94/47/1ec2478f3d4e526fb8d667b01a75b22093b2e66aea665b5369dd656ceec9/locust_cloud-1.20.7.tar.gz", hash = "sha256:24c16b767adffab51b97f489bcf142e16e2439354fb4296ecbb3e87ad20e220a", size = 448622, upload_time = "2025-04-28T11:01:49.381Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/07/62b5b174c77d4281235405f1ffd439f12a877e434e007a24a5299c461e39/locust_cloud-1.20.7-py3-none-any.whl", hash = "sha256:f38214e77993d0ee87114dafa857e1689789ed4bfe4ae57c2b9dc754674f08bc", size = 406619, upload_time = "2025-04-28T11:01:43.135Z" }, ] [[package]] @@ -1231,47 +1257,47 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mdurl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload_time = "2023-06-03T06:41:14.443Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload_time = "2023-06-03T06:41:11.019Z" }, ] [[package]] name = "markupsafe" version = "2.1.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/7c/59a3248f411813f8ccba92a55feaac4bf360d29e2ff05ee7d8e1ef2d7dbf/MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", size = 19132 } +sdist = { url = "https://files.pythonhosted.org/packages/6d/7c/59a3248f411813f8ccba92a55feaac4bf360d29e2ff05ee7d8e1ef2d7dbf/MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", size = 19132, upload_time = "2023-06-02T21:43:45.578Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/1d/713d443799d935f4d26a4f1510c9e61b1d288592fb869845e5cc92a1e055/MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", size = 17846 }, - { url = "https://files.pythonhosted.org/packages/f7/9c/86cbd8e0e1d81f0ba420f20539dd459c50537c7751e28102dbfee2b6f28c/MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", size = 13720 }, - { url = "https://files.pythonhosted.org/packages/a6/56/f1d4ee39e898a9e63470cbb7fae1c58cce6874f25f54220b89213a47f273/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", size = 26498 }, - { url = "https://files.pythonhosted.org/packages/12/b3/d9ed2c0971e1435b8a62354b18d3060b66c8cb1d368399ec0b9baa7c0ee5/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", size = 25691 }, - { url = "https://files.pythonhosted.org/packages/bf/b7/c5ba9b7ad9ad21fc4a60df226615cf43ead185d328b77b0327d603d00cc5/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", size = 25366 }, - { url = "https://files.pythonhosted.org/packages/71/61/f5673d7aac2cf7f203859008bb3fc2b25187aa330067c5e9955e5c5ebbab/MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", size = 30505 }, - { url = "https://files.pythonhosted.org/packages/47/26/932140621773bfd4df3223fbdd9e78de3477f424f0d2987c313b1cb655ff/MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", size = 29616 }, - { url = "https://files.pythonhosted.org/packages/3c/c8/74d13c999cbb49e3460bf769025659a37ef4a8e884de629720ab4e42dcdb/MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", size = 29891 }, - { url = "https://files.pythonhosted.org/packages/96/e4/4db3b1abc5a1fe7295aa0683eafd13832084509c3b8236f3faf8dd4eff75/MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", size = 16525 }, - { url = "https://files.pythonhosted.org/packages/84/a8/c4aebb8a14a1d39d5135eb8233a0b95831cdc42c4088358449c3ed657044/MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", size = 17083 }, - { url = "https://files.pythonhosted.org/packages/fe/09/c31503cb8150cf688c1534a7135cc39bb9092f8e0e6369ec73494d16ee0e/MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", size = 17862 }, - { url = "https://files.pythonhosted.org/packages/c0/c7/171f5ac6b065e1425e8fabf4a4dfbeca76fd8070072c6a41bd5c07d90d8b/MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", size = 13738 }, - { url = "https://files.pythonhosted.org/packages/a2/f7/9175ad1b8152092f7c3b78c513c1bdfe9287e0564447d1c2d3d1a2471540/MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", size = 28891 }, - { url = "https://files.pythonhosted.org/packages/fe/21/2eff1de472ca6c99ec3993eab11308787b9879af9ca8bbceb4868cf4f2ca/MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", size = 28096 }, - { url = "https://files.pythonhosted.org/packages/f4/a0/103f94793c3bf829a18d2415117334ece115aeca56f2df1c47fa02c6dbd6/MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", size = 27631 }, - { url = "https://files.pythonhosted.org/packages/43/70/f24470f33b2035b035ef0c0ffebf57006beb2272cf3df068fc5154e04ead/MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", size = 33863 }, - { url = "https://files.pythonhosted.org/packages/32/d4/ce98c4ca713d91c4a17c1a184785cc00b9e9c25699d618956c2b9999500a/MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", size = 32591 }, - { url = "https://files.pythonhosted.org/packages/bb/82/f88ccb3ca6204a4536cf7af5abdad7c3657adac06ab33699aa67279e0744/MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", size = 33186 }, - { url = "https://files.pythonhosted.org/packages/44/53/93405d37bb04a10c43b1bdd6f548097478d494d7eadb4b364e3e1337f0cc/MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", size = 16537 }, - { url = "https://files.pythonhosted.org/packages/be/bb/08b85bc194034efbf572e70c3951549c8eca0ada25363afc154386b5390a/MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", size = 17089 }, - { url = "https://files.pythonhosted.org/packages/89/5a/ee546f2aa73a1d6fcfa24272f356fe06d29acca81e76b8d32ca53e429a2e/MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", size = 17849 }, - { url = "https://files.pythonhosted.org/packages/3a/72/9f683a059bde096776e8acf9aa34cbbba21ddc399861fe3953790d4f2cde/MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", size = 13700 }, - { url = "https://files.pythonhosted.org/packages/9d/78/92f15eb9b1e8f1668a9787ba103cf6f8d19a9efed8150245404836145c24/MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11", size = 29319 }, - { url = "https://files.pythonhosted.org/packages/51/94/9a04085114ff2c24f7424dbc890a281d73c5a74ea935dc2e69c66a3bd558/MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", size = 28314 }, - { url = "https://files.pythonhosted.org/packages/ec/53/fcb3214bd370185e223b209ce6bb010fb887ea57173ca4f75bd211b24e10/MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", size = 27696 }, - { url = "https://files.pythonhosted.org/packages/e7/33/54d29854716725d7826079b8984dd235fac76dab1c32321e555d493e61f5/MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", size = 33746 }, - { url = "https://files.pythonhosted.org/packages/11/40/ea7f85e2681d29bc9301c757257de561923924f24de1802d9c3baa396bb4/MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", size = 32131 }, - { url = "https://files.pythonhosted.org/packages/41/f1/bc770c37ecd58638c18f8ec85df205dacb818ccf933692082fd93010a4bc/MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", size = 32878 }, - { url = "https://files.pythonhosted.org/packages/49/74/bf95630aab0a9ed6a67556cd4e54f6aeb0e74f4cb0fd2f229154873a4be4/MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", size = 16426 }, - { url = "https://files.pythonhosted.org/packages/44/44/dbaf65876e258facd65f586dde158387ab89963e7f2235551afc9c2e24c2/MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", size = 16979 }, + { url = "https://files.pythonhosted.org/packages/20/1d/713d443799d935f4d26a4f1510c9e61b1d288592fb869845e5cc92a1e055/MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", size = 17846, upload_time = "2023-06-02T21:42:33.954Z" }, + { url = "https://files.pythonhosted.org/packages/f7/9c/86cbd8e0e1d81f0ba420f20539dd459c50537c7751e28102dbfee2b6f28c/MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", size = 13720, upload_time = "2023-06-02T21:42:35.102Z" }, + { url = "https://files.pythonhosted.org/packages/a6/56/f1d4ee39e898a9e63470cbb7fae1c58cce6874f25f54220b89213a47f273/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", size = 26498, upload_time = "2023-06-02T21:42:36.608Z" }, + { url = "https://files.pythonhosted.org/packages/12/b3/d9ed2c0971e1435b8a62354b18d3060b66c8cb1d368399ec0b9baa7c0ee5/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", size = 25691, upload_time = "2023-06-02T21:42:37.778Z" }, + { url = "https://files.pythonhosted.org/packages/bf/b7/c5ba9b7ad9ad21fc4a60df226615cf43ead185d328b77b0327d603d00cc5/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", size = 25366, upload_time = "2023-06-02T21:42:39.441Z" }, + { url = "https://files.pythonhosted.org/packages/71/61/f5673d7aac2cf7f203859008bb3fc2b25187aa330067c5e9955e5c5ebbab/MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", size = 30505, upload_time = "2023-06-02T21:42:41.088Z" }, + { url = "https://files.pythonhosted.org/packages/47/26/932140621773bfd4df3223fbdd9e78de3477f424f0d2987c313b1cb655ff/MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", size = 29616, upload_time = "2023-06-02T21:42:42.273Z" }, + { url = "https://files.pythonhosted.org/packages/3c/c8/74d13c999cbb49e3460bf769025659a37ef4a8e884de629720ab4e42dcdb/MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", size = 29891, upload_time = "2023-06-02T21:42:43.635Z" }, + { url = "https://files.pythonhosted.org/packages/96/e4/4db3b1abc5a1fe7295aa0683eafd13832084509c3b8236f3faf8dd4eff75/MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", size = 16525, upload_time = "2023-06-02T21:42:45.271Z" }, + { url = "https://files.pythonhosted.org/packages/84/a8/c4aebb8a14a1d39d5135eb8233a0b95831cdc42c4088358449c3ed657044/MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", size = 17083, upload_time = "2023-06-02T21:42:46.948Z" }, + { url = "https://files.pythonhosted.org/packages/fe/09/c31503cb8150cf688c1534a7135cc39bb9092f8e0e6369ec73494d16ee0e/MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", size = 17862, upload_time = "2023-06-02T21:42:48.569Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c7/171f5ac6b065e1425e8fabf4a4dfbeca76fd8070072c6a41bd5c07d90d8b/MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", size = 13738, upload_time = "2023-06-02T21:42:49.727Z" }, + { url = "https://files.pythonhosted.org/packages/a2/f7/9175ad1b8152092f7c3b78c513c1bdfe9287e0564447d1c2d3d1a2471540/MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", size = 28891, upload_time = "2023-06-02T21:42:51.33Z" }, + { url = "https://files.pythonhosted.org/packages/fe/21/2eff1de472ca6c99ec3993eab11308787b9879af9ca8bbceb4868cf4f2ca/MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", size = 28096, upload_time = "2023-06-02T21:42:52.966Z" }, + { url = "https://files.pythonhosted.org/packages/f4/a0/103f94793c3bf829a18d2415117334ece115aeca56f2df1c47fa02c6dbd6/MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", size = 27631, upload_time = "2023-06-02T21:42:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/43/70/f24470f33b2035b035ef0c0ffebf57006beb2272cf3df068fc5154e04ead/MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", size = 33863, upload_time = "2023-06-02T21:42:55.777Z" }, + { url = "https://files.pythonhosted.org/packages/32/d4/ce98c4ca713d91c4a17c1a184785cc00b9e9c25699d618956c2b9999500a/MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", size = 32591, upload_time = "2023-06-02T21:42:57.415Z" }, + { url = "https://files.pythonhosted.org/packages/bb/82/f88ccb3ca6204a4536cf7af5abdad7c3657adac06ab33699aa67279e0744/MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", size = 33186, upload_time = "2023-06-02T21:42:59.107Z" }, + { url = "https://files.pythonhosted.org/packages/44/53/93405d37bb04a10c43b1bdd6f548097478d494d7eadb4b364e3e1337f0cc/MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", size = 16537, upload_time = "2023-06-02T21:43:00.927Z" }, + { url = "https://files.pythonhosted.org/packages/be/bb/08b85bc194034efbf572e70c3951549c8eca0ada25363afc154386b5390a/MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", size = 17089, upload_time = "2023-06-02T21:43:02.355Z" }, + { url = "https://files.pythonhosted.org/packages/89/5a/ee546f2aa73a1d6fcfa24272f356fe06d29acca81e76b8d32ca53e429a2e/MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", size = 17849, upload_time = "2023-09-07T16:00:43.795Z" }, + { url = "https://files.pythonhosted.org/packages/3a/72/9f683a059bde096776e8acf9aa34cbbba21ddc399861fe3953790d4f2cde/MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", size = 13700, upload_time = "2023-09-07T16:00:45.384Z" }, + { url = "https://files.pythonhosted.org/packages/9d/78/92f15eb9b1e8f1668a9787ba103cf6f8d19a9efed8150245404836145c24/MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11", size = 29319, upload_time = "2023-09-07T16:00:46.48Z" }, + { url = "https://files.pythonhosted.org/packages/51/94/9a04085114ff2c24f7424dbc890a281d73c5a74ea935dc2e69c66a3bd558/MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", size = 28314, upload_time = "2023-09-07T16:00:47.64Z" }, + { url = "https://files.pythonhosted.org/packages/ec/53/fcb3214bd370185e223b209ce6bb010fb887ea57173ca4f75bd211b24e10/MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", size = 27696, upload_time = "2023-09-07T16:00:48.92Z" }, + { url = "https://files.pythonhosted.org/packages/e7/33/54d29854716725d7826079b8984dd235fac76dab1c32321e555d493e61f5/MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", size = 33746, upload_time = "2023-09-07T16:00:50.081Z" }, + { url = "https://files.pythonhosted.org/packages/11/40/ea7f85e2681d29bc9301c757257de561923924f24de1802d9c3baa396bb4/MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", size = 32131, upload_time = "2023-09-07T16:00:51.822Z" }, + { url = "https://files.pythonhosted.org/packages/41/f1/bc770c37ecd58638c18f8ec85df205dacb818ccf933692082fd93010a4bc/MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", size = 32878, upload_time = "2023-09-07T16:00:53.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/74/bf95630aab0a9ed6a67556cd4e54f6aeb0e74f4cb0fd2f229154873a4be4/MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", size = 16426, upload_time = "2023-09-07T16:00:55.987Z" }, + { url = "https://files.pythonhosted.org/packages/44/44/dbaf65876e258facd65f586dde158387ab89963e7f2235551afc9c2e24c2/MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", size = 16979, upload_time = "2023-09-07T16:00:57.77Z" }, ] [[package]] @@ -1289,85 +1315,85 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/ab/38a0e94cb01dacb50f06957c2bed1c83b8f9dac6618988a37b2487862944/matplotlib-3.8.2.tar.gz", hash = "sha256:01a978b871b881ee76017152f1f1a0cbf6bd5f7b8ff8c96df0df1bd57d8755a1", size = 35866957 } +sdist = { url = "https://files.pythonhosted.org/packages/fb/ab/38a0e94cb01dacb50f06957c2bed1c83b8f9dac6618988a37b2487862944/matplotlib-3.8.2.tar.gz", hash = "sha256:01a978b871b881ee76017152f1f1a0cbf6bd5f7b8ff8c96df0df1bd57d8755a1", size = 35866957, upload_time = "2023-11-17T21:16:40.15Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/92/d0/fc5f6796a1956f5b9a33555611d01a3cec038f000c3d70ecb051b1631ac4/matplotlib-3.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:09796f89fb71a0c0e1e2f4bdaf63fb2cefc84446bb963ecdeb40dfee7dfa98c7", size = 7590640 }, - { url = "https://files.pythonhosted.org/packages/57/44/007b592809f50883c910db9ec4b81b16dfa0136407250fb581824daabf03/matplotlib-3.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f9c6976748a25e8b9be51ea028df49b8e561eed7809146da7a47dbecebab367", size = 7484350 }, - { url = "https://files.pythonhosted.org/packages/01/87/c7b24f3048234fe10184560263be2173311376dc3d1fa329de7f012d6ce5/matplotlib-3.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b78e4f2cedf303869b782071b55fdde5987fda3038e9d09e58c91cc261b5ad18", size = 11382388 }, - { url = "https://files.pythonhosted.org/packages/19/e5/a4ea514515f270224435c69359abb7a3d152ed31b9ee3ba5e63017461945/matplotlib-3.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e208f46cf6576a7624195aa047cb344a7f802e113bb1a06cfd4bee431de5e31", size = 11611959 }, - { url = "https://files.pythonhosted.org/packages/09/23/ab5a562c9acb81e351b084bea39f65b153918417fb434619cf5a19f44a55/matplotlib-3.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:46a569130ff53798ea5f50afce7406e91fdc471ca1e0e26ba976a8c734c9427a", size = 9536938 }, - { url = "https://files.pythonhosted.org/packages/46/37/b5e27ab30ecc0a3694c8a78287b5ef35dad0c3095c144fcc43081170bfd6/matplotlib-3.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:830f00640c965c5b7f6bc32f0d4ce0c36dfe0379f7dd65b07a00c801713ec40a", size = 7643836 }, - { url = "https://files.pythonhosted.org/packages/a9/0d/53afb186adafc7326d093b8333e8a79974c495095771659f4304626c4bc7/matplotlib-3.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d86593ccf546223eb75a39b44c32788e6f6440d13cfc4750c1c15d0fcb850b63", size = 7593458 }, - { url = "https://files.pythonhosted.org/packages/ce/25/a557ee10ac9dce1300850024707ce1850a6958f1673a9194be878b99d631/matplotlib-3.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a5430836811b7652991939012f43d2808a2db9b64ee240387e8c43e2e5578c8", size = 7486840 }, - { url = "https://files.pythonhosted.org/packages/e7/3d/72712b3895ee180f6e342638a8591c31912fbcc09ce9084cc256da16d0a0/matplotlib-3.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9576723858a78751d5aacd2497b8aef29ffea6d1c95981505877f7ac28215c6", size = 11387332 }, - { url = "https://files.pythonhosted.org/packages/92/1a/cd3e0c90d1a763ad90073e13b189b4702f11becf4e71dbbad70a7a149811/matplotlib-3.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ba9cbd8ac6cf422f3102622b20f8552d601bf8837e49a3afed188d560152788", size = 11616911 }, - { url = "https://files.pythonhosted.org/packages/78/4a/bad239071477305a3758eb4810615e310a113399cddd7682998be9f01e97/matplotlib-3.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:03f9d160a29e0b65c0790bb07f4f45d6a181b1ac33eb1bb0dd225986450148f0", size = 9549260 }, - { url = "https://files.pythonhosted.org/packages/26/5a/27fd341e4510257789f19a4b4be8bb90d1113b8f176c3dab562b4f21466e/matplotlib-3.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:3773002da767f0a9323ba1a9b9b5d00d6257dbd2a93107233167cfb581f64717", size = 7645742 }, - { url = "https://files.pythonhosted.org/packages/e4/1b/864d28d5a72d586ac137f4ca54d5afc8b869720e30d508dbd9adcce4d231/matplotlib-3.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:4c318c1e95e2f5926fba326f68177dee364aa791d6df022ceb91b8221bd0a627", size = 7590988 }, - { url = "https://files.pythonhosted.org/packages/9a/b0/dd2b60f2dd90fbc21d1d3129c36a453c322d7995d5e3589f5b3c59ee528d/matplotlib-3.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:091275d18d942cf1ee9609c830a1bc36610607d8223b1b981c37d5c9fc3e46a4", size = 7483594 }, - { url = "https://files.pythonhosted.org/packages/33/da/9942533ad9f96753bde0e5a5d48eacd6c21de8ea1ad16570e31bda8a017f/matplotlib-3.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b0f3b8ea0e99e233a4bcc44590f01604840d833c280ebb8fe5554fd3e6cfe8d", size = 11380843 }, - { url = "https://files.pythonhosted.org/packages/fc/52/bfd36eb4745a3b21b3946c2c3a15679b620e14574fe2b98e9451b65ef578/matplotlib-3.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b1704a530395aaf73912be741c04d181f82ca78084fbd80bc737be04848331", size = 11604608 }, - { url = "https://files.pythonhosted.org/packages/6d/8c/0cdfbf604d4ea3dfa77435176c51e233cc408ad8f3efbf8d2c9f57cbdafb/matplotlib-3.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533b0e3b0c6768eef8cbe4b583731ce25a91ab54a22f830db2b031e83cca9213", size = 9545252 }, - { url = "https://files.pythonhosted.org/packages/2e/51/c77a14869b7eb9d6fb440e811b754fc3950d6868c38ace57d0632b674415/matplotlib-3.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:0f4fc5d72b75e2c18e55eb32292659cf731d9d5b312a6eb036506304f4675630", size = 7645067 }, + { url = "https://files.pythonhosted.org/packages/92/d0/fc5f6796a1956f5b9a33555611d01a3cec038f000c3d70ecb051b1631ac4/matplotlib-3.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:09796f89fb71a0c0e1e2f4bdaf63fb2cefc84446bb963ecdeb40dfee7dfa98c7", size = 7590640, upload_time = "2023-11-17T21:17:02.834Z" }, + { url = "https://files.pythonhosted.org/packages/57/44/007b592809f50883c910db9ec4b81b16dfa0136407250fb581824daabf03/matplotlib-3.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f9c6976748a25e8b9be51ea028df49b8e561eed7809146da7a47dbecebab367", size = 7484350, upload_time = "2023-11-17T21:17:12.281Z" }, + { url = "https://files.pythonhosted.org/packages/01/87/c7b24f3048234fe10184560263be2173311376dc3d1fa329de7f012d6ce5/matplotlib-3.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b78e4f2cedf303869b782071b55fdde5987fda3038e9d09e58c91cc261b5ad18", size = 11382388, upload_time = "2023-11-17T21:17:26.461Z" }, + { url = "https://files.pythonhosted.org/packages/19/e5/a4ea514515f270224435c69359abb7a3d152ed31b9ee3ba5e63017461945/matplotlib-3.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e208f46cf6576a7624195aa047cb344a7f802e113bb1a06cfd4bee431de5e31", size = 11611959, upload_time = "2023-11-17T21:17:40.541Z" }, + { url = "https://files.pythonhosted.org/packages/09/23/ab5a562c9acb81e351b084bea39f65b153918417fb434619cf5a19f44a55/matplotlib-3.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:46a569130ff53798ea5f50afce7406e91fdc471ca1e0e26ba976a8c734c9427a", size = 9536938, upload_time = "2023-11-17T21:17:49.925Z" }, + { url = "https://files.pythonhosted.org/packages/46/37/b5e27ab30ecc0a3694c8a78287b5ef35dad0c3095c144fcc43081170bfd6/matplotlib-3.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:830f00640c965c5b7f6bc32f0d4ce0c36dfe0379f7dd65b07a00c801713ec40a", size = 7643836, upload_time = "2023-11-17T21:17:58.379Z" }, + { url = "https://files.pythonhosted.org/packages/a9/0d/53afb186adafc7326d093b8333e8a79974c495095771659f4304626c4bc7/matplotlib-3.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d86593ccf546223eb75a39b44c32788e6f6440d13cfc4750c1c15d0fcb850b63", size = 7593458, upload_time = "2023-11-17T21:18:06.141Z" }, + { url = "https://files.pythonhosted.org/packages/ce/25/a557ee10ac9dce1300850024707ce1850a6958f1673a9194be878b99d631/matplotlib-3.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a5430836811b7652991939012f43d2808a2db9b64ee240387e8c43e2e5578c8", size = 7486840, upload_time = "2023-11-17T21:18:13.706Z" }, + { url = "https://files.pythonhosted.org/packages/e7/3d/72712b3895ee180f6e342638a8591c31912fbcc09ce9084cc256da16d0a0/matplotlib-3.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9576723858a78751d5aacd2497b8aef29ffea6d1c95981505877f7ac28215c6", size = 11387332, upload_time = "2023-11-17T21:18:23.699Z" }, + { url = "https://files.pythonhosted.org/packages/92/1a/cd3e0c90d1a763ad90073e13b189b4702f11becf4e71dbbad70a7a149811/matplotlib-3.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ba9cbd8ac6cf422f3102622b20f8552d601bf8837e49a3afed188d560152788", size = 11616911, upload_time = "2023-11-17T21:18:35.27Z" }, + { url = "https://files.pythonhosted.org/packages/78/4a/bad239071477305a3758eb4810615e310a113399cddd7682998be9f01e97/matplotlib-3.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:03f9d160a29e0b65c0790bb07f4f45d6a181b1ac33eb1bb0dd225986450148f0", size = 9549260, upload_time = "2023-11-17T21:18:44.836Z" }, + { url = "https://files.pythonhosted.org/packages/26/5a/27fd341e4510257789f19a4b4be8bb90d1113b8f176c3dab562b4f21466e/matplotlib-3.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:3773002da767f0a9323ba1a9b9b5d00d6257dbd2a93107233167cfb581f64717", size = 7645742, upload_time = "2023-11-17T21:18:53.448Z" }, + { url = "https://files.pythonhosted.org/packages/e4/1b/864d28d5a72d586ac137f4ca54d5afc8b869720e30d508dbd9adcce4d231/matplotlib-3.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:4c318c1e95e2f5926fba326f68177dee364aa791d6df022ceb91b8221bd0a627", size = 7590988, upload_time = "2023-11-17T21:19:01.119Z" }, + { url = "https://files.pythonhosted.org/packages/9a/b0/dd2b60f2dd90fbc21d1d3129c36a453c322d7995d5e3589f5b3c59ee528d/matplotlib-3.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:091275d18d942cf1ee9609c830a1bc36610607d8223b1b981c37d5c9fc3e46a4", size = 7483594, upload_time = "2023-11-17T21:19:09.865Z" }, + { url = "https://files.pythonhosted.org/packages/33/da/9942533ad9f96753bde0e5a5d48eacd6c21de8ea1ad16570e31bda8a017f/matplotlib-3.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b0f3b8ea0e99e233a4bcc44590f01604840d833c280ebb8fe5554fd3e6cfe8d", size = 11380843, upload_time = "2023-11-17T21:19:20.46Z" }, + { url = "https://files.pythonhosted.org/packages/fc/52/bfd36eb4745a3b21b3946c2c3a15679b620e14574fe2b98e9451b65ef578/matplotlib-3.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b1704a530395aaf73912be741c04d181f82ca78084fbd80bc737be04848331", size = 11604608, upload_time = "2023-11-17T21:19:31.363Z" }, + { url = "https://files.pythonhosted.org/packages/6d/8c/0cdfbf604d4ea3dfa77435176c51e233cc408ad8f3efbf8d2c9f57cbdafb/matplotlib-3.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533b0e3b0c6768eef8cbe4b583731ce25a91ab54a22f830db2b031e83cca9213", size = 9545252, upload_time = "2023-11-17T21:19:42.271Z" }, + { url = "https://files.pythonhosted.org/packages/2e/51/c77a14869b7eb9d6fb440e811b754fc3950d6868c38ace57d0632b674415/matplotlib-3.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:0f4fc5d72b75e2c18e55eb32292659cf731d9d5b312a6eb036506304f4675630", size = 7645067, upload_time = "2023-11-17T21:19:50.091Z" }, ] [[package]] name = "mdurl" version = "0.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload_time = "2022-08-14T12:40:10.846Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload_time = "2022-08-14T12:40:09.779Z" }, ] [[package]] name = "mpmath" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload_time = "2023-03-07T16:47:11.061Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload_time = "2023-03-07T16:47:09.197Z" }, ] [[package]] name = "msgpack" version = "1.0.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c2/d5/5662032db1571110b5b51647aed4b56dfbd01bfae789fa566a2be1f385d1/msgpack-1.0.7.tar.gz", hash = "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87", size = 166311 } +sdist = { url = "https://files.pythonhosted.org/packages/c2/d5/5662032db1571110b5b51647aed4b56dfbd01bfae789fa566a2be1f385d1/msgpack-1.0.7.tar.gz", hash = "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87", size = 166311, upload_time = "2023-09-28T13:20:36.726Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/41/3a/2e2e902afcd751738e38d88af976fc4010b16e8e821945f4cbf32f75f9c3/msgpack-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862", size = 304827 }, - { url = "https://files.pythonhosted.org/packages/86/a6/490792a524a82e855bdf3885ecb73d7b3a0b17744b3cf4a40aea13ceca38/msgpack-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329", size = 234959 }, - { url = "https://files.pythonhosted.org/packages/ad/72/d39ed43bfb2ec6968d768318477adb90c474bdc59b2437170c6697ee4115/msgpack-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b", size = 231970 }, - { url = "https://files.pythonhosted.org/packages/a2/90/2d769e693654f036acfb462b54dacb3ae345699999897ca34f6bd9534fe9/msgpack-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6", size = 522440 }, - { url = "https://files.pythonhosted.org/packages/46/95/d0440400485eab1bf50f1efe5118967b539f3191d994c3dfc220657594cd/msgpack-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee", size = 530797 }, - { url = "https://files.pythonhosted.org/packages/76/33/35df717bc095c6e938b3c65ed117b95048abc24d1614427685123fb2f0af/msgpack-1.0.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d", size = 520372 }, - { url = "https://files.pythonhosted.org/packages/af/d1/abbdd58a43827fbec5d98427a7a535c620890289b9d927154465313d6967/msgpack-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d", size = 527287 }, - { url = "https://files.pythonhosted.org/packages/0c/ac/66625b05091b97ca2c7418eb2d2af152f033d969519f9315556a4ed800fe/msgpack-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1", size = 560715 }, - { url = "https://files.pythonhosted.org/packages/de/4e/a0e8611f94bac32d2c1c4ad05bb1c0ae61132e3398e0b44a93e6d7830968/msgpack-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681", size = 532614 }, - { url = "https://files.pythonhosted.org/packages/9b/07/0b3f089684ca330602b2994248eda2898a7232e4b63882b9271164ef672e/msgpack-1.0.7-cp310-cp310-win32.whl", hash = "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9", size = 216340 }, - { url = "https://files.pythonhosted.org/packages/4b/14/c62fbc8dff118f1558e43b9469d56a1f37bbb35febadc3163efaedd01500/msgpack-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415", size = 222828 }, - { url = "https://files.pythonhosted.org/packages/f9/b3/309de40dc7406b7f3492332c5ee2b492a593c2a9bb97ea48ebf2f5279999/msgpack-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84", size = 305096 }, - { url = "https://files.pythonhosted.org/packages/15/56/a677cd761a2cefb2e3ffe7e684633294dccb161d78e8ea6da9277e45b4a2/msgpack-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93", size = 235210 }, - { url = "https://files.pythonhosted.org/packages/f5/4e/1ab4a982cbd90f988e49f849fc1212f2c04a59870c59daabf8950617e2aa/msgpack-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8", size = 231952 }, - { url = "https://files.pythonhosted.org/packages/6d/74/bd02044eb628c7361ad2bd8c1a6147af5c6c2bbceb77b3b1da20f4a8a9c5/msgpack-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46", size = 549511 }, - { url = "https://files.pythonhosted.org/packages/df/09/dee50913ba5cc047f7fd7162f09453a676e7935c84b3bf3a398e12108677/msgpack-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b", size = 557980 }, - { url = "https://files.pythonhosted.org/packages/26/a5/78a7d87f5f8ffe4c32167afa15d4957db649bab4822f909d8d765339bbab/msgpack-1.0.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e", size = 545547 }, - { url = "https://files.pythonhosted.org/packages/d4/53/698c10913947f97f6fe7faad86a34e6aa1b66cea2df6f99105856bd346d9/msgpack-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002", size = 554669 }, - { url = "https://files.pythonhosted.org/packages/f5/3f/9730c6cb574b15d349b80cd8523a7df4b82058528339f952ea1c32ac8a10/msgpack-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c", size = 583353 }, - { url = "https://files.pythonhosted.org/packages/4c/bc/dc184d943692671149848438fb3bed3a3de288ce7998cb91bc98f40f201b/msgpack-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e", size = 557455 }, - { url = "https://files.pythonhosted.org/packages/cf/7b/1bc69d4a56c8d2f4f2dfbe4722d40344af9a85b6fb3b09cfb350ba6a42f6/msgpack-1.0.7-cp311-cp311-win32.whl", hash = "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1", size = 216367 }, - { url = "https://files.pythonhosted.org/packages/b4/3d/c8dd23050eefa3d9b9c5b8329ed3308c2f2f80f65825e9ea4b7fa621cdab/msgpack-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82", size = 222860 }, - { url = "https://files.pythonhosted.org/packages/d7/47/20dff6b4512cf3575550c8801bc53fe7d540f4efef9c5c37af51760fcdcf/msgpack-1.0.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b", size = 305759 }, - { url = "https://files.pythonhosted.org/packages/6f/8a/34f1726d2c9feccec3d946776e9bce8f20ae09d8b91899fc20b296c942af/msgpack-1.0.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4", size = 235330 }, - { url = "https://files.pythonhosted.org/packages/9c/f6/e64c72577d6953789c3cb051b059a4b56317056b3c65013952338ed8a34e/msgpack-1.0.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee", size = 232537 }, - { url = "https://files.pythonhosted.org/packages/89/75/1ed3a96e12941873fd957e016cc40c0c178861a872bd45e75b9a188eb422/msgpack-1.0.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5", size = 546561 }, - { url = "https://files.pythonhosted.org/packages/e5/0a/c6a1390f9c6a31da0fecbbfdb86b1cb39ad302d9e24f9cca3d9e14c364f0/msgpack-1.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672", size = 559009 }, - { url = "https://files.pythonhosted.org/packages/a5/74/99f6077754665613ea1f37b3d91c10129f6976b7721ab4d0973023808e5a/msgpack-1.0.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075", size = 543882 }, - { url = "https://files.pythonhosted.org/packages/9c/7e/dc0dc8de2bf27743b31691149258f9b1bd4bf3c44c105df3df9b97081cd1/msgpack-1.0.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba", size = 546949 }, - { url = "https://files.pythonhosted.org/packages/78/61/91bae9474def032f6c333d62889bbeda9e1554c6b123375ceeb1767efd78/msgpack-1.0.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c", size = 579836 }, - { url = "https://files.pythonhosted.org/packages/5d/4d/d98592099d4f18945f89cf3e634dc0cb128bb33b1b93f85a84173d35e181/msgpack-1.0.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5", size = 556587 }, - { url = "https://files.pythonhosted.org/packages/5e/44/6556ffe169bf2c0e974e2ea25fb82a7e55ebcf52a81b03a5e01820de5f84/msgpack-1.0.7-cp312-cp312-win32.whl", hash = "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9", size = 216509 }, - { url = "https://files.pythonhosted.org/packages/dc/c1/63903f30d51d165e132e5221a2a4a1bbfab7508b68131c871d70bffac78a/msgpack-1.0.7-cp312-cp312-win_amd64.whl", hash = "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf", size = 223287 }, + { url = "https://files.pythonhosted.org/packages/41/3a/2e2e902afcd751738e38d88af976fc4010b16e8e821945f4cbf32f75f9c3/msgpack-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862", size = 304827, upload_time = "2023-09-28T13:18:30.258Z" }, + { url = "https://files.pythonhosted.org/packages/86/a6/490792a524a82e855bdf3885ecb73d7b3a0b17744b3cf4a40aea13ceca38/msgpack-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329", size = 234959, upload_time = "2023-09-28T13:18:32.146Z" }, + { url = "https://files.pythonhosted.org/packages/ad/72/d39ed43bfb2ec6968d768318477adb90c474bdc59b2437170c6697ee4115/msgpack-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b", size = 231970, upload_time = "2023-09-28T13:18:34.134Z" }, + { url = "https://files.pythonhosted.org/packages/a2/90/2d769e693654f036acfb462b54dacb3ae345699999897ca34f6bd9534fe9/msgpack-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6", size = 522440, upload_time = "2023-09-28T13:18:35.866Z" }, + { url = "https://files.pythonhosted.org/packages/46/95/d0440400485eab1bf50f1efe5118967b539f3191d994c3dfc220657594cd/msgpack-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee", size = 530797, upload_time = "2023-09-28T13:18:37.653Z" }, + { url = "https://files.pythonhosted.org/packages/76/33/35df717bc095c6e938b3c65ed117b95048abc24d1614427685123fb2f0af/msgpack-1.0.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d", size = 520372, upload_time = "2023-09-28T13:18:39.685Z" }, + { url = "https://files.pythonhosted.org/packages/af/d1/abbdd58a43827fbec5d98427a7a535c620890289b9d927154465313d6967/msgpack-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d", size = 527287, upload_time = "2023-09-28T13:18:41.051Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ac/66625b05091b97ca2c7418eb2d2af152f033d969519f9315556a4ed800fe/msgpack-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1", size = 560715, upload_time = "2023-09-28T13:18:42.883Z" }, + { url = "https://files.pythonhosted.org/packages/de/4e/a0e8611f94bac32d2c1c4ad05bb1c0ae61132e3398e0b44a93e6d7830968/msgpack-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681", size = 532614, upload_time = "2023-09-28T13:18:44.679Z" }, + { url = "https://files.pythonhosted.org/packages/9b/07/0b3f089684ca330602b2994248eda2898a7232e4b63882b9271164ef672e/msgpack-1.0.7-cp310-cp310-win32.whl", hash = "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9", size = 216340, upload_time = "2023-09-28T13:18:46.588Z" }, + { url = "https://files.pythonhosted.org/packages/4b/14/c62fbc8dff118f1558e43b9469d56a1f37bbb35febadc3163efaedd01500/msgpack-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415", size = 222828, upload_time = "2023-09-28T13:18:47.875Z" }, + { url = "https://files.pythonhosted.org/packages/f9/b3/309de40dc7406b7f3492332c5ee2b492a593c2a9bb97ea48ebf2f5279999/msgpack-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84", size = 305096, upload_time = "2023-09-28T13:18:49.678Z" }, + { url = "https://files.pythonhosted.org/packages/15/56/a677cd761a2cefb2e3ffe7e684633294dccb161d78e8ea6da9277e45b4a2/msgpack-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93", size = 235210, upload_time = "2023-09-28T13:18:51.039Z" }, + { url = "https://files.pythonhosted.org/packages/f5/4e/1ab4a982cbd90f988e49f849fc1212f2c04a59870c59daabf8950617e2aa/msgpack-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8", size = 231952, upload_time = "2023-09-28T13:18:52.871Z" }, + { url = "https://files.pythonhosted.org/packages/6d/74/bd02044eb628c7361ad2bd8c1a6147af5c6c2bbceb77b3b1da20f4a8a9c5/msgpack-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46", size = 549511, upload_time = "2023-09-28T13:18:54.422Z" }, + { url = "https://files.pythonhosted.org/packages/df/09/dee50913ba5cc047f7fd7162f09453a676e7935c84b3bf3a398e12108677/msgpack-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b", size = 557980, upload_time = "2023-09-28T13:18:56.058Z" }, + { url = "https://files.pythonhosted.org/packages/26/a5/78a7d87f5f8ffe4c32167afa15d4957db649bab4822f909d8d765339bbab/msgpack-1.0.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e", size = 545547, upload_time = "2023-09-28T13:18:57.396Z" }, + { url = "https://files.pythonhosted.org/packages/d4/53/698c10913947f97f6fe7faad86a34e6aa1b66cea2df6f99105856bd346d9/msgpack-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002", size = 554669, upload_time = "2023-09-28T13:18:58.957Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3f/9730c6cb574b15d349b80cd8523a7df4b82058528339f952ea1c32ac8a10/msgpack-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c", size = 583353, upload_time = "2023-09-28T13:19:01.186Z" }, + { url = "https://files.pythonhosted.org/packages/4c/bc/dc184d943692671149848438fb3bed3a3de288ce7998cb91bc98f40f201b/msgpack-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e", size = 557455, upload_time = "2023-09-28T13:19:03.201Z" }, + { url = "https://files.pythonhosted.org/packages/cf/7b/1bc69d4a56c8d2f4f2dfbe4722d40344af9a85b6fb3b09cfb350ba6a42f6/msgpack-1.0.7-cp311-cp311-win32.whl", hash = "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1", size = 216367, upload_time = "2023-09-28T13:19:04.554Z" }, + { url = "https://files.pythonhosted.org/packages/b4/3d/c8dd23050eefa3d9b9c5b8329ed3308c2f2f80f65825e9ea4b7fa621cdab/msgpack-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82", size = 222860, upload_time = "2023-09-28T13:19:06.397Z" }, + { url = "https://files.pythonhosted.org/packages/d7/47/20dff6b4512cf3575550c8801bc53fe7d540f4efef9c5c37af51760fcdcf/msgpack-1.0.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b", size = 305759, upload_time = "2023-09-28T13:19:08.148Z" }, + { url = "https://files.pythonhosted.org/packages/6f/8a/34f1726d2c9feccec3d946776e9bce8f20ae09d8b91899fc20b296c942af/msgpack-1.0.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4", size = 235330, upload_time = "2023-09-28T13:19:09.417Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f6/e64c72577d6953789c3cb051b059a4b56317056b3c65013952338ed8a34e/msgpack-1.0.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee", size = 232537, upload_time = "2023-09-28T13:19:10.898Z" }, + { url = "https://files.pythonhosted.org/packages/89/75/1ed3a96e12941873fd957e016cc40c0c178861a872bd45e75b9a188eb422/msgpack-1.0.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5", size = 546561, upload_time = "2023-09-28T13:19:12.779Z" }, + { url = "https://files.pythonhosted.org/packages/e5/0a/c6a1390f9c6a31da0fecbbfdb86b1cb39ad302d9e24f9cca3d9e14c364f0/msgpack-1.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672", size = 559009, upload_time = "2023-09-28T13:19:14.373Z" }, + { url = "https://files.pythonhosted.org/packages/a5/74/99f6077754665613ea1f37b3d91c10129f6976b7721ab4d0973023808e5a/msgpack-1.0.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075", size = 543882, upload_time = "2023-09-28T13:19:16.277Z" }, + { url = "https://files.pythonhosted.org/packages/9c/7e/dc0dc8de2bf27743b31691149258f9b1bd4bf3c44c105df3df9b97081cd1/msgpack-1.0.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba", size = 546949, upload_time = "2023-09-28T13:19:18.114Z" }, + { url = "https://files.pythonhosted.org/packages/78/61/91bae9474def032f6c333d62889bbeda9e1554c6b123375ceeb1767efd78/msgpack-1.0.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c", size = 579836, upload_time = "2023-09-28T13:19:19.729Z" }, + { url = "https://files.pythonhosted.org/packages/5d/4d/d98592099d4f18945f89cf3e634dc0cb128bb33b1b93f85a84173d35e181/msgpack-1.0.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5", size = 556587, upload_time = "2023-09-28T13:19:21.666Z" }, + { url = "https://files.pythonhosted.org/packages/5e/44/6556ffe169bf2c0e974e2ea25fb82a7e55ebcf52a81b03a5e01820de5f84/msgpack-1.0.7-cp312-cp312-win32.whl", hash = "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9", size = 216509, upload_time = "2023-09-28T13:19:23.161Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c1/63903f30d51d165e132e5221a2a4a1bbfab7508b68131c871d70bffac78a/msgpack-1.0.7-cp312-cp312-win_amd64.whl", hash = "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf", size = 223287, upload_time = "2023-09-28T13:19:25.097Z" }, ] [[package]] @@ -1379,83 +1405,83 @@ dependencies = [ { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717 } +sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717, upload_time = "2025-02-05T03:50:34.655Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/f8/65a7ce8d0e09b6329ad0c8d40330d100ea343bd4dd04c4f8ae26462d0a17/mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13", size = 10738433 }, - { url = "https://files.pythonhosted.org/packages/b4/95/9c0ecb8eacfe048583706249439ff52105b3f552ea9c4024166c03224270/mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559", size = 9861472 }, - { url = "https://files.pythonhosted.org/packages/84/09/9ec95e982e282e20c0d5407bc65031dfd0f0f8ecc66b69538296e06fcbee/mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b", size = 11611424 }, - { url = "https://files.pythonhosted.org/packages/78/13/f7d14e55865036a1e6a0a69580c240f43bc1f37407fe9235c0d4ef25ffb0/mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3", size = 12365450 }, - { url = "https://files.pythonhosted.org/packages/48/e1/301a73852d40c241e915ac6d7bcd7fedd47d519246db2d7b86b9d7e7a0cb/mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b", size = 12551765 }, - { url = "https://files.pythonhosted.org/packages/77/ba/c37bc323ae5fe7f3f15a28e06ab012cd0b7552886118943e90b15af31195/mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828", size = 9274701 }, - { url = "https://files.pythonhosted.org/packages/03/bc/f6339726c627bd7ca1ce0fa56c9ae2d0144604a319e0e339bdadafbbb599/mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f", size = 10662338 }, - { url = "https://files.pythonhosted.org/packages/e2/90/8dcf506ca1a09b0d17555cc00cd69aee402c203911410136cd716559efe7/mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5", size = 9787540 }, - { url = "https://files.pythonhosted.org/packages/05/05/a10f9479681e5da09ef2f9426f650d7b550d4bafbef683b69aad1ba87457/mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e", size = 11538051 }, - { url = "https://files.pythonhosted.org/packages/e9/9a/1f7d18b30edd57441a6411fcbc0c6869448d1a4bacbaee60656ac0fc29c8/mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c", size = 12286751 }, - { url = "https://files.pythonhosted.org/packages/72/af/19ff499b6f1dafcaf56f9881f7a965ac2f474f69f6f618b5175b044299f5/mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f", size = 12421783 }, - { url = "https://files.pythonhosted.org/packages/96/39/11b57431a1f686c1aed54bf794870efe0f6aeca11aca281a0bd87a5ad42c/mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f", size = 9265618 }, - { url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981 }, - { url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175 }, - { url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675 }, - { url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020 }, - { url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582 }, - { url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614 }, - { url = "https://files.pythonhosted.org/packages/6a/9b/fd2e05d6ffff24d912f150b87db9e364fa8282045c875654ce7e32fffa66/mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445", size = 10788592 }, - { url = "https://files.pythonhosted.org/packages/74/37/b246d711c28a03ead1fd906bbc7106659aed7c089d55fe40dd58db812628/mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d", size = 9753611 }, - { url = "https://files.pythonhosted.org/packages/a6/ac/395808a92e10cfdac8003c3de9a2ab6dc7cde6c0d2a4df3df1b815ffd067/mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5", size = 11438443 }, - { url = "https://files.pythonhosted.org/packages/d2/8b/801aa06445d2de3895f59e476f38f3f8d610ef5d6908245f07d002676cbf/mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036", size = 12402541 }, - { url = "https://files.pythonhosted.org/packages/c7/67/5a4268782eb77344cc613a4cf23540928e41f018a9a1ec4c6882baf20ab8/mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357", size = 12494348 }, - { url = "https://files.pythonhosted.org/packages/83/3e/57bb447f7bbbfaabf1712d96f9df142624a386d98fb026a761532526057e/mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf", size = 9373648 }, - { url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777 }, + { url = "https://files.pythonhosted.org/packages/68/f8/65a7ce8d0e09b6329ad0c8d40330d100ea343bd4dd04c4f8ae26462d0a17/mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13", size = 10738433, upload_time = "2025-02-05T03:49:29.145Z" }, + { url = "https://files.pythonhosted.org/packages/b4/95/9c0ecb8eacfe048583706249439ff52105b3f552ea9c4024166c03224270/mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559", size = 9861472, upload_time = "2025-02-05T03:49:16.986Z" }, + { url = "https://files.pythonhosted.org/packages/84/09/9ec95e982e282e20c0d5407bc65031dfd0f0f8ecc66b69538296e06fcbee/mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b", size = 11611424, upload_time = "2025-02-05T03:49:46.908Z" }, + { url = "https://files.pythonhosted.org/packages/78/13/f7d14e55865036a1e6a0a69580c240f43bc1f37407fe9235c0d4ef25ffb0/mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3", size = 12365450, upload_time = "2025-02-05T03:50:05.89Z" }, + { url = "https://files.pythonhosted.org/packages/48/e1/301a73852d40c241e915ac6d7bcd7fedd47d519246db2d7b86b9d7e7a0cb/mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b", size = 12551765, upload_time = "2025-02-05T03:49:33.56Z" }, + { url = "https://files.pythonhosted.org/packages/77/ba/c37bc323ae5fe7f3f15a28e06ab012cd0b7552886118943e90b15af31195/mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828", size = 9274701, upload_time = "2025-02-05T03:49:38.981Z" }, + { url = "https://files.pythonhosted.org/packages/03/bc/f6339726c627bd7ca1ce0fa56c9ae2d0144604a319e0e339bdadafbbb599/mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f", size = 10662338, upload_time = "2025-02-05T03:50:17.287Z" }, + { url = "https://files.pythonhosted.org/packages/e2/90/8dcf506ca1a09b0d17555cc00cd69aee402c203911410136cd716559efe7/mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5", size = 9787540, upload_time = "2025-02-05T03:49:51.21Z" }, + { url = "https://files.pythonhosted.org/packages/05/05/a10f9479681e5da09ef2f9426f650d7b550d4bafbef683b69aad1ba87457/mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e", size = 11538051, upload_time = "2025-02-05T03:50:20.885Z" }, + { url = "https://files.pythonhosted.org/packages/e9/9a/1f7d18b30edd57441a6411fcbc0c6869448d1a4bacbaee60656ac0fc29c8/mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c", size = 12286751, upload_time = "2025-02-05T03:49:42.408Z" }, + { url = "https://files.pythonhosted.org/packages/72/af/19ff499b6f1dafcaf56f9881f7a965ac2f474f69f6f618b5175b044299f5/mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f", size = 12421783, upload_time = "2025-02-05T03:49:07.707Z" }, + { url = "https://files.pythonhosted.org/packages/96/39/11b57431a1f686c1aed54bf794870efe0f6aeca11aca281a0bd87a5ad42c/mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f", size = 9265618, upload_time = "2025-02-05T03:49:54.581Z" }, + { url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981, upload_time = "2025-02-05T03:50:28.25Z" }, + { url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175, upload_time = "2025-02-05T03:50:13.411Z" }, + { url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675, upload_time = "2025-02-05T03:50:31.421Z" }, + { url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020, upload_time = "2025-02-05T03:48:48.705Z" }, + { url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582, upload_time = "2025-02-05T03:49:03.628Z" }, + { url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614, upload_time = "2025-02-05T03:50:00.313Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9b/fd2e05d6ffff24d912f150b87db9e364fa8282045c875654ce7e32fffa66/mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445", size = 10788592, upload_time = "2025-02-05T03:48:55.789Z" }, + { url = "https://files.pythonhosted.org/packages/74/37/b246d711c28a03ead1fd906bbc7106659aed7c089d55fe40dd58db812628/mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d", size = 9753611, upload_time = "2025-02-05T03:48:44.581Z" }, + { url = "https://files.pythonhosted.org/packages/a6/ac/395808a92e10cfdac8003c3de9a2ab6dc7cde6c0d2a4df3df1b815ffd067/mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5", size = 11438443, upload_time = "2025-02-05T03:49:25.514Z" }, + { url = "https://files.pythonhosted.org/packages/d2/8b/801aa06445d2de3895f59e476f38f3f8d610ef5d6908245f07d002676cbf/mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036", size = 12402541, upload_time = "2025-02-05T03:49:57.623Z" }, + { url = "https://files.pythonhosted.org/packages/c7/67/5a4268782eb77344cc613a4cf23540928e41f018a9a1ec4c6882baf20ab8/mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357", size = 12494348, upload_time = "2025-02-05T03:48:52.361Z" }, + { url = "https://files.pythonhosted.org/packages/83/3e/57bb447f7bbbfaabf1712d96f9df142624a386d98fb026a761532526057e/mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf", size = 9373648, upload_time = "2025-02-05T03:49:11.395Z" }, + { url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777, upload_time = "2025-02-05T03:50:08.348Z" }, ] [[package]] name = "mypy-extensions" version = "1.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } +sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433, upload_time = "2023-02-04T12:11:27.157Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, + { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695, upload_time = "2023-02-04T12:11:25.002Z" }, ] [[package]] name = "networkx" version = "3.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c4/80/a84676339aaae2f1cfdf9f418701dd634aef9cc76f708ef55c36ff39c3ca/networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6", size = 2073928 } +sdist = { url = "https://files.pythonhosted.org/packages/c4/80/a84676339aaae2f1cfdf9f418701dd634aef9cc76f708ef55c36ff39c3ca/networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6", size = 2073928, upload_time = "2023-10-28T08:41:39.364Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/f0/8fbc882ca80cf077f1b246c0e3c3465f7f415439bdea6b899f6b19f61f70/networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2", size = 1647772 }, + { url = "https://files.pythonhosted.org/packages/d5/f0/8fbc882ca80cf077f1b246c0e3c3465f7f415439bdea6b899f6b19f61f70/networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2", size = 1647772, upload_time = "2023-10-28T08:41:36.945Z" }, ] [[package]] name = "numpy" version = "1.26.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129 } +sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129, upload_time = "2024-02-06T00:26:44.495Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468 }, - { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411 }, - { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016 }, - { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889 }, - { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746 }, - { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620 }, - { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659 }, - { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905 }, - { url = "https://files.pythonhosted.org/packages/11/57/baae43d14fe163fa0e4c47f307b6b2511ab8d7d30177c491960504252053/numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", size = 20630554 }, - { url = "https://files.pythonhosted.org/packages/1a/2e/151484f49fd03944c4a3ad9c418ed193cfd02724e138ac8a9505d056c582/numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", size = 13997127 }, - { url = "https://files.pythonhosted.org/packages/79/ae/7e5b85136806f9dadf4878bf73cf223fe5c2636818ba3ab1c585d0403164/numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", size = 14222994 }, - { url = "https://files.pythonhosted.org/packages/3a/d0/edc009c27b406c4f9cbc79274d6e46d634d139075492ad055e3d68445925/numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5", size = 18252005 }, - { url = "https://files.pythonhosted.org/packages/09/bf/2b1aaf8f525f2923ff6cfcf134ae5e750e279ac65ebf386c75a0cf6da06a/numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a", size = 13885297 }, - { url = "https://files.pythonhosted.org/packages/df/a0/4e0f14d847cfc2a633a1c8621d00724f3206cfeddeb66d35698c4e2cf3d2/numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a", size = 18093567 }, - { url = "https://files.pythonhosted.org/packages/d2/b7/a734c733286e10a7f1a8ad1ae8c90f2d33bf604a96548e0a4a3a6739b468/numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20", size = 5968812 }, - { url = "https://files.pythonhosted.org/packages/3f/6b/5610004206cf7f8e7ad91c5a85a8c71b2f2f8051a0c0c4d5916b76d6cbb2/numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2", size = 15811913 }, - { url = "https://files.pythonhosted.org/packages/95/12/8f2020a8e8b8383ac0177dc9570aad031a3beb12e38847f7129bacd96228/numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218", size = 20335901 }, - { url = "https://files.pythonhosted.org/packages/75/5b/ca6c8bd14007e5ca171c7c03102d17b4f4e0ceb53957e8c44343a9546dcc/numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b", size = 13685868 }, - { url = "https://files.pythonhosted.org/packages/79/f8/97f10e6755e2a7d027ca783f63044d5b1bc1ae7acb12afe6a9b4286eac17/numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b", size = 13925109 }, - { url = "https://files.pythonhosted.org/packages/0f/50/de23fde84e45f5c4fda2488c759b69990fd4512387a8632860f3ac9cd225/numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed", size = 17950613 }, - { url = "https://files.pythonhosted.org/packages/4c/0c/9c603826b6465e82591e05ca230dfc13376da512b25ccd0894709b054ed0/numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a", size = 13572172 }, - { url = "https://files.pythonhosted.org/packages/76/8c/2ba3902e1a0fc1c74962ea9bb33a534bb05984ad7ff9515bf8d07527cadd/numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0", size = 17786643 }, - { url = "https://files.pythonhosted.org/packages/28/4a/46d9e65106879492374999e76eb85f87b15328e06bd1550668f79f7b18c6/numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110", size = 5677803 }, - { url = "https://files.pythonhosted.org/packages/16/2e/86f24451c2d530c88daf997cb8d6ac622c1d40d19f5a031ed68a4b73a374/numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818", size = 15517754 }, + { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468, upload_time = "2024-02-05T23:48:01.194Z" }, + { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411, upload_time = "2024-02-05T23:48:29.038Z" }, + { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016, upload_time = "2024-02-05T23:48:54.098Z" }, + { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889, upload_time = "2024-02-05T23:49:25.361Z" }, + { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746, upload_time = "2024-02-05T23:49:51.983Z" }, + { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620, upload_time = "2024-02-05T23:50:22.515Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659, upload_time = "2024-02-05T23:50:35.834Z" }, + { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905, upload_time = "2024-02-05T23:51:03.701Z" }, + { url = "https://files.pythonhosted.org/packages/11/57/baae43d14fe163fa0e4c47f307b6b2511ab8d7d30177c491960504252053/numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", size = 20630554, upload_time = "2024-02-05T23:51:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/1a/2e/151484f49fd03944c4a3ad9c418ed193cfd02724e138ac8a9505d056c582/numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", size = 13997127, upload_time = "2024-02-05T23:52:15.314Z" }, + { url = "https://files.pythonhosted.org/packages/79/ae/7e5b85136806f9dadf4878bf73cf223fe5c2636818ba3ab1c585d0403164/numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", size = 14222994, upload_time = "2024-02-05T23:52:47.569Z" }, + { url = "https://files.pythonhosted.org/packages/3a/d0/edc009c27b406c4f9cbc79274d6e46d634d139075492ad055e3d68445925/numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5", size = 18252005, upload_time = "2024-02-05T23:53:15.637Z" }, + { url = "https://files.pythonhosted.org/packages/09/bf/2b1aaf8f525f2923ff6cfcf134ae5e750e279ac65ebf386c75a0cf6da06a/numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a", size = 13885297, upload_time = "2024-02-05T23:53:42.16Z" }, + { url = "https://files.pythonhosted.org/packages/df/a0/4e0f14d847cfc2a633a1c8621d00724f3206cfeddeb66d35698c4e2cf3d2/numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a", size = 18093567, upload_time = "2024-02-05T23:54:11.696Z" }, + { url = "https://files.pythonhosted.org/packages/d2/b7/a734c733286e10a7f1a8ad1ae8c90f2d33bf604a96548e0a4a3a6739b468/numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20", size = 5968812, upload_time = "2024-02-05T23:54:26.453Z" }, + { url = "https://files.pythonhosted.org/packages/3f/6b/5610004206cf7f8e7ad91c5a85a8c71b2f2f8051a0c0c4d5916b76d6cbb2/numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2", size = 15811913, upload_time = "2024-02-05T23:54:53.933Z" }, + { url = "https://files.pythonhosted.org/packages/95/12/8f2020a8e8b8383ac0177dc9570aad031a3beb12e38847f7129bacd96228/numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218", size = 20335901, upload_time = "2024-02-05T23:55:32.801Z" }, + { url = "https://files.pythonhosted.org/packages/75/5b/ca6c8bd14007e5ca171c7c03102d17b4f4e0ceb53957e8c44343a9546dcc/numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b", size = 13685868, upload_time = "2024-02-05T23:55:56.28Z" }, + { url = "https://files.pythonhosted.org/packages/79/f8/97f10e6755e2a7d027ca783f63044d5b1bc1ae7acb12afe6a9b4286eac17/numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b", size = 13925109, upload_time = "2024-02-05T23:56:20.368Z" }, + { url = "https://files.pythonhosted.org/packages/0f/50/de23fde84e45f5c4fda2488c759b69990fd4512387a8632860f3ac9cd225/numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed", size = 17950613, upload_time = "2024-02-05T23:56:56.054Z" }, + { url = "https://files.pythonhosted.org/packages/4c/0c/9c603826b6465e82591e05ca230dfc13376da512b25ccd0894709b054ed0/numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a", size = 13572172, upload_time = "2024-02-05T23:57:21.56Z" }, + { url = "https://files.pythonhosted.org/packages/76/8c/2ba3902e1a0fc1c74962ea9bb33a534bb05984ad7ff9515bf8d07527cadd/numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0", size = 17786643, upload_time = "2024-02-05T23:57:56.585Z" }, + { url = "https://files.pythonhosted.org/packages/28/4a/46d9e65106879492374999e76eb85f87b15328e06bd1550668f79f7b18c6/numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110", size = 5677803, upload_time = "2024-02-05T23:58:08.963Z" }, + { url = "https://files.pythonhosted.org/packages/16/2e/86f24451c2d530c88daf997cb8d6ac622c1d40d19f5a031ed68a4b73a374/numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818", size = 15517754, upload_time = "2024-02-05T23:58:36.364Z" }, ] [[package]] @@ -1466,26 +1492,26 @@ dependencies = [ { name = "numpy" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fe/0978403c8d710ece2f34006367e78de80410743fe0e7680c8f33f2dab20d/onnx-1.16.0.tar.gz", hash = "sha256:237c6987c6c59d9f44b6136f5819af79574f8d96a760a1fa843bede11f3822f7", size = 12303017 } +sdist = { url = "https://files.pythonhosted.org/packages/b3/fe/0978403c8d710ece2f34006367e78de80410743fe0e7680c8f33f2dab20d/onnx-1.16.0.tar.gz", hash = "sha256:237c6987c6c59d9f44b6136f5819af79574f8d96a760a1fa843bede11f3822f7", size = 12303017, upload_time = "2024-03-25T15:33:46.091Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/0b/f4705e4a3fa6fd0de971302fdae17ad176b024eca8c24360f0e37c00f9df/onnx-1.16.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:9eadbdce25b19d6216f426d6d99b8bc877a65ed92cbef9707751c6669190ba4f", size = 16514483 }, - { url = "https://files.pythonhosted.org/packages/b8/1c/50310a559857951fc6e069cf5d89deebe34287997d1c5928bca435456f62/onnx-1.16.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:034ae21a2aaa2e9c14119a840d2926d213c27aad29e5e3edaa30145a745048e1", size = 15012939 }, - { url = "https://files.pythonhosted.org/packages/ef/6e/96be6692ebcd8da568084d753f386ce08efa1f99b216f346ee281edd6cc3/onnx-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec22a43d74eb1f2303373e2fbe7fbcaa45fb225f4eb146edfed1356ada7a9aea", size = 15791856 }, - { url = "https://files.pythonhosted.org/packages/49/5f/d8e1a24247f506a77cbe22341c72ca91bea3b468c5d6bca2047d885ea3c6/onnx-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:298f28a2b5ac09145fa958513d3d1e6b349ccf86a877dbdcccad57713fe360b3", size = 15922279 }, - { url = "https://files.pythonhosted.org/packages/cb/14/562e4ac22cdf41f4465e3b114ef1a9467d513eeff0b9c2285c2da5db6ed1/onnx-1.16.0-cp310-cp310-win32.whl", hash = "sha256:66300197b52beca08bc6262d43c103289c5d45fde43fb51922ed1eb83658cf0c", size = 14335703 }, - { url = "https://files.pythonhosted.org/packages/3b/e2/471ff83b3862967791d67f630000afce038756afbdf0665a3d767677c851/onnx-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:ae0029f5e47bf70a1a62e7f88c80bca4ef39b844a89910039184221775df5e43", size = 14435099 }, - { url = "https://files.pythonhosted.org/packages/a4/b8/7accf3f93eee498711f0b7f07f6e93906e031622473e85ce9cd3578f6a92/onnx-1.16.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:f51179d4af3372b4f3800c558d204b592c61e4b4a18b8f61e0eea7f46211221a", size = 16514376 }, - { url = "https://files.pythonhosted.org/packages/cc/24/a328236b594d5fea23f70a3a8139e730cb43334f0b24693831c47c9064f0/onnx-1.16.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:5202559070afec5144332db216c20f2fff8323cf7f6512b0ca11b215eacc5bf3", size = 15012839 }, - { url = "https://files.pythonhosted.org/packages/80/12/57187bab3f830a47fa65eafe4fbaef01dfdf5042cf82a41fa440fab68766/onnx-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77579e7c15b4df39d29465b216639a5f9b74026bdd9e4b6306cd19a32dcfe67c", size = 15791944 }, - { url = "https://files.pythonhosted.org/packages/df/48/63f68b65d041aedffab41eea930563ca52aab70dbaa7d4820501618c1a70/onnx-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e60ca76ac24b65c25860d0f2d2cdd96d6320d062a01dd8ce87c5743603789b8", size = 15922450 }, - { url = "https://files.pythonhosted.org/packages/08/1b/4bdf4534f5ff08973725ba5409f95bbf64e2789cd20be615880dae689973/onnx-1.16.0-cp311-cp311-win32.whl", hash = "sha256:81b4ee01bc554e8a2b11ac6439882508a5377a1c6b452acd69a1eebb83571117", size = 14335808 }, - { url = "https://files.pythonhosted.org/packages/aa/d0/0514d02d2e84e7bb48a105877eae4065e54d7dabb60d0b60214fe2677346/onnx-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:7449241e70b847b9c3eb8dae622df8c1b456d11032a9d7e26e0ee8a698d5bf86", size = 14434905 }, - { url = "https://files.pythonhosted.org/packages/42/87/577adadda30ee08041e81ef02a331ca9d1a8df93a2e4c4c53ec56fbbc2ac/onnx-1.16.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:03a627488b1a9975d95d6a55582af3e14c7f3bb87444725b999935ddd271d352", size = 16516304 }, - { url = "https://files.pythonhosted.org/packages/e3/1b/6e1ea37e081cc49a28f0e4d3830b4c8525081354cf9f5529c6c92268fc77/onnx-1.16.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:c392faeabd9283ee344ccb4b067d1fea9dfc614fa1f0de7c47589efd79e15e78", size = 15016538 }, - { url = "https://files.pythonhosted.org/packages/6d/07/f8fefd5eb0984be42ef677f0b7db7527edc4529224a34a3c31f7b12ec80d/onnx-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0efeb46985de08f0efe758cb54ad3457e821a05c2eaf5ba2ccb8cd1602c08084", size = 15790415 }, - { url = "https://files.pythonhosted.org/packages/11/71/c219ce6d4b5205c77405af7f2de2511ad4eeffbfeb77a422151e893de0ea/onnx-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddf14a3d32234f23e44abb73a755cb96a423fac7f004e8f046f36b10214151ee", size = 15922224 }, - { url = "https://files.pythonhosted.org/packages/8e/a4/554a6e5741b42406c5b1970d04685d7f2012019d4178408ed4b3ec953033/onnx-1.16.0-cp312-cp312-win32.whl", hash = "sha256:62a2e27ae8ba5fc9b4a2620301446a517b5ffaaf8566611de7a7c2160f5bcf4c", size = 14336234 }, - { url = "https://files.pythonhosted.org/packages/e9/a1/8aecec497010ad34e7656408df1868d94483c5c56bc991f4088c06150896/onnx-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:3e0860fea94efde777e81a6f68f65761ed5e5f3adea2e050d7fbe373a9ae05b3", size = 14436591 }, + { url = "https://files.pythonhosted.org/packages/c8/0b/f4705e4a3fa6fd0de971302fdae17ad176b024eca8c24360f0e37c00f9df/onnx-1.16.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:9eadbdce25b19d6216f426d6d99b8bc877a65ed92cbef9707751c6669190ba4f", size = 16514483, upload_time = "2024-03-25T15:25:07.947Z" }, + { url = "https://files.pythonhosted.org/packages/b8/1c/50310a559857951fc6e069cf5d89deebe34287997d1c5928bca435456f62/onnx-1.16.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:034ae21a2aaa2e9c14119a840d2926d213c27aad29e5e3edaa30145a745048e1", size = 15012939, upload_time = "2024-03-25T15:25:11.632Z" }, + { url = "https://files.pythonhosted.org/packages/ef/6e/96be6692ebcd8da568084d753f386ce08efa1f99b216f346ee281edd6cc3/onnx-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec22a43d74eb1f2303373e2fbe7fbcaa45fb225f4eb146edfed1356ada7a9aea", size = 15791856, upload_time = "2024-03-25T15:25:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/49/5f/d8e1a24247f506a77cbe22341c72ca91bea3b468c5d6bca2047d885ea3c6/onnx-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:298f28a2b5ac09145fa958513d3d1e6b349ccf86a877dbdcccad57713fe360b3", size = 15922279, upload_time = "2024-03-25T15:25:18.939Z" }, + { url = "https://files.pythonhosted.org/packages/cb/14/562e4ac22cdf41f4465e3b114ef1a9467d513eeff0b9c2285c2da5db6ed1/onnx-1.16.0-cp310-cp310-win32.whl", hash = "sha256:66300197b52beca08bc6262d43c103289c5d45fde43fb51922ed1eb83658cf0c", size = 14335703, upload_time = "2024-03-25T15:25:22.611Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e2/471ff83b3862967791d67f630000afce038756afbdf0665a3d767677c851/onnx-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:ae0029f5e47bf70a1a62e7f88c80bca4ef39b844a89910039184221775df5e43", size = 14435099, upload_time = "2024-03-25T15:25:25.05Z" }, + { url = "https://files.pythonhosted.org/packages/a4/b8/7accf3f93eee498711f0b7f07f6e93906e031622473e85ce9cd3578f6a92/onnx-1.16.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:f51179d4af3372b4f3800c558d204b592c61e4b4a18b8f61e0eea7f46211221a", size = 16514376, upload_time = "2024-03-25T15:25:27.899Z" }, + { url = "https://files.pythonhosted.org/packages/cc/24/a328236b594d5fea23f70a3a8139e730cb43334f0b24693831c47c9064f0/onnx-1.16.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:5202559070afec5144332db216c20f2fff8323cf7f6512b0ca11b215eacc5bf3", size = 15012839, upload_time = "2024-03-25T15:25:31.16Z" }, + { url = "https://files.pythonhosted.org/packages/80/12/57187bab3f830a47fa65eafe4fbaef01dfdf5042cf82a41fa440fab68766/onnx-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77579e7c15b4df39d29465b216639a5f9b74026bdd9e4b6306cd19a32dcfe67c", size = 15791944, upload_time = "2024-03-25T15:25:34.778Z" }, + { url = "https://files.pythonhosted.org/packages/df/48/63f68b65d041aedffab41eea930563ca52aab70dbaa7d4820501618c1a70/onnx-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e60ca76ac24b65c25860d0f2d2cdd96d6320d062a01dd8ce87c5743603789b8", size = 15922450, upload_time = "2024-03-25T15:25:37.983Z" }, + { url = "https://files.pythonhosted.org/packages/08/1b/4bdf4534f5ff08973725ba5409f95bbf64e2789cd20be615880dae689973/onnx-1.16.0-cp311-cp311-win32.whl", hash = "sha256:81b4ee01bc554e8a2b11ac6439882508a5377a1c6b452acd69a1eebb83571117", size = 14335808, upload_time = "2024-03-25T15:25:40.523Z" }, + { url = "https://files.pythonhosted.org/packages/aa/d0/0514d02d2e84e7bb48a105877eae4065e54d7dabb60d0b60214fe2677346/onnx-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:7449241e70b847b9c3eb8dae622df8c1b456d11032a9d7e26e0ee8a698d5bf86", size = 14434905, upload_time = "2024-03-25T15:25:42.905Z" }, + { url = "https://files.pythonhosted.org/packages/42/87/577adadda30ee08041e81ef02a331ca9d1a8df93a2e4c4c53ec56fbbc2ac/onnx-1.16.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:03a627488b1a9975d95d6a55582af3e14c7f3bb87444725b999935ddd271d352", size = 16516304, upload_time = "2024-03-25T15:25:45.875Z" }, + { url = "https://files.pythonhosted.org/packages/e3/1b/6e1ea37e081cc49a28f0e4d3830b4c8525081354cf9f5529c6c92268fc77/onnx-1.16.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:c392faeabd9283ee344ccb4b067d1fea9dfc614fa1f0de7c47589efd79e15e78", size = 15016538, upload_time = "2024-03-25T15:25:49.396Z" }, + { url = "https://files.pythonhosted.org/packages/6d/07/f8fefd5eb0984be42ef677f0b7db7527edc4529224a34a3c31f7b12ec80d/onnx-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0efeb46985de08f0efe758cb54ad3457e821a05c2eaf5ba2ccb8cd1602c08084", size = 15790415, upload_time = "2024-03-25T15:25:51.929Z" }, + { url = "https://files.pythonhosted.org/packages/11/71/c219ce6d4b5205c77405af7f2de2511ad4eeffbfeb77a422151e893de0ea/onnx-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddf14a3d32234f23e44abb73a755cb96a423fac7f004e8f046f36b10214151ee", size = 15922224, upload_time = "2024-03-25T15:25:55.049Z" }, + { url = "https://files.pythonhosted.org/packages/8e/a4/554a6e5741b42406c5b1970d04685d7f2012019d4178408ed4b3ec953033/onnx-1.16.0-cp312-cp312-win32.whl", hash = "sha256:62a2e27ae8ba5fc9b4a2620301446a517b5ffaaf8566611de7a7c2160f5bcf4c", size = 14336234, upload_time = "2024-03-25T15:25:57.998Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a1/8aecec497010ad34e7656408df1868d94483c5c56bc991f4088c06150896/onnx-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:3e0860fea94efde777e81a6f68f65761ed5e5f3adea2e050d7fbe373a9ae05b3", size = 14436591, upload_time = "2024-03-25T15:26:01.252Z" }, ] [[package]] @@ -1501,27 +1527,27 @@ dependencies = [ { name = "sympy" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/28/99f903b0eb1cd6f3faa0e343217d9fb9f47b84bca98bd9859884631336ee/onnxruntime-1.20.1-cp310-cp310-macosx_13_0_universal2.whl", hash = "sha256:e50ba5ff7fed4f7d9253a6baf801ca2883cc08491f9d32d78a80da57256a5439", size = 30996314 }, - { url = "https://files.pythonhosted.org/packages/6d/c6/c4c0860bee2fde6037bdd9dcd12d323f6e38cf00fcc9a5065b394337fc55/onnxruntime-1.20.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7b2908b50101a19e99c4d4e97ebb9905561daf61829403061c1adc1b588bc0de", size = 11954010 }, - { url = "https://files.pythonhosted.org/packages/63/47/3dc0b075ab539f16b3d8b09df6b504f51836086ee709690a6278d791737d/onnxruntime-1.20.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d82daaec24045a2e87598b8ac2b417b1cce623244e80e663882e9fe1aae86410", size = 13330452 }, - { url = "https://files.pythonhosted.org/packages/27/ef/80fab86289ecc01a734b7ddf115dfb93d8b2e004bd1e1977e12881c72b12/onnxruntime-1.20.1-cp310-cp310-win32.whl", hash = "sha256:4c4b251a725a3b8cf2aab284f7d940c26094ecd9d442f07dd81ab5470e99b83f", size = 9813849 }, - { url = "https://files.pythonhosted.org/packages/a9/e6/33ab10066c9875a29d55e66ae97c3bf91b9b9b987179455d67c32261a49c/onnxruntime-1.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:d3b616bb53a77a9463707bb313637223380fc327f5064c9a782e8ec69c22e6a2", size = 11329702 }, - { url = "https://files.pythonhosted.org/packages/95/8d/2634e2959b34aa8a0037989f4229e9abcfa484e9c228f99633b3241768a6/onnxruntime-1.20.1-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:06bfbf02ca9ab5f28946e0f912a562a5f005301d0c419283dc57b3ed7969bb7b", size = 30998725 }, - { url = "https://files.pythonhosted.org/packages/a5/da/c44bf9bd66cd6d9018a921f053f28d819445c4d84b4dd4777271b0fe52a2/onnxruntime-1.20.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f6243e34d74423bdd1edf0ae9596dd61023b260f546ee17d701723915f06a9f7", size = 11955227 }, - { url = "https://files.pythonhosted.org/packages/11/ac/4120dfb74c8e45cce1c664fc7f7ce010edd587ba67ac41489f7432eb9381/onnxruntime-1.20.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5eec64c0269dcdb8d9a9a53dc4d64f87b9e0c19801d9321246a53b7eb5a7d1bc", size = 13331703 }, - { url = "https://files.pythonhosted.org/packages/12/f1/cefacac137f7bb7bfba57c50c478150fcd3c54aca72762ac2c05ce0532c1/onnxruntime-1.20.1-cp311-cp311-win32.whl", hash = "sha256:a19bc6e8c70e2485a1725b3d517a2319603acc14c1f1a017dda0afe6d4665b41", size = 9813977 }, - { url = "https://files.pythonhosted.org/packages/2c/2d/2d4d202c0bcfb3a4cc2b171abb9328672d7f91d7af9ea52572722c6d8d96/onnxruntime-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:8508887eb1c5f9537a4071768723ec7c30c28eb2518a00d0adcd32c89dea3221", size = 11329895 }, - { url = "https://files.pythonhosted.org/packages/e5/39/9335e0874f68f7d27103cbffc0e235e32e26759202df6085716375c078bb/onnxruntime-1.20.1-cp312-cp312-macosx_13_0_universal2.whl", hash = "sha256:22b0655e2bf4f2161d52706e31f517a0e54939dc393e92577df51808a7edc8c9", size = 31007580 }, - { url = "https://files.pythonhosted.org/packages/c5/9d/a42a84e10f1744dd27c6f2f9280cc3fb98f869dd19b7cd042e391ee2ab61/onnxruntime-1.20.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1f56e898815963d6dc4ee1c35fc6c36506466eff6d16f3cb9848cea4e8c8172", size = 11952833 }, - { url = "https://files.pythonhosted.org/packages/47/42/2f71f5680834688a9c81becbe5c5bb996fd33eaed5c66ae0606c3b1d6a02/onnxruntime-1.20.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb71a814f66517a65628c9e4a2bb530a6edd2cd5d87ffa0af0f6f773a027d99e", size = 13333903 }, - { url = "https://files.pythonhosted.org/packages/c8/f1/aabfdf91d013320aa2fc46cf43c88ca0182860ff15df872b4552254a9680/onnxruntime-1.20.1-cp312-cp312-win32.whl", hash = "sha256:bd386cc9ee5f686ee8a75ba74037750aca55183085bf1941da8efcfe12d5b120", size = 9814562 }, - { url = "https://files.pythonhosted.org/packages/dd/80/76979e0b744307d488c79e41051117634b956612cc731f1028eb17ee7294/onnxruntime-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:19c2d843eb074f385e8bbb753a40df780511061a63f9def1b216bf53860223fb", size = 11331482 }, - { url = "https://files.pythonhosted.org/packages/f7/71/c5d980ac4189589267a06f758bd6c5667d07e55656bed6c6c0580733ad07/onnxruntime-1.20.1-cp313-cp313-macosx_13_0_universal2.whl", hash = "sha256:cc01437a32d0042b606f462245c8bbae269e5442797f6213e36ce61d5abdd8cc", size = 31007574 }, - { url = "https://files.pythonhosted.org/packages/81/0d/13bbd9489be2a6944f4a940084bfe388f1100472f38c07080a46fbd4ab96/onnxruntime-1.20.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb44b08e017a648924dbe91b82d89b0c105b1adcfe31e90d1dc06b8677ad37be", size = 11951459 }, - { url = "https://files.pythonhosted.org/packages/c0/ea/4454ae122874fd52bbb8a961262de81c5f932edeb1b72217f594c700d6ef/onnxruntime-1.20.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bda6aebdf7917c1d811f21d41633df00c58aff2bef2f598f69289c1f1dabc4b3", size = 13331620 }, - { url = "https://files.pythonhosted.org/packages/d8/e0/50db43188ca1c945decaa8fc2a024c33446d31afed40149897d4f9de505f/onnxruntime-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:d30367df7e70f1d9fc5a6a68106f5961686d39b54d3221f760085524e8d38e16", size = 11331758 }, - { url = "https://files.pythonhosted.org/packages/d8/55/3821c5fd60b52a6c82a00bba18531793c93c4addfe64fbf061e235c5617a/onnxruntime-1.20.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9158465745423b2b5d97ed25aa7740c7d38d2993ee2e5c3bfacb0c4145c49d8", size = 11950342 }, - { url = "https://files.pythonhosted.org/packages/14/56/fd990ca222cef4f9f4a9400567b9a15b220dee2eafffb16b2adbc55c8281/onnxruntime-1.20.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0df6f2df83d61f46e842dbcde610ede27218947c33e994545a22333491e72a3b", size = 13337040 }, + { url = "https://files.pythonhosted.org/packages/4e/28/99f903b0eb1cd6f3faa0e343217d9fb9f47b84bca98bd9859884631336ee/onnxruntime-1.20.1-cp310-cp310-macosx_13_0_universal2.whl", hash = "sha256:e50ba5ff7fed4f7d9253a6baf801ca2883cc08491f9d32d78a80da57256a5439", size = 30996314, upload_time = "2024-11-21T00:48:31.43Z" }, + { url = "https://files.pythonhosted.org/packages/6d/c6/c4c0860bee2fde6037bdd9dcd12d323f6e38cf00fcc9a5065b394337fc55/onnxruntime-1.20.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7b2908b50101a19e99c4d4e97ebb9905561daf61829403061c1adc1b588bc0de", size = 11954010, upload_time = "2024-11-21T00:48:35.254Z" }, + { url = "https://files.pythonhosted.org/packages/63/47/3dc0b075ab539f16b3d8b09df6b504f51836086ee709690a6278d791737d/onnxruntime-1.20.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d82daaec24045a2e87598b8ac2b417b1cce623244e80e663882e9fe1aae86410", size = 13330452, upload_time = "2024-11-21T00:48:40.02Z" }, + { url = "https://files.pythonhosted.org/packages/27/ef/80fab86289ecc01a734b7ddf115dfb93d8b2e004bd1e1977e12881c72b12/onnxruntime-1.20.1-cp310-cp310-win32.whl", hash = "sha256:4c4b251a725a3b8cf2aab284f7d940c26094ecd9d442f07dd81ab5470e99b83f", size = 9813849, upload_time = "2024-11-21T00:48:43.569Z" }, + { url = "https://files.pythonhosted.org/packages/a9/e6/33ab10066c9875a29d55e66ae97c3bf91b9b9b987179455d67c32261a49c/onnxruntime-1.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:d3b616bb53a77a9463707bb313637223380fc327f5064c9a782e8ec69c22e6a2", size = 11329702, upload_time = "2024-11-21T00:48:46.599Z" }, + { url = "https://files.pythonhosted.org/packages/95/8d/2634e2959b34aa8a0037989f4229e9abcfa484e9c228f99633b3241768a6/onnxruntime-1.20.1-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:06bfbf02ca9ab5f28946e0f912a562a5f005301d0c419283dc57b3ed7969bb7b", size = 30998725, upload_time = "2024-11-21T00:48:51.013Z" }, + { url = "https://files.pythonhosted.org/packages/a5/da/c44bf9bd66cd6d9018a921f053f28d819445c4d84b4dd4777271b0fe52a2/onnxruntime-1.20.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f6243e34d74423bdd1edf0ae9596dd61023b260f546ee17d701723915f06a9f7", size = 11955227, upload_time = "2024-11-21T00:48:54.556Z" }, + { url = "https://files.pythonhosted.org/packages/11/ac/4120dfb74c8e45cce1c664fc7f7ce010edd587ba67ac41489f7432eb9381/onnxruntime-1.20.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5eec64c0269dcdb8d9a9a53dc4d64f87b9e0c19801d9321246a53b7eb5a7d1bc", size = 13331703, upload_time = "2024-11-21T00:48:57.97Z" }, + { url = "https://files.pythonhosted.org/packages/12/f1/cefacac137f7bb7bfba57c50c478150fcd3c54aca72762ac2c05ce0532c1/onnxruntime-1.20.1-cp311-cp311-win32.whl", hash = "sha256:a19bc6e8c70e2485a1725b3d517a2319603acc14c1f1a017dda0afe6d4665b41", size = 9813977, upload_time = "2024-11-21T00:49:00.519Z" }, + { url = "https://files.pythonhosted.org/packages/2c/2d/2d4d202c0bcfb3a4cc2b171abb9328672d7f91d7af9ea52572722c6d8d96/onnxruntime-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:8508887eb1c5f9537a4071768723ec7c30c28eb2518a00d0adcd32c89dea3221", size = 11329895, upload_time = "2024-11-21T00:49:03.845Z" }, + { url = "https://files.pythonhosted.org/packages/e5/39/9335e0874f68f7d27103cbffc0e235e32e26759202df6085716375c078bb/onnxruntime-1.20.1-cp312-cp312-macosx_13_0_universal2.whl", hash = "sha256:22b0655e2bf4f2161d52706e31f517a0e54939dc393e92577df51808a7edc8c9", size = 31007580, upload_time = "2024-11-21T00:49:07.029Z" }, + { url = "https://files.pythonhosted.org/packages/c5/9d/a42a84e10f1744dd27c6f2f9280cc3fb98f869dd19b7cd042e391ee2ab61/onnxruntime-1.20.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1f56e898815963d6dc4ee1c35fc6c36506466eff6d16f3cb9848cea4e8c8172", size = 11952833, upload_time = "2024-11-21T00:49:10.563Z" }, + { url = "https://files.pythonhosted.org/packages/47/42/2f71f5680834688a9c81becbe5c5bb996fd33eaed5c66ae0606c3b1d6a02/onnxruntime-1.20.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb71a814f66517a65628c9e4a2bb530a6edd2cd5d87ffa0af0f6f773a027d99e", size = 13333903, upload_time = "2024-11-21T00:49:12.984Z" }, + { url = "https://files.pythonhosted.org/packages/c8/f1/aabfdf91d013320aa2fc46cf43c88ca0182860ff15df872b4552254a9680/onnxruntime-1.20.1-cp312-cp312-win32.whl", hash = "sha256:bd386cc9ee5f686ee8a75ba74037750aca55183085bf1941da8efcfe12d5b120", size = 9814562, upload_time = "2024-11-21T00:49:15.453Z" }, + { url = "https://files.pythonhosted.org/packages/dd/80/76979e0b744307d488c79e41051117634b956612cc731f1028eb17ee7294/onnxruntime-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:19c2d843eb074f385e8bbb753a40df780511061a63f9def1b216bf53860223fb", size = 11331482, upload_time = "2024-11-21T00:49:19.412Z" }, + { url = "https://files.pythonhosted.org/packages/f7/71/c5d980ac4189589267a06f758bd6c5667d07e55656bed6c6c0580733ad07/onnxruntime-1.20.1-cp313-cp313-macosx_13_0_universal2.whl", hash = "sha256:cc01437a32d0042b606f462245c8bbae269e5442797f6213e36ce61d5abdd8cc", size = 31007574, upload_time = "2024-11-21T00:49:23.225Z" }, + { url = "https://files.pythonhosted.org/packages/81/0d/13bbd9489be2a6944f4a940084bfe388f1100472f38c07080a46fbd4ab96/onnxruntime-1.20.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb44b08e017a648924dbe91b82d89b0c105b1adcfe31e90d1dc06b8677ad37be", size = 11951459, upload_time = "2024-11-21T00:49:26.269Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ea/4454ae122874fd52bbb8a961262de81c5f932edeb1b72217f594c700d6ef/onnxruntime-1.20.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bda6aebdf7917c1d811f21d41633df00c58aff2bef2f598f69289c1f1dabc4b3", size = 13331620, upload_time = "2024-11-21T00:49:28.875Z" }, + { url = "https://files.pythonhosted.org/packages/d8/e0/50db43188ca1c945decaa8fc2a024c33446d31afed40149897d4f9de505f/onnxruntime-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:d30367df7e70f1d9fc5a6a68106f5961686d39b54d3221f760085524e8d38e16", size = 11331758, upload_time = "2024-11-21T00:49:31.417Z" }, + { url = "https://files.pythonhosted.org/packages/d8/55/3821c5fd60b52a6c82a00bba18531793c93c4addfe64fbf061e235c5617a/onnxruntime-1.20.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9158465745423b2b5d97ed25aa7740c7d38d2993ee2e5c3bfacb0c4145c49d8", size = 11950342, upload_time = "2024-11-21T00:49:34.164Z" }, + { url = "https://files.pythonhosted.org/packages/14/56/fd990ca222cef4f9f4a9400567b9a15b220dee2eafffb16b2adbc55c8281/onnxruntime-1.20.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0df6f2df83d61f46e842dbcde610ede27218947c33e994545a22333491e72a3b", size = 13337040, upload_time = "2024-11-21T00:49:37.271Z" }, ] [[package]] @@ -1558,10 +1584,10 @@ dependencies = [ { name = "sympy" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/57/e9a080f2477b2a4c16925f766e4615fc545098b0f4e20cf8ad803e7a9672/onnxruntime_openvino-1.18.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:565b874d21bcd48126da7d62f57db019f5ec0e1f82ae9b0740afa2ad91f8d331", size = 41971800 }, - { url = "https://files.pythonhosted.org/packages/34/7d/b75913bce58f4ee9bf6a02d1b513b9fc82303a496ec698e6fb1f9d597cb4/onnxruntime_openvino-1.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:7f1931060f710a6c8e32121bb73044c4772ef5925802fc8776d3fe1e87ab3f75", size = 5963263 }, - { url = "https://files.pythonhosted.org/packages/7e/d3/8299b7285dc8fa7bd986b6f0d7c50b7f0fd13db50dd3b88b93ec269b1e08/onnxruntime_openvino-1.18.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eb1723d386f70a8e26398d983ebe35d2c25ba56e9cdb382670ebbf1f5139f8ba", size = 41971927 }, - { url = "https://files.pythonhosted.org/packages/88/d9/ca0bfd7ed37153d9664ccdcfb4d0e5b1963563553b05cb4338b46968feb2/onnxruntime_openvino-1.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:874a1e263dd86674593e5a879257650b06a8609c4d5768c3d8ed8dc4ae874b9c", size = 5963464 }, + { url = "https://files.pythonhosted.org/packages/b3/57/e9a080f2477b2a4c16925f766e4615fc545098b0f4e20cf8ad803e7a9672/onnxruntime_openvino-1.18.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:565b874d21bcd48126da7d62f57db019f5ec0e1f82ae9b0740afa2ad91f8d331", size = 41971800, upload_time = "2024-06-25T06:30:37.042Z" }, + { url = "https://files.pythonhosted.org/packages/34/7d/b75913bce58f4ee9bf6a02d1b513b9fc82303a496ec698e6fb1f9d597cb4/onnxruntime_openvino-1.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:7f1931060f710a6c8e32121bb73044c4772ef5925802fc8776d3fe1e87ab3f75", size = 5963263, upload_time = "2024-06-24T13:38:15.906Z" }, + { url = "https://files.pythonhosted.org/packages/7e/d3/8299b7285dc8fa7bd986b6f0d7c50b7f0fd13db50dd3b88b93ec269b1e08/onnxruntime_openvino-1.18.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eb1723d386f70a8e26398d983ebe35d2c25ba56e9cdb382670ebbf1f5139f8ba", size = 41971927, upload_time = "2024-06-25T06:30:43.765Z" }, + { url = "https://files.pythonhosted.org/packages/88/d9/ca0bfd7ed37153d9664ccdcfb4d0e5b1963563553b05cb4338b46968feb2/onnxruntime_openvino-1.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:874a1e263dd86674593e5a879257650b06a8609c4d5768c3d8ed8dc4ae874b9c", size = 5963464, upload_time = "2024-06-24T13:38:18.437Z" }, ] [[package]] @@ -1571,171 +1597,171 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929 } +sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929, upload_time = "2025-01-16T13:53:40.22Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/53/2c50afa0b1e05ecdb4603818e85f7d174e683d874ef63a6abe3ac92220c8/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:48128188ade4a7e517237c8e1e11a9cdf5c282761473383e77beb875bb1e61ca", size = 37326460 }, - { url = "https://files.pythonhosted.org/packages/3b/43/68555327df94bb9b59a1fd645f63fafb0762515344d2046698762fc19d58/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:a66c1b286a9de872c343ee7c3553b084244299714ebb50fbdcd76f07ebbe6c81", size = 56723330 }, - { url = "https://files.pythonhosted.org/packages/45/be/1438ce43ebe65317344a87e4b150865c5585f4c0db880a34cdae5ac46881/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6efabcaa9df731f29e5ea9051776715b1bdd1845d7c9530065c7951d2a2899eb", size = 29487060 }, - { url = "https://files.pythonhosted.org/packages/dd/5c/c139a7876099916879609372bfa513b7f1257f7f1a908b0bdc1c2328241b/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e0a27c19dd1f40ddff94976cfe43066fbbe9dfbb2ec1907d66c19caef42a57b", size = 49969856 }, - { url = "https://files.pythonhosted.org/packages/95/dd/ed1191c9dc91abcc9f752b499b7928aacabf10567bb2c2535944d848af18/opencv_python_headless-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:f447d8acbb0b6f2808da71fddd29c1cdd448d2bc98f72d9bb78a7a898fc9621b", size = 29324425 }, - { url = "https://files.pythonhosted.org/packages/86/8a/69176a64335aed183529207ba8bc3d329c2999d852b4f3818027203f50e6/opencv_python_headless-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:6c304df9caa7a6a5710b91709dd4786bf20a74d57672b3c31f7033cc638174ca", size = 39402386 }, + { url = "https://files.pythonhosted.org/packages/dc/53/2c50afa0b1e05ecdb4603818e85f7d174e683d874ef63a6abe3ac92220c8/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:48128188ade4a7e517237c8e1e11a9cdf5c282761473383e77beb875bb1e61ca", size = 37326460, upload_time = "2025-01-16T13:52:57.015Z" }, + { url = "https://files.pythonhosted.org/packages/3b/43/68555327df94bb9b59a1fd645f63fafb0762515344d2046698762fc19d58/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:a66c1b286a9de872c343ee7c3553b084244299714ebb50fbdcd76f07ebbe6c81", size = 56723330, upload_time = "2025-01-16T13:55:45.731Z" }, + { url = "https://files.pythonhosted.org/packages/45/be/1438ce43ebe65317344a87e4b150865c5585f4c0db880a34cdae5ac46881/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6efabcaa9df731f29e5ea9051776715b1bdd1845d7c9530065c7951d2a2899eb", size = 29487060, upload_time = "2025-01-16T13:51:59.625Z" }, + { url = "https://files.pythonhosted.org/packages/dd/5c/c139a7876099916879609372bfa513b7f1257f7f1a908b0bdc1c2328241b/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e0a27c19dd1f40ddff94976cfe43066fbbe9dfbb2ec1907d66c19caef42a57b", size = 49969856, upload_time = "2025-01-16T13:53:29.654Z" }, + { url = "https://files.pythonhosted.org/packages/95/dd/ed1191c9dc91abcc9f752b499b7928aacabf10567bb2c2535944d848af18/opencv_python_headless-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:f447d8acbb0b6f2808da71fddd29c1cdd448d2bc98f72d9bb78a7a898fc9621b", size = 29324425, upload_time = "2025-01-16T13:52:49.048Z" }, + { url = "https://files.pythonhosted.org/packages/86/8a/69176a64335aed183529207ba8bc3d329c2999d852b4f3818027203f50e6/opencv_python_headless-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:6c304df9caa7a6a5710b91709dd4786bf20a74d57672b3c31f7033cc638174ca", size = 39402386, upload_time = "2025-01-16T13:52:56.418Z" }, ] [[package]] name = "orjson" version = "3.10.16" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/c7/03913cc4332174071950acf5b0735463e3f63760c80585ef369270c2b372/orjson-3.10.16.tar.gz", hash = "sha256:d2aaa5c495e11d17b9b93205f5fa196737ee3202f000aaebf028dc9a73750f10", size = 5410415 } +sdist = { url = "https://files.pythonhosted.org/packages/98/c7/03913cc4332174071950acf5b0735463e3f63760c80585ef369270c2b372/orjson-3.10.16.tar.gz", hash = "sha256:d2aaa5c495e11d17b9b93205f5fa196737ee3202f000aaebf028dc9a73750f10", size = 5410415, upload_time = "2025-03-24T17:00:23.312Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/a6/22cb9b03baf167bc2d659c9e74d7580147f36e6a155e633801badfd5a74d/orjson-3.10.16-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4cb473b8e79154fa778fb56d2d73763d977be3dcc140587e07dbc545bbfc38f8", size = 249179 }, - { url = "https://files.pythonhosted.org/packages/d7/ce/3e68cc33020a6ebd8f359b8628b69d2132cd84fea68155c33057e502ee51/orjson-3.10.16-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:622a8e85eeec1948690409a19ca1c7d9fd8ff116f4861d261e6ae2094fe59a00", size = 138510 }, - { url = "https://files.pythonhosted.org/packages/dc/12/63bee7764ce12052f7c1a1393ce7f26dc392c93081eb8754dd3dce9b7c6b/orjson-3.10.16-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c682d852d0ce77613993dc967e90e151899fe2d8e71c20e9be164080f468e370", size = 132373 }, - { url = "https://files.pythonhosted.org/packages/b3/d5/2998c2f319adcd572f2b03ba2083e8176863d1055d8d713683ddcf927b71/orjson-3.10.16-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c520ae736acd2e32df193bcff73491e64c936f3e44a2916b548da048a48b46b", size = 136774 }, - { url = "https://files.pythonhosted.org/packages/00/03/88c236ae307bd0604623204d4a835e15fbf9c75b8535c8f13ef45abd413f/orjson-3.10.16-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:134f87c76bfae00f2094d85cfab261b289b76d78c6da8a7a3b3c09d362fd1e06", size = 138030 }, - { url = "https://files.pythonhosted.org/packages/66/ba/3e256ddfeb364f98fd6ac65774844090d356158b2d1de8998db2bf984503/orjson-3.10.16-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b59afde79563e2cf37cfe62ee3b71c063fd5546c8e662d7fcfc2a3d5031a5c4c", size = 142677 }, - { url = "https://files.pythonhosted.org/packages/2c/71/73a1214bd27baa2ea5184fff4aa6193a114dfb0aa5663dad48fe63e8cd29/orjson-3.10.16-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:113602f8241daaff05d6fad25bd481d54c42d8d72ef4c831bb3ab682a54d9e15", size = 132798 }, - { url = "https://files.pythonhosted.org/packages/53/ac/0b2f41c0a1e8c095439d0fab3b33103cf41a39be8e6aa2c56298a6034259/orjson-3.10.16-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4fc0077d101f8fab4031e6554fc17b4c2ad8fdbc56ee64a727f3c95b379e31da", size = 135450 }, - { url = "https://files.pythonhosted.org/packages/d9/ca/7524c7b0bc815d426ca134dab54cad519802287b808a3846b047a5b2b7a3/orjson-3.10.16-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:9c6bf6ff180cd69e93f3f50380224218cfab79953a868ea3908430bcfaf9cb5e", size = 412356 }, - { url = "https://files.pythonhosted.org/packages/05/1d/3ae2367c255276bf16ff7e1b210dd0af18bc8da20c4e4295755fc7de1268/orjson-3.10.16-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5673eadfa952f95a7cd76418ff189df11b0a9c34b1995dff43a6fdbce5d63bf4", size = 152769 }, - { url = "https://files.pythonhosted.org/packages/d3/2d/8eb10b6b1d30bb69c35feb15e5ba5ac82466cf743d562e3e8047540efd2f/orjson-3.10.16-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5fe638a423d852b0ae1e1a79895851696cb0d9fa0946fdbfd5da5072d9bb9551", size = 137223 }, - { url = "https://files.pythonhosted.org/packages/47/42/f043717930cb2de5fbebe47f308f101bed9ec2b3580b1f99c8284b2f5fe8/orjson-3.10.16-cp310-cp310-win32.whl", hash = "sha256:33af58f479b3c6435ab8f8b57999874b4b40c804c7a36b5cc6b54d8f28e1d3dd", size = 141734 }, - { url = "https://files.pythonhosted.org/packages/67/99/795ad7282b425b9fddcfb8a31bded5dcf84dba78ecb1e7ae716e84e794da/orjson-3.10.16-cp310-cp310-win_amd64.whl", hash = "sha256:0338356b3f56d71293c583350af26f053017071836b07e064e92819ecf1aa055", size = 133779 }, - { url = "https://files.pythonhosted.org/packages/97/29/43f91a5512b5d2535594438eb41c5357865fd5e64dec745d90a588820c75/orjson-3.10.16-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44fcbe1a1884f8bc9e2e863168b0f84230c3d634afe41c678637d2728ea8e739", size = 249180 }, - { url = "https://files.pythonhosted.org/packages/0c/36/2a72d55e266473c19a86d97b7363bb8bf558ab450f75205689a287d5ce61/orjson-3.10.16-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78177bf0a9d0192e0b34c3d78bcff7fe21d1b5d84aeb5ebdfe0dbe637b885225", size = 138510 }, - { url = "https://files.pythonhosted.org/packages/bb/ad/f86d6f55c1a68b57ff6ea7966bce5f4e5163f2e526ddb7db9fc3c2c8d1c4/orjson-3.10.16-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12824073a010a754bb27330cad21d6e9b98374f497f391b8707752b96f72e741", size = 132373 }, - { url = "https://files.pythonhosted.org/packages/5e/8b/d18f2711493a809f3082a88fda89342bc8e16767743b909cd3c34989fba3/orjson-3.10.16-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddd41007e56284e9867864aa2f29f3136bb1dd19a49ca43c0b4eda22a579cf53", size = 136773 }, - { url = "https://files.pythonhosted.org/packages/a1/dc/ce025f002f8e0749e3f057c4d773a4d4de32b7b4c1fc5a50b429e7532586/orjson-3.10.16-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0877c4d35de639645de83666458ca1f12560d9fa7aa9b25d8bb8f52f61627d14", size = 138029 }, - { url = "https://files.pythonhosted.org/packages/0e/1b/cf9df85852b91160029d9f26014230366a2b4deb8cc51fabe68e250a8c1a/orjson-3.10.16-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9a09a539e9cc3beead3e7107093b4ac176d015bec64f811afb5965fce077a03c", size = 142677 }, - { url = "https://files.pythonhosted.org/packages/92/18/5b1e1e995bffad49dc4311a0bdfd874bc6f135fd20f0e1f671adc2c9910e/orjson-3.10.16-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31b98bc9b40610fec971d9a4d67bb2ed02eec0a8ae35f8ccd2086320c28526ca", size = 132800 }, - { url = "https://files.pythonhosted.org/packages/d6/eb/467f25b580e942fcca1344adef40633b7f05ac44a65a63fc913f9a805d58/orjson-3.10.16-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0ce243f5a8739f3a18830bc62dc2e05b69a7545bafd3e3249f86668b2bcd8e50", size = 135451 }, - { url = "https://files.pythonhosted.org/packages/8d/4b/9d10888038975cb375982e9339d9495bac382d5c976c500b8d6f2c8e2e4e/orjson-3.10.16-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:64792c0025bae049b3074c6abe0cf06f23c8e9f5a445f4bab31dc5ca23dbf9e1", size = 412358 }, - { url = "https://files.pythonhosted.org/packages/3b/e2/cfbcfcc4fbe619e0ca9bdbbfccb2d62b540bbfe41e0ee77d44a628594f59/orjson-3.10.16-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ea53f7e68eec718b8e17e942f7ca56c6bd43562eb19db3f22d90d75e13f0431d", size = 152772 }, - { url = "https://files.pythonhosted.org/packages/b9/d6/627a1b00569be46173007c11dde3da4618c9bfe18409325b0e3e2a82fe29/orjson-3.10.16-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a741ba1a9488c92227711bde8c8c2b63d7d3816883268c808fbeada00400c164", size = 137225 }, - { url = "https://files.pythonhosted.org/packages/0a/7b/a73c67b505021af845b9f05c7c848793258ea141fa2058b52dd9b067c2b4/orjson-3.10.16-cp311-cp311-win32.whl", hash = "sha256:c7ed2c61bb8226384c3fdf1fb01c51b47b03e3f4536c985078cccc2fd19f1619", size = 141733 }, - { url = "https://files.pythonhosted.org/packages/f4/22/5e8217c48d68c0adbfb181e749d6a733761074e598b083c69a1383d18147/orjson-3.10.16-cp311-cp311-win_amd64.whl", hash = "sha256:cd67d8b3e0e56222a2e7b7f7da9031e30ecd1fe251c023340b9f12caca85ab60", size = 133784 }, - { url = "https://files.pythonhosted.org/packages/5d/15/67ce9d4c959c83f112542222ea3b9209c1d424231d71d74c4890ea0acd2b/orjson-3.10.16-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6d3444abbfa71ba21bb042caa4b062535b122248259fdb9deea567969140abca", size = 249325 }, - { url = "https://files.pythonhosted.org/packages/da/2c/1426b06f30a1b9ada74b6f512c1ddf9d2760f53f61cdb59efeb9ad342133/orjson-3.10.16-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:30245c08d818fdcaa48b7d5b81499b8cae09acabb216fe61ca619876b128e184", size = 133621 }, - { url = "https://files.pythonhosted.org/packages/9e/88/18d26130954bc73bee3be10f95371ea1dfb8679e0e2c46b0f6d8c6289402/orjson-3.10.16-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0ba1d0baa71bf7579a4ccdcf503e6f3098ef9542106a0eca82395898c8a500a", size = 138270 }, - { url = "https://files.pythonhosted.org/packages/4f/f9/6d8b64fcd58fae072e80ee7981be8ba0d7c26ace954e5cd1d027fc80518f/orjson-3.10.16-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb0beefa5ef3af8845f3a69ff2a4aa62529b5acec1cfe5f8a6b4141033fd46ef", size = 132346 }, - { url = "https://files.pythonhosted.org/packages/16/3f/2513fd5bc786f40cd12af569c23cae6381aeddbefeed2a98f0a666eb5d0d/orjson-3.10.16-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6daa0e1c9bf2e030e93c98394de94506f2a4d12e1e9dadd7c53d5e44d0f9628e", size = 136845 }, - { url = "https://files.pythonhosted.org/packages/6d/42/b0e7b36720f5ab722b48e8ccf06514d4f769358dd73c51abd8728ef58d0b/orjson-3.10.16-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9da9019afb21e02410ef600e56666652b73eb3e4d213a0ec919ff391a7dd52aa", size = 138078 }, - { url = "https://files.pythonhosted.org/packages/a3/a8/d220afb8a439604be74fc755dbc740bded5ed14745ca536b304ed32eb18a/orjson-3.10.16-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:daeb3a1ee17b69981d3aae30c3b4e786b0f8c9e6c71f2b48f1aef934f63f38f4", size = 142712 }, - { url = "https://files.pythonhosted.org/packages/8c/88/7e41e9883c00f84f92fe357a8371edae816d9d7ef39c67b5106960c20389/orjson-3.10.16-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80fed80eaf0e20a31942ae5d0728849862446512769692474be5e6b73123a23b", size = 133136 }, - { url = "https://files.pythonhosted.org/packages/e9/ca/61116095307ad0be828ea26093febaf59e38596d84a9c8d765c3c5e4934f/orjson-3.10.16-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73390ed838f03764540a7bdc4071fe0123914c2cc02fb6abf35182d5fd1b7a42", size = 135258 }, - { url = "https://files.pythonhosted.org/packages/dc/1b/09493cf7d801505f094c9295f79c98c1e0af2ac01c7ed8d25b30fcb19ada/orjson-3.10.16-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a22bba012a0c94ec02a7768953020ab0d3e2b884760f859176343a36c01adf87", size = 412326 }, - { url = "https://files.pythonhosted.org/packages/ea/02/125d7bbd7f7a500190ddc8ae5d2d3c39d87ed3ed28f5b37cfe76962c678d/orjson-3.10.16-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5385bbfdbc90ff5b2635b7e6bebf259652db00a92b5e3c45b616df75b9058e88", size = 152800 }, - { url = "https://files.pythonhosted.org/packages/f9/09/7658a9e3e793d5b3b00598023e0fb6935d0e7bbb8ff72311c5415a8ce677/orjson-3.10.16-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:02c6279016346e774dd92625d46c6c40db687b8a0d685aadb91e26e46cc33e1e", size = 137516 }, - { url = "https://files.pythonhosted.org/packages/29/87/32b7a4831e909d347278101a48d4cf9f3f25901b2295e7709df1651f65a1/orjson-3.10.16-cp312-cp312-win32.whl", hash = "sha256:7ca55097a11426db80f79378e873a8c51f4dde9ffc22de44850f9696b7eb0e8c", size = 141759 }, - { url = "https://files.pythonhosted.org/packages/35/ce/81a27e7b439b807bd393585271364cdddf50dc281fc57c4feef7ccb186a6/orjson-3.10.16-cp312-cp312-win_amd64.whl", hash = "sha256:86d127efdd3f9bf5f04809b70faca1e6836556ea3cc46e662b44dab3fe71f3d6", size = 133944 }, - { url = "https://files.pythonhosted.org/packages/87/b9/ff6aa28b8c86af9526160905593a2fe8d004ac7a5e592ee0b0ff71017511/orjson-3.10.16-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:148a97f7de811ba14bc6dbc4a433e0341ffd2cc285065199fb5f6a98013744bd", size = 249289 }, - { url = "https://files.pythonhosted.org/packages/6c/81/6d92a586149b52684ab8fd70f3623c91d0e6a692f30fd8c728916ab2263c/orjson-3.10.16-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:1d960c1bf0e734ea36d0adc880076de3846aaec45ffad29b78c7f1b7962516b8", size = 133640 }, - { url = "https://files.pythonhosted.org/packages/c2/88/b72443f4793d2e16039ab85d0026677932b15ab968595fb7149750d74134/orjson-3.10.16-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a318cd184d1269f68634464b12871386808dc8b7c27de8565234d25975a7a137", size = 138286 }, - { url = "https://files.pythonhosted.org/packages/c3/3c/72a22d4b28c076c4016d5a52bd644a8e4d849d3bb0373d9e377f9e3b2250/orjson-3.10.16-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df23f8df3ef9223d1d6748bea63fca55aae7da30a875700809c500a05975522b", size = 132307 }, - { url = "https://files.pythonhosted.org/packages/8a/a2/f1259561bdb6ad7061ff1b95dab082fe32758c4bc143ba8d3d70831f0a06/orjson-3.10.16-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b94dda8dd6d1378f1037d7f3f6b21db769ef911c4567cbaa962bb6dc5021cf90", size = 136739 }, - { url = "https://files.pythonhosted.org/packages/3d/af/c7583c4b34f33d8b8b90cfaab010ff18dd64e7074cc1e117a5f1eff20dcf/orjson-3.10.16-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f12970a26666a8775346003fd94347d03ccb98ab8aa063036818381acf5f523e", size = 138076 }, - { url = "https://files.pythonhosted.org/packages/d7/59/d7fc7fbdd3d4a64c2eae4fc7341a5aa39cf9549bd5e2d7f6d3c07f8b715b/orjson-3.10.16-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15a1431a245d856bd56e4d29ea0023eb4d2c8f71efe914beb3dee8ab3f0cd7fb", size = 142643 }, - { url = "https://files.pythonhosted.org/packages/92/0e/3bd8f2197d27601f16b4464ae948826da2bcf128af31230a9dbbad7ceb57/orjson-3.10.16-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c83655cfc247f399a222567d146524674a7b217af7ef8289c0ff53cfe8db09f0", size = 133168 }, - { url = "https://files.pythonhosted.org/packages/af/a8/351fd87b664b02f899f9144d2c3dc848b33ac04a5df05234cbfb9e2a7540/orjson-3.10.16-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fa59ae64cb6ddde8f09bdbf7baf933c4cd05734ad84dcf4e43b887eb24e37652", size = 135271 }, - { url = "https://files.pythonhosted.org/packages/ba/b0/a6d42a7d412d867c60c0337d95123517dd5a9370deea705ea1be0f89389e/orjson-3.10.16-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ca5426e5aacc2e9507d341bc169d8af9c3cbe88f4cd4c1cf2f87e8564730eb56", size = 412444 }, - { url = "https://files.pythonhosted.org/packages/79/ec/7572cd4e20863f60996f3f10bc0a6da64a6fd9c35954189a914cec0b7377/orjson-3.10.16-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6fd5da4edf98a400946cd3a195680de56f1e7575109b9acb9493331047157430", size = 152737 }, - { url = "https://files.pythonhosted.org/packages/a9/19/ceb9e8fed5403b2e76a8ac15f581b9d25780a3be3c9b3aa54b7777a210d5/orjson-3.10.16-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:980ecc7a53e567169282a5e0ff078393bac78320d44238da4e246d71a4e0e8f5", size = 137482 }, - { url = "https://files.pythonhosted.org/packages/1b/78/a78bb810f3786579dbbbd94768284cbe8f2fd65167cd7020260679665c17/orjson-3.10.16-cp313-cp313-win32.whl", hash = "sha256:28f79944dd006ac540a6465ebd5f8f45dfdf0948ff998eac7a908275b4c1add6", size = 141714 }, - { url = "https://files.pythonhosted.org/packages/81/9c/b66ce9245ff319df2c3278acd351a3f6145ef34b4a2d7f4b0f739368370f/orjson-3.10.16-cp313-cp313-win_amd64.whl", hash = "sha256:fe0a145e96d51971407cb8ba947e63ead2aa915db59d6631a355f5f2150b56b7", size = 133954 }, + { url = "https://files.pythonhosted.org/packages/9d/a6/22cb9b03baf167bc2d659c9e74d7580147f36e6a155e633801badfd5a74d/orjson-3.10.16-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4cb473b8e79154fa778fb56d2d73763d977be3dcc140587e07dbc545bbfc38f8", size = 249179, upload_time = "2025-03-24T16:58:41.294Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/3e68cc33020a6ebd8f359b8628b69d2132cd84fea68155c33057e502ee51/orjson-3.10.16-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:622a8e85eeec1948690409a19ca1c7d9fd8ff116f4861d261e6ae2094fe59a00", size = 138510, upload_time = "2025-03-24T16:58:43.732Z" }, + { url = "https://files.pythonhosted.org/packages/dc/12/63bee7764ce12052f7c1a1393ce7f26dc392c93081eb8754dd3dce9b7c6b/orjson-3.10.16-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c682d852d0ce77613993dc967e90e151899fe2d8e71c20e9be164080f468e370", size = 132373, upload_time = "2025-03-24T16:58:45.094Z" }, + { url = "https://files.pythonhosted.org/packages/b3/d5/2998c2f319adcd572f2b03ba2083e8176863d1055d8d713683ddcf927b71/orjson-3.10.16-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c520ae736acd2e32df193bcff73491e64c936f3e44a2916b548da048a48b46b", size = 136774, upload_time = "2025-03-24T16:58:46.273Z" }, + { url = "https://files.pythonhosted.org/packages/00/03/88c236ae307bd0604623204d4a835e15fbf9c75b8535c8f13ef45abd413f/orjson-3.10.16-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:134f87c76bfae00f2094d85cfab261b289b76d78c6da8a7a3b3c09d362fd1e06", size = 138030, upload_time = "2025-03-24T16:58:47.921Z" }, + { url = "https://files.pythonhosted.org/packages/66/ba/3e256ddfeb364f98fd6ac65774844090d356158b2d1de8998db2bf984503/orjson-3.10.16-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b59afde79563e2cf37cfe62ee3b71c063fd5546c8e662d7fcfc2a3d5031a5c4c", size = 142677, upload_time = "2025-03-24T16:58:49.191Z" }, + { url = "https://files.pythonhosted.org/packages/2c/71/73a1214bd27baa2ea5184fff4aa6193a114dfb0aa5663dad48fe63e8cd29/orjson-3.10.16-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:113602f8241daaff05d6fad25bd481d54c42d8d72ef4c831bb3ab682a54d9e15", size = 132798, upload_time = "2025-03-24T16:58:50.746Z" }, + { url = "https://files.pythonhosted.org/packages/53/ac/0b2f41c0a1e8c095439d0fab3b33103cf41a39be8e6aa2c56298a6034259/orjson-3.10.16-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4fc0077d101f8fab4031e6554fc17b4c2ad8fdbc56ee64a727f3c95b379e31da", size = 135450, upload_time = "2025-03-24T16:58:52.481Z" }, + { url = "https://files.pythonhosted.org/packages/d9/ca/7524c7b0bc815d426ca134dab54cad519802287b808a3846b047a5b2b7a3/orjson-3.10.16-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:9c6bf6ff180cd69e93f3f50380224218cfab79953a868ea3908430bcfaf9cb5e", size = 412356, upload_time = "2025-03-24T16:58:54.17Z" }, + { url = "https://files.pythonhosted.org/packages/05/1d/3ae2367c255276bf16ff7e1b210dd0af18bc8da20c4e4295755fc7de1268/orjson-3.10.16-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5673eadfa952f95a7cd76418ff189df11b0a9c34b1995dff43a6fdbce5d63bf4", size = 152769, upload_time = "2025-03-24T16:58:55.821Z" }, + { url = "https://files.pythonhosted.org/packages/d3/2d/8eb10b6b1d30bb69c35feb15e5ba5ac82466cf743d562e3e8047540efd2f/orjson-3.10.16-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5fe638a423d852b0ae1e1a79895851696cb0d9fa0946fdbfd5da5072d9bb9551", size = 137223, upload_time = "2025-03-24T16:58:57.136Z" }, + { url = "https://files.pythonhosted.org/packages/47/42/f043717930cb2de5fbebe47f308f101bed9ec2b3580b1f99c8284b2f5fe8/orjson-3.10.16-cp310-cp310-win32.whl", hash = "sha256:33af58f479b3c6435ab8f8b57999874b4b40c804c7a36b5cc6b54d8f28e1d3dd", size = 141734, upload_time = "2025-03-24T16:58:58.516Z" }, + { url = "https://files.pythonhosted.org/packages/67/99/795ad7282b425b9fddcfb8a31bded5dcf84dba78ecb1e7ae716e84e794da/orjson-3.10.16-cp310-cp310-win_amd64.whl", hash = "sha256:0338356b3f56d71293c583350af26f053017071836b07e064e92819ecf1aa055", size = 133779, upload_time = "2025-03-24T16:59:00.254Z" }, + { url = "https://files.pythonhosted.org/packages/97/29/43f91a5512b5d2535594438eb41c5357865fd5e64dec745d90a588820c75/orjson-3.10.16-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44fcbe1a1884f8bc9e2e863168b0f84230c3d634afe41c678637d2728ea8e739", size = 249180, upload_time = "2025-03-24T16:59:01.507Z" }, + { url = "https://files.pythonhosted.org/packages/0c/36/2a72d55e266473c19a86d97b7363bb8bf558ab450f75205689a287d5ce61/orjson-3.10.16-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78177bf0a9d0192e0b34c3d78bcff7fe21d1b5d84aeb5ebdfe0dbe637b885225", size = 138510, upload_time = "2025-03-24T16:59:02.876Z" }, + { url = "https://files.pythonhosted.org/packages/bb/ad/f86d6f55c1a68b57ff6ea7966bce5f4e5163f2e526ddb7db9fc3c2c8d1c4/orjson-3.10.16-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12824073a010a754bb27330cad21d6e9b98374f497f391b8707752b96f72e741", size = 132373, upload_time = "2025-03-24T16:59:04.103Z" }, + { url = "https://files.pythonhosted.org/packages/5e/8b/d18f2711493a809f3082a88fda89342bc8e16767743b909cd3c34989fba3/orjson-3.10.16-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddd41007e56284e9867864aa2f29f3136bb1dd19a49ca43c0b4eda22a579cf53", size = 136773, upload_time = "2025-03-24T16:59:05.636Z" }, + { url = "https://files.pythonhosted.org/packages/a1/dc/ce025f002f8e0749e3f057c4d773a4d4de32b7b4c1fc5a50b429e7532586/orjson-3.10.16-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0877c4d35de639645de83666458ca1f12560d9fa7aa9b25d8bb8f52f61627d14", size = 138029, upload_time = "2025-03-24T16:59:06.99Z" }, + { url = "https://files.pythonhosted.org/packages/0e/1b/cf9df85852b91160029d9f26014230366a2b4deb8cc51fabe68e250a8c1a/orjson-3.10.16-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9a09a539e9cc3beead3e7107093b4ac176d015bec64f811afb5965fce077a03c", size = 142677, upload_time = "2025-03-24T16:59:08.22Z" }, + { url = "https://files.pythonhosted.org/packages/92/18/5b1e1e995bffad49dc4311a0bdfd874bc6f135fd20f0e1f671adc2c9910e/orjson-3.10.16-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31b98bc9b40610fec971d9a4d67bb2ed02eec0a8ae35f8ccd2086320c28526ca", size = 132800, upload_time = "2025-03-24T16:59:09.529Z" }, + { url = "https://files.pythonhosted.org/packages/d6/eb/467f25b580e942fcca1344adef40633b7f05ac44a65a63fc913f9a805d58/orjson-3.10.16-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0ce243f5a8739f3a18830bc62dc2e05b69a7545bafd3e3249f86668b2bcd8e50", size = 135451, upload_time = "2025-03-24T16:59:10.823Z" }, + { url = "https://files.pythonhosted.org/packages/8d/4b/9d10888038975cb375982e9339d9495bac382d5c976c500b8d6f2c8e2e4e/orjson-3.10.16-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:64792c0025bae049b3074c6abe0cf06f23c8e9f5a445f4bab31dc5ca23dbf9e1", size = 412358, upload_time = "2025-03-24T16:59:12.113Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e2/cfbcfcc4fbe619e0ca9bdbbfccb2d62b540bbfe41e0ee77d44a628594f59/orjson-3.10.16-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ea53f7e68eec718b8e17e942f7ca56c6bd43562eb19db3f22d90d75e13f0431d", size = 152772, upload_time = "2025-03-24T16:59:13.919Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d6/627a1b00569be46173007c11dde3da4618c9bfe18409325b0e3e2a82fe29/orjson-3.10.16-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a741ba1a9488c92227711bde8c8c2b63d7d3816883268c808fbeada00400c164", size = 137225, upload_time = "2025-03-24T16:59:15.355Z" }, + { url = "https://files.pythonhosted.org/packages/0a/7b/a73c67b505021af845b9f05c7c848793258ea141fa2058b52dd9b067c2b4/orjson-3.10.16-cp311-cp311-win32.whl", hash = "sha256:c7ed2c61bb8226384c3fdf1fb01c51b47b03e3f4536c985078cccc2fd19f1619", size = 141733, upload_time = "2025-03-24T16:59:16.791Z" }, + { url = "https://files.pythonhosted.org/packages/f4/22/5e8217c48d68c0adbfb181e749d6a733761074e598b083c69a1383d18147/orjson-3.10.16-cp311-cp311-win_amd64.whl", hash = "sha256:cd67d8b3e0e56222a2e7b7f7da9031e30ecd1fe251c023340b9f12caca85ab60", size = 133784, upload_time = "2025-03-24T16:59:18.106Z" }, + { url = "https://files.pythonhosted.org/packages/5d/15/67ce9d4c959c83f112542222ea3b9209c1d424231d71d74c4890ea0acd2b/orjson-3.10.16-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6d3444abbfa71ba21bb042caa4b062535b122248259fdb9deea567969140abca", size = 249325, upload_time = "2025-03-24T16:59:19.784Z" }, + { url = "https://files.pythonhosted.org/packages/da/2c/1426b06f30a1b9ada74b6f512c1ddf9d2760f53f61cdb59efeb9ad342133/orjson-3.10.16-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:30245c08d818fdcaa48b7d5b81499b8cae09acabb216fe61ca619876b128e184", size = 133621, upload_time = "2025-03-24T16:59:21.207Z" }, + { url = "https://files.pythonhosted.org/packages/9e/88/18d26130954bc73bee3be10f95371ea1dfb8679e0e2c46b0f6d8c6289402/orjson-3.10.16-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0ba1d0baa71bf7579a4ccdcf503e6f3098ef9542106a0eca82395898c8a500a", size = 138270, upload_time = "2025-03-24T16:59:22.514Z" }, + { url = "https://files.pythonhosted.org/packages/4f/f9/6d8b64fcd58fae072e80ee7981be8ba0d7c26ace954e5cd1d027fc80518f/orjson-3.10.16-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb0beefa5ef3af8845f3a69ff2a4aa62529b5acec1cfe5f8a6b4141033fd46ef", size = 132346, upload_time = "2025-03-24T16:59:24.277Z" }, + { url = "https://files.pythonhosted.org/packages/16/3f/2513fd5bc786f40cd12af569c23cae6381aeddbefeed2a98f0a666eb5d0d/orjson-3.10.16-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6daa0e1c9bf2e030e93c98394de94506f2a4d12e1e9dadd7c53d5e44d0f9628e", size = 136845, upload_time = "2025-03-24T16:59:25.588Z" }, + { url = "https://files.pythonhosted.org/packages/6d/42/b0e7b36720f5ab722b48e8ccf06514d4f769358dd73c51abd8728ef58d0b/orjson-3.10.16-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9da9019afb21e02410ef600e56666652b73eb3e4d213a0ec919ff391a7dd52aa", size = 138078, upload_time = "2025-03-24T16:59:27.288Z" }, + { url = "https://files.pythonhosted.org/packages/a3/a8/d220afb8a439604be74fc755dbc740bded5ed14745ca536b304ed32eb18a/orjson-3.10.16-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:daeb3a1ee17b69981d3aae30c3b4e786b0f8c9e6c71f2b48f1aef934f63f38f4", size = 142712, upload_time = "2025-03-24T16:59:28.613Z" }, + { url = "https://files.pythonhosted.org/packages/8c/88/7e41e9883c00f84f92fe357a8371edae816d9d7ef39c67b5106960c20389/orjson-3.10.16-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80fed80eaf0e20a31942ae5d0728849862446512769692474be5e6b73123a23b", size = 133136, upload_time = "2025-03-24T16:59:29.987Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ca/61116095307ad0be828ea26093febaf59e38596d84a9c8d765c3c5e4934f/orjson-3.10.16-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73390ed838f03764540a7bdc4071fe0123914c2cc02fb6abf35182d5fd1b7a42", size = 135258, upload_time = "2025-03-24T16:59:31.339Z" }, + { url = "https://files.pythonhosted.org/packages/dc/1b/09493cf7d801505f094c9295f79c98c1e0af2ac01c7ed8d25b30fcb19ada/orjson-3.10.16-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a22bba012a0c94ec02a7768953020ab0d3e2b884760f859176343a36c01adf87", size = 412326, upload_time = "2025-03-24T16:59:32.709Z" }, + { url = "https://files.pythonhosted.org/packages/ea/02/125d7bbd7f7a500190ddc8ae5d2d3c39d87ed3ed28f5b37cfe76962c678d/orjson-3.10.16-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5385bbfdbc90ff5b2635b7e6bebf259652db00a92b5e3c45b616df75b9058e88", size = 152800, upload_time = "2025-03-24T16:59:34.134Z" }, + { url = "https://files.pythonhosted.org/packages/f9/09/7658a9e3e793d5b3b00598023e0fb6935d0e7bbb8ff72311c5415a8ce677/orjson-3.10.16-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:02c6279016346e774dd92625d46c6c40db687b8a0d685aadb91e26e46cc33e1e", size = 137516, upload_time = "2025-03-24T16:59:35.446Z" }, + { url = "https://files.pythonhosted.org/packages/29/87/32b7a4831e909d347278101a48d4cf9f3f25901b2295e7709df1651f65a1/orjson-3.10.16-cp312-cp312-win32.whl", hash = "sha256:7ca55097a11426db80f79378e873a8c51f4dde9ffc22de44850f9696b7eb0e8c", size = 141759, upload_time = "2025-03-24T16:59:37.509Z" }, + { url = "https://files.pythonhosted.org/packages/35/ce/81a27e7b439b807bd393585271364cdddf50dc281fc57c4feef7ccb186a6/orjson-3.10.16-cp312-cp312-win_amd64.whl", hash = "sha256:86d127efdd3f9bf5f04809b70faca1e6836556ea3cc46e662b44dab3fe71f3d6", size = 133944, upload_time = "2025-03-24T16:59:38.814Z" }, + { url = "https://files.pythonhosted.org/packages/87/b9/ff6aa28b8c86af9526160905593a2fe8d004ac7a5e592ee0b0ff71017511/orjson-3.10.16-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:148a97f7de811ba14bc6dbc4a433e0341ffd2cc285065199fb5f6a98013744bd", size = 249289, upload_time = "2025-03-24T16:59:40.117Z" }, + { url = "https://files.pythonhosted.org/packages/6c/81/6d92a586149b52684ab8fd70f3623c91d0e6a692f30fd8c728916ab2263c/orjson-3.10.16-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:1d960c1bf0e734ea36d0adc880076de3846aaec45ffad29b78c7f1b7962516b8", size = 133640, upload_time = "2025-03-24T16:59:41.469Z" }, + { url = "https://files.pythonhosted.org/packages/c2/88/b72443f4793d2e16039ab85d0026677932b15ab968595fb7149750d74134/orjson-3.10.16-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a318cd184d1269f68634464b12871386808dc8b7c27de8565234d25975a7a137", size = 138286, upload_time = "2025-03-24T16:59:42.769Z" }, + { url = "https://files.pythonhosted.org/packages/c3/3c/72a22d4b28c076c4016d5a52bd644a8e4d849d3bb0373d9e377f9e3b2250/orjson-3.10.16-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df23f8df3ef9223d1d6748bea63fca55aae7da30a875700809c500a05975522b", size = 132307, upload_time = "2025-03-24T16:59:44.143Z" }, + { url = "https://files.pythonhosted.org/packages/8a/a2/f1259561bdb6ad7061ff1b95dab082fe32758c4bc143ba8d3d70831f0a06/orjson-3.10.16-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b94dda8dd6d1378f1037d7f3f6b21db769ef911c4567cbaa962bb6dc5021cf90", size = 136739, upload_time = "2025-03-24T16:59:45.995Z" }, + { url = "https://files.pythonhosted.org/packages/3d/af/c7583c4b34f33d8b8b90cfaab010ff18dd64e7074cc1e117a5f1eff20dcf/orjson-3.10.16-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f12970a26666a8775346003fd94347d03ccb98ab8aa063036818381acf5f523e", size = 138076, upload_time = "2025-03-24T16:59:47.776Z" }, + { url = "https://files.pythonhosted.org/packages/d7/59/d7fc7fbdd3d4a64c2eae4fc7341a5aa39cf9549bd5e2d7f6d3c07f8b715b/orjson-3.10.16-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15a1431a245d856bd56e4d29ea0023eb4d2c8f71efe914beb3dee8ab3f0cd7fb", size = 142643, upload_time = "2025-03-24T16:59:49.258Z" }, + { url = "https://files.pythonhosted.org/packages/92/0e/3bd8f2197d27601f16b4464ae948826da2bcf128af31230a9dbbad7ceb57/orjson-3.10.16-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c83655cfc247f399a222567d146524674a7b217af7ef8289c0ff53cfe8db09f0", size = 133168, upload_time = "2025-03-24T16:59:51.027Z" }, + { url = "https://files.pythonhosted.org/packages/af/a8/351fd87b664b02f899f9144d2c3dc848b33ac04a5df05234cbfb9e2a7540/orjson-3.10.16-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fa59ae64cb6ddde8f09bdbf7baf933c4cd05734ad84dcf4e43b887eb24e37652", size = 135271, upload_time = "2025-03-24T16:59:52.449Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b0/a6d42a7d412d867c60c0337d95123517dd5a9370deea705ea1be0f89389e/orjson-3.10.16-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ca5426e5aacc2e9507d341bc169d8af9c3cbe88f4cd4c1cf2f87e8564730eb56", size = 412444, upload_time = "2025-03-24T16:59:53.825Z" }, + { url = "https://files.pythonhosted.org/packages/79/ec/7572cd4e20863f60996f3f10bc0a6da64a6fd9c35954189a914cec0b7377/orjson-3.10.16-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6fd5da4edf98a400946cd3a195680de56f1e7575109b9acb9493331047157430", size = 152737, upload_time = "2025-03-24T16:59:55.599Z" }, + { url = "https://files.pythonhosted.org/packages/a9/19/ceb9e8fed5403b2e76a8ac15f581b9d25780a3be3c9b3aa54b7777a210d5/orjson-3.10.16-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:980ecc7a53e567169282a5e0ff078393bac78320d44238da4e246d71a4e0e8f5", size = 137482, upload_time = "2025-03-24T16:59:57.045Z" }, + { url = "https://files.pythonhosted.org/packages/1b/78/a78bb810f3786579dbbbd94768284cbe8f2fd65167cd7020260679665c17/orjson-3.10.16-cp313-cp313-win32.whl", hash = "sha256:28f79944dd006ac540a6465ebd5f8f45dfdf0948ff998eac7a908275b4c1add6", size = 141714, upload_time = "2025-03-24T16:59:58.666Z" }, + { url = "https://files.pythonhosted.org/packages/81/9c/b66ce9245ff319df2c3278acd351a3f6145ef34b4a2d7f4b0f739368370f/orjson-3.10.16-cp313-cp313-win_amd64.whl", hash = "sha256:fe0a145e96d51971407cb8ba947e63ead2aa915db59d6631a355f5f2150b56b7", size = 133954, upload_time = "2025-03-24T17:00:00.101Z" }, ] [[package]] name = "packaging" version = "23.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fb/2b/9b9c33ffed44ee921d0967086d653047286054117d584f1b1a7c22ceaf7b/packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", size = 146714 } +sdist = { url = "https://files.pythonhosted.org/packages/fb/2b/9b9c33ffed44ee921d0967086d653047286054117d584f1b1a7c22ceaf7b/packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", size = 146714, upload_time = "2023-10-01T13:50:05.279Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/1a/610693ac4ee14fcdf2d9bf3c493370e4f2ef7ae2e19217d7a237ff42367d/packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7", size = 53011 }, + { url = "https://files.pythonhosted.org/packages/ec/1a/610693ac4ee14fcdf2d9bf3c493370e4f2ef7ae2e19217d7a237ff42367d/packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7", size = 53011, upload_time = "2023-10-01T13:50:03.745Z" }, ] [[package]] name = "pathspec" version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload_time = "2023-12-10T22:30:45Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload_time = "2023-12-10T22:30:43.14Z" }, ] [[package]] name = "pillow" version = "10.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059 } +sdist = { url = "https://files.pythonhosted.org/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059, upload_time = "2024-07-01T09:48:43.583Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/69/a31cccd538ca0b5272be2a38347f8839b97a14be104ea08b0db92f749c74/pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e", size = 3509271 }, - { url = "https://files.pythonhosted.org/packages/9a/9e/4143b907be8ea0bce215f2ae4f7480027473f8b61fcedfda9d851082a5d2/pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d", size = 3375658 }, - { url = "https://files.pythonhosted.org/packages/8a/25/1fc45761955f9359b1169aa75e241551e74ac01a09f487adaaf4c3472d11/pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856", size = 4332075 }, - { url = "https://files.pythonhosted.org/packages/5e/dd/425b95d0151e1d6c951f45051112394f130df3da67363b6bc75dc4c27aba/pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f", size = 4444808 }, - { url = "https://files.pythonhosted.org/packages/b1/84/9a15cc5726cbbfe7f9f90bfb11f5d028586595907cd093815ca6644932e3/pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b", size = 4356290 }, - { url = "https://files.pythonhosted.org/packages/b5/5b/6651c288b08df3b8c1e2f8c1152201e0b25d240e22ddade0f1e242fc9fa0/pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc", size = 4525163 }, - { url = "https://files.pythonhosted.org/packages/07/8b/34854bf11a83c248505c8cb0fcf8d3d0b459a2246c8809b967963b6b12ae/pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e", size = 4463100 }, - { url = "https://files.pythonhosted.org/packages/78/63/0632aee4e82476d9cbe5200c0cdf9ba41ee04ed77887432845264d81116d/pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46", size = 4592880 }, - { url = "https://files.pythonhosted.org/packages/df/56/b8663d7520671b4398b9d97e1ed9f583d4afcbefbda3c6188325e8c297bd/pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984", size = 2235218 }, - { url = "https://files.pythonhosted.org/packages/f4/72/0203e94a91ddb4a9d5238434ae6c1ca10e610e8487036132ea9bf806ca2a/pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141", size = 2554487 }, - { url = "https://files.pythonhosted.org/packages/bd/52/7e7e93d7a6e4290543f17dc6f7d3af4bd0b3dd9926e2e8a35ac2282bc5f4/pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1", size = 2243219 }, - { url = "https://files.pythonhosted.org/packages/a7/62/c9449f9c3043c37f73e7487ec4ef0c03eb9c9afc91a92b977a67b3c0bbc5/pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c", size = 3509265 }, - { url = "https://files.pythonhosted.org/packages/f4/5f/491dafc7bbf5a3cc1845dc0430872e8096eb9e2b6f8161509d124594ec2d/pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be", size = 3375655 }, - { url = "https://files.pythonhosted.org/packages/73/d5/c4011a76f4207a3c151134cd22a1415741e42fa5ddecec7c0182887deb3d/pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3", size = 4340304 }, - { url = "https://files.pythonhosted.org/packages/ac/10/c67e20445a707f7a610699bba4fe050583b688d8cd2d202572b257f46600/pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6", size = 4452804 }, - { url = "https://files.pythonhosted.org/packages/a9/83/6523837906d1da2b269dee787e31df3b0acb12e3d08f024965a3e7f64665/pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe", size = 4365126 }, - { url = "https://files.pythonhosted.org/packages/ba/e5/8c68ff608a4203085158cff5cc2a3c534ec384536d9438c405ed6370d080/pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319", size = 4533541 }, - { url = "https://files.pythonhosted.org/packages/f4/7c/01b8dbdca5bc6785573f4cee96e2358b0918b7b2c7b60d8b6f3abf87a070/pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d", size = 4471616 }, - { url = "https://files.pythonhosted.org/packages/c8/57/2899b82394a35a0fbfd352e290945440e3b3785655a03365c0ca8279f351/pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696", size = 4600802 }, - { url = "https://files.pythonhosted.org/packages/4d/d7/a44f193d4c26e58ee5d2d9db3d4854b2cfb5b5e08d360a5e03fe987c0086/pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496", size = 2235213 }, - { url = "https://files.pythonhosted.org/packages/c1/d0/5866318eec2b801cdb8c82abf190c8343d8a1cd8bf5a0c17444a6f268291/pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91", size = 2554498 }, - { url = "https://files.pythonhosted.org/packages/d4/c8/310ac16ac2b97e902d9eb438688de0d961660a87703ad1561fd3dfbd2aa0/pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22", size = 2243219 }, - { url = "https://files.pythonhosted.org/packages/05/cb/0353013dc30c02a8be34eb91d25e4e4cf594b59e5a55ea1128fde1e5f8ea/pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94", size = 3509350 }, - { url = "https://files.pythonhosted.org/packages/e7/cf/5c558a0f247e0bf9cec92bff9b46ae6474dd736f6d906315e60e4075f737/pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597", size = 3374980 }, - { url = "https://files.pythonhosted.org/packages/84/48/6e394b86369a4eb68b8a1382c78dc092245af517385c086c5094e3b34428/pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80", size = 4343799 }, - { url = "https://files.pythonhosted.org/packages/3b/f3/a8c6c11fa84b59b9df0cd5694492da8c039a24cd159f0f6918690105c3be/pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca", size = 4459973 }, - { url = "https://files.pythonhosted.org/packages/7d/1b/c14b4197b80150fb64453585247e6fb2e1d93761fa0fa9cf63b102fde822/pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef", size = 4370054 }, - { url = "https://files.pythonhosted.org/packages/55/77/40daddf677897a923d5d33329acd52a2144d54a9644f2a5422c028c6bf2d/pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a", size = 4539484 }, - { url = "https://files.pythonhosted.org/packages/40/54/90de3e4256b1207300fb2b1d7168dd912a2fb4b2401e439ba23c2b2cabde/pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b", size = 4477375 }, - { url = "https://files.pythonhosted.org/packages/13/24/1bfba52f44193860918ff7c93d03d95e3f8748ca1de3ceaf11157a14cf16/pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9", size = 4608773 }, - { url = "https://files.pythonhosted.org/packages/55/04/5e6de6e6120451ec0c24516c41dbaf80cce1b6451f96561235ef2429da2e/pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42", size = 2235690 }, - { url = "https://files.pythonhosted.org/packages/74/0a/d4ce3c44bca8635bd29a2eab5aa181b654a734a29b263ca8efe013beea98/pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a", size = 2554951 }, - { url = "https://files.pythonhosted.org/packages/b5/ca/184349ee40f2e92439be9b3502ae6cfc43ac4b50bc4fc6b3de7957563894/pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9", size = 2243427 }, - { url = "https://files.pythonhosted.org/packages/c3/00/706cebe7c2c12a6318aabe5d354836f54adff7156fd9e1bd6c89f4ba0e98/pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3", size = 3525685 }, - { url = "https://files.pythonhosted.org/packages/cf/76/f658cbfa49405e5ecbfb9ba42d07074ad9792031267e782d409fd8fe7c69/pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb", size = 3374883 }, - { url = "https://files.pythonhosted.org/packages/46/2b/99c28c4379a85e65378211971c0b430d9c7234b1ec4d59b2668f6299e011/pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70", size = 4339837 }, - { url = "https://files.pythonhosted.org/packages/f1/74/b1ec314f624c0c43711fdf0d8076f82d9d802afd58f1d62c2a86878e8615/pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be", size = 4455562 }, - { url = "https://files.pythonhosted.org/packages/4a/2a/4b04157cb7b9c74372fa867096a1607e6fedad93a44deeff553ccd307868/pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0", size = 4366761 }, - { url = "https://files.pythonhosted.org/packages/ac/7b/8f1d815c1a6a268fe90481232c98dd0e5fa8c75e341a75f060037bd5ceae/pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc", size = 4536767 }, - { url = "https://files.pythonhosted.org/packages/e5/77/05fa64d1f45d12c22c314e7b97398ffb28ef2813a485465017b7978b3ce7/pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a", size = 4477989 }, - { url = "https://files.pythonhosted.org/packages/12/63/b0397cfc2caae05c3fb2f4ed1b4fc4fc878f0243510a7a6034ca59726494/pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309", size = 4610255 }, - { url = "https://files.pythonhosted.org/packages/7b/f9/cfaa5082ca9bc4a6de66ffe1c12c2d90bf09c309a5f52b27759a596900e7/pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060", size = 2235603 }, - { url = "https://files.pythonhosted.org/packages/01/6a/30ff0eef6e0c0e71e55ded56a38d4859bf9d3634a94a88743897b5f96936/pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea", size = 2554972 }, - { url = "https://files.pythonhosted.org/packages/48/2c/2e0a52890f269435eee38b21c8218e102c621fe8d8df8b9dd06fabf879ba/pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d", size = 2243375 }, - { url = "https://files.pythonhosted.org/packages/38/30/095d4f55f3a053392f75e2eae45eba3228452783bab3d9a920b951ac495c/pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4", size = 3493889 }, - { url = "https://files.pythonhosted.org/packages/f3/e8/4ff79788803a5fcd5dc35efdc9386af153569853767bff74540725b45863/pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da", size = 3346160 }, - { url = "https://files.pythonhosted.org/packages/d7/ac/4184edd511b14f760c73f5bb8a5d6fd85c591c8aff7c2229677a355c4179/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026", size = 3435020 }, - { url = "https://files.pythonhosted.org/packages/da/21/1749cd09160149c0a246a81d646e05f35041619ce76f6493d6a96e8d1103/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e", size = 3490539 }, - { url = "https://files.pythonhosted.org/packages/b6/f5/f71fe1888b96083b3f6dfa0709101f61fc9e972c0c8d04e9d93ccef2a045/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5", size = 3476125 }, - { url = "https://files.pythonhosted.org/packages/96/b9/c0362c54290a31866c3526848583a2f45a535aa9d725fd31e25d318c805f/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885", size = 3579373 }, - { url = "https://files.pythonhosted.org/packages/52/3b/ce7a01026a7cf46e5452afa86f97a5e88ca97f562cafa76570178ab56d8d/pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5", size = 2554661 }, + { url = "https://files.pythonhosted.org/packages/0e/69/a31cccd538ca0b5272be2a38347f8839b97a14be104ea08b0db92f749c74/pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e", size = 3509271, upload_time = "2024-07-01T09:45:22.07Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9e/4143b907be8ea0bce215f2ae4f7480027473f8b61fcedfda9d851082a5d2/pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d", size = 3375658, upload_time = "2024-07-01T09:45:25.292Z" }, + { url = "https://files.pythonhosted.org/packages/8a/25/1fc45761955f9359b1169aa75e241551e74ac01a09f487adaaf4c3472d11/pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856", size = 4332075, upload_time = "2024-07-01T09:45:27.94Z" }, + { url = "https://files.pythonhosted.org/packages/5e/dd/425b95d0151e1d6c951f45051112394f130df3da67363b6bc75dc4c27aba/pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f", size = 4444808, upload_time = "2024-07-01T09:45:30.305Z" }, + { url = "https://files.pythonhosted.org/packages/b1/84/9a15cc5726cbbfe7f9f90bfb11f5d028586595907cd093815ca6644932e3/pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b", size = 4356290, upload_time = "2024-07-01T09:45:32.868Z" }, + { url = "https://files.pythonhosted.org/packages/b5/5b/6651c288b08df3b8c1e2f8c1152201e0b25d240e22ddade0f1e242fc9fa0/pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc", size = 4525163, upload_time = "2024-07-01T09:45:35.279Z" }, + { url = "https://files.pythonhosted.org/packages/07/8b/34854bf11a83c248505c8cb0fcf8d3d0b459a2246c8809b967963b6b12ae/pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e", size = 4463100, upload_time = "2024-07-01T09:45:37.74Z" }, + { url = "https://files.pythonhosted.org/packages/78/63/0632aee4e82476d9cbe5200c0cdf9ba41ee04ed77887432845264d81116d/pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46", size = 4592880, upload_time = "2024-07-01T09:45:39.89Z" }, + { url = "https://files.pythonhosted.org/packages/df/56/b8663d7520671b4398b9d97e1ed9f583d4afcbefbda3c6188325e8c297bd/pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984", size = 2235218, upload_time = "2024-07-01T09:45:42.771Z" }, + { url = "https://files.pythonhosted.org/packages/f4/72/0203e94a91ddb4a9d5238434ae6c1ca10e610e8487036132ea9bf806ca2a/pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141", size = 2554487, upload_time = "2024-07-01T09:45:45.176Z" }, + { url = "https://files.pythonhosted.org/packages/bd/52/7e7e93d7a6e4290543f17dc6f7d3af4bd0b3dd9926e2e8a35ac2282bc5f4/pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1", size = 2243219, upload_time = "2024-07-01T09:45:47.274Z" }, + { url = "https://files.pythonhosted.org/packages/a7/62/c9449f9c3043c37f73e7487ec4ef0c03eb9c9afc91a92b977a67b3c0bbc5/pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c", size = 3509265, upload_time = "2024-07-01T09:45:49.812Z" }, + { url = "https://files.pythonhosted.org/packages/f4/5f/491dafc7bbf5a3cc1845dc0430872e8096eb9e2b6f8161509d124594ec2d/pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be", size = 3375655, upload_time = "2024-07-01T09:45:52.462Z" }, + { url = "https://files.pythonhosted.org/packages/73/d5/c4011a76f4207a3c151134cd22a1415741e42fa5ddecec7c0182887deb3d/pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3", size = 4340304, upload_time = "2024-07-01T09:45:55.006Z" }, + { url = "https://files.pythonhosted.org/packages/ac/10/c67e20445a707f7a610699bba4fe050583b688d8cd2d202572b257f46600/pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6", size = 4452804, upload_time = "2024-07-01T09:45:58.437Z" }, + { url = "https://files.pythonhosted.org/packages/a9/83/6523837906d1da2b269dee787e31df3b0acb12e3d08f024965a3e7f64665/pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe", size = 4365126, upload_time = "2024-07-01T09:46:00.713Z" }, + { url = "https://files.pythonhosted.org/packages/ba/e5/8c68ff608a4203085158cff5cc2a3c534ec384536d9438c405ed6370d080/pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319", size = 4533541, upload_time = "2024-07-01T09:46:03.235Z" }, + { url = "https://files.pythonhosted.org/packages/f4/7c/01b8dbdca5bc6785573f4cee96e2358b0918b7b2c7b60d8b6f3abf87a070/pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d", size = 4471616, upload_time = "2024-07-01T09:46:05.356Z" }, + { url = "https://files.pythonhosted.org/packages/c8/57/2899b82394a35a0fbfd352e290945440e3b3785655a03365c0ca8279f351/pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696", size = 4600802, upload_time = "2024-07-01T09:46:08.145Z" }, + { url = "https://files.pythonhosted.org/packages/4d/d7/a44f193d4c26e58ee5d2d9db3d4854b2cfb5b5e08d360a5e03fe987c0086/pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496", size = 2235213, upload_time = "2024-07-01T09:46:10.211Z" }, + { url = "https://files.pythonhosted.org/packages/c1/d0/5866318eec2b801cdb8c82abf190c8343d8a1cd8bf5a0c17444a6f268291/pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91", size = 2554498, upload_time = "2024-07-01T09:46:12.685Z" }, + { url = "https://files.pythonhosted.org/packages/d4/c8/310ac16ac2b97e902d9eb438688de0d961660a87703ad1561fd3dfbd2aa0/pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22", size = 2243219, upload_time = "2024-07-01T09:46:14.83Z" }, + { url = "https://files.pythonhosted.org/packages/05/cb/0353013dc30c02a8be34eb91d25e4e4cf594b59e5a55ea1128fde1e5f8ea/pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94", size = 3509350, upload_time = "2024-07-01T09:46:17.177Z" }, + { url = "https://files.pythonhosted.org/packages/e7/cf/5c558a0f247e0bf9cec92bff9b46ae6474dd736f6d906315e60e4075f737/pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597", size = 3374980, upload_time = "2024-07-01T09:46:19.169Z" }, + { url = "https://files.pythonhosted.org/packages/84/48/6e394b86369a4eb68b8a1382c78dc092245af517385c086c5094e3b34428/pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80", size = 4343799, upload_time = "2024-07-01T09:46:21.883Z" }, + { url = "https://files.pythonhosted.org/packages/3b/f3/a8c6c11fa84b59b9df0cd5694492da8c039a24cd159f0f6918690105c3be/pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca", size = 4459973, upload_time = "2024-07-01T09:46:24.321Z" }, + { url = "https://files.pythonhosted.org/packages/7d/1b/c14b4197b80150fb64453585247e6fb2e1d93761fa0fa9cf63b102fde822/pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef", size = 4370054, upload_time = "2024-07-01T09:46:26.825Z" }, + { url = "https://files.pythonhosted.org/packages/55/77/40daddf677897a923d5d33329acd52a2144d54a9644f2a5422c028c6bf2d/pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a", size = 4539484, upload_time = "2024-07-01T09:46:29.355Z" }, + { url = "https://files.pythonhosted.org/packages/40/54/90de3e4256b1207300fb2b1d7168dd912a2fb4b2401e439ba23c2b2cabde/pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b", size = 4477375, upload_time = "2024-07-01T09:46:31.756Z" }, + { url = "https://files.pythonhosted.org/packages/13/24/1bfba52f44193860918ff7c93d03d95e3f8748ca1de3ceaf11157a14cf16/pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9", size = 4608773, upload_time = "2024-07-01T09:46:33.73Z" }, + { url = "https://files.pythonhosted.org/packages/55/04/5e6de6e6120451ec0c24516c41dbaf80cce1b6451f96561235ef2429da2e/pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42", size = 2235690, upload_time = "2024-07-01T09:46:36.587Z" }, + { url = "https://files.pythonhosted.org/packages/74/0a/d4ce3c44bca8635bd29a2eab5aa181b654a734a29b263ca8efe013beea98/pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a", size = 2554951, upload_time = "2024-07-01T09:46:38.777Z" }, + { url = "https://files.pythonhosted.org/packages/b5/ca/184349ee40f2e92439be9b3502ae6cfc43ac4b50bc4fc6b3de7957563894/pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9", size = 2243427, upload_time = "2024-07-01T09:46:43.15Z" }, + { url = "https://files.pythonhosted.org/packages/c3/00/706cebe7c2c12a6318aabe5d354836f54adff7156fd9e1bd6c89f4ba0e98/pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3", size = 3525685, upload_time = "2024-07-01T09:46:45.194Z" }, + { url = "https://files.pythonhosted.org/packages/cf/76/f658cbfa49405e5ecbfb9ba42d07074ad9792031267e782d409fd8fe7c69/pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb", size = 3374883, upload_time = "2024-07-01T09:46:47.331Z" }, + { url = "https://files.pythonhosted.org/packages/46/2b/99c28c4379a85e65378211971c0b430d9c7234b1ec4d59b2668f6299e011/pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70", size = 4339837, upload_time = "2024-07-01T09:46:49.647Z" }, + { url = "https://files.pythonhosted.org/packages/f1/74/b1ec314f624c0c43711fdf0d8076f82d9d802afd58f1d62c2a86878e8615/pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be", size = 4455562, upload_time = "2024-07-01T09:46:51.811Z" }, + { url = "https://files.pythonhosted.org/packages/4a/2a/4b04157cb7b9c74372fa867096a1607e6fedad93a44deeff553ccd307868/pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0", size = 4366761, upload_time = "2024-07-01T09:46:53.961Z" }, + { url = "https://files.pythonhosted.org/packages/ac/7b/8f1d815c1a6a268fe90481232c98dd0e5fa8c75e341a75f060037bd5ceae/pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc", size = 4536767, upload_time = "2024-07-01T09:46:56.664Z" }, + { url = "https://files.pythonhosted.org/packages/e5/77/05fa64d1f45d12c22c314e7b97398ffb28ef2813a485465017b7978b3ce7/pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a", size = 4477989, upload_time = "2024-07-01T09:46:58.977Z" }, + { url = "https://files.pythonhosted.org/packages/12/63/b0397cfc2caae05c3fb2f4ed1b4fc4fc878f0243510a7a6034ca59726494/pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309", size = 4610255, upload_time = "2024-07-01T09:47:01.189Z" }, + { url = "https://files.pythonhosted.org/packages/7b/f9/cfaa5082ca9bc4a6de66ffe1c12c2d90bf09c309a5f52b27759a596900e7/pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060", size = 2235603, upload_time = "2024-07-01T09:47:03.918Z" }, + { url = "https://files.pythonhosted.org/packages/01/6a/30ff0eef6e0c0e71e55ded56a38d4859bf9d3634a94a88743897b5f96936/pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea", size = 2554972, upload_time = "2024-07-01T09:47:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/48/2c/2e0a52890f269435eee38b21c8218e102c621fe8d8df8b9dd06fabf879ba/pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d", size = 2243375, upload_time = "2024-07-01T09:47:09.065Z" }, + { url = "https://files.pythonhosted.org/packages/38/30/095d4f55f3a053392f75e2eae45eba3228452783bab3d9a920b951ac495c/pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4", size = 3493889, upload_time = "2024-07-01T09:48:04.815Z" }, + { url = "https://files.pythonhosted.org/packages/f3/e8/4ff79788803a5fcd5dc35efdc9386af153569853767bff74540725b45863/pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da", size = 3346160, upload_time = "2024-07-01T09:48:07.206Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ac/4184edd511b14f760c73f5bb8a5d6fd85c591c8aff7c2229677a355c4179/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026", size = 3435020, upload_time = "2024-07-01T09:48:09.66Z" }, + { url = "https://files.pythonhosted.org/packages/da/21/1749cd09160149c0a246a81d646e05f35041619ce76f6493d6a96e8d1103/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e", size = 3490539, upload_time = "2024-07-01T09:48:12.529Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f5/f71fe1888b96083b3f6dfa0709101f61fc9e972c0c8d04e9d93ccef2a045/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5", size = 3476125, upload_time = "2024-07-01T09:48:14.891Z" }, + { url = "https://files.pythonhosted.org/packages/96/b9/c0362c54290a31866c3526848583a2f45a535aa9d725fd31e25d318c805f/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885", size = 3579373, upload_time = "2024-07-01T09:48:17.601Z" }, + { url = "https://files.pythonhosted.org/packages/52/3b/ce7a01026a7cf46e5452afa86f97a5e88ca97f562cafa76570178ab56d8d/pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5", size = 2554661, upload_time = "2024-07-01T09:48:20.293Z" }, ] [[package]] name = "platformdirs" -version = "4.1.0" +version = "4.3.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/62/d1/7feaaacb1a3faeba96c06e6c5091f90695cc0f94b7e8e1a3a3fe2b33ff9a/platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420", size = 19760 } +sdist = { url = "https://files.pythonhosted.org/packages/b6/2d/7d512a3913d60623e7eb945c6d1b4f0bddf1d0b7ada5225274c87e5b53d1/platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351", size = 21291, upload_time = "2025-03-19T20:36:10.989Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/be/53/42fe5eab4a09d251a76d0043e018172db324a23fcdac70f77a551c11f618/platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380", size = 17420 }, + { url = "https://files.pythonhosted.org/packages/6d/45/59578566b3275b8fd9157885918fcd0c4d74162928a5310926887b856a51/platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", size = 18499, upload_time = "2025-03-19T20:36:09.038Z" }, ] [[package]] name = "pluggy" version = "1.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955, upload_time = "2024-04-20T21:34:42.531Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload_time = "2024-04-20T21:34:40.434Z" }, ] [[package]] @@ -1745,46 +1771,46 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e1/c0/5e9c4d2a643a00a6f67578ef35485173de273a4567279e4f0c200c01386b/prettytable-3.9.0.tar.gz", hash = "sha256:f4ed94803c23073a90620b201965e5dc0bccf1760b7a7eaf3158cab8aaffdf34", size = 47874 } +sdist = { url = "https://files.pythonhosted.org/packages/e1/c0/5e9c4d2a643a00a6f67578ef35485173de273a4567279e4f0c200c01386b/prettytable-3.9.0.tar.gz", hash = "sha256:f4ed94803c23073a90620b201965e5dc0bccf1760b7a7eaf3158cab8aaffdf34", size = 47874, upload_time = "2023-09-11T14:04:14.548Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/81/316b6a55a0d1f327d04cc7b0ba9d04058cb62de6c3a4d4b0df280cbe3b0b/prettytable-3.9.0-py3-none-any.whl", hash = "sha256:a71292ab7769a5de274b146b276ce938786f56c31cf7cea88b6f3775d82fe8c8", size = 27772 }, + { url = "https://files.pythonhosted.org/packages/4d/81/316b6a55a0d1f327d04cc7b0ba9d04058cb62de6c3a4d4b0df280cbe3b0b/prettytable-3.9.0-py3-none-any.whl", hash = "sha256:a71292ab7769a5de274b146b276ce938786f56c31cf7cea88b6f3775d82fe8c8", size = 27772, upload_time = "2023-09-11T14:03:45.582Z" }, ] [[package]] name = "protobuf" version = "4.25.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/a5/05ea470f4e793c9408bc975ce1c6957447e3134ce7f7a58c13be8b2c216f/protobuf-4.25.2.tar.gz", hash = "sha256:fe599e175cb347efc8ee524bcd4b902d11f7262c0e569ececcb89995c15f0a5e", size = 380282 } +sdist = { url = "https://files.pythonhosted.org/packages/db/a5/05ea470f4e793c9408bc975ce1c6957447e3134ce7f7a58c13be8b2c216f/protobuf-4.25.2.tar.gz", hash = "sha256:fe599e175cb347efc8ee524bcd4b902d11f7262c0e569ececcb89995c15f0a5e", size = 380282, upload_time = "2024-01-10T19:37:42.958Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/2f/01f63896ddf22cbb0173ab51f54fde70b0208ca6c2f5e8416950977930e1/protobuf-4.25.2-cp310-abi3-win32.whl", hash = "sha256:b50c949608682b12efb0b2717f53256f03636af5f60ac0c1d900df6213910fd6", size = 392408 }, - { url = "https://files.pythonhosted.org/packages/c1/00/c3ae19cabb36cfabc94ff0b102aac21b471c9f91a1357f8aafffb9efe8e0/protobuf-4.25.2-cp310-abi3-win_amd64.whl", hash = "sha256:8f62574857ee1de9f770baf04dde4165e30b15ad97ba03ceac65f760ff018ac9", size = 413397 }, - { url = "https://files.pythonhosted.org/packages/b3/81/0017aefacf23273d4efd1154ef958a27eed9c177c4cc09d2d4ba398fb47f/protobuf-4.25.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:2db9f8fa64fbdcdc93767d3cf81e0f2aef176284071507e3ede160811502fd3d", size = 394159 }, - { url = "https://files.pythonhosted.org/packages/23/17/405ba44f60a693dfe96c7a18e843707cffa0fcfad80bd8fc4f227f499ea5/protobuf-4.25.2-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:10894a2885b7175d3984f2be8d9850712c57d5e7587a2410720af8be56cdaf62", size = 293698 }, - { url = "https://files.pythonhosted.org/packages/81/9e/63501b8d5b4e40c7260049836bd15ec3270c936e83bc57b85e4603cc212c/protobuf-4.25.2-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:fc381d1dd0516343f1440019cedf08a7405f791cd49eef4ae1ea06520bc1c020", size = 294609 }, - { url = "https://files.pythonhosted.org/packages/ff/52/5d23df1fe3b368133ec3e2436fb3dd4ccedf44c8d5ac7f4a88087c75180b/protobuf-4.25.2-py3-none-any.whl", hash = "sha256:a8b7a98d4ce823303145bf3c1a8bdb0f2f4642a414b196f04ad9853ed0c8f830", size = 156463 }, + { url = "https://files.pythonhosted.org/packages/36/2f/01f63896ddf22cbb0173ab51f54fde70b0208ca6c2f5e8416950977930e1/protobuf-4.25.2-cp310-abi3-win32.whl", hash = "sha256:b50c949608682b12efb0b2717f53256f03636af5f60ac0c1d900df6213910fd6", size = 392408, upload_time = "2024-01-10T19:37:23.466Z" }, + { url = "https://files.pythonhosted.org/packages/c1/00/c3ae19cabb36cfabc94ff0b102aac21b471c9f91a1357f8aafffb9efe8e0/protobuf-4.25.2-cp310-abi3-win_amd64.whl", hash = "sha256:8f62574857ee1de9f770baf04dde4165e30b15ad97ba03ceac65f760ff018ac9", size = 413397, upload_time = "2024-01-10T19:37:26.321Z" }, + { url = "https://files.pythonhosted.org/packages/b3/81/0017aefacf23273d4efd1154ef958a27eed9c177c4cc09d2d4ba398fb47f/protobuf-4.25.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:2db9f8fa64fbdcdc93767d3cf81e0f2aef176284071507e3ede160811502fd3d", size = 394159, upload_time = "2024-01-10T19:37:28.932Z" }, + { url = "https://files.pythonhosted.org/packages/23/17/405ba44f60a693dfe96c7a18e843707cffa0fcfad80bd8fc4f227f499ea5/protobuf-4.25.2-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:10894a2885b7175d3984f2be8d9850712c57d5e7587a2410720af8be56cdaf62", size = 293698, upload_time = "2024-01-10T19:37:30.666Z" }, + { url = "https://files.pythonhosted.org/packages/81/9e/63501b8d5b4e40c7260049836bd15ec3270c936e83bc57b85e4603cc212c/protobuf-4.25.2-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:fc381d1dd0516343f1440019cedf08a7405f791cd49eef4ae1ea06520bc1c020", size = 294609, upload_time = "2024-01-10T19:37:32.777Z" }, + { url = "https://files.pythonhosted.org/packages/ff/52/5d23df1fe3b368133ec3e2436fb3dd4ccedf44c8d5ac7f4a88087c75180b/protobuf-4.25.2-py3-none-any.whl", hash = "sha256:a8b7a98d4ce823303145bf3c1a8bdb0f2f4642a414b196f04ad9853ed0c8f830", size = 156463, upload_time = "2024-01-10T19:37:41.24Z" }, ] [[package]] name = "psutil" version = "5.9.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a0/d0/c9ae661a302931735237791f04cb7086ac244377f78692ba3b3eae3a9619/psutil-5.9.7.tar.gz", hash = "sha256:3f02134e82cfb5d089fddf20bb2e03fd5cd52395321d1c8458a9e58500ff417c", size = 498429 } +sdist = { url = "https://files.pythonhosted.org/packages/a0/d0/c9ae661a302931735237791f04cb7086ac244377f78692ba3b3eae3a9619/psutil-5.9.7.tar.gz", hash = "sha256:3f02134e82cfb5d089fddf20bb2e03fd5cd52395321d1c8458a9e58500ff417c", size = 498429, upload_time = "2023-12-17T11:25:21.22Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/63/86a4ccc640b4ee1193800f57bbd20b766853c0cdbdbb248a27cdfafe6cbf/psutil-5.9.7-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ea36cc62e69a13ec52b2f625c27527f6e4479bca2b340b7a452af55b34fcbe2e", size = 245972 }, - { url = "https://files.pythonhosted.org/packages/58/80/cc6666b3968646f2d94de66bbc63d701d501f4aa04de43dd7d1f5dc477dd/psutil-5.9.7-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1132704b876e58d277168cd729d64750633d5ff0183acf5b3c986b8466cd0284", size = 282514 }, - { url = "https://files.pythonhosted.org/packages/be/fa/f1f626620e3b47e6237dcc64cb8cc1472f139e99422e5b9fa5bbcf457f48/psutil-5.9.7-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8b7f07948f1304497ce4f4684881250cd859b16d06a1dc4d7941eeb6233bfe", size = 285469 }, - { url = "https://files.pythonhosted.org/packages/7c/b8/dc6ebfc030b47cccc5f5229eeb15e64142b4782796c3ce169ccd60b4d511/psutil-5.9.7-cp37-abi3-win32.whl", hash = "sha256:c727ca5a9b2dd5193b8644b9f0c883d54f1248310023b5ad3e92036c5e2ada68", size = 248406 }, - { url = "https://files.pythonhosted.org/packages/50/28/92b74d95dd991c837813ffac0c79a581a3d129eb0fa7c1dd616d9901e0f3/psutil-5.9.7-cp37-abi3-win_amd64.whl", hash = "sha256:f37f87e4d73b79e6c5e749440c3113b81d1ee7d26f21c19c47371ddea834f414", size = 252245 }, - { url = "https://files.pythonhosted.org/packages/ba/8a/000d0e80156f0b96c55bda6c60f5ed6543d7b5e893ccab83117e50de1400/psutil-5.9.7-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:032f4f2c909818c86cea4fe2cc407f1c0f0cde8e6c6d702b28b8ce0c0d143340", size = 246739 }, + { url = "https://files.pythonhosted.org/packages/6c/63/86a4ccc640b4ee1193800f57bbd20b766853c0cdbdbb248a27cdfafe6cbf/psutil-5.9.7-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ea36cc62e69a13ec52b2f625c27527f6e4479bca2b340b7a452af55b34fcbe2e", size = 245972, upload_time = "2023-12-17T11:25:48.202Z" }, + { url = "https://files.pythonhosted.org/packages/58/80/cc6666b3968646f2d94de66bbc63d701d501f4aa04de43dd7d1f5dc477dd/psutil-5.9.7-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1132704b876e58d277168cd729d64750633d5ff0183acf5b3c986b8466cd0284", size = 282514, upload_time = "2023-12-17T11:25:51.371Z" }, + { url = "https://files.pythonhosted.org/packages/be/fa/f1f626620e3b47e6237dcc64cb8cc1472f139e99422e5b9fa5bbcf457f48/psutil-5.9.7-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8b7f07948f1304497ce4f4684881250cd859b16d06a1dc4d7941eeb6233bfe", size = 285469, upload_time = "2023-12-17T11:25:54.25Z" }, + { url = "https://files.pythonhosted.org/packages/7c/b8/dc6ebfc030b47cccc5f5229eeb15e64142b4782796c3ce169ccd60b4d511/psutil-5.9.7-cp37-abi3-win32.whl", hash = "sha256:c727ca5a9b2dd5193b8644b9f0c883d54f1248310023b5ad3e92036c5e2ada68", size = 248406, upload_time = "2023-12-17T12:38:50.326Z" }, + { url = "https://files.pythonhosted.org/packages/50/28/92b74d95dd991c837813ffac0c79a581a3d129eb0fa7c1dd616d9901e0f3/psutil-5.9.7-cp37-abi3-win_amd64.whl", hash = "sha256:f37f87e4d73b79e6c5e749440c3113b81d1ee7d26f21c19c47371ddea834f414", size = 252245, upload_time = "2023-12-17T12:39:00.686Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8a/000d0e80156f0b96c55bda6c60f5ed6543d7b5e893ccab83117e50de1400/psutil-5.9.7-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:032f4f2c909818c86cea4fe2cc407f1c0f0cde8e6c6d702b28b8ce0c0d143340", size = 246739, upload_time = "2023-12-17T11:25:57.305Z" }, ] [[package]] name = "pycparser" version = "2.21" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/0b/95d387f5f4433cb0f53ff7ad859bd2c6051051cebbb564f139a999ab46de/pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206", size = 170877 } +sdist = { url = "https://files.pythonhosted.org/packages/5e/0b/95d387f5f4433cb0f53ff7ad859bd2c6051051cebbb564f139a999ab46de/pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206", size = 170877, upload_time = "2021-11-06T12:48:46.095Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/d5/5f610ebe421e85889f2e55e33b7f9a6795bd982198517d912eb1c76e1a53/pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", size = 118697 }, + { url = "https://files.pythonhosted.org/packages/62/d5/5f610ebe421e85889f2e55e33b7f9a6795bd982198517d912eb1c76e1a53/pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", size = 118697, upload_time = "2021-11-06T12:50:13.61Z" }, ] [[package]] @@ -1797,9 +1823,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/10/2e/ca897f093ee6c5f3b0bee123ee4465c50e75431c3d5b6a3b44a47134e891/pydantic-2.11.3.tar.gz", hash = "sha256:7471657138c16adad9322fe3070c0116dd6c3ad8d649300e3cbdfe91f4db4ec3", size = 785513 } +sdist = { url = "https://files.pythonhosted.org/packages/10/2e/ca897f093ee6c5f3b0bee123ee4465c50e75431c3d5b6a3b44a47134e891/pydantic-2.11.3.tar.gz", hash = "sha256:7471657138c16adad9322fe3070c0116dd6c3ad8d649300e3cbdfe91f4db4ec3", size = 785513, upload_time = "2025-04-08T13:27:06.399Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/1d/407b29780a289868ed696d1616f4aad49d6388e5a77f567dcd2629dcd7b8/pydantic-2.11.3-py3-none-any.whl", hash = "sha256:a082753436a07f9ba1289c6ffa01cd93db3548776088aa917cc43b63f68fa60f", size = 443591 }, + { url = "https://files.pythonhosted.org/packages/b0/1d/407b29780a289868ed696d1616f4aad49d6388e5a77f567dcd2629dcd7b8/pydantic-2.11.3-py3-none-any.whl", hash = "sha256:a082753436a07f9ba1289c6ffa01cd93db3548776088aa917cc43b63f68fa60f", size = 443591, upload_time = "2025-04-08T13:27:03.789Z" }, ] [[package]] @@ -1809,124 +1835,125 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/17/19/ed6a078a5287aea7922de6841ef4c06157931622c89c2a47940837b5eecd/pydantic_core-2.33.1.tar.gz", hash = "sha256:bcc9c6fdb0ced789245b02b7d6603e17d1563064ddcfc36f046b61c0c05dd9df", size = 434395 } +sdist = { url = "https://files.pythonhosted.org/packages/17/19/ed6a078a5287aea7922de6841ef4c06157931622c89c2a47940837b5eecd/pydantic_core-2.33.1.tar.gz", hash = "sha256:bcc9c6fdb0ced789245b02b7d6603e17d1563064ddcfc36f046b61c0c05dd9df", size = 434395, upload_time = "2025-04-02T09:49:41.8Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/ea/5f572806ab4d4223d11551af814d243b0e3e02cc6913def4d1fe4a5ca41c/pydantic_core-2.33.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3077cfdb6125cc8dab61b155fdd714663e401f0e6883f9632118ec12cf42df26", size = 2044021 }, - { url = "https://files.pythonhosted.org/packages/8c/d1/f86cc96d2aa80e3881140d16d12ef2b491223f90b28b9a911346c04ac359/pydantic_core-2.33.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8ffab8b2908d152e74862d276cf5017c81a2f3719f14e8e3e8d6b83fda863927", size = 1861742 }, - { url = "https://files.pythonhosted.org/packages/37/08/fbd2cd1e9fc735a0df0142fac41c114ad9602d1c004aea340169ae90973b/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5183e4f6a2d468787243ebcd70cf4098c247e60d73fb7d68d5bc1e1beaa0c4db", size = 1910414 }, - { url = "https://files.pythonhosted.org/packages/7f/73/3ac217751decbf8d6cb9443cec9b9eb0130eeada6ae56403e11b486e277e/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:398a38d323f37714023be1e0285765f0a27243a8b1506b7b7de87b647b517e48", size = 1996848 }, - { url = "https://files.pythonhosted.org/packages/9a/f5/5c26b265cdcff2661e2520d2d1e9db72d117ea00eb41e00a76efe68cb009/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87d3776f0001b43acebfa86f8c64019c043b55cc5a6a2e313d728b5c95b46969", size = 2141055 }, - { url = "https://files.pythonhosted.org/packages/5d/14/a9c3cee817ef2f8347c5ce0713e91867a0dceceefcb2973942855c917379/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c566dd9c5f63d22226409553531f89de0cac55397f2ab8d97d6f06cfce6d947e", size = 2753806 }, - { url = "https://files.pythonhosted.org/packages/f2/68/866ce83a51dd37e7c604ce0050ff6ad26de65a7799df89f4db87dd93d1d6/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d5f3acc81452c56895e90643a625302bd6be351e7010664151cc55b7b97f89", size = 2007777 }, - { url = "https://files.pythonhosted.org/packages/b6/a8/36771f4404bb3e49bd6d4344da4dede0bf89cc1e01f3b723c47248a3761c/pydantic_core-2.33.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d3a07fadec2a13274a8d861d3d37c61e97a816beae717efccaa4b36dfcaadcde", size = 2122803 }, - { url = "https://files.pythonhosted.org/packages/18/9c/730a09b2694aa89360d20756369822d98dc2f31b717c21df33b64ffd1f50/pydantic_core-2.33.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f99aeda58dce827f76963ee87a0ebe75e648c72ff9ba1174a253f6744f518f65", size = 2086755 }, - { url = "https://files.pythonhosted.org/packages/54/8e/2dccd89602b5ec31d1c58138d02340ecb2ebb8c2cac3cc66b65ce3edb6ce/pydantic_core-2.33.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:902dbc832141aa0ec374f4310f1e4e7febeebc3256f00dc359a9ac3f264a45dc", size = 2257358 }, - { url = "https://files.pythonhosted.org/packages/d1/9c/126e4ac1bfad8a95a9837acdd0963695d69264179ba4ede8b8c40d741702/pydantic_core-2.33.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fe44d56aa0b00d66640aa84a3cbe80b7a3ccdc6f0b1ca71090696a6d4777c091", size = 2257916 }, - { url = "https://files.pythonhosted.org/packages/7d/ba/91eea2047e681a6853c81c20aeca9dcdaa5402ccb7404a2097c2adf9d038/pydantic_core-2.33.1-cp310-cp310-win32.whl", hash = "sha256:ed3eb16d51257c763539bde21e011092f127a2202692afaeaccb50db55a31383", size = 1923823 }, - { url = "https://files.pythonhosted.org/packages/94/c0/fcdf739bf60d836a38811476f6ecd50374880b01e3014318b6e809ddfd52/pydantic_core-2.33.1-cp310-cp310-win_amd64.whl", hash = "sha256:694ad99a7f6718c1a498dc170ca430687a39894a60327f548e02a9c7ee4b6504", size = 1952494 }, - { url = "https://files.pythonhosted.org/packages/d6/7f/c6298830cb780c46b4f46bb24298d01019ffa4d21769f39b908cd14bbd50/pydantic_core-2.33.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6e966fc3caaf9f1d96b349b0341c70c8d6573bf1bac7261f7b0ba88f96c56c24", size = 2044224 }, - { url = "https://files.pythonhosted.org/packages/a8/65/6ab3a536776cad5343f625245bd38165d6663256ad43f3a200e5936afd6c/pydantic_core-2.33.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bfd0adeee563d59c598ceabddf2c92eec77abcb3f4a391b19aa7366170bd9e30", size = 1858845 }, - { url = "https://files.pythonhosted.org/packages/e9/15/9a22fd26ba5ee8c669d4b8c9c244238e940cd5d818649603ca81d1c69861/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91815221101ad3c6b507804178a7bb5cb7b2ead9ecd600041669c8d805ebd595", size = 1910029 }, - { url = "https://files.pythonhosted.org/packages/d5/33/8cb1a62818974045086f55f604044bf35b9342900318f9a2a029a1bec460/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9fea9c1869bb4742d174a57b4700c6dadea951df8b06de40c2fedb4f02931c2e", size = 1997784 }, - { url = "https://files.pythonhosted.org/packages/c0/ca/49958e4df7715c71773e1ea5be1c74544923d10319173264e6db122543f9/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d20eb4861329bb2484c021b9d9a977566ab16d84000a57e28061151c62b349a", size = 2141075 }, - { url = "https://files.pythonhosted.org/packages/7b/a6/0b3a167a9773c79ba834b959b4e18c3ae9216b8319bd8422792abc8a41b1/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb935c5591573ae3201640579f30128ccc10739b45663f93c06796854405505", size = 2745849 }, - { url = "https://files.pythonhosted.org/packages/0b/60/516484135173aa9e5861d7a0663dce82e4746d2e7f803627d8c25dfa5578/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c964fd24e6166420d18fb53996d8c9fd6eac9bf5ae3ec3d03015be4414ce497f", size = 2005794 }, - { url = "https://files.pythonhosted.org/packages/86/70/05b1eb77459ad47de00cf78ee003016da0cedf8b9170260488d7c21e9181/pydantic_core-2.33.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:681d65e9011f7392db5aa002b7423cc442d6a673c635668c227c6c8d0e5a4f77", size = 2123237 }, - { url = "https://files.pythonhosted.org/packages/c7/57/12667a1409c04ae7dc95d3b43158948eb0368e9c790be8b095cb60611459/pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e100c52f7355a48413e2999bfb4e139d2977a904495441b374f3d4fb4a170961", size = 2086351 }, - { url = "https://files.pythonhosted.org/packages/57/61/cc6d1d1c1664b58fdd6ecc64c84366c34ec9b606aeb66cafab6f4088974c/pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:048831bd363490be79acdd3232f74a0e9951b11b2b4cc058aeb72b22fdc3abe1", size = 2258914 }, - { url = "https://files.pythonhosted.org/packages/d1/0a/edb137176a1f5419b2ddee8bde6a0a548cfa3c74f657f63e56232df8de88/pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bdc84017d28459c00db6f918a7272a5190bec3090058334e43a76afb279eac7c", size = 2257385 }, - { url = "https://files.pythonhosted.org/packages/26/3c/48ca982d50e4b0e1d9954919c887bdc1c2b462801bf408613ccc641b3daa/pydantic_core-2.33.1-cp311-cp311-win32.whl", hash = "sha256:32cd11c5914d1179df70406427097c7dcde19fddf1418c787540f4b730289896", size = 1923765 }, - { url = "https://files.pythonhosted.org/packages/33/cd/7ab70b99e5e21559f5de38a0928ea84e6f23fdef2b0d16a6feaf942b003c/pydantic_core-2.33.1-cp311-cp311-win_amd64.whl", hash = "sha256:2ea62419ba8c397e7da28a9170a16219d310d2cf4970dbc65c32faf20d828c83", size = 1950688 }, - { url = "https://files.pythonhosted.org/packages/4b/ae/db1fc237b82e2cacd379f63e3335748ab88b5adde98bf7544a1b1bd10a84/pydantic_core-2.33.1-cp311-cp311-win_arm64.whl", hash = "sha256:fc903512177361e868bc1f5b80ac8c8a6e05fcdd574a5fb5ffeac5a9982b9e89", size = 1908185 }, - { url = "https://files.pythonhosted.org/packages/c8/ce/3cb22b07c29938f97ff5f5bb27521f95e2ebec399b882392deb68d6c440e/pydantic_core-2.33.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1293d7febb995e9d3ec3ea09caf1a26214eec45b0f29f6074abb004723fc1de8", size = 2026640 }, - { url = "https://files.pythonhosted.org/packages/19/78/f381d643b12378fee782a72126ec5d793081ef03791c28a0fd542a5bee64/pydantic_core-2.33.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:99b56acd433386c8f20be5c4000786d1e7ca0523c8eefc995d14d79c7a081498", size = 1852649 }, - { url = "https://files.pythonhosted.org/packages/9d/2b/98a37b80b15aac9eb2c6cfc6dbd35e5058a352891c5cce3a8472d77665a6/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35a5ec3fa8c2fe6c53e1b2ccc2454398f95d5393ab398478f53e1afbbeb4d939", size = 1892472 }, - { url = "https://files.pythonhosted.org/packages/4e/d4/3c59514e0f55a161004792b9ff3039da52448f43f5834f905abef9db6e4a/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b172f7b9d2f3abc0efd12e3386f7e48b576ef309544ac3a63e5e9cdd2e24585d", size = 1977509 }, - { url = "https://files.pythonhosted.org/packages/a9/b6/c2c7946ef70576f79a25db59a576bce088bdc5952d1b93c9789b091df716/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9097b9f17f91eea659b9ec58148c0747ec354a42f7389b9d50701610d86f812e", size = 2128702 }, - { url = "https://files.pythonhosted.org/packages/88/fe/65a880f81e3f2a974312b61f82a03d85528f89a010ce21ad92f109d94deb/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc77ec5b7e2118b152b0d886c7514a4653bcb58c6b1d760134a9fab915f777b3", size = 2679428 }, - { url = "https://files.pythonhosted.org/packages/6f/ff/4459e4146afd0462fb483bb98aa2436d69c484737feaceba1341615fb0ac/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3d15245b08fa4a84cefc6c9222e6f37c98111c8679fbd94aa145f9a0ae23d", size = 2008753 }, - { url = "https://files.pythonhosted.org/packages/7c/76/1c42e384e8d78452ededac8b583fe2550c84abfef83a0552e0e7478ccbc3/pydantic_core-2.33.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef99779001d7ac2e2461d8ab55d3373fe7315caefdbecd8ced75304ae5a6fc6b", size = 2114849 }, - { url = "https://files.pythonhosted.org/packages/00/72/7d0cf05095c15f7ffe0eb78914b166d591c0eed72f294da68378da205101/pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fc6bf8869e193855e8d91d91f6bf59699a5cdfaa47a404e278e776dd7f168b39", size = 2069541 }, - { url = "https://files.pythonhosted.org/packages/b3/69/94a514066bb7d8be499aa764926937409d2389c09be0b5107a970286ef81/pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:b1caa0bc2741b043db7823843e1bde8aaa58a55a58fda06083b0569f8b45693a", size = 2239225 }, - { url = "https://files.pythonhosted.org/packages/84/b0/e390071eadb44b41f4f54c3cef64d8bf5f9612c92686c9299eaa09e267e2/pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ec259f62538e8bf364903a7d0d0239447059f9434b284f5536e8402b7dd198db", size = 2248373 }, - { url = "https://files.pythonhosted.org/packages/d6/b2/288b3579ffc07e92af66e2f1a11be3b056fe1214aab314748461f21a31c3/pydantic_core-2.33.1-cp312-cp312-win32.whl", hash = "sha256:e14f369c98a7c15772b9da98987f58e2b509a93235582838bd0d1d8c08b68fda", size = 1907034 }, - { url = "https://files.pythonhosted.org/packages/02/28/58442ad1c22b5b6742b992ba9518420235adced665513868f99a1c2638a5/pydantic_core-2.33.1-cp312-cp312-win_amd64.whl", hash = "sha256:1c607801d85e2e123357b3893f82c97a42856192997b95b4d8325deb1cd0c5f4", size = 1956848 }, - { url = "https://files.pythonhosted.org/packages/a1/eb/f54809b51c7e2a1d9f439f158b8dd94359321abcc98767e16fc48ae5a77e/pydantic_core-2.33.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d13f0276806ee722e70a1c93da19748594f19ac4299c7e41237fc791d1861ea", size = 1903986 }, - { url = "https://files.pythonhosted.org/packages/7a/24/eed3466a4308d79155f1cdd5c7432c80ddcc4530ba8623b79d5ced021641/pydantic_core-2.33.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70af6a21237b53d1fe7b9325b20e65cbf2f0a848cf77bed492b029139701e66a", size = 2033551 }, - { url = "https://files.pythonhosted.org/packages/ab/14/df54b1a0bc9b6ded9b758b73139d2c11b4e8eb43e8ab9c5847c0a2913ada/pydantic_core-2.33.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:282b3fe1bbbe5ae35224a0dbd05aed9ccabccd241e8e6b60370484234b456266", size = 1852785 }, - { url = "https://files.pythonhosted.org/packages/fa/96/e275f15ff3d34bb04b0125d9bc8848bf69f25d784d92a63676112451bfb9/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b315e596282bbb5822d0c7ee9d255595bd7506d1cb20c2911a4da0b970187d3", size = 1897758 }, - { url = "https://files.pythonhosted.org/packages/b7/d8/96bc536e975b69e3a924b507d2a19aedbf50b24e08c80fb00e35f9baaed8/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1dfae24cf9921875ca0ca6a8ecb4bb2f13c855794ed0d468d6abbec6e6dcd44a", size = 1986109 }, - { url = "https://files.pythonhosted.org/packages/90/72/ab58e43ce7e900b88cb571ed057b2fcd0e95b708a2e0bed475b10130393e/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6dd8ecfde08d8bfadaea669e83c63939af76f4cf5538a72597016edfa3fad516", size = 2129159 }, - { url = "https://files.pythonhosted.org/packages/dc/3f/52d85781406886c6870ac995ec0ba7ccc028b530b0798c9080531b409fdb/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f593494876eae852dc98c43c6f260f45abdbfeec9e4324e31a481d948214764", size = 2680222 }, - { url = "https://files.pythonhosted.org/packages/f4/56/6e2ef42f363a0eec0fd92f74a91e0ac48cd2e49b695aac1509ad81eee86a/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:948b73114f47fd7016088e5186d13faf5e1b2fe83f5e320e371f035557fd264d", size = 2006980 }, - { url = "https://files.pythonhosted.org/packages/4c/c0/604536c4379cc78359f9ee0aa319f4aedf6b652ec2854953f5a14fc38c5a/pydantic_core-2.33.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e11f3864eb516af21b01e25fac915a82e9ddad3bb0fb9e95a246067398b435a4", size = 2120840 }, - { url = "https://files.pythonhosted.org/packages/1f/46/9eb764814f508f0edfb291a0f75d10854d78113fa13900ce13729aaec3ae/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:549150be302428b56fdad0c23c2741dcdb5572413776826c965619a25d9c6bde", size = 2072518 }, - { url = "https://files.pythonhosted.org/packages/42/e3/fb6b2a732b82d1666fa6bf53e3627867ea3131c5f39f98ce92141e3e3dc1/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:495bc156026efafd9ef2d82372bd38afce78ddd82bf28ef5276c469e57c0c83e", size = 2248025 }, - { url = "https://files.pythonhosted.org/packages/5c/9d/fbe8fe9d1aa4dac88723f10a921bc7418bd3378a567cb5e21193a3c48b43/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ec79de2a8680b1a67a07490bddf9636d5c2fab609ba8c57597e855fa5fa4dacd", size = 2254991 }, - { url = "https://files.pythonhosted.org/packages/aa/99/07e2237b8a66438d9b26482332cda99a9acccb58d284af7bc7c946a42fd3/pydantic_core-2.33.1-cp313-cp313-win32.whl", hash = "sha256:ee12a7be1742f81b8a65b36c6921022301d466b82d80315d215c4c691724986f", size = 1915262 }, - { url = "https://files.pythonhosted.org/packages/8a/f4/e457a7849beeed1e5defbcf5051c6f7b3c91a0624dd31543a64fc9adcf52/pydantic_core-2.33.1-cp313-cp313-win_amd64.whl", hash = "sha256:ede9b407e39949d2afc46385ce6bd6e11588660c26f80576c11c958e6647bc40", size = 1956626 }, - { url = "https://files.pythonhosted.org/packages/20/d0/e8d567a7cff7b04e017ae164d98011f1e1894269fe8e90ea187a3cbfb562/pydantic_core-2.33.1-cp313-cp313-win_arm64.whl", hash = "sha256:aa687a23d4b7871a00e03ca96a09cad0f28f443690d300500603bd0adba4b523", size = 1909590 }, - { url = "https://files.pythonhosted.org/packages/ef/fd/24ea4302d7a527d672c5be06e17df16aabfb4e9fdc6e0b345c21580f3d2a/pydantic_core-2.33.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:401d7b76e1000d0dd5538e6381d28febdcacb097c8d340dde7d7fc6e13e9f95d", size = 1812963 }, - { url = "https://files.pythonhosted.org/packages/5f/95/4fbc2ecdeb5c1c53f1175a32d870250194eb2fdf6291b795ab08c8646d5d/pydantic_core-2.33.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7aeb055a42d734c0255c9e489ac67e75397d59c6fbe60d155851e9782f276a9c", size = 1986896 }, - { url = "https://files.pythonhosted.org/packages/71/ae/fe31e7f4a62431222d8f65a3bd02e3fa7e6026d154a00818e6d30520ea77/pydantic_core-2.33.1-cp313-cp313t-win_amd64.whl", hash = "sha256:338ea9b73e6e109f15ab439e62cb3b78aa752c7fd9536794112e14bee02c8d18", size = 1931810 }, - { url = "https://files.pythonhosted.org/packages/9c/c7/8b311d5adb0fe00a93ee9b4e92a02b0ec08510e9838885ef781ccbb20604/pydantic_core-2.33.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c834f54f8f4640fd7e4b193f80eb25a0602bba9e19b3cd2fc7ffe8199f5ae02", size = 2041659 }, - { url = "https://files.pythonhosted.org/packages/8a/d6/4f58d32066a9e26530daaf9adc6664b01875ae0691570094968aaa7b8fcc/pydantic_core-2.33.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:049e0de24cf23766f12cc5cc71d8abc07d4a9deb9061b334b62093dedc7cb068", size = 1873294 }, - { url = "https://files.pythonhosted.org/packages/f7/3f/53cc9c45d9229da427909c751f8ed2bf422414f7664ea4dde2d004f596ba/pydantic_core-2.33.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a28239037b3d6f16916a4c831a5a0eadf856bdd6d2e92c10a0da3a59eadcf3e", size = 1903771 }, - { url = "https://files.pythonhosted.org/packages/f0/49/bf0783279ce674eb9903fb9ae43f6c614cb2f1c4951370258823f795368b/pydantic_core-2.33.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d3da303ab5f378a268fa7d45f37d7d85c3ec19769f28d2cc0c61826a8de21fe", size = 2083558 }, - { url = "https://files.pythonhosted.org/packages/9c/5b/0d998367687f986c7d8484a2c476d30f07bf5b8b1477649a6092bd4c540e/pydantic_core-2.33.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:25626fb37b3c543818c14821afe0fd3830bc327a43953bc88db924b68c5723f1", size = 2118038 }, - { url = "https://files.pythonhosted.org/packages/b3/33/039287d410230ee125daee57373ac01940d3030d18dba1c29cd3089dc3ca/pydantic_core-2.33.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3ab2d36e20fbfcce8f02d73c33a8a7362980cff717926bbae030b93ae46b56c7", size = 2079315 }, - { url = "https://files.pythonhosted.org/packages/1f/85/6d8b2646d99c062d7da2d0ab2faeb0d6ca9cca4c02da6076376042a20da3/pydantic_core-2.33.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:2f9284e11c751b003fd4215ad92d325d92c9cb19ee6729ebd87e3250072cdcde", size = 2249063 }, - { url = "https://files.pythonhosted.org/packages/17/d7/c37d208d5738f7b9ad8f22ae8a727d88ebf9c16c04ed2475122cc3f7224a/pydantic_core-2.33.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:048c01eee07d37cbd066fc512b9d8b5ea88ceeb4e629ab94b3e56965ad655add", size = 2254631 }, - { url = "https://files.pythonhosted.org/packages/13/e0/bafa46476d328e4553b85ab9b2f7409e7aaef0ce4c937c894821c542d347/pydantic_core-2.33.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5ccd429694cf26af7997595d627dd2637e7932214486f55b8a357edaac9dae8c", size = 2080877 }, - { url = "https://files.pythonhosted.org/packages/0b/76/1794e440c1801ed35415238d2c728f26cd12695df9057154ad768b7b991c/pydantic_core-2.33.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3a371dc00282c4b84246509a5ddc808e61b9864aa1eae9ecc92bb1268b82db4a", size = 2042858 }, - { url = "https://files.pythonhosted.org/packages/73/b4/9cd7b081fb0b1b4f8150507cd59d27b275c3e22ad60b35cb19ea0977d9b9/pydantic_core-2.33.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f59295ecc75a1788af8ba92f2e8c6eeaa5a94c22fc4d151e8d9638814f85c8fc", size = 1873745 }, - { url = "https://files.pythonhosted.org/packages/e1/d7/9ddb7575d4321e40d0363903c2576c8c0c3280ebea137777e5ab58d723e3/pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08530b8ac922003033f399128505f513e30ca770527cc8bbacf75a84fcc2c74b", size = 1904188 }, - { url = "https://files.pythonhosted.org/packages/d1/a8/3194ccfe461bb08da19377ebec8cb4f13c9bd82e13baebc53c5c7c39a029/pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bae370459da6a5466978c0eacf90690cb57ec9d533f8e63e564ef3822bfa04fe", size = 2083479 }, - { url = "https://files.pythonhosted.org/packages/42/c7/84cb569555d7179ca0b3f838cef08f66f7089b54432f5b8599aac6e9533e/pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e3de2777e3b9f4d603112f78006f4ae0acb936e95f06da6cb1a45fbad6bdb4b5", size = 2118415 }, - { url = "https://files.pythonhosted.org/packages/3b/67/72abb8c73e0837716afbb58a59cc9e3ae43d1aa8677f3b4bc72c16142716/pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a64e81e8cba118e108d7126362ea30e021291b7805d47e4896e52c791be2761", size = 2079623 }, - { url = "https://files.pythonhosted.org/packages/0b/cd/c59707e35a47ba4cbbf153c3f7c56420c58653b5801b055dc52cccc8e2dc/pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:52928d8c1b6bda03cc6d811e8923dffc87a2d3c8b3bfd2ce16471c7147a24850", size = 2250175 }, - { url = "https://files.pythonhosted.org/packages/84/32/e4325a6676b0bed32d5b084566ec86ed7fd1e9bcbfc49c578b1755bde920/pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1b30d92c9412beb5ac6b10a3eb7ef92ccb14e3f2a8d7732e2d739f58b3aa7544", size = 2254674 }, - { url = "https://files.pythonhosted.org/packages/12/6f/5596dc418f2e292ffc661d21931ab34591952e2843e7168ea5a52591f6ff/pydantic_core-2.33.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f995719707e0e29f0f41a8aa3bcea6e761a36c9136104d3189eafb83f5cec5e5", size = 2080951 }, + { url = "https://files.pythonhosted.org/packages/38/ea/5f572806ab4d4223d11551af814d243b0e3e02cc6913def4d1fe4a5ca41c/pydantic_core-2.33.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3077cfdb6125cc8dab61b155fdd714663e401f0e6883f9632118ec12cf42df26", size = 2044021, upload_time = "2025-04-02T09:46:45.065Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d1/f86cc96d2aa80e3881140d16d12ef2b491223f90b28b9a911346c04ac359/pydantic_core-2.33.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8ffab8b2908d152e74862d276cf5017c81a2f3719f14e8e3e8d6b83fda863927", size = 1861742, upload_time = "2025-04-02T09:46:46.684Z" }, + { url = "https://files.pythonhosted.org/packages/37/08/fbd2cd1e9fc735a0df0142fac41c114ad9602d1c004aea340169ae90973b/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5183e4f6a2d468787243ebcd70cf4098c247e60d73fb7d68d5bc1e1beaa0c4db", size = 1910414, upload_time = "2025-04-02T09:46:48.263Z" }, + { url = "https://files.pythonhosted.org/packages/7f/73/3ac217751decbf8d6cb9443cec9b9eb0130eeada6ae56403e11b486e277e/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:398a38d323f37714023be1e0285765f0a27243a8b1506b7b7de87b647b517e48", size = 1996848, upload_time = "2025-04-02T09:46:49.441Z" }, + { url = "https://files.pythonhosted.org/packages/9a/f5/5c26b265cdcff2661e2520d2d1e9db72d117ea00eb41e00a76efe68cb009/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87d3776f0001b43acebfa86f8c64019c043b55cc5a6a2e313d728b5c95b46969", size = 2141055, upload_time = "2025-04-02T09:46:50.602Z" }, + { url = "https://files.pythonhosted.org/packages/5d/14/a9c3cee817ef2f8347c5ce0713e91867a0dceceefcb2973942855c917379/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c566dd9c5f63d22226409553531f89de0cac55397f2ab8d97d6f06cfce6d947e", size = 2753806, upload_time = "2025-04-02T09:46:52.116Z" }, + { url = "https://files.pythonhosted.org/packages/f2/68/866ce83a51dd37e7c604ce0050ff6ad26de65a7799df89f4db87dd93d1d6/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d5f3acc81452c56895e90643a625302bd6be351e7010664151cc55b7b97f89", size = 2007777, upload_time = "2025-04-02T09:46:53.675Z" }, + { url = "https://files.pythonhosted.org/packages/b6/a8/36771f4404bb3e49bd6d4344da4dede0bf89cc1e01f3b723c47248a3761c/pydantic_core-2.33.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d3a07fadec2a13274a8d861d3d37c61e97a816beae717efccaa4b36dfcaadcde", size = 2122803, upload_time = "2025-04-02T09:46:55.789Z" }, + { url = "https://files.pythonhosted.org/packages/18/9c/730a09b2694aa89360d20756369822d98dc2f31b717c21df33b64ffd1f50/pydantic_core-2.33.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f99aeda58dce827f76963ee87a0ebe75e648c72ff9ba1174a253f6744f518f65", size = 2086755, upload_time = "2025-04-02T09:46:56.956Z" }, + { url = "https://files.pythonhosted.org/packages/54/8e/2dccd89602b5ec31d1c58138d02340ecb2ebb8c2cac3cc66b65ce3edb6ce/pydantic_core-2.33.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:902dbc832141aa0ec374f4310f1e4e7febeebc3256f00dc359a9ac3f264a45dc", size = 2257358, upload_time = "2025-04-02T09:46:58.445Z" }, + { url = "https://files.pythonhosted.org/packages/d1/9c/126e4ac1bfad8a95a9837acdd0963695d69264179ba4ede8b8c40d741702/pydantic_core-2.33.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fe44d56aa0b00d66640aa84a3cbe80b7a3ccdc6f0b1ca71090696a6d4777c091", size = 2257916, upload_time = "2025-04-02T09:46:59.726Z" }, + { url = "https://files.pythonhosted.org/packages/7d/ba/91eea2047e681a6853c81c20aeca9dcdaa5402ccb7404a2097c2adf9d038/pydantic_core-2.33.1-cp310-cp310-win32.whl", hash = "sha256:ed3eb16d51257c763539bde21e011092f127a2202692afaeaccb50db55a31383", size = 1923823, upload_time = "2025-04-02T09:47:01.278Z" }, + { url = "https://files.pythonhosted.org/packages/94/c0/fcdf739bf60d836a38811476f6ecd50374880b01e3014318b6e809ddfd52/pydantic_core-2.33.1-cp310-cp310-win_amd64.whl", hash = "sha256:694ad99a7f6718c1a498dc170ca430687a39894a60327f548e02a9c7ee4b6504", size = 1952494, upload_time = "2025-04-02T09:47:02.976Z" }, + { url = "https://files.pythonhosted.org/packages/d6/7f/c6298830cb780c46b4f46bb24298d01019ffa4d21769f39b908cd14bbd50/pydantic_core-2.33.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6e966fc3caaf9f1d96b349b0341c70c8d6573bf1bac7261f7b0ba88f96c56c24", size = 2044224, upload_time = "2025-04-02T09:47:04.199Z" }, + { url = "https://files.pythonhosted.org/packages/a8/65/6ab3a536776cad5343f625245bd38165d6663256ad43f3a200e5936afd6c/pydantic_core-2.33.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bfd0adeee563d59c598ceabddf2c92eec77abcb3f4a391b19aa7366170bd9e30", size = 1858845, upload_time = "2025-04-02T09:47:05.686Z" }, + { url = "https://files.pythonhosted.org/packages/e9/15/9a22fd26ba5ee8c669d4b8c9c244238e940cd5d818649603ca81d1c69861/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91815221101ad3c6b507804178a7bb5cb7b2ead9ecd600041669c8d805ebd595", size = 1910029, upload_time = "2025-04-02T09:47:07.042Z" }, + { url = "https://files.pythonhosted.org/packages/d5/33/8cb1a62818974045086f55f604044bf35b9342900318f9a2a029a1bec460/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9fea9c1869bb4742d174a57b4700c6dadea951df8b06de40c2fedb4f02931c2e", size = 1997784, upload_time = "2025-04-02T09:47:08.63Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ca/49958e4df7715c71773e1ea5be1c74544923d10319173264e6db122543f9/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d20eb4861329bb2484c021b9d9a977566ab16d84000a57e28061151c62b349a", size = 2141075, upload_time = "2025-04-02T09:47:10.267Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a6/0b3a167a9773c79ba834b959b4e18c3ae9216b8319bd8422792abc8a41b1/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb935c5591573ae3201640579f30128ccc10739b45663f93c06796854405505", size = 2745849, upload_time = "2025-04-02T09:47:11.724Z" }, + { url = "https://files.pythonhosted.org/packages/0b/60/516484135173aa9e5861d7a0663dce82e4746d2e7f803627d8c25dfa5578/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c964fd24e6166420d18fb53996d8c9fd6eac9bf5ae3ec3d03015be4414ce497f", size = 2005794, upload_time = "2025-04-02T09:47:13.099Z" }, + { url = "https://files.pythonhosted.org/packages/86/70/05b1eb77459ad47de00cf78ee003016da0cedf8b9170260488d7c21e9181/pydantic_core-2.33.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:681d65e9011f7392db5aa002b7423cc442d6a673c635668c227c6c8d0e5a4f77", size = 2123237, upload_time = "2025-04-02T09:47:14.355Z" }, + { url = "https://files.pythonhosted.org/packages/c7/57/12667a1409c04ae7dc95d3b43158948eb0368e9c790be8b095cb60611459/pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e100c52f7355a48413e2999bfb4e139d2977a904495441b374f3d4fb4a170961", size = 2086351, upload_time = "2025-04-02T09:47:15.676Z" }, + { url = "https://files.pythonhosted.org/packages/57/61/cc6d1d1c1664b58fdd6ecc64c84366c34ec9b606aeb66cafab6f4088974c/pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:048831bd363490be79acdd3232f74a0e9951b11b2b4cc058aeb72b22fdc3abe1", size = 2258914, upload_time = "2025-04-02T09:47:17Z" }, + { url = "https://files.pythonhosted.org/packages/d1/0a/edb137176a1f5419b2ddee8bde6a0a548cfa3c74f657f63e56232df8de88/pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bdc84017d28459c00db6f918a7272a5190bec3090058334e43a76afb279eac7c", size = 2257385, upload_time = "2025-04-02T09:47:18.631Z" }, + { url = "https://files.pythonhosted.org/packages/26/3c/48ca982d50e4b0e1d9954919c887bdc1c2b462801bf408613ccc641b3daa/pydantic_core-2.33.1-cp311-cp311-win32.whl", hash = "sha256:32cd11c5914d1179df70406427097c7dcde19fddf1418c787540f4b730289896", size = 1923765, upload_time = "2025-04-02T09:47:20.34Z" }, + { url = "https://files.pythonhosted.org/packages/33/cd/7ab70b99e5e21559f5de38a0928ea84e6f23fdef2b0d16a6feaf942b003c/pydantic_core-2.33.1-cp311-cp311-win_amd64.whl", hash = "sha256:2ea62419ba8c397e7da28a9170a16219d310d2cf4970dbc65c32faf20d828c83", size = 1950688, upload_time = "2025-04-02T09:47:22.029Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ae/db1fc237b82e2cacd379f63e3335748ab88b5adde98bf7544a1b1bd10a84/pydantic_core-2.33.1-cp311-cp311-win_arm64.whl", hash = "sha256:fc903512177361e868bc1f5b80ac8c8a6e05fcdd574a5fb5ffeac5a9982b9e89", size = 1908185, upload_time = "2025-04-02T09:47:23.385Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ce/3cb22b07c29938f97ff5f5bb27521f95e2ebec399b882392deb68d6c440e/pydantic_core-2.33.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1293d7febb995e9d3ec3ea09caf1a26214eec45b0f29f6074abb004723fc1de8", size = 2026640, upload_time = "2025-04-02T09:47:25.394Z" }, + { url = "https://files.pythonhosted.org/packages/19/78/f381d643b12378fee782a72126ec5d793081ef03791c28a0fd542a5bee64/pydantic_core-2.33.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:99b56acd433386c8f20be5c4000786d1e7ca0523c8eefc995d14d79c7a081498", size = 1852649, upload_time = "2025-04-02T09:47:27.417Z" }, + { url = "https://files.pythonhosted.org/packages/9d/2b/98a37b80b15aac9eb2c6cfc6dbd35e5058a352891c5cce3a8472d77665a6/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35a5ec3fa8c2fe6c53e1b2ccc2454398f95d5393ab398478f53e1afbbeb4d939", size = 1892472, upload_time = "2025-04-02T09:47:29.006Z" }, + { url = "https://files.pythonhosted.org/packages/4e/d4/3c59514e0f55a161004792b9ff3039da52448f43f5834f905abef9db6e4a/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b172f7b9d2f3abc0efd12e3386f7e48b576ef309544ac3a63e5e9cdd2e24585d", size = 1977509, upload_time = "2025-04-02T09:47:33.464Z" }, + { url = "https://files.pythonhosted.org/packages/a9/b6/c2c7946ef70576f79a25db59a576bce088bdc5952d1b93c9789b091df716/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9097b9f17f91eea659b9ec58148c0747ec354a42f7389b9d50701610d86f812e", size = 2128702, upload_time = "2025-04-02T09:47:34.812Z" }, + { url = "https://files.pythonhosted.org/packages/88/fe/65a880f81e3f2a974312b61f82a03d85528f89a010ce21ad92f109d94deb/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc77ec5b7e2118b152b0d886c7514a4653bcb58c6b1d760134a9fab915f777b3", size = 2679428, upload_time = "2025-04-02T09:47:37.315Z" }, + { url = "https://files.pythonhosted.org/packages/6f/ff/4459e4146afd0462fb483bb98aa2436d69c484737feaceba1341615fb0ac/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3d15245b08fa4a84cefc6c9222e6f37c98111c8679fbd94aa145f9a0ae23d", size = 2008753, upload_time = "2025-04-02T09:47:39.013Z" }, + { url = "https://files.pythonhosted.org/packages/7c/76/1c42e384e8d78452ededac8b583fe2550c84abfef83a0552e0e7478ccbc3/pydantic_core-2.33.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef99779001d7ac2e2461d8ab55d3373fe7315caefdbecd8ced75304ae5a6fc6b", size = 2114849, upload_time = "2025-04-02T09:47:40.427Z" }, + { url = "https://files.pythonhosted.org/packages/00/72/7d0cf05095c15f7ffe0eb78914b166d591c0eed72f294da68378da205101/pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fc6bf8869e193855e8d91d91f6bf59699a5cdfaa47a404e278e776dd7f168b39", size = 2069541, upload_time = "2025-04-02T09:47:42.01Z" }, + { url = "https://files.pythonhosted.org/packages/b3/69/94a514066bb7d8be499aa764926937409d2389c09be0b5107a970286ef81/pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:b1caa0bc2741b043db7823843e1bde8aaa58a55a58fda06083b0569f8b45693a", size = 2239225, upload_time = "2025-04-02T09:47:43.425Z" }, + { url = "https://files.pythonhosted.org/packages/84/b0/e390071eadb44b41f4f54c3cef64d8bf5f9612c92686c9299eaa09e267e2/pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ec259f62538e8bf364903a7d0d0239447059f9434b284f5536e8402b7dd198db", size = 2248373, upload_time = "2025-04-02T09:47:44.979Z" }, + { url = "https://files.pythonhosted.org/packages/d6/b2/288b3579ffc07e92af66e2f1a11be3b056fe1214aab314748461f21a31c3/pydantic_core-2.33.1-cp312-cp312-win32.whl", hash = "sha256:e14f369c98a7c15772b9da98987f58e2b509a93235582838bd0d1d8c08b68fda", size = 1907034, upload_time = "2025-04-02T09:47:46.843Z" }, + { url = "https://files.pythonhosted.org/packages/02/28/58442ad1c22b5b6742b992ba9518420235adced665513868f99a1c2638a5/pydantic_core-2.33.1-cp312-cp312-win_amd64.whl", hash = "sha256:1c607801d85e2e123357b3893f82c97a42856192997b95b4d8325deb1cd0c5f4", size = 1956848, upload_time = "2025-04-02T09:47:48.404Z" }, + { url = "https://files.pythonhosted.org/packages/a1/eb/f54809b51c7e2a1d9f439f158b8dd94359321abcc98767e16fc48ae5a77e/pydantic_core-2.33.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d13f0276806ee722e70a1c93da19748594f19ac4299c7e41237fc791d1861ea", size = 1903986, upload_time = "2025-04-02T09:47:49.839Z" }, + { url = "https://files.pythonhosted.org/packages/7a/24/eed3466a4308d79155f1cdd5c7432c80ddcc4530ba8623b79d5ced021641/pydantic_core-2.33.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70af6a21237b53d1fe7b9325b20e65cbf2f0a848cf77bed492b029139701e66a", size = 2033551, upload_time = "2025-04-02T09:47:51.648Z" }, + { url = "https://files.pythonhosted.org/packages/ab/14/df54b1a0bc9b6ded9b758b73139d2c11b4e8eb43e8ab9c5847c0a2913ada/pydantic_core-2.33.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:282b3fe1bbbe5ae35224a0dbd05aed9ccabccd241e8e6b60370484234b456266", size = 1852785, upload_time = "2025-04-02T09:47:53.149Z" }, + { url = "https://files.pythonhosted.org/packages/fa/96/e275f15ff3d34bb04b0125d9bc8848bf69f25d784d92a63676112451bfb9/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b315e596282bbb5822d0c7ee9d255595bd7506d1cb20c2911a4da0b970187d3", size = 1897758, upload_time = "2025-04-02T09:47:55.006Z" }, + { url = "https://files.pythonhosted.org/packages/b7/d8/96bc536e975b69e3a924b507d2a19aedbf50b24e08c80fb00e35f9baaed8/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1dfae24cf9921875ca0ca6a8ecb4bb2f13c855794ed0d468d6abbec6e6dcd44a", size = 1986109, upload_time = "2025-04-02T09:47:56.532Z" }, + { url = "https://files.pythonhosted.org/packages/90/72/ab58e43ce7e900b88cb571ed057b2fcd0e95b708a2e0bed475b10130393e/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6dd8ecfde08d8bfadaea669e83c63939af76f4cf5538a72597016edfa3fad516", size = 2129159, upload_time = "2025-04-02T09:47:58.088Z" }, + { url = "https://files.pythonhosted.org/packages/dc/3f/52d85781406886c6870ac995ec0ba7ccc028b530b0798c9080531b409fdb/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f593494876eae852dc98c43c6f260f45abdbfeec9e4324e31a481d948214764", size = 2680222, upload_time = "2025-04-02T09:47:59.591Z" }, + { url = "https://files.pythonhosted.org/packages/f4/56/6e2ef42f363a0eec0fd92f74a91e0ac48cd2e49b695aac1509ad81eee86a/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:948b73114f47fd7016088e5186d13faf5e1b2fe83f5e320e371f035557fd264d", size = 2006980, upload_time = "2025-04-02T09:48:01.397Z" }, + { url = "https://files.pythonhosted.org/packages/4c/c0/604536c4379cc78359f9ee0aa319f4aedf6b652ec2854953f5a14fc38c5a/pydantic_core-2.33.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e11f3864eb516af21b01e25fac915a82e9ddad3bb0fb9e95a246067398b435a4", size = 2120840, upload_time = "2025-04-02T09:48:03.056Z" }, + { url = "https://files.pythonhosted.org/packages/1f/46/9eb764814f508f0edfb291a0f75d10854d78113fa13900ce13729aaec3ae/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:549150be302428b56fdad0c23c2741dcdb5572413776826c965619a25d9c6bde", size = 2072518, upload_time = "2025-04-02T09:48:04.662Z" }, + { url = "https://files.pythonhosted.org/packages/42/e3/fb6b2a732b82d1666fa6bf53e3627867ea3131c5f39f98ce92141e3e3dc1/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:495bc156026efafd9ef2d82372bd38afce78ddd82bf28ef5276c469e57c0c83e", size = 2248025, upload_time = "2025-04-02T09:48:06.226Z" }, + { url = "https://files.pythonhosted.org/packages/5c/9d/fbe8fe9d1aa4dac88723f10a921bc7418bd3378a567cb5e21193a3c48b43/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ec79de2a8680b1a67a07490bddf9636d5c2fab609ba8c57597e855fa5fa4dacd", size = 2254991, upload_time = "2025-04-02T09:48:08.114Z" }, + { url = "https://files.pythonhosted.org/packages/aa/99/07e2237b8a66438d9b26482332cda99a9acccb58d284af7bc7c946a42fd3/pydantic_core-2.33.1-cp313-cp313-win32.whl", hash = "sha256:ee12a7be1742f81b8a65b36c6921022301d466b82d80315d215c4c691724986f", size = 1915262, upload_time = "2025-04-02T09:48:09.708Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f4/e457a7849beeed1e5defbcf5051c6f7b3c91a0624dd31543a64fc9adcf52/pydantic_core-2.33.1-cp313-cp313-win_amd64.whl", hash = "sha256:ede9b407e39949d2afc46385ce6bd6e11588660c26f80576c11c958e6647bc40", size = 1956626, upload_time = "2025-04-02T09:48:11.288Z" }, + { url = "https://files.pythonhosted.org/packages/20/d0/e8d567a7cff7b04e017ae164d98011f1e1894269fe8e90ea187a3cbfb562/pydantic_core-2.33.1-cp313-cp313-win_arm64.whl", hash = "sha256:aa687a23d4b7871a00e03ca96a09cad0f28f443690d300500603bd0adba4b523", size = 1909590, upload_time = "2025-04-02T09:48:12.861Z" }, + { url = "https://files.pythonhosted.org/packages/ef/fd/24ea4302d7a527d672c5be06e17df16aabfb4e9fdc6e0b345c21580f3d2a/pydantic_core-2.33.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:401d7b76e1000d0dd5538e6381d28febdcacb097c8d340dde7d7fc6e13e9f95d", size = 1812963, upload_time = "2025-04-02T09:48:14.553Z" }, + { url = "https://files.pythonhosted.org/packages/5f/95/4fbc2ecdeb5c1c53f1175a32d870250194eb2fdf6291b795ab08c8646d5d/pydantic_core-2.33.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7aeb055a42d734c0255c9e489ac67e75397d59c6fbe60d155851e9782f276a9c", size = 1986896, upload_time = "2025-04-02T09:48:16.222Z" }, + { url = "https://files.pythonhosted.org/packages/71/ae/fe31e7f4a62431222d8f65a3bd02e3fa7e6026d154a00818e6d30520ea77/pydantic_core-2.33.1-cp313-cp313t-win_amd64.whl", hash = "sha256:338ea9b73e6e109f15ab439e62cb3b78aa752c7fd9536794112e14bee02c8d18", size = 1931810, upload_time = "2025-04-02T09:48:17.97Z" }, + { url = "https://files.pythonhosted.org/packages/9c/c7/8b311d5adb0fe00a93ee9b4e92a02b0ec08510e9838885ef781ccbb20604/pydantic_core-2.33.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c834f54f8f4640fd7e4b193f80eb25a0602bba9e19b3cd2fc7ffe8199f5ae02", size = 2041659, upload_time = "2025-04-02T09:48:45.342Z" }, + { url = "https://files.pythonhosted.org/packages/8a/d6/4f58d32066a9e26530daaf9adc6664b01875ae0691570094968aaa7b8fcc/pydantic_core-2.33.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:049e0de24cf23766f12cc5cc71d8abc07d4a9deb9061b334b62093dedc7cb068", size = 1873294, upload_time = "2025-04-02T09:48:47.548Z" }, + { url = "https://files.pythonhosted.org/packages/f7/3f/53cc9c45d9229da427909c751f8ed2bf422414f7664ea4dde2d004f596ba/pydantic_core-2.33.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a28239037b3d6f16916a4c831a5a0eadf856bdd6d2e92c10a0da3a59eadcf3e", size = 1903771, upload_time = "2025-04-02T09:48:49.468Z" }, + { url = "https://files.pythonhosted.org/packages/f0/49/bf0783279ce674eb9903fb9ae43f6c614cb2f1c4951370258823f795368b/pydantic_core-2.33.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d3da303ab5f378a268fa7d45f37d7d85c3ec19769f28d2cc0c61826a8de21fe", size = 2083558, upload_time = "2025-04-02T09:48:51.409Z" }, + { url = "https://files.pythonhosted.org/packages/9c/5b/0d998367687f986c7d8484a2c476d30f07bf5b8b1477649a6092bd4c540e/pydantic_core-2.33.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:25626fb37b3c543818c14821afe0fd3830bc327a43953bc88db924b68c5723f1", size = 2118038, upload_time = "2025-04-02T09:48:53.702Z" }, + { url = "https://files.pythonhosted.org/packages/b3/33/039287d410230ee125daee57373ac01940d3030d18dba1c29cd3089dc3ca/pydantic_core-2.33.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3ab2d36e20fbfcce8f02d73c33a8a7362980cff717926bbae030b93ae46b56c7", size = 2079315, upload_time = "2025-04-02T09:48:55.555Z" }, + { url = "https://files.pythonhosted.org/packages/1f/85/6d8b2646d99c062d7da2d0ab2faeb0d6ca9cca4c02da6076376042a20da3/pydantic_core-2.33.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:2f9284e11c751b003fd4215ad92d325d92c9cb19ee6729ebd87e3250072cdcde", size = 2249063, upload_time = "2025-04-02T09:48:57.479Z" }, + { url = "https://files.pythonhosted.org/packages/17/d7/c37d208d5738f7b9ad8f22ae8a727d88ebf9c16c04ed2475122cc3f7224a/pydantic_core-2.33.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:048c01eee07d37cbd066fc512b9d8b5ea88ceeb4e629ab94b3e56965ad655add", size = 2254631, upload_time = "2025-04-02T09:48:59.581Z" }, + { url = "https://files.pythonhosted.org/packages/13/e0/bafa46476d328e4553b85ab9b2f7409e7aaef0ce4c937c894821c542d347/pydantic_core-2.33.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5ccd429694cf26af7997595d627dd2637e7932214486f55b8a357edaac9dae8c", size = 2080877, upload_time = "2025-04-02T09:49:01.52Z" }, + { url = "https://files.pythonhosted.org/packages/0b/76/1794e440c1801ed35415238d2c728f26cd12695df9057154ad768b7b991c/pydantic_core-2.33.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3a371dc00282c4b84246509a5ddc808e61b9864aa1eae9ecc92bb1268b82db4a", size = 2042858, upload_time = "2025-04-02T09:49:03.419Z" }, + { url = "https://files.pythonhosted.org/packages/73/b4/9cd7b081fb0b1b4f8150507cd59d27b275c3e22ad60b35cb19ea0977d9b9/pydantic_core-2.33.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f59295ecc75a1788af8ba92f2e8c6eeaa5a94c22fc4d151e8d9638814f85c8fc", size = 1873745, upload_time = "2025-04-02T09:49:05.391Z" }, + { url = "https://files.pythonhosted.org/packages/e1/d7/9ddb7575d4321e40d0363903c2576c8c0c3280ebea137777e5ab58d723e3/pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08530b8ac922003033f399128505f513e30ca770527cc8bbacf75a84fcc2c74b", size = 1904188, upload_time = "2025-04-02T09:49:07.352Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a8/3194ccfe461bb08da19377ebec8cb4f13c9bd82e13baebc53c5c7c39a029/pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bae370459da6a5466978c0eacf90690cb57ec9d533f8e63e564ef3822bfa04fe", size = 2083479, upload_time = "2025-04-02T09:49:09.304Z" }, + { url = "https://files.pythonhosted.org/packages/42/c7/84cb569555d7179ca0b3f838cef08f66f7089b54432f5b8599aac6e9533e/pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e3de2777e3b9f4d603112f78006f4ae0acb936e95f06da6cb1a45fbad6bdb4b5", size = 2118415, upload_time = "2025-04-02T09:49:11.25Z" }, + { url = "https://files.pythonhosted.org/packages/3b/67/72abb8c73e0837716afbb58a59cc9e3ae43d1aa8677f3b4bc72c16142716/pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a64e81e8cba118e108d7126362ea30e021291b7805d47e4896e52c791be2761", size = 2079623, upload_time = "2025-04-02T09:49:13.292Z" }, + { url = "https://files.pythonhosted.org/packages/0b/cd/c59707e35a47ba4cbbf153c3f7c56420c58653b5801b055dc52cccc8e2dc/pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:52928d8c1b6bda03cc6d811e8923dffc87a2d3c8b3bfd2ce16471c7147a24850", size = 2250175, upload_time = "2025-04-02T09:49:15.597Z" }, + { url = "https://files.pythonhosted.org/packages/84/32/e4325a6676b0bed32d5b084566ec86ed7fd1e9bcbfc49c578b1755bde920/pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1b30d92c9412beb5ac6b10a3eb7ef92ccb14e3f2a8d7732e2d739f58b3aa7544", size = 2254674, upload_time = "2025-04-02T09:49:17.61Z" }, + { url = "https://files.pythonhosted.org/packages/12/6f/5596dc418f2e292ffc661d21931ab34591952e2843e7168ea5a52591f6ff/pydantic_core-2.33.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f995719707e0e29f0f41a8aa3bcea6e761a36c9136104d3189eafb83f5cec5e5", size = 2080951, upload_time = "2025-04-02T09:49:19.559Z" }, ] [[package]] name = "pydantic-settings" -version = "2.8.1" +version = "2.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, + { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/88/82/c79424d7d8c29b994fb01d277da57b0a9b09cc03c3ff875f9bd8a86b2145/pydantic_settings-2.8.1.tar.gz", hash = "sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585", size = 83550 } +sdist = { url = "https://files.pythonhosted.org/packages/67/1d/42628a2c33e93f8e9acbde0d5d735fa0850f3e6a2f8cb1eb6c40b9a732ac/pydantic_settings-2.9.1.tar.gz", hash = "sha256:c509bf79d27563add44e8446233359004ed85066cd096d8b510f715e6ef5d268", size = 163234, upload_time = "2025-04-18T16:44:48.265Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/53/a64f03044927dc47aafe029c42a5b7aabc38dfb813475e0e1bf71c4a59d0/pydantic_settings-2.8.1-py3-none-any.whl", hash = "sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c", size = 30839 }, + { url = "https://files.pythonhosted.org/packages/b6/5f/d6d641b490fd3ec2c4c13b4244d68deea3a1b970a97be64f34fb5504ff72/pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef", size = 44356, upload_time = "2025-04-18T16:44:46.617Z" }, ] [[package]] name = "pygments" version = "2.17.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/55/59/8bccf4157baf25e4aa5a0bb7fa3ba8600907de105ebc22b0c78cfbf6f565/pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367", size = 4827772 } +sdist = { url = "https://files.pythonhosted.org/packages/55/59/8bccf4157baf25e4aa5a0bb7fa3ba8600907de105ebc22b0c78cfbf6f565/pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367", size = 4827772, upload_time = "2023-11-21T20:43:53.875Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/97/9c/372fef8377a6e340b1704768d20daaded98bf13282b5327beb2e2fe2c7ef/pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c", size = 1179756 }, + { url = "https://files.pythonhosted.org/packages/97/9c/372fef8377a6e340b1704768d20daaded98bf13282b5327beb2e2fe2c7ef/pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c", size = 1179756, upload_time = "2023-11-21T20:43:49.423Z" }, ] [[package]] name = "pyparsing" version = "3.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/37/fe/65c989f70bd630b589adfbbcd6ed238af22319e90f059946c26b4835e44b/pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db", size = 884814 } +sdist = { url = "https://files.pythonhosted.org/packages/37/fe/65c989f70bd630b589adfbbcd6ed238af22319e90f059946c26b4835e44b/pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db", size = 884814, upload_time = "2023-07-30T15:07:02.617Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/39/92/8486ede85fcc088f1b3dba4ce92dd29d126fd96b0008ea213167940a2475/pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb", size = 103139 }, + { url = "https://files.pythonhosted.org/packages/39/92/8486ede85fcc088f1b3dba4ce92dd29d126fd96b0008ea213167940a2475/pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb", size = 103139, upload_time = "2023-07-30T15:06:59.829Z" }, ] [[package]] name = "pyreadline3" version = "3.4.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/86/3d61a61f36a0067874a00cb4dceb9028d34b6060e47828f7fc86fb9f7ee9/pyreadline3-3.4.1.tar.gz", hash = "sha256:6f3d1f7b8a31ba32b73917cefc1f28cc660562f39aea8646d30bd6eff21f7bae", size = 86465 } +sdist = { url = "https://files.pythonhosted.org/packages/d7/86/3d61a61f36a0067874a00cb4dceb9028d34b6060e47828f7fc86fb9f7ee9/pyreadline3-3.4.1.tar.gz", hash = "sha256:6f3d1f7b8a31ba32b73917cefc1f28cc660562f39aea8646d30bd6eff21f7bae", size = 86465, upload_time = "2022-01-24T20:05:11.66Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/fc/a3c13ded7b3057680c8ae95a9b6cc83e63657c38e0005c400a5d018a33a7/pyreadline3-3.4.1-py3-none-any.whl", hash = "sha256:b0efb6516fd4fb07b45949053826a62fa4cb353db5be2bbb4a7aa1fdd1e345fb", size = 95203 }, + { url = "https://files.pythonhosted.org/packages/56/fc/a3c13ded7b3057680c8ae95a9b6cc83e63657c38e0005c400a5d018a33a7/pyreadline3-3.4.1-py3-none-any.whl", hash = "sha256:b0efb6516fd4fb07b45949053826a62fa4cb353db5be2bbb4a7aa1fdd1e345fb", size = 95203, upload_time = "2022-01-24T20:05:10.442Z" }, ] [[package]] @@ -1941,9 +1968,9 @@ dependencies = [ { name = "pluggy" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 } +sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891, upload_time = "2025-03-02T12:54:54.503Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 }, + { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634, upload_time = "2025-03-02T12:54:52.069Z" }, ] [[package]] @@ -1953,9 +1980,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8e/c4/453c52c659521066969523e87d85d54139bbd17b78f09532fb8eb8cdb58e/pytest_asyncio-0.26.0.tar.gz", hash = "sha256:c4df2a697648241ff39e7f0e4a73050b03f123f760673956cf0d72a4990e312f", size = 54156 } +sdist = { url = "https://files.pythonhosted.org/packages/8e/c4/453c52c659521066969523e87d85d54139bbd17b78f09532fb8eb8cdb58e/pytest_asyncio-0.26.0.tar.gz", hash = "sha256:c4df2a697648241ff39e7f0e4a73050b03f123f760673956cf0d72a4990e312f", size = 54156, upload_time = "2025-03-25T06:22:28.883Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/7f/338843f449ace853647ace35870874f69a764d251872ed1b4de9f234822c/pytest_asyncio-0.26.0-py3-none-any.whl", hash = "sha256:7b51ed894f4fbea1340262bdae5135797ebbe21d8638978e35d31c6d19f72fb0", size = 19694 }, + { url = "https://files.pythonhosted.org/packages/20/7f/338843f449ace853647ace35870874f69a764d251872ed1b4de9f234822c/pytest_asyncio-0.26.0-py3-none-any.whl", hash = "sha256:7b51ed894f4fbea1340262bdae5135797ebbe21d8638978e35d31c6d19f72fb0", size = 19694, upload_time = "2025-03-25T06:22:27.807Z" }, ] [[package]] @@ -1966,9 +1993,9 @@ dependencies = [ { name = "coverage", extra = ["toml"] }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/69/5f1e57f6c5a39f81411b550027bf72842c4567ff5fd572bed1edc9e4b5d9/pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a", size = 66857 } +sdist = { url = "https://files.pythonhosted.org/packages/25/69/5f1e57f6c5a39f81411b550027bf72842c4567ff5fd572bed1edc9e4b5d9/pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a", size = 66857, upload_time = "2025-04-05T14:07:51.592Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde", size = 23841 }, + { url = "https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde", size = 23841, upload_time = "2025-04-05T14:07:49.641Z" }, ] [[package]] @@ -1978,9 +2005,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c6/90/a955c3ab35ccd41ad4de556596fa86685bf4fc5ffcc62d22d856cfd4e29a/pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0", size = 32814 } +sdist = { url = "https://files.pythonhosted.org/packages/c6/90/a955c3ab35ccd41ad4de556596fa86685bf4fc5ffcc62d22d856cfd4e29a/pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0", size = 32814, upload_time = "2024-03-21T22:14:04.964Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863 }, + { url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863, upload_time = "2024-03-21T22:14:02.694Z" }, ] [[package]] @@ -1990,27 +2017,58 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4c/c4/13b4776ea2d76c115c1d1b84579f3764ee6d57204f6be27119f13a61d0a9/python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", size = 357324 } +sdist = { url = "https://files.pythonhosted.org/packages/4c/c4/13b4776ea2d76c115c1d1b84579f3764ee6d57204f6be27119f13a61d0a9/python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", size = 357324, upload_time = "2021-07-14T08:19:19.783Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/7a/87837f39d0296e723bb9b62bbb257d0355c7f6128853c78955f57342a56d/python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9", size = 247702 }, + { url = "https://files.pythonhosted.org/packages/36/7a/87837f39d0296e723bb9b62bbb257d0355c7f6128853c78955f57342a56d/python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9", size = 247702, upload_time = "2021-07-14T08:19:18.161Z" }, ] [[package]] name = "python-dotenv" version = "1.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/31/06/1ef763af20d0572c032fa22882cfbfb005fba6e7300715a37840858c919e/python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba", size = 37399 } +sdist = { url = "https://files.pythonhosted.org/packages/31/06/1ef763af20d0572c032fa22882cfbfb005fba6e7300715a37840858c919e/python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba", size = 37399, upload_time = "2023-02-24T06:46:37.282Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/2f/62ea1c8b593f4e093cc1a7768f0d46112107e790c3e478532329e434f00b/python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a", size = 19482 }, + { url = "https://files.pythonhosted.org/packages/44/2f/62ea1c8b593f4e093cc1a7768f0d46112107e790c3e478532329e434f00b/python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a", size = 19482, upload_time = "2023-02-24T06:46:36.009Z" }, +] + +[[package]] +name = "python-engineio" +version = "4.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "simple-websocket" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f7/e1/eee1129544b7f78fa2afa9fa0fce153cdcb21015b9b331d1b8adf90f45cb/python_engineio-4.12.0.tar.gz", hash = "sha256:f42a36a868d7063aa10ddccf6bd6117a169b6bd00d7ca53999772093b62014f9", size = 91503, upload_time = "2025-04-12T15:30:23.905Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/f7/0aeea75424c47633c1d98557a2323be23bed31fa950f00161b34a5150d06/python_engineio-4.12.0-py3-none-any.whl", hash = "sha256:a0c47c129c39777e8ebc6d18011efd50db2144e4e8f08983acae8a3614626535", size = 59319, upload_time = "2025-04-12T15:30:22.325Z" }, ] [[package]] name = "python-multipart" version = "0.0.20" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158 } +sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload_time = "2024-12-16T19:45:46.972Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 }, + { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload_time = "2024-12-16T19:45:44.423Z" }, +] + +[[package]] +name = "python-socketio" +version = "5.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bidict" }, + { name = "python-engineio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/21/1a/396d50ccf06ee539fa758ce5623b59a9cb27637fc4b2dc07ed08bf495e77/python_socketio-5.13.0.tar.gz", hash = "sha256:ac4e19a0302ae812e23b712ec8b6427ca0521f7c582d6abb096e36e24a263029", size = 121125, upload_time = "2025-04-12T15:46:59.933Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/32/b4fb8585d1be0f68bde7e110dffbcf354915f77ad8c778563f0ad9655c02/python_socketio-5.13.0-py3-none-any.whl", hash = "sha256:51f68d6499f2df8524668c24bcec13ba1414117cfb3a90115c559b601ab10caf", size = 77800, upload_time = "2025-04-12T15:46:58.412Z" }, +] + +[package.optional-dependencies] +client = [ + { name = "requests" }, + { name = "websocket-client" }, ] [[package]] @@ -2018,45 +2076,45 @@ name = "pywin32" version = "306" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/08/dc/28c668097edfaf4eac4617ef7adf081b9cf50d254672fcf399a70f5efc41/pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d", size = 8506422 }, - { url = "https://files.pythonhosted.org/packages/d3/d6/891894edec688e72c2e308b3243fad98b4066e1839fd2fe78f04129a9d31/pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8", size = 9226392 }, - { url = "https://files.pythonhosted.org/packages/8b/1e/fc18ad83ca553e01b97aa8393ff10e33c1fb57801db05488b83282ee9913/pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407", size = 8507689 }, - { url = "https://files.pythonhosted.org/packages/7e/9e/ad6b1ae2a5ad1066dc509350e0fbf74d8d50251a51e420a2a8feaa0cecbd/pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e", size = 9227547 }, - { url = "https://files.pythonhosted.org/packages/91/20/f744bff1da8f43388498503634378dbbefbe493e65675f2cc52f7185c2c2/pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a", size = 10388324 }, - { url = "https://files.pythonhosted.org/packages/14/91/17e016d5923e178346aabda3dfec6629d1a26efe587d19667542105cf0a6/pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b", size = 8507705 }, - { url = "https://files.pythonhosted.org/packages/83/1c/25b79fc3ec99b19b0a0730cc47356f7e2959863bf9f3cd314332bddb4f68/pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e", size = 9227429 }, - { url = "https://files.pythonhosted.org/packages/1c/43/e3444dc9a12f8365d9603c2145d16bf0a2f8180f343cf87be47f5579e547/pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040", size = 10388145 }, + { url = "https://files.pythonhosted.org/packages/08/dc/28c668097edfaf4eac4617ef7adf081b9cf50d254672fcf399a70f5efc41/pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d", size = 8506422, upload_time = "2023-03-26T03:27:46.303Z" }, + { url = "https://files.pythonhosted.org/packages/d3/d6/891894edec688e72c2e308b3243fad98b4066e1839fd2fe78f04129a9d31/pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8", size = 9226392, upload_time = "2023-03-26T03:27:53.591Z" }, + { url = "https://files.pythonhosted.org/packages/8b/1e/fc18ad83ca553e01b97aa8393ff10e33c1fb57801db05488b83282ee9913/pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407", size = 8507689, upload_time = "2023-03-25T23:50:08.499Z" }, + { url = "https://files.pythonhosted.org/packages/7e/9e/ad6b1ae2a5ad1066dc509350e0fbf74d8d50251a51e420a2a8feaa0cecbd/pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e", size = 9227547, upload_time = "2023-03-25T23:50:20.331Z" }, + { url = "https://files.pythonhosted.org/packages/91/20/f744bff1da8f43388498503634378dbbefbe493e65675f2cc52f7185c2c2/pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a", size = 10388324, upload_time = "2023-03-25T23:50:30.904Z" }, + { url = "https://files.pythonhosted.org/packages/14/91/17e016d5923e178346aabda3dfec6629d1a26efe587d19667542105cf0a6/pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b", size = 8507705, upload_time = "2023-03-25T23:50:40.279Z" }, + { url = "https://files.pythonhosted.org/packages/83/1c/25b79fc3ec99b19b0a0730cc47356f7e2959863bf9f3cd314332bddb4f68/pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e", size = 9227429, upload_time = "2023-03-25T23:50:50.222Z" }, + { url = "https://files.pythonhosted.org/packages/1c/43/e3444dc9a12f8365d9603c2145d16bf0a2f8180f343cf87be47f5579e547/pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040", size = 10388145, upload_time = "2023-03-25T23:51:01.401Z" }, ] [[package]] name = "pyyaml" version = "6.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/e5/af35f7ea75cf72f2cd079c95ee16797de7cd71f29ea7c68ae5ce7be1eda0/PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", size = 125201 } +sdist = { url = "https://files.pythonhosted.org/packages/cd/e5/af35f7ea75cf72f2cd079c95ee16797de7cd71f29ea7c68ae5ce7be1eda0/PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", size = 125201, upload_time = "2023-07-18T00:00:23.308Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/06/4beb652c0fe16834032e54f0956443d4cc797fe645527acee59e7deaa0a2/PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", size = 189447 }, - { url = "https://files.pythonhosted.org/packages/5b/07/10033a403b23405a8fc48975444463d3d10a5c2736b7eb2550b07b367429/PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f", size = 169264 }, - { url = "https://files.pythonhosted.org/packages/f1/26/55e4f21db1f72eaef092015d9017c11510e7e6301c62a6cfee91295d13c6/PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", size = 677003 }, - { url = "https://files.pythonhosted.org/packages/ba/91/090818dfa62e85181f3ae23dd1e8b7ea7f09684864a900cab72d29c57346/PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", size = 699070 }, - { url = "https://files.pythonhosted.org/packages/29/61/bf33c6c85c55bc45a29eee3195848ff2d518d84735eb0e2d8cb42e0d285e/PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", size = 705525 }, - { url = "https://files.pythonhosted.org/packages/07/91/45dfd0ef821a7f41d9d0136ea3608bb5b1653e42fd56a7970532cb5c003f/PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", size = 707514 }, - { url = "https://files.pythonhosted.org/packages/b6/a0/b6700da5d49e9fed49dc3243d3771b598dad07abb37cc32e524607f96adc/PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", size = 130488 }, - { url = "https://files.pythonhosted.org/packages/24/97/9b59b43431f98d01806b288532da38099cc6f2fea0f3d712e21e269c0279/PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", size = 145338 }, - { url = "https://files.pythonhosted.org/packages/ec/0d/26fb23e8863e0aeaac0c64e03fd27367ad2ae3f3cccf3798ee98ce160368/PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", size = 187867 }, - { url = "https://files.pythonhosted.org/packages/28/09/55f715ddbf95a054b764b547f617e22f1d5e45d83905660e9a088078fe67/PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", size = 167530 }, - { url = "https://files.pythonhosted.org/packages/5e/94/7d5ee059dfb92ca9e62f4057dcdec9ac08a9e42679644854dc01177f8145/PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", size = 732244 }, - { url = "https://files.pythonhosted.org/packages/06/92/e0224aa6ebf9dc54a06a4609da37da40bb08d126f5535d81bff6b417b2ae/PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", size = 752871 }, - { url = "https://files.pythonhosted.org/packages/7b/5e/efd033ab7199a0b2044dab3b9f7a4f6670e6a52c089de572e928d2873b06/PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", size = 757729 }, - { url = "https://files.pythonhosted.org/packages/03/5c/c4671451b2f1d76ebe352c0945d4cd13500adb5d05f5a51ee296d80152f7/PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", size = 748528 }, - { url = "https://files.pythonhosted.org/packages/73/9c/766e78d1efc0d1fca637a6b62cea1b4510a7fb93617eb805223294fef681/PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", size = 130286 }, - { url = "https://files.pythonhosted.org/packages/b3/34/65bb4b2d7908044963ebf614fe0fdb080773fc7030d7e39c8d3eddcd4257/PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", size = 144699 }, - { url = "https://files.pythonhosted.org/packages/bc/06/1b305bf6aa704343be85444c9d011f626c763abb40c0edc1cad13bfd7f86/PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", size = 178692 }, - { url = "https://files.pythonhosted.org/packages/84/02/404de95ced348b73dd84f70e15a41843d817ff8c1744516bf78358f2ffd2/PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", size = 165622 }, - { url = "https://files.pythonhosted.org/packages/c7/4c/4a2908632fc980da6d918b9de9c1d9d7d7e70b2672b1ad5166ed27841ef7/PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef", size = 696937 }, - { url = "https://files.pythonhosted.org/packages/b4/33/720548182ffa8344418126017aa1d4ab4aeec9a2275f04ce3f3573d8ace8/PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", size = 724969 }, - { url = "https://files.pythonhosted.org/packages/4f/78/77b40157b6cb5f2d3d31a3d9b2efd1ba3505371f76730d267e8b32cf4b7f/PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", size = 712604 }, - { url = "https://files.pythonhosted.org/packages/2e/97/3e0e089ee85e840f4b15bfa00e4e63d84a3691ababbfea92d6f820ea6f21/PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", size = 126098 }, - { url = "https://files.pythonhosted.org/packages/2b/9f/fbade56564ad486809c27b322d0f7e6a89c01f6b4fe208402e90d4443a99/PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", size = 138675 }, + { url = "https://files.pythonhosted.org/packages/96/06/4beb652c0fe16834032e54f0956443d4cc797fe645527acee59e7deaa0a2/PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", size = 189447, upload_time = "2023-07-17T23:57:04.325Z" }, + { url = "https://files.pythonhosted.org/packages/5b/07/10033a403b23405a8fc48975444463d3d10a5c2736b7eb2550b07b367429/PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f", size = 169264, upload_time = "2023-07-17T23:57:07.787Z" }, + { url = "https://files.pythonhosted.org/packages/f1/26/55e4f21db1f72eaef092015d9017c11510e7e6301c62a6cfee91295d13c6/PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", size = 677003, upload_time = "2023-07-17T23:57:13.144Z" }, + { url = "https://files.pythonhosted.org/packages/ba/91/090818dfa62e85181f3ae23dd1e8b7ea7f09684864a900cab72d29c57346/PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", size = 699070, upload_time = "2023-07-17T23:57:19.402Z" }, + { url = "https://files.pythonhosted.org/packages/29/61/bf33c6c85c55bc45a29eee3195848ff2d518d84735eb0e2d8cb42e0d285e/PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", size = 705525, upload_time = "2023-07-17T23:57:25.272Z" }, + { url = "https://files.pythonhosted.org/packages/07/91/45dfd0ef821a7f41d9d0136ea3608bb5b1653e42fd56a7970532cb5c003f/PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", size = 707514, upload_time = "2023-08-28T18:43:20.945Z" }, + { url = "https://files.pythonhosted.org/packages/b6/a0/b6700da5d49e9fed49dc3243d3771b598dad07abb37cc32e524607f96adc/PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", size = 130488, upload_time = "2023-07-17T23:57:28.144Z" }, + { url = "https://files.pythonhosted.org/packages/24/97/9b59b43431f98d01806b288532da38099cc6f2fea0f3d712e21e269c0279/PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", size = 145338, upload_time = "2023-07-17T23:57:31.118Z" }, + { url = "https://files.pythonhosted.org/packages/ec/0d/26fb23e8863e0aeaac0c64e03fd27367ad2ae3f3cccf3798ee98ce160368/PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", size = 187867, upload_time = "2023-07-17T23:57:34.35Z" }, + { url = "https://files.pythonhosted.org/packages/28/09/55f715ddbf95a054b764b547f617e22f1d5e45d83905660e9a088078fe67/PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", size = 167530, upload_time = "2023-07-17T23:57:36.975Z" }, + { url = "https://files.pythonhosted.org/packages/5e/94/7d5ee059dfb92ca9e62f4057dcdec9ac08a9e42679644854dc01177f8145/PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", size = 732244, upload_time = "2023-07-17T23:57:43.774Z" }, + { url = "https://files.pythonhosted.org/packages/06/92/e0224aa6ebf9dc54a06a4609da37da40bb08d126f5535d81bff6b417b2ae/PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", size = 752871, upload_time = "2023-07-17T23:57:51.921Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5e/efd033ab7199a0b2044dab3b9f7a4f6670e6a52c089de572e928d2873b06/PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", size = 757729, upload_time = "2023-07-17T23:57:59.865Z" }, + { url = "https://files.pythonhosted.org/packages/03/5c/c4671451b2f1d76ebe352c0945d4cd13500adb5d05f5a51ee296d80152f7/PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", size = 748528, upload_time = "2023-08-28T18:43:23.207Z" }, + { url = "https://files.pythonhosted.org/packages/73/9c/766e78d1efc0d1fca637a6b62cea1b4510a7fb93617eb805223294fef681/PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", size = 130286, upload_time = "2023-07-17T23:58:02.964Z" }, + { url = "https://files.pythonhosted.org/packages/b3/34/65bb4b2d7908044963ebf614fe0fdb080773fc7030d7e39c8d3eddcd4257/PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", size = 144699, upload_time = "2023-07-17T23:58:05.586Z" }, + { url = "https://files.pythonhosted.org/packages/bc/06/1b305bf6aa704343be85444c9d011f626c763abb40c0edc1cad13bfd7f86/PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", size = 178692, upload_time = "2023-08-28T18:43:24.924Z" }, + { url = "https://files.pythonhosted.org/packages/84/02/404de95ced348b73dd84f70e15a41843d817ff8c1744516bf78358f2ffd2/PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", size = 165622, upload_time = "2023-08-28T18:43:26.54Z" }, + { url = "https://files.pythonhosted.org/packages/c7/4c/4a2908632fc980da6d918b9de9c1d9d7d7e70b2672b1ad5166ed27841ef7/PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef", size = 696937, upload_time = "2024-01-18T20:40:22.92Z" }, + { url = "https://files.pythonhosted.org/packages/b4/33/720548182ffa8344418126017aa1d4ab4aeec9a2275f04ce3f3573d8ace8/PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", size = 724969, upload_time = "2023-08-28T18:43:28.56Z" }, + { url = "https://files.pythonhosted.org/packages/4f/78/77b40157b6cb5f2d3d31a3d9b2efd1ba3505371f76730d267e8b32cf4b7f/PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", size = 712604, upload_time = "2023-08-28T18:43:30.206Z" }, + { url = "https://files.pythonhosted.org/packages/2e/97/3e0e089ee85e840f4b15bfa00e4e63d84a3691ababbfea92d6f820ea6f21/PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", size = 126098, upload_time = "2023-08-28T18:43:31.835Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/fbade56564ad486809c27b322d0f7e6a89c01f6b4fe208402e90d4443a99/PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", size = 138675, upload_time = "2023-08-28T18:43:33.613Z" }, ] [[package]] @@ -2066,46 +2124,46 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "implementation_name == 'pypy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3a/33/1a3683fc9a4bd64d8ccc0290da75c8f042184a1a49c146d28398414d3341/pyzmq-25.1.2.tar.gz", hash = "sha256:93f1aa311e8bb912e34f004cf186407a4e90eec4f0ecc0efd26056bf7eda0226", size = 1402339 } +sdist = { url = "https://files.pythonhosted.org/packages/3a/33/1a3683fc9a4bd64d8ccc0290da75c8f042184a1a49c146d28398414d3341/pyzmq-25.1.2.tar.gz", hash = "sha256:93f1aa311e8bb912e34f004cf186407a4e90eec4f0ecc0efd26056bf7eda0226", size = 1402339, upload_time = "2023-12-05T07:34:47.976Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/f4/901edb48b2b2c00ad73de0db2ee76e24ce5903ef815ad0ad10e14555d989/pyzmq-25.1.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:e624c789359f1a16f83f35e2c705d07663ff2b4d4479bad35621178d8f0f6ea4", size = 1872310 }, - { url = "https://files.pythonhosted.org/packages/5e/46/2de69c7c79fd78bf4c22a9e8165fa6312f5d49410f1be6ddab51a6fe7236/pyzmq-25.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:49151b0efece79f6a79d41a461d78535356136ee70084a1c22532fc6383f4ad0", size = 1249619 }, - { url = "https://files.pythonhosted.org/packages/d1/f5/d6b9755713843bf9701ae86bf6fd97ec294a52cf2af719cd14fdf9392f65/pyzmq-25.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9a5f194cf730f2b24d6af1f833c14c10f41023da46a7f736f48b6d35061e76e", size = 897360 }, - { url = "https://files.pythonhosted.org/packages/7c/88/c1aef8820f12e710d136024d231e70e24684a01314aa1814f0758960ba01/pyzmq-25.1.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:faf79a302f834d9e8304fafdc11d0d042266667ac45209afa57e5efc998e3872", size = 1156959 }, - { url = "https://files.pythonhosted.org/packages/82/1b/b25d2c4ac3b4dae238c98e63395dbb88daf11968b168948d3c6289c3e95c/pyzmq-25.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f51a7b4ead28d3fca8dda53216314a553b0f7a91ee8fc46a72b402a78c3e43d", size = 1100585 }, - { url = "https://files.pythonhosted.org/packages/67/bf/6bc0977acd934b66eacab79cec303ecf08ae4a6150d57c628aa919615488/pyzmq-25.1.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0ddd6d71d4ef17ba5a87becf7ddf01b371eaba553c603477679ae817a8d84d75", size = 1109267 }, - { url = "https://files.pythonhosted.org/packages/64/fb/4f07424e56c6a5fb47306d9ba744c3c250250c2e7272f9c81efbf8daaccf/pyzmq-25.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:246747b88917e4867e2367b005fc8eefbb4a54b7db363d6c92f89d69abfff4b6", size = 1431853 }, - { url = "https://files.pythonhosted.org/packages/a2/10/2b88c1d4beb59a1d45c13983c4b7c5dcd6ef7988db3c03d23b0cabc5adca/pyzmq-25.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:00c48ae2fd81e2a50c3485de1b9d5c7c57cd85dc8ec55683eac16846e57ac979", size = 1766212 }, - { url = "https://files.pythonhosted.org/packages/bc/ab/c9a22eacfd5bd82620501ae426a3dd6ffa374ac335b21e54209d7a93d3fb/pyzmq-25.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5a68d491fc20762b630e5db2191dd07ff89834086740f70e978bb2ef2668be08", size = 1653737 }, - { url = "https://files.pythonhosted.org/packages/d6/e5/71bd89e47eedb7ebec31ef9a49dcdb0517dbbb063bd5de363980a6911eb1/pyzmq-25.1.2-cp310-cp310-win32.whl", hash = "sha256:09dfe949e83087da88c4a76767df04b22304a682d6154de2c572625c62ad6886", size = 906288 }, - { url = "https://files.pythonhosted.org/packages/9d/5f/2defc8a579e8b5679d92720ab3a4cb93e3a77d923070bf4c1a103d3ae478/pyzmq-25.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:fa99973d2ed20417744fca0073390ad65ce225b546febb0580358e36aa90dba6", size = 1170923 }, - { url = "https://files.pythonhosted.org/packages/35/de/7579518bc58cebf92568b48e354a702fb52525d0fab166dc544f2a0615dc/pyzmq-25.1.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:82544e0e2d0c1811482d37eef297020a040c32e0687c1f6fc23a75b75db8062c", size = 1870360 }, - { url = "https://files.pythonhosted.org/packages/ce/f9/58b6cc9a110b1832f666fa6b5a67dc4d520fabfc680ca87a8167b2061d5d/pyzmq-25.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:01171fc48542348cd1a360a4b6c3e7d8f46cdcf53a8d40f84db6707a6768acc1", size = 1249008 }, - { url = "https://files.pythonhosted.org/packages/bc/4a/ac6469c01813cb3652ab4e30ec4a37815cc9949afc18af33f64e2ec704aa/pyzmq-25.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc69c96735ab501419c432110016329bf0dea8898ce16fab97c6d9106dc0b348", size = 904394 }, - { url = "https://files.pythonhosted.org/packages/77/b7/8cee519b11bdd3f76c1a6eb537ab13c1bfef2964d725717705c86f524e4c/pyzmq-25.1.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3e124e6b1dd3dfbeb695435dff0e383256655bb18082e094a8dd1f6293114642", size = 1161453 }, - { url = "https://files.pythonhosted.org/packages/b6/1d/c35a956a44b333b064ae1b1c588c2dfa0e01b7ec90884c1972bfcef119c3/pyzmq-25.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7598d2ba821caa37a0f9d54c25164a4fa351ce019d64d0b44b45540950458840", size = 1105501 }, - { url = "https://files.pythonhosted.org/packages/18/d1/b3d1e985318ed7287737ea9e6b6e21748cc7c89accc2443347cd2c8d5f0f/pyzmq-25.1.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d1299d7e964c13607efd148ca1f07dcbf27c3ab9e125d1d0ae1d580a1682399d", size = 1109513 }, - { url = "https://files.pythonhosted.org/packages/14/9b/341cdfb47440069010101403298dc24d449150370c6cb322e73bfa1949bd/pyzmq-25.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4e6f689880d5ad87918430957297c975203a082d9a036cc426648fcbedae769b", size = 1433541 }, - { url = "https://files.pythonhosted.org/packages/fa/52/c6d4e76e020c554e965459d41a98201b4d45277a288648f53a4e5a2429cc/pyzmq-25.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cc69949484171cc961e6ecd4a8911b9ce7a0d1f738fcae717177c231bf77437b", size = 1766133 }, - { url = "https://files.pythonhosted.org/packages/1d/6d/0cbd8dd5b8979fd6b9cf1852ed067b9d2cd6fa0c09c3bafe6874d2d2e03c/pyzmq-25.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9880078f683466b7f567b8624bfc16cad65077be046b6e8abb53bed4eeb82dd3", size = 1653636 }, - { url = "https://files.pythonhosted.org/packages/f5/af/d90eed9cf3840685d54d4a35d5f9e242a8a48b5410d41146f14c1e098302/pyzmq-25.1.2-cp311-cp311-win32.whl", hash = "sha256:4e5837af3e5aaa99a091302df5ee001149baff06ad22b722d34e30df5f0d9097", size = 904865 }, - { url = "https://files.pythonhosted.org/packages/20/d2/09443dc73053ad01c846d7fb77e09fe9d93c09d4e900215f3c8b7b56bfec/pyzmq-25.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:25c2dbb97d38b5ac9fd15586e048ec5eb1e38f3d47fe7d92167b0c77bb3584e9", size = 1171332 }, - { url = "https://files.pythonhosted.org/packages/6e/f0/d71cf69dc039c9adc8b625efc3bad3684f3660a570e47f0f0c64df787b41/pyzmq-25.1.2-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:11e70516688190e9c2db14fcf93c04192b02d457b582a1f6190b154691b4c93a", size = 1871111 }, - { url = "https://files.pythonhosted.org/packages/68/62/d365773edf56ad71993579ee574105f02f83530caf600ebf28bea15d88d0/pyzmq-25.1.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:313c3794d650d1fccaaab2df942af9f2c01d6217c846177cfcbc693c7410839e", size = 1248844 }, - { url = "https://files.pythonhosted.org/packages/72/55/cc3163e20f40615a49245fa7041badec6103e8ee7e482dbb0feea00a7b84/pyzmq-25.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b3cbba2f47062b85fe0ef9de5b987612140a9ba3a9c6d2543c6dec9f7c2ab27", size = 899373 }, - { url = "https://files.pythonhosted.org/packages/40/aa/ae292bd85deda637230970bbc53c1dc53696a99e82fc7cd6d373ec173853/pyzmq-25.1.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc31baa0c32a2ca660784d5af3b9487e13b61b3032cb01a115fce6588e1bed30", size = 1160901 }, - { url = "https://files.pythonhosted.org/packages/93/b7/6e291eafbbbc66d0e87658dd21383ec2b4ab35edcfb283902c580a6db76f/pyzmq-25.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c9087b109070c5ab0b383079fa1b5f797f8d43e9a66c07a4b8b8bdecfd88ee", size = 1101147 }, - { url = "https://files.pythonhosted.org/packages/3a/f1/e296d5a507eac519d1fe1382851b1a4575f690bc2b2d2c8eca2ed7e4bd1f/pyzmq-25.1.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f8429b17cbb746c3e043cb986328da023657e79d5ed258b711c06a70c2ea7537", size = 1105315 }, - { url = "https://files.pythonhosted.org/packages/56/63/5c2abb556ab4cf013d98e01782d5bd642238a0ed9b019e965a7d7e957f56/pyzmq-25.1.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5074adeacede5f810b7ef39607ee59d94e948b4fd954495bdb072f8c54558181", size = 1427747 }, - { url = "https://files.pythonhosted.org/packages/b1/71/5dba5f6b12ef54fb977c9b9279075e151c04fc0dd6851e9663d9e66b593f/pyzmq-25.1.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7ae8f354b895cbd85212da245f1a5ad8159e7840e37d78b476bb4f4c3f32a9fe", size = 1762221 }, - { url = "https://files.pythonhosted.org/packages/cf/49/54d7e8bb3df82a3509325b11491d33450dc91580d4826b62fa5e554bb9cf/pyzmq-25.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b264bf2cc96b5bc43ce0e852be995e400376bd87ceb363822e2cb1964fcdc737", size = 1649505 }, - { url = "https://files.pythonhosted.org/packages/34/14/58e5037229bc37963e2ce804c2c075a3a541e3f84bf1c231e7c9779d36f1/pyzmq-25.1.2-cp312-cp312-win32.whl", hash = "sha256:02bbc1a87b76e04fd780b45e7f695471ae6de747769e540da909173d50ff8e2d", size = 954891 }, - { url = "https://files.pythonhosted.org/packages/2c/2d/04fab685ef3a8e6e955220fd2a54dc99efaee960a88675bf5c92cd277164/pyzmq-25.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:ced111c2e81506abd1dc142e6cd7b68dd53747b3b7ae5edbea4578c5eeff96b7", size = 1252773 }, - { url = "https://files.pythonhosted.org/packages/6b/fe/ed38fe12c540bafc1cae32c3ff638e9df32528f5cf91b5e400e6a8f5b3ec/pyzmq-25.1.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a8c1d566344aee826b74e472e16edae0a02e2a044f14f7c24e123002dcff1c05", size = 963654 }, - { url = "https://files.pythonhosted.org/packages/44/97/a760a2dff0672c408f22f726f2ea10a7a516ffa5001ca5a3641e355a45f9/pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:759cfd391a0996345ba94b6a5110fca9c557ad4166d86a6e81ea526c376a01e8", size = 609436 }, - { url = "https://files.pythonhosted.org/packages/41/81/ace39daa19c78b2f4fc12ef217d9d5f1ac658d5828d692bbbb68240cd55b/pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c61e346ac34b74028ede1c6b4bcecf649d69b707b3ff9dc0fab453821b04d1e", size = 843396 }, - { url = "https://files.pythonhosted.org/packages/4c/43/150b0b203f5461a9aeadaa925c55167e2b4215c9322b6911a64360d2243e/pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cb8fc1f8d69b411b8ec0b5f1ffbcaf14c1db95b6bccea21d83610987435f1a4", size = 800856 }, - { url = "https://files.pythonhosted.org/packages/5f/91/a618b56aaabe40dddcd25db85624d7408768fd32f5bfcf81bc0af5b1ce75/pyzmq-25.1.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3c00c9b7d1ca8165c610437ca0c92e7b5607b2f9076f4eb4b095c85d6e680a1d", size = 413836 }, + { url = "https://files.pythonhosted.org/packages/5e/f4/901edb48b2b2c00ad73de0db2ee76e24ce5903ef815ad0ad10e14555d989/pyzmq-25.1.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:e624c789359f1a16f83f35e2c705d07663ff2b4d4479bad35621178d8f0f6ea4", size = 1872310, upload_time = "2023-12-05T07:48:13.713Z" }, + { url = "https://files.pythonhosted.org/packages/5e/46/2de69c7c79fd78bf4c22a9e8165fa6312f5d49410f1be6ddab51a6fe7236/pyzmq-25.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:49151b0efece79f6a79d41a461d78535356136ee70084a1c22532fc6383f4ad0", size = 1249619, upload_time = "2023-12-05T07:50:38.691Z" }, + { url = "https://files.pythonhosted.org/packages/d1/f5/d6b9755713843bf9701ae86bf6fd97ec294a52cf2af719cd14fdf9392f65/pyzmq-25.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9a5f194cf730f2b24d6af1f833c14c10f41023da46a7f736f48b6d35061e76e", size = 897360, upload_time = "2023-12-05T07:42:26.268Z" }, + { url = "https://files.pythonhosted.org/packages/7c/88/c1aef8820f12e710d136024d231e70e24684a01314aa1814f0758960ba01/pyzmq-25.1.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:faf79a302f834d9e8304fafdc11d0d042266667ac45209afa57e5efc998e3872", size = 1156959, upload_time = "2023-12-05T07:44:29.904Z" }, + { url = "https://files.pythonhosted.org/packages/82/1b/b25d2c4ac3b4dae238c98e63395dbb88daf11968b168948d3c6289c3e95c/pyzmq-25.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f51a7b4ead28d3fca8dda53216314a553b0f7a91ee8fc46a72b402a78c3e43d", size = 1100585, upload_time = "2023-12-05T07:45:05.518Z" }, + { url = "https://files.pythonhosted.org/packages/67/bf/6bc0977acd934b66eacab79cec303ecf08ae4a6150d57c628aa919615488/pyzmq-25.1.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0ddd6d71d4ef17ba5a87becf7ddf01b371eaba553c603477679ae817a8d84d75", size = 1109267, upload_time = "2023-12-05T07:39:51.21Z" }, + { url = "https://files.pythonhosted.org/packages/64/fb/4f07424e56c6a5fb47306d9ba744c3c250250c2e7272f9c81efbf8daaccf/pyzmq-25.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:246747b88917e4867e2367b005fc8eefbb4a54b7db363d6c92f89d69abfff4b6", size = 1431853, upload_time = "2023-12-05T07:41:09.261Z" }, + { url = "https://files.pythonhosted.org/packages/a2/10/2b88c1d4beb59a1d45c13983c4b7c5dcd6ef7988db3c03d23b0cabc5adca/pyzmq-25.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:00c48ae2fd81e2a50c3485de1b9d5c7c57cd85dc8ec55683eac16846e57ac979", size = 1766212, upload_time = "2023-12-05T07:49:05.926Z" }, + { url = "https://files.pythonhosted.org/packages/bc/ab/c9a22eacfd5bd82620501ae426a3dd6ffa374ac335b21e54209d7a93d3fb/pyzmq-25.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5a68d491fc20762b630e5db2191dd07ff89834086740f70e978bb2ef2668be08", size = 1653737, upload_time = "2023-12-05T07:49:09.096Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e5/71bd89e47eedb7ebec31ef9a49dcdb0517dbbb063bd5de363980a6911eb1/pyzmq-25.1.2-cp310-cp310-win32.whl", hash = "sha256:09dfe949e83087da88c4a76767df04b22304a682d6154de2c572625c62ad6886", size = 906288, upload_time = "2023-12-05T07:42:05.509Z" }, + { url = "https://files.pythonhosted.org/packages/9d/5f/2defc8a579e8b5679d92720ab3a4cb93e3a77d923070bf4c1a103d3ae478/pyzmq-25.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:fa99973d2ed20417744fca0073390ad65ce225b546febb0580358e36aa90dba6", size = 1170923, upload_time = "2023-12-05T07:44:54.296Z" }, + { url = "https://files.pythonhosted.org/packages/35/de/7579518bc58cebf92568b48e354a702fb52525d0fab166dc544f2a0615dc/pyzmq-25.1.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:82544e0e2d0c1811482d37eef297020a040c32e0687c1f6fc23a75b75db8062c", size = 1870360, upload_time = "2023-12-05T07:48:16.153Z" }, + { url = "https://files.pythonhosted.org/packages/ce/f9/58b6cc9a110b1832f666fa6b5a67dc4d520fabfc680ca87a8167b2061d5d/pyzmq-25.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:01171fc48542348cd1a360a4b6c3e7d8f46cdcf53a8d40f84db6707a6768acc1", size = 1249008, upload_time = "2023-12-05T07:50:40.442Z" }, + { url = "https://files.pythonhosted.org/packages/bc/4a/ac6469c01813cb3652ab4e30ec4a37815cc9949afc18af33f64e2ec704aa/pyzmq-25.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc69c96735ab501419c432110016329bf0dea8898ce16fab97c6d9106dc0b348", size = 904394, upload_time = "2023-12-05T07:42:27.815Z" }, + { url = "https://files.pythonhosted.org/packages/77/b7/8cee519b11bdd3f76c1a6eb537ab13c1bfef2964d725717705c86f524e4c/pyzmq-25.1.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3e124e6b1dd3dfbeb695435dff0e383256655bb18082e094a8dd1f6293114642", size = 1161453, upload_time = "2023-12-05T07:44:32.003Z" }, + { url = "https://files.pythonhosted.org/packages/b6/1d/c35a956a44b333b064ae1b1c588c2dfa0e01b7ec90884c1972bfcef119c3/pyzmq-25.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7598d2ba821caa37a0f9d54c25164a4fa351ce019d64d0b44b45540950458840", size = 1105501, upload_time = "2023-12-05T07:45:07.18Z" }, + { url = "https://files.pythonhosted.org/packages/18/d1/b3d1e985318ed7287737ea9e6b6e21748cc7c89accc2443347cd2c8d5f0f/pyzmq-25.1.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d1299d7e964c13607efd148ca1f07dcbf27c3ab9e125d1d0ae1d580a1682399d", size = 1109513, upload_time = "2023-12-05T07:39:53.338Z" }, + { url = "https://files.pythonhosted.org/packages/14/9b/341cdfb47440069010101403298dc24d449150370c6cb322e73bfa1949bd/pyzmq-25.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4e6f689880d5ad87918430957297c975203a082d9a036cc426648fcbedae769b", size = 1433541, upload_time = "2023-12-05T07:41:10.786Z" }, + { url = "https://files.pythonhosted.org/packages/fa/52/c6d4e76e020c554e965459d41a98201b4d45277a288648f53a4e5a2429cc/pyzmq-25.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cc69949484171cc961e6ecd4a8911b9ce7a0d1f738fcae717177c231bf77437b", size = 1766133, upload_time = "2023-12-05T07:49:11.204Z" }, + { url = "https://files.pythonhosted.org/packages/1d/6d/0cbd8dd5b8979fd6b9cf1852ed067b9d2cd6fa0c09c3bafe6874d2d2e03c/pyzmq-25.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9880078f683466b7f567b8624bfc16cad65077be046b6e8abb53bed4eeb82dd3", size = 1653636, upload_time = "2023-12-05T07:49:13.787Z" }, + { url = "https://files.pythonhosted.org/packages/f5/af/d90eed9cf3840685d54d4a35d5f9e242a8a48b5410d41146f14c1e098302/pyzmq-25.1.2-cp311-cp311-win32.whl", hash = "sha256:4e5837af3e5aaa99a091302df5ee001149baff06ad22b722d34e30df5f0d9097", size = 904865, upload_time = "2023-12-05T07:42:07.189Z" }, + { url = "https://files.pythonhosted.org/packages/20/d2/09443dc73053ad01c846d7fb77e09fe9d93c09d4e900215f3c8b7b56bfec/pyzmq-25.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:25c2dbb97d38b5ac9fd15586e048ec5eb1e38f3d47fe7d92167b0c77bb3584e9", size = 1171332, upload_time = "2023-12-05T07:44:56.111Z" }, + { url = "https://files.pythonhosted.org/packages/6e/f0/d71cf69dc039c9adc8b625efc3bad3684f3660a570e47f0f0c64df787b41/pyzmq-25.1.2-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:11e70516688190e9c2db14fcf93c04192b02d457b582a1f6190b154691b4c93a", size = 1871111, upload_time = "2023-12-05T07:48:17.868Z" }, + { url = "https://files.pythonhosted.org/packages/68/62/d365773edf56ad71993579ee574105f02f83530caf600ebf28bea15d88d0/pyzmq-25.1.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:313c3794d650d1fccaaab2df942af9f2c01d6217c846177cfcbc693c7410839e", size = 1248844, upload_time = "2023-12-05T07:50:42.922Z" }, + { url = "https://files.pythonhosted.org/packages/72/55/cc3163e20f40615a49245fa7041badec6103e8ee7e482dbb0feea00a7b84/pyzmq-25.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b3cbba2f47062b85fe0ef9de5b987612140a9ba3a9c6d2543c6dec9f7c2ab27", size = 899373, upload_time = "2023-12-05T07:42:29.595Z" }, + { url = "https://files.pythonhosted.org/packages/40/aa/ae292bd85deda637230970bbc53c1dc53696a99e82fc7cd6d373ec173853/pyzmq-25.1.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc31baa0c32a2ca660784d5af3b9487e13b61b3032cb01a115fce6588e1bed30", size = 1160901, upload_time = "2023-12-05T07:44:33.819Z" }, + { url = "https://files.pythonhosted.org/packages/93/b7/6e291eafbbbc66d0e87658dd21383ec2b4ab35edcfb283902c580a6db76f/pyzmq-25.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c9087b109070c5ab0b383079fa1b5f797f8d43e9a66c07a4b8b8bdecfd88ee", size = 1101147, upload_time = "2023-12-05T07:45:10.058Z" }, + { url = "https://files.pythonhosted.org/packages/3a/f1/e296d5a507eac519d1fe1382851b1a4575f690bc2b2d2c8eca2ed7e4bd1f/pyzmq-25.1.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f8429b17cbb746c3e043cb986328da023657e79d5ed258b711c06a70c2ea7537", size = 1105315, upload_time = "2023-12-05T07:39:55.851Z" }, + { url = "https://files.pythonhosted.org/packages/56/63/5c2abb556ab4cf013d98e01782d5bd642238a0ed9b019e965a7d7e957f56/pyzmq-25.1.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5074adeacede5f810b7ef39607ee59d94e948b4fd954495bdb072f8c54558181", size = 1427747, upload_time = "2023-12-05T07:41:13.219Z" }, + { url = "https://files.pythonhosted.org/packages/b1/71/5dba5f6b12ef54fb977c9b9279075e151c04fc0dd6851e9663d9e66b593f/pyzmq-25.1.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7ae8f354b895cbd85212da245f1a5ad8159e7840e37d78b476bb4f4c3f32a9fe", size = 1762221, upload_time = "2023-12-05T07:49:16.352Z" }, + { url = "https://files.pythonhosted.org/packages/cf/49/54d7e8bb3df82a3509325b11491d33450dc91580d4826b62fa5e554bb9cf/pyzmq-25.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b264bf2cc96b5bc43ce0e852be995e400376bd87ceb363822e2cb1964fcdc737", size = 1649505, upload_time = "2023-12-05T07:49:18.952Z" }, + { url = "https://files.pythonhosted.org/packages/34/14/58e5037229bc37963e2ce804c2c075a3a541e3f84bf1c231e7c9779d36f1/pyzmq-25.1.2-cp312-cp312-win32.whl", hash = "sha256:02bbc1a87b76e04fd780b45e7f695471ae6de747769e540da909173d50ff8e2d", size = 954891, upload_time = "2023-12-05T07:42:09.208Z" }, + { url = "https://files.pythonhosted.org/packages/2c/2d/04fab685ef3a8e6e955220fd2a54dc99efaee960a88675bf5c92cd277164/pyzmq-25.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:ced111c2e81506abd1dc142e6cd7b68dd53747b3b7ae5edbea4578c5eeff96b7", size = 1252773, upload_time = "2023-12-05T07:44:58.16Z" }, + { url = "https://files.pythonhosted.org/packages/6b/fe/ed38fe12c540bafc1cae32c3ff638e9df32528f5cf91b5e400e6a8f5b3ec/pyzmq-25.1.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a8c1d566344aee826b74e472e16edae0a02e2a044f14f7c24e123002dcff1c05", size = 963654, upload_time = "2023-12-05T07:47:03.874Z" }, + { url = "https://files.pythonhosted.org/packages/44/97/a760a2dff0672c408f22f726f2ea10a7a516ffa5001ca5a3641e355a45f9/pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:759cfd391a0996345ba94b6a5110fca9c557ad4166d86a6e81ea526c376a01e8", size = 609436, upload_time = "2023-12-05T07:42:37.762Z" }, + { url = "https://files.pythonhosted.org/packages/41/81/ace39daa19c78b2f4fc12ef217d9d5f1ac658d5828d692bbbb68240cd55b/pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c61e346ac34b74028ede1c6b4bcecf649d69b707b3ff9dc0fab453821b04d1e", size = 843396, upload_time = "2023-12-05T07:44:43.727Z" }, + { url = "https://files.pythonhosted.org/packages/4c/43/150b0b203f5461a9aeadaa925c55167e2b4215c9322b6911a64360d2243e/pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cb8fc1f8d69b411b8ec0b5f1ffbcaf14c1db95b6bccea21d83610987435f1a4", size = 800856, upload_time = "2023-12-05T07:45:21.117Z" }, + { url = "https://files.pythonhosted.org/packages/5f/91/a618b56aaabe40dddcd25db85624d7408768fd32f5bfcf81bc0af5b1ce75/pyzmq-25.1.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3c00c9b7d1ca8165c610437ca0c92e7b5607b2f9076f4eb4b095c85d6e680a1d", size = 413836, upload_time = "2023-12-05T07:53:22.583Z" }, ] [[package]] @@ -2118,9 +2176,9 @@ dependencies = [ { name = "scikit-learn" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3e/2d/bab8babd9dc9a9e4df6eb115540cee4322c1a74078fb6f3b3ebc452a22b3/qudida-0.0.4.tar.gz", hash = "sha256:db198e2887ab0c9aa0023e565afbff41dfb76b361f85fd5e13f780d75ba18cc8", size = 3100 } +sdist = { url = "https://files.pythonhosted.org/packages/3e/2d/bab8babd9dc9a9e4df6eb115540cee4322c1a74078fb6f3b3ebc452a22b3/qudida-0.0.4.tar.gz", hash = "sha256:db198e2887ab0c9aa0023e565afbff41dfb76b361f85fd5e13f780d75ba18cc8", size = 3100, upload_time = "2021-08-09T16:47:55.807Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/a1/a5f4bebaa31d109003909809d88aeb0d4b201463a9ea29308d9e4f9e7655/qudida-0.0.4-py3-none-any.whl", hash = "sha256:4519714c40cd0f2e6c51e1735edae8f8b19f4efe1f33be13e9d644ca5f736dd6", size = 3478 }, + { url = "https://files.pythonhosted.org/packages/f0/a1/a5f4bebaa31d109003909809d88aeb0d4b201463a9ea29308d9e4f9e7655/qudida-0.0.4-py3-none-any.whl", hash = "sha256:4519714c40cd0f2e6c51e1735edae8f8b19f4efe1f33be13e9d644ca5f736dd6", size = 3478, upload_time = "2021-08-09T16:47:54.637Z" }, ] [[package]] @@ -2133,9 +2191,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload_time = "2024-05-29T15:37:49.536Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload_time = "2024-05-29T15:37:47.027Z" }, ] [[package]] @@ -2147,9 +2205,9 @@ dependencies = [ { name = "pygments" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 } +sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149, upload_time = "2024-11-01T16:43:57.873Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 }, + { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424, upload_time = "2024-11-01T16:43:55.817Z" }, ] [[package]] @@ -2162,9 +2220,9 @@ dependencies = [ { name = "ruamel-yaml" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/77/6af374a4a8cd2aee762a1fb8a3050dcf3f129134bbdc4bb6bed755c4325b/rknn_toolkit_lite2-2.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b6733689bd09a262bcb6ba4744e690dd4b37ebeac4ed427cf45242c4b4ce9a4", size = 559372 }, - { url = "https://files.pythonhosted.org/packages/9b/0c/76ff1eb09d09ce4394a6959d2343a321d28dd9e604348ffdafceafdc344c/rknn_toolkit_lite2-2.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3e4fefe355dc34a155680e4bcb9e4abb37ebc271f045ec9e0a4a3a018bc5beb", size = 569149 }, - { url = "https://files.pythonhosted.org/packages/0d/6e/8679562028051b02312212defc6e8c07248953f10dd7ad506e941b575bf3/rknn_toolkit_lite2-2.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37394371d1561f470c553f39869d7c35ff93405dffe3d0d72babf297a2b0aee9", size = 527457 }, + { url = "https://files.pythonhosted.org/packages/ed/77/6af374a4a8cd2aee762a1fb8a3050dcf3f129134bbdc4bb6bed755c4325b/rknn_toolkit_lite2-2.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b6733689bd09a262bcb6ba4744e690dd4b37ebeac4ed427cf45242c4b4ce9a4", size = 559372, upload_time = "2024-11-11T03:51:20.599Z" }, + { url = "https://files.pythonhosted.org/packages/9b/0c/76ff1eb09d09ce4394a6959d2343a321d28dd9e604348ffdafceafdc344c/rknn_toolkit_lite2-2.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3e4fefe355dc34a155680e4bcb9e4abb37ebc271f045ec9e0a4a3a018bc5beb", size = 569149, upload_time = "2024-11-11T03:51:22.838Z" }, + { url = "https://files.pythonhosted.org/packages/0d/6e/8679562028051b02312212defc6e8c07248953f10dd7ad506e941b575bf3/rknn_toolkit_lite2-2.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37394371d1561f470c553f39869d7c35ff93405dffe3d0d72babf297a2b0aee9", size = 527457, upload_time = "2024-11-11T03:51:25.456Z" }, ] [[package]] @@ -2174,78 +2232,78 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ruamel-yaml-clib", marker = "python_full_version < '3.13' and platform_python_implementation == 'CPython'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ea/46/f44d8be06b85bc7c4d8c95d658be2b68f27711f279bf9dd0612a5e4794f5/ruamel.yaml-0.18.10.tar.gz", hash = "sha256:20c86ab29ac2153f80a428e1254a8adf686d3383df04490514ca3b79a362db58", size = 143447 } +sdist = { url = "https://files.pythonhosted.org/packages/ea/46/f44d8be06b85bc7c4d8c95d658be2b68f27711f279bf9dd0612a5e4794f5/ruamel.yaml-0.18.10.tar.gz", hash = "sha256:20c86ab29ac2153f80a428e1254a8adf686d3383df04490514ca3b79a362db58", size = 143447, upload_time = "2025-01-06T14:08:51.334Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/36/dfc1ebc0081e6d39924a2cc53654497f967a084a436bb64402dfce4254d9/ruamel.yaml-0.18.10-py3-none-any.whl", hash = "sha256:30f22513ab2301b3d2b577adc121c6471f28734d3d9728581245f1e76468b4f1", size = 117729 }, + { url = "https://files.pythonhosted.org/packages/c2/36/dfc1ebc0081e6d39924a2cc53654497f967a084a436bb64402dfce4254d9/ruamel.yaml-0.18.10-py3-none-any.whl", hash = "sha256:30f22513ab2301b3d2b577adc121c6471f28734d3d9728581245f1e76468b4f1", size = 117729, upload_time = "2025-01-06T14:08:47.471Z" }, ] [[package]] name = "ruamel-yaml-clib" version = "0.2.12" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/84/80203abff8ea4993a87d823a5f632e4d92831ef75d404c9fc78d0176d2b5/ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f", size = 225315 } +sdist = { url = "https://files.pythonhosted.org/packages/20/84/80203abff8ea4993a87d823a5f632e4d92831ef75d404c9fc78d0176d2b5/ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f", size = 225315, upload_time = "2024-10-20T10:10:56.22Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/57/40a958e863e299f0c74ef32a3bde9f2d1ea8d69669368c0c502a0997f57f/ruamel.yaml.clib-0.2.12-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5", size = 131301 }, - { url = "https://files.pythonhosted.org/packages/98/a8/29a3eb437b12b95f50a6bcc3d7d7214301c6c529d8fdc227247fa84162b5/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969", size = 633728 }, - { url = "https://files.pythonhosted.org/packages/35/6d/ae05a87a3ad540259c3ad88d71275cbd1c0f2d30ae04c65dcbfb6dcd4b9f/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df", size = 722230 }, - { url = "https://files.pythonhosted.org/packages/7f/b7/20c6f3c0b656fe609675d69bc135c03aac9e3865912444be6339207b6648/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76", size = 686712 }, - { url = "https://files.pythonhosted.org/packages/cd/11/d12dbf683471f888d354dac59593873c2b45feb193c5e3e0f2ebf85e68b9/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6", size = 663936 }, - { url = "https://files.pythonhosted.org/packages/72/14/4c268f5077db5c83f743ee1daeb236269fa8577133a5cfa49f8b382baf13/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd", size = 696580 }, - { url = "https://files.pythonhosted.org/packages/30/fc/8cd12f189c6405a4c1cf37bd633aa740a9538c8e40497c231072d0fef5cf/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a", size = 663393 }, - { url = "https://files.pythonhosted.org/packages/80/29/c0a017b704aaf3cbf704989785cd9c5d5b8ccec2dae6ac0c53833c84e677/ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da", size = 100326 }, - { url = "https://files.pythonhosted.org/packages/3a/65/fa39d74db4e2d0cd252355732d966a460a41cd01c6353b820a0952432839/ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28", size = 118079 }, - { url = "https://files.pythonhosted.org/packages/fb/8f/683c6ad562f558cbc4f7c029abcd9599148c51c54b5ef0f24f2638da9fbb/ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6", size = 132224 }, - { url = "https://files.pythonhosted.org/packages/3c/d2/b79b7d695e2f21da020bd44c782490578f300dd44f0a4c57a92575758a76/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e", size = 641480 }, - { url = "https://files.pythonhosted.org/packages/68/6e/264c50ce2a31473a9fdbf4fa66ca9b2b17c7455b31ef585462343818bd6c/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e", size = 739068 }, - { url = "https://files.pythonhosted.org/packages/86/29/88c2567bc893c84d88b4c48027367c3562ae69121d568e8a3f3a8d363f4d/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52", size = 703012 }, - { url = "https://files.pythonhosted.org/packages/11/46/879763c619b5470820f0cd6ca97d134771e502776bc2b844d2adb6e37753/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642", size = 704352 }, - { url = "https://files.pythonhosted.org/packages/02/80/ece7e6034256a4186bbe50dee28cd032d816974941a6abf6a9d65e4228a7/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2", size = 737344 }, - { url = "https://files.pythonhosted.org/packages/f0/ca/e4106ac7e80efbabdf4bf91d3d32fc424e41418458251712f5672eada9ce/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3", size = 714498 }, - { url = "https://files.pythonhosted.org/packages/67/58/b1f60a1d591b771298ffa0428237afb092c7f29ae23bad93420b1eb10703/ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4", size = 100205 }, - { url = "https://files.pythonhosted.org/packages/b4/4f/b52f634c9548a9291a70dfce26ca7ebce388235c93588a1068028ea23fcc/ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb", size = 118185 }, - { url = "https://files.pythonhosted.org/packages/48/41/e7a405afbdc26af961678474a55373e1b323605a4f5e2ddd4a80ea80f628/ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632", size = 133433 }, - { url = "https://files.pythonhosted.org/packages/ec/b0/b850385604334c2ce90e3ee1013bd911aedf058a934905863a6ea95e9eb4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d", size = 647362 }, - { url = "https://files.pythonhosted.org/packages/44/d0/3f68a86e006448fb6c005aee66565b9eb89014a70c491d70c08de597f8e4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c", size = 754118 }, - { url = "https://files.pythonhosted.org/packages/52/a9/d39f3c5ada0a3bb2870d7db41901125dbe2434fa4f12ca8c5b83a42d7c53/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd", size = 706497 }, - { url = "https://files.pythonhosted.org/packages/b0/fa/097e38135dadd9ac25aecf2a54be17ddf6e4c23e43d538492a90ab3d71c6/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31", size = 698042 }, - { url = "https://files.pythonhosted.org/packages/ec/d5/a659ca6f503b9379b930f13bc6b130c9f176469b73b9834296822a83a132/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680", size = 745831 }, - { url = "https://files.pythonhosted.org/packages/db/5d/36619b61ffa2429eeaefaab4f3374666adf36ad8ac6330d855848d7d36fd/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d", size = 715692 }, - { url = "https://files.pythonhosted.org/packages/b1/82/85cb92f15a4231c89b95dfe08b09eb6adca929ef7df7e17ab59902b6f589/ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5", size = 98777 }, - { url = "https://files.pythonhosted.org/packages/d7/8f/c3654f6f1ddb75daf3922c3d8fc6005b1ab56671ad56ffb874d908bfa668/ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4", size = 115523 }, - { url = "https://files.pythonhosted.org/packages/29/00/4864119668d71a5fa45678f380b5923ff410701565821925c69780356ffa/ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a", size = 132011 }, - { url = "https://files.pythonhosted.org/packages/7f/5e/212f473a93ae78c669ffa0cb051e3fee1139cb2d385d2ae1653d64281507/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475", size = 642488 }, - { url = "https://files.pythonhosted.org/packages/1f/8f/ecfbe2123ade605c49ef769788f79c38ddb1c8fa81e01f4dbf5cf1a44b16/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef", size = 745066 }, - { url = "https://files.pythonhosted.org/packages/e2/a9/28f60726d29dfc01b8decdb385de4ced2ced9faeb37a847bd5cf26836815/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6", size = 701785 }, - { url = "https://files.pythonhosted.org/packages/84/7e/8e7ec45920daa7f76046578e4f677a3215fe8f18ee30a9cb7627a19d9b4c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf", size = 693017 }, - { url = "https://files.pythonhosted.org/packages/c5/b3/d650eaade4ca225f02a648321e1ab835b9d361c60d51150bac49063b83fa/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1", size = 741270 }, - { url = "https://files.pythonhosted.org/packages/87/b8/01c29b924dcbbed75cc45b30c30d565d763b9c4d540545a0eeecffb8f09c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01", size = 709059 }, - { url = "https://files.pythonhosted.org/packages/30/8c/ed73f047a73638257aa9377ad356bea4d96125b305c34a28766f4445cc0f/ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6", size = 98583 }, - { url = "https://files.pythonhosted.org/packages/b0/85/e8e751d8791564dd333d5d9a4eab0a7a115f7e349595417fd50ecae3395c/ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3", size = 115190 }, + { url = "https://files.pythonhosted.org/packages/70/57/40a958e863e299f0c74ef32a3bde9f2d1ea8d69669368c0c502a0997f57f/ruamel.yaml.clib-0.2.12-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5", size = 131301, upload_time = "2024-10-20T10:12:35.876Z" }, + { url = "https://files.pythonhosted.org/packages/98/a8/29a3eb437b12b95f50a6bcc3d7d7214301c6c529d8fdc227247fa84162b5/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969", size = 633728, upload_time = "2024-10-20T10:12:37.858Z" }, + { url = "https://files.pythonhosted.org/packages/35/6d/ae05a87a3ad540259c3ad88d71275cbd1c0f2d30ae04c65dcbfb6dcd4b9f/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df", size = 722230, upload_time = "2024-10-20T10:12:39.457Z" }, + { url = "https://files.pythonhosted.org/packages/7f/b7/20c6f3c0b656fe609675d69bc135c03aac9e3865912444be6339207b6648/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76", size = 686712, upload_time = "2024-10-20T10:12:41.119Z" }, + { url = "https://files.pythonhosted.org/packages/cd/11/d12dbf683471f888d354dac59593873c2b45feb193c5e3e0f2ebf85e68b9/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6", size = 663936, upload_time = "2024-10-21T11:26:37.419Z" }, + { url = "https://files.pythonhosted.org/packages/72/14/4c268f5077db5c83f743ee1daeb236269fa8577133a5cfa49f8b382baf13/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd", size = 696580, upload_time = "2024-10-21T11:26:39.503Z" }, + { url = "https://files.pythonhosted.org/packages/30/fc/8cd12f189c6405a4c1cf37bd633aa740a9538c8e40497c231072d0fef5cf/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a", size = 663393, upload_time = "2024-12-11T19:58:13.873Z" }, + { url = "https://files.pythonhosted.org/packages/80/29/c0a017b704aaf3cbf704989785cd9c5d5b8ccec2dae6ac0c53833c84e677/ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da", size = 100326, upload_time = "2024-10-20T10:12:42.967Z" }, + { url = "https://files.pythonhosted.org/packages/3a/65/fa39d74db4e2d0cd252355732d966a460a41cd01c6353b820a0952432839/ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28", size = 118079, upload_time = "2024-10-20T10:12:44.117Z" }, + { url = "https://files.pythonhosted.org/packages/fb/8f/683c6ad562f558cbc4f7c029abcd9599148c51c54b5ef0f24f2638da9fbb/ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6", size = 132224, upload_time = "2024-10-20T10:12:45.162Z" }, + { url = "https://files.pythonhosted.org/packages/3c/d2/b79b7d695e2f21da020bd44c782490578f300dd44f0a4c57a92575758a76/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e", size = 641480, upload_time = "2024-10-20T10:12:46.758Z" }, + { url = "https://files.pythonhosted.org/packages/68/6e/264c50ce2a31473a9fdbf4fa66ca9b2b17c7455b31ef585462343818bd6c/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e", size = 739068, upload_time = "2024-10-20T10:12:48.605Z" }, + { url = "https://files.pythonhosted.org/packages/86/29/88c2567bc893c84d88b4c48027367c3562ae69121d568e8a3f3a8d363f4d/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52", size = 703012, upload_time = "2024-10-20T10:12:51.124Z" }, + { url = "https://files.pythonhosted.org/packages/11/46/879763c619b5470820f0cd6ca97d134771e502776bc2b844d2adb6e37753/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642", size = 704352, upload_time = "2024-10-21T11:26:41.438Z" }, + { url = "https://files.pythonhosted.org/packages/02/80/ece7e6034256a4186bbe50dee28cd032d816974941a6abf6a9d65e4228a7/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2", size = 737344, upload_time = "2024-10-21T11:26:43.62Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ca/e4106ac7e80efbabdf4bf91d3d32fc424e41418458251712f5672eada9ce/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3", size = 714498, upload_time = "2024-12-11T19:58:15.592Z" }, + { url = "https://files.pythonhosted.org/packages/67/58/b1f60a1d591b771298ffa0428237afb092c7f29ae23bad93420b1eb10703/ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4", size = 100205, upload_time = "2024-10-20T10:12:52.865Z" }, + { url = "https://files.pythonhosted.org/packages/b4/4f/b52f634c9548a9291a70dfce26ca7ebce388235c93588a1068028ea23fcc/ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb", size = 118185, upload_time = "2024-10-20T10:12:54.652Z" }, + { url = "https://files.pythonhosted.org/packages/48/41/e7a405afbdc26af961678474a55373e1b323605a4f5e2ddd4a80ea80f628/ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632", size = 133433, upload_time = "2024-10-20T10:12:55.657Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b0/b850385604334c2ce90e3ee1013bd911aedf058a934905863a6ea95e9eb4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d", size = 647362, upload_time = "2024-10-20T10:12:57.155Z" }, + { url = "https://files.pythonhosted.org/packages/44/d0/3f68a86e006448fb6c005aee66565b9eb89014a70c491d70c08de597f8e4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c", size = 754118, upload_time = "2024-10-20T10:12:58.501Z" }, + { url = "https://files.pythonhosted.org/packages/52/a9/d39f3c5ada0a3bb2870d7db41901125dbe2434fa4f12ca8c5b83a42d7c53/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd", size = 706497, upload_time = "2024-10-20T10:13:00.211Z" }, + { url = "https://files.pythonhosted.org/packages/b0/fa/097e38135dadd9ac25aecf2a54be17ddf6e4c23e43d538492a90ab3d71c6/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31", size = 698042, upload_time = "2024-10-21T11:26:46.038Z" }, + { url = "https://files.pythonhosted.org/packages/ec/d5/a659ca6f503b9379b930f13bc6b130c9f176469b73b9834296822a83a132/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680", size = 745831, upload_time = "2024-10-21T11:26:47.487Z" }, + { url = "https://files.pythonhosted.org/packages/db/5d/36619b61ffa2429eeaefaab4f3374666adf36ad8ac6330d855848d7d36fd/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d", size = 715692, upload_time = "2024-12-11T19:58:17.252Z" }, + { url = "https://files.pythonhosted.org/packages/b1/82/85cb92f15a4231c89b95dfe08b09eb6adca929ef7df7e17ab59902b6f589/ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5", size = 98777, upload_time = "2024-10-20T10:13:01.395Z" }, + { url = "https://files.pythonhosted.org/packages/d7/8f/c3654f6f1ddb75daf3922c3d8fc6005b1ab56671ad56ffb874d908bfa668/ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4", size = 115523, upload_time = "2024-10-20T10:13:02.768Z" }, + { url = "https://files.pythonhosted.org/packages/29/00/4864119668d71a5fa45678f380b5923ff410701565821925c69780356ffa/ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a", size = 132011, upload_time = "2024-10-20T10:13:04.377Z" }, + { url = "https://files.pythonhosted.org/packages/7f/5e/212f473a93ae78c669ffa0cb051e3fee1139cb2d385d2ae1653d64281507/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475", size = 642488, upload_time = "2024-10-20T10:13:05.906Z" }, + { url = "https://files.pythonhosted.org/packages/1f/8f/ecfbe2123ade605c49ef769788f79c38ddb1c8fa81e01f4dbf5cf1a44b16/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef", size = 745066, upload_time = "2024-10-20T10:13:07.26Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a9/28f60726d29dfc01b8decdb385de4ced2ced9faeb37a847bd5cf26836815/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6", size = 701785, upload_time = "2024-10-20T10:13:08.504Z" }, + { url = "https://files.pythonhosted.org/packages/84/7e/8e7ec45920daa7f76046578e4f677a3215fe8f18ee30a9cb7627a19d9b4c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf", size = 693017, upload_time = "2024-10-21T11:26:48.866Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b3/d650eaade4ca225f02a648321e1ab835b9d361c60d51150bac49063b83fa/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1", size = 741270, upload_time = "2024-10-21T11:26:50.213Z" }, + { url = "https://files.pythonhosted.org/packages/87/b8/01c29b924dcbbed75cc45b30c30d565d763b9c4d540545a0eeecffb8f09c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01", size = 709059, upload_time = "2024-12-11T19:58:18.846Z" }, + { url = "https://files.pythonhosted.org/packages/30/8c/ed73f047a73638257aa9377ad356bea4d96125b305c34a28766f4445cc0f/ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6", size = 98583, upload_time = "2024-10-20T10:13:09.658Z" }, + { url = "https://files.pythonhosted.org/packages/b0/85/e8e751d8791564dd333d5d9a4eab0a7a115f7e349595417fd50ecae3395c/ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3", size = 115190, upload_time = "2024-10-20T10:13:10.66Z" }, ] [[package]] name = "ruff" -version = "0.11.5" +version = "0.11.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/45/71/5759b2a6b2279bb77fe15b1435b89473631c2cd6374d45ccdb6b785810be/ruff-0.11.5.tar.gz", hash = "sha256:cae2e2439cb88853e421901ec040a758960b576126dab520fa08e9de431d1bef", size = 3976488 } +sdist = { url = "https://files.pythonhosted.org/packages/5b/89/6f9c9674818ac2e9cc2f2b35b704b7768656e6b7c139064fc7ba8fbc99f1/ruff-0.11.7.tar.gz", hash = "sha256:655089ad3224070736dc32844fde783454f8558e71f501cb207485fe4eee23d4", size = 4054861, upload_time = "2025-04-24T18:49:37.007Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/23/db/6efda6381778eec7f35875b5cbefd194904832a1153d68d36d6b269d81a8/ruff-0.11.5-py3-none-linux_armv6l.whl", hash = "sha256:2561294e108eb648e50f210671cc56aee590fb6167b594144401532138c66c7b", size = 10103150 }, - { url = "https://files.pythonhosted.org/packages/44/f2/06cd9006077a8db61956768bc200a8e52515bf33a8f9b671ee527bb10d77/ruff-0.11.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac12884b9e005c12d0bd121f56ccf8033e1614f736f766c118ad60780882a077", size = 10898637 }, - { url = "https://files.pythonhosted.org/packages/18/f5/af390a013c56022fe6f72b95c86eb7b2585c89cc25d63882d3bfe411ecf1/ruff-0.11.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4bfd80a6ec559a5eeb96c33f832418bf0fb96752de0539905cf7b0cc1d31d779", size = 10236012 }, - { url = "https://files.pythonhosted.org/packages/b8/ca/b9bf954cfed165e1a0c24b86305d5c8ea75def256707f2448439ac5e0d8b/ruff-0.11.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0947c0a1afa75dcb5db4b34b070ec2bccee869d40e6cc8ab25aca11a7d527794", size = 10415338 }, - { url = "https://files.pythonhosted.org/packages/d9/4d/2522dde4e790f1b59885283f8786ab0046958dfd39959c81acc75d347467/ruff-0.11.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ad871ff74b5ec9caa66cb725b85d4ef89b53f8170f47c3406e32ef040400b038", size = 9965277 }, - { url = "https://files.pythonhosted.org/packages/e5/7a/749f56f150eef71ce2f626a2f6988446c620af2f9ba2a7804295ca450397/ruff-0.11.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6cf918390cfe46d240732d4d72fa6e18e528ca1f60e318a10835cf2fa3dc19f", size = 11541614 }, - { url = "https://files.pythonhosted.org/packages/89/b2/7d9b8435222485b6aac627d9c29793ba89be40b5de11584ca604b829e960/ruff-0.11.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:56145ee1478582f61c08f21076dc59153310d606ad663acc00ea3ab5b2125f82", size = 12198873 }, - { url = "https://files.pythonhosted.org/packages/00/e0/a1a69ef5ffb5c5f9c31554b27e030a9c468fc6f57055886d27d316dfbabd/ruff-0.11.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5f66f8f1e8c9fc594cbd66fbc5f246a8d91f916cb9667e80208663ec3728304", size = 11670190 }, - { url = "https://files.pythonhosted.org/packages/05/61/c1c16df6e92975072c07f8b20dad35cd858e8462b8865bc856fe5d6ccb63/ruff-0.11.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80b4df4d335a80315ab9afc81ed1cff62be112bd165e162b5eed8ac55bfc8470", size = 13902301 }, - { url = "https://files.pythonhosted.org/packages/79/89/0af10c8af4363304fd8cb833bd407a2850c760b71edf742c18d5a87bb3ad/ruff-0.11.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3068befab73620b8a0cc2431bd46b3cd619bc17d6f7695a3e1bb166b652c382a", size = 11350132 }, - { url = "https://files.pythonhosted.org/packages/b9/e1/ecb4c687cbf15164dd00e38cf62cbab238cad05dd8b6b0fc68b0c2785e15/ruff-0.11.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5da2e710a9641828e09aa98b92c9ebbc60518fdf3921241326ca3e8f8e55b8b", size = 10312937 }, - { url = "https://files.pythonhosted.org/packages/cf/4f/0e53fe5e500b65934500949361e3cd290c5ba60f0324ed59d15f46479c06/ruff-0.11.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ef39f19cb8ec98cbc762344921e216f3857a06c47412030374fffd413fb8fd3a", size = 9936683 }, - { url = "https://files.pythonhosted.org/packages/04/a8/8183c4da6d35794ae7f76f96261ef5960853cd3f899c2671961f97a27d8e/ruff-0.11.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b2a7cedf47244f431fd11aa5a7e2806dda2e0c365873bda7834e8f7d785ae159", size = 10950217 }, - { url = "https://files.pythonhosted.org/packages/26/88/9b85a5a8af21e46a0639b107fcf9bfc31da4f1d263f2fc7fbe7199b47f0a/ruff-0.11.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:81be52e7519f3d1a0beadcf8e974715b2dfc808ae8ec729ecfc79bddf8dbb783", size = 11404521 }, - { url = "https://files.pythonhosted.org/packages/fc/52/047f35d3b20fd1ae9ccfe28791ef0f3ca0ef0b3e6c1a58badd97d450131b/ruff-0.11.5-py3-none-win32.whl", hash = "sha256:e268da7b40f56e3eca571508a7e567e794f9bfcc0f412c4b607931d3af9c4afe", size = 10320697 }, - { url = "https://files.pythonhosted.org/packages/b9/fe/00c78010e3332a6e92762424cf4c1919065707e962232797d0b57fd8267e/ruff-0.11.5-py3-none-win_amd64.whl", hash = "sha256:6c6dc38af3cfe2863213ea25b6dc616d679205732dc0fb673356c2d69608f800", size = 11378665 }, - { url = "https://files.pythonhosted.org/packages/43/7c/c83fe5cbb70ff017612ff36654edfebec4b1ef79b558b8e5fd933bab836b/ruff-0.11.5-py3-none-win_arm64.whl", hash = "sha256:67e241b4314f4eacf14a601d586026a962f4002a475aa702c69980a38087aa4e", size = 10460287 }, + { url = "https://files.pythonhosted.org/packages/b4/ec/21927cb906c5614b786d1621dba405e3d44f6e473872e6df5d1a6bca0455/ruff-0.11.7-py3-none-linux_armv6l.whl", hash = "sha256:d29e909d9a8d02f928d72ab7837b5cbc450a5bdf578ab9ebee3263d0a525091c", size = 10245403, upload_time = "2025-04-24T18:48:40.459Z" }, + { url = "https://files.pythonhosted.org/packages/e2/af/fec85b6c2c725bcb062a354dd7cbc1eed53c33ff3aa665165871c9c16ddf/ruff-0.11.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dd1fb86b168ae349fb01dd497d83537b2c5541fe0626e70c786427dd8363aaee", size = 11007166, upload_time = "2025-04-24T18:48:44.742Z" }, + { url = "https://files.pythonhosted.org/packages/31/9a/2d0d260a58e81f388800343a45898fd8df73c608b8261c370058b675319a/ruff-0.11.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d3d7d2e140a6fbbc09033bce65bd7ea29d6a0adeb90b8430262fbacd58c38ada", size = 10378076, upload_time = "2025-04-24T18:48:47.918Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c4/9b09b45051404d2e7dd6d9dbcbabaa5ab0093f9febcae664876a77b9ad53/ruff-0.11.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4809df77de390a1c2077d9b7945d82f44b95d19ceccf0c287c56e4dc9b91ca64", size = 10557138, upload_time = "2025-04-24T18:48:51.707Z" }, + { url = "https://files.pythonhosted.org/packages/5e/5e/f62a1b6669870a591ed7db771c332fabb30f83c967f376b05e7c91bccd14/ruff-0.11.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3a0c2e169e6b545f8e2dba185eabbd9db4f08880032e75aa0e285a6d3f48201", size = 10095726, upload_time = "2025-04-24T18:48:54.243Z" }, + { url = "https://files.pythonhosted.org/packages/45/59/a7aa8e716f4cbe07c3500a391e58c52caf665bb242bf8be42c62adef649c/ruff-0.11.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49b888200a320dd96a68e86736cf531d6afba03e4f6cf098401406a257fcf3d6", size = 11672265, upload_time = "2025-04-24T18:48:57.639Z" }, + { url = "https://files.pythonhosted.org/packages/dd/e3/101a8b707481f37aca5f0fcc3e42932fa38b51add87bfbd8e41ab14adb24/ruff-0.11.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2b19cdb9cf7dae00d5ee2e7c013540cdc3b31c4f281f1dacb5a799d610e90db4", size = 12331418, upload_time = "2025-04-24T18:49:00.697Z" }, + { url = "https://files.pythonhosted.org/packages/dd/71/037f76cbe712f5cbc7b852e4916cd3cf32301a30351818d32ab71580d1c0/ruff-0.11.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64e0ee994c9e326b43539d133a36a455dbaab477bc84fe7bfbd528abe2f05c1e", size = 11794506, upload_time = "2025-04-24T18:49:03.545Z" }, + { url = "https://files.pythonhosted.org/packages/ca/de/e450b6bab1fc60ef263ef8fcda077fb4977601184877dce1c59109356084/ruff-0.11.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bad82052311479a5865f52c76ecee5d468a58ba44fb23ee15079f17dd4c8fd63", size = 13939084, upload_time = "2025-04-24T18:49:07.159Z" }, + { url = "https://files.pythonhosted.org/packages/0e/2c/1e364cc92970075d7d04c69c928430b23e43a433f044474f57e425cbed37/ruff-0.11.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7940665e74e7b65d427b82bffc1e46710ec7f30d58b4b2d5016e3f0321436502", size = 11450441, upload_time = "2025-04-24T18:49:11.41Z" }, + { url = "https://files.pythonhosted.org/packages/9d/7d/1b048eb460517ff9accd78bca0fa6ae61df2b276010538e586f834f5e402/ruff-0.11.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:169027e31c52c0e36c44ae9a9c7db35e505fee0b39f8d9fca7274a6305295a92", size = 10441060, upload_time = "2025-04-24T18:49:14.184Z" }, + { url = "https://files.pythonhosted.org/packages/3a/57/8dc6ccfd8380e5ca3d13ff7591e8ba46a3b330323515a4996b991b10bd5d/ruff-0.11.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:305b93f9798aee582e91e34437810439acb28b5fc1fee6b8205c78c806845a94", size = 10058689, upload_time = "2025-04-24T18:49:17.559Z" }, + { url = "https://files.pythonhosted.org/packages/23/bf/20487561ed72654147817885559ba2aa705272d8b5dee7654d3ef2dbf912/ruff-0.11.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a681db041ef55550c371f9cd52a3cf17a0da4c75d6bd691092dfc38170ebc4b6", size = 11073703, upload_time = "2025-04-24T18:49:20.247Z" }, + { url = "https://files.pythonhosted.org/packages/9d/27/04f2db95f4ef73dccedd0c21daf9991cc3b7f29901a4362057b132075aa4/ruff-0.11.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:07f1496ad00a4a139f4de220b0c97da6d4c85e0e4aa9b2624167b7d4d44fd6b6", size = 11532822, upload_time = "2025-04-24T18:49:23.765Z" }, + { url = "https://files.pythonhosted.org/packages/e1/72/43b123e4db52144c8add336581de52185097545981ff6e9e58a21861c250/ruff-0.11.7-py3-none-win32.whl", hash = "sha256:f25dfb853ad217e6e5f1924ae8a5b3f6709051a13e9dad18690de6c8ff299e26", size = 10362436, upload_time = "2025-04-24T18:49:27.377Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a0/3e58cd76fdee53d5c8ce7a56d84540833f924ccdf2c7d657cb009e604d82/ruff-0.11.7-py3-none-win_amd64.whl", hash = "sha256:0a931d85959ceb77e92aea4bbedfded0a31534ce191252721128f77e5ae1f98a", size = 11566676, upload_time = "2025-04-24T18:49:30.938Z" }, + { url = "https://files.pythonhosted.org/packages/68/ca/69d7c7752bce162d1516e5592b1cc6b6668e9328c0d270609ddbeeadd7cf/ruff-0.11.7-py3-none-win_arm64.whl", hash = "sha256:778c1e5d6f9e91034142dfd06110534ca13220bfaad5c3735f6cb844654f6177", size = 10677936, upload_time = "2025-04-24T18:49:34.392Z" }, ] [[package]] @@ -2262,23 +2320,23 @@ dependencies = [ { name = "scipy" }, { name = "tifffile" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/65/c1/a49da20845f0f0e1afbb1c2586d406dc0acb84c26ae293bad6d7e7f718bc/scikit_image-0.22.0.tar.gz", hash = "sha256:018d734df1d2da2719087d15f679d19285fce97cd37695103deadfaef2873236", size = 22685018 } +sdist = { url = "https://files.pythonhosted.org/packages/65/c1/a49da20845f0f0e1afbb1c2586d406dc0acb84c26ae293bad6d7e7f718bc/scikit_image-0.22.0.tar.gz", hash = "sha256:018d734df1d2da2719087d15f679d19285fce97cd37695103deadfaef2873236", size = 22685018, upload_time = "2023-10-03T21:36:34.274Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/8c/381ae42b37cf3e9e99a1deb3ffe76ca5ff5dd18ffa368293476164507fad/scikit_image-0.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74ec5c1d4693506842cc7c9487c89d8fc32aed064e9363def7af08b8f8cbb31d", size = 13905039 }, - { url = "https://files.pythonhosted.org/packages/16/06/4bfba08f5cce26d5070bb2cf4e3f9f479480978806355d1c5bea6f26a17c/scikit_image-0.22.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:a05ae4fe03d802587ed8974e900b943275548cde6a6807b785039d63e9a7a5ff", size = 13279212 }, - { url = "https://files.pythonhosted.org/packages/74/57/dbf744ca00eea2a09b1848c9dec28a43978c16dc049b1fba949cb050bedf/scikit_image-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a92dca3d95b1301442af055e196a54b5a5128c6768b79fc0a4098f1d662dee6", size = 14091779 }, - { url = "https://files.pythonhosted.org/packages/f1/6c/49f5a0ce8ddcdbdac5ac69c129654938cc6de0a936303caa6cad495ceb2a/scikit_image-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3663d063d8bf2fb9bdfb0ca967b9ee3b6593139c860c7abc2d2351a8a8863938", size = 14682042 }, - { url = "https://files.pythonhosted.org/packages/86/f0/18895318109f9b508f2310f136922e455a453550826a8240b412063c2528/scikit_image-0.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:ebdbdc901bae14dab637f8d5c99f6d5cc7aaf4a3b6f4003194e003e9f688a6fc", size = 24492345 }, - { url = "https://files.pythonhosted.org/packages/9f/d9/dc99e527d1a0050f0353d2fff3548273b4df6151884806e324f26572fd6b/scikit_image-0.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:95d6da2d8a44a36ae04437c76d32deb4e3c993ffc846b394b9949fd8ded73cb2", size = 13883619 }, - { url = "https://files.pythonhosted.org/packages/80/37/7670020b112ff9a47e49b1e36f438d000db5b632aab8a8fd7e6be545d065/scikit_image-0.22.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:2c6ef454a85f569659b813ac2a93948022b0298516b757c9c6c904132be327e2", size = 13264761 }, - { url = "https://files.pythonhosted.org/packages/ad/85/dadf1194793ac1c895370f3ed048bb91dda083775b42e11d9672a50494d5/scikit_image-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e87872f067444ee90a00dd49ca897208308645382e8a24bd3e76f301af2352cd", size = 14070710 }, - { url = "https://files.pythonhosted.org/packages/d4/34/e27bf2bfe7b52b884b49bd71ea91ff81e4737246735ee5ea383314c31876/scikit_image-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5c378db54e61b491b9edeefff87e49fcf7fdf729bb93c777d7a5f15d36f743e", size = 14664172 }, - { url = "https://files.pythonhosted.org/packages/ce/d0/a3f60c9f57ed295b3076e4acdb29a37bbd8823452562ab2ad51b03d6f377/scikit_image-0.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:2bcb74adb0634258a67f66c2bb29978c9a3e222463e003b67ba12056c003971b", size = 24491321 }, - { url = "https://files.pythonhosted.org/packages/da/a4/b0b69bde4d6360e801d647691591dc9967a25a18a4c63ecf7f87d94e3fac/scikit_image-0.22.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:003ca2274ac0fac252280e7179ff986ff783407001459ddea443fe7916e38cff", size = 13968808 }, - { url = "https://files.pythonhosted.org/packages/e4/65/3c0f77e7a9bae100a8f7f5cebde410fca1a3cf64e1ecdd343666e27b11d4/scikit_image-0.22.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:cf3c0c15b60ae3e557a0c7575fbd352f0c3ce0afca562febfe3ab80efbeec0e9", size = 13323763 }, - { url = "https://files.pythonhosted.org/packages/4a/ed/7faf9f7a55d5b3095d33990a85603b66866cce2a608b27f0e1487d70a451/scikit_image-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5b23908dd4d120e6aecb1ed0277563e8cbc8d6c0565bdc4c4c6475d53608452", size = 13877233 }, - { url = "https://files.pythonhosted.org/packages/ae/9d/09d06f36ce71fa276e1d9453fb4b04250a7038292b13b8c273a5a1a8f7c0/scikit_image-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be79d7493f320a964f8fcf603121595ba82f84720de999db0fcca002266a549a", size = 14954814 }, - { url = "https://files.pythonhosted.org/packages/dc/35/e6327ae498c6f557cb0a7c3fc284effe7958d2d1c43fb61cd77804fc2c4f/scikit_image-0.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:722b970aa5da725dca55252c373b18bbea7858c1cdb406e19f9b01a4a73b30b2", size = 25004857 }, + { url = "https://files.pythonhosted.org/packages/9c/8c/381ae42b37cf3e9e99a1deb3ffe76ca5ff5dd18ffa368293476164507fad/scikit_image-0.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74ec5c1d4693506842cc7c9487c89d8fc32aed064e9363def7af08b8f8cbb31d", size = 13905039, upload_time = "2023-10-03T21:35:27.279Z" }, + { url = "https://files.pythonhosted.org/packages/16/06/4bfba08f5cce26d5070bb2cf4e3f9f479480978806355d1c5bea6f26a17c/scikit_image-0.22.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:a05ae4fe03d802587ed8974e900b943275548cde6a6807b785039d63e9a7a5ff", size = 13279212, upload_time = "2023-10-03T21:35:30.864Z" }, + { url = "https://files.pythonhosted.org/packages/74/57/dbf744ca00eea2a09b1848c9dec28a43978c16dc049b1fba949cb050bedf/scikit_image-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a92dca3d95b1301442af055e196a54b5a5128c6768b79fc0a4098f1d662dee6", size = 14091779, upload_time = "2023-10-03T21:35:34.273Z" }, + { url = "https://files.pythonhosted.org/packages/f1/6c/49f5a0ce8ddcdbdac5ac69c129654938cc6de0a936303caa6cad495ceb2a/scikit_image-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3663d063d8bf2fb9bdfb0ca967b9ee3b6593139c860c7abc2d2351a8a8863938", size = 14682042, upload_time = "2023-10-03T21:35:37.787Z" }, + { url = "https://files.pythonhosted.org/packages/86/f0/18895318109f9b508f2310f136922e455a453550826a8240b412063c2528/scikit_image-0.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:ebdbdc901bae14dab637f8d5c99f6d5cc7aaf4a3b6f4003194e003e9f688a6fc", size = 24492345, upload_time = "2023-10-03T21:35:41.122Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d9/dc99e527d1a0050f0353d2fff3548273b4df6151884806e324f26572fd6b/scikit_image-0.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:95d6da2d8a44a36ae04437c76d32deb4e3c993ffc846b394b9949fd8ded73cb2", size = 13883619, upload_time = "2023-10-03T21:35:44.88Z" }, + { url = "https://files.pythonhosted.org/packages/80/37/7670020b112ff9a47e49b1e36f438d000db5b632aab8a8fd7e6be545d065/scikit_image-0.22.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:2c6ef454a85f569659b813ac2a93948022b0298516b757c9c6c904132be327e2", size = 13264761, upload_time = "2023-10-03T21:35:48.865Z" }, + { url = "https://files.pythonhosted.org/packages/ad/85/dadf1194793ac1c895370f3ed048bb91dda083775b42e11d9672a50494d5/scikit_image-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e87872f067444ee90a00dd49ca897208308645382e8a24bd3e76f301af2352cd", size = 14070710, upload_time = "2023-10-03T21:35:51.711Z" }, + { url = "https://files.pythonhosted.org/packages/d4/34/e27bf2bfe7b52b884b49bd71ea91ff81e4737246735ee5ea383314c31876/scikit_image-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5c378db54e61b491b9edeefff87e49fcf7fdf729bb93c777d7a5f15d36f743e", size = 14664172, upload_time = "2023-10-03T21:35:55.752Z" }, + { url = "https://files.pythonhosted.org/packages/ce/d0/a3f60c9f57ed295b3076e4acdb29a37bbd8823452562ab2ad51b03d6f377/scikit_image-0.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:2bcb74adb0634258a67f66c2bb29978c9a3e222463e003b67ba12056c003971b", size = 24491321, upload_time = "2023-10-03T21:35:58.847Z" }, + { url = "https://files.pythonhosted.org/packages/da/a4/b0b69bde4d6360e801d647691591dc9967a25a18a4c63ecf7f87d94e3fac/scikit_image-0.22.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:003ca2274ac0fac252280e7179ff986ff783407001459ddea443fe7916e38cff", size = 13968808, upload_time = "2023-10-03T21:36:02.526Z" }, + { url = "https://files.pythonhosted.org/packages/e4/65/3c0f77e7a9bae100a8f7f5cebde410fca1a3cf64e1ecdd343666e27b11d4/scikit_image-0.22.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:cf3c0c15b60ae3e557a0c7575fbd352f0c3ce0afca562febfe3ab80efbeec0e9", size = 13323763, upload_time = "2023-10-03T21:36:05.504Z" }, + { url = "https://files.pythonhosted.org/packages/4a/ed/7faf9f7a55d5b3095d33990a85603b66866cce2a608b27f0e1487d70a451/scikit_image-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5b23908dd4d120e6aecb1ed0277563e8cbc8d6c0565bdc4c4c6475d53608452", size = 13877233, upload_time = "2023-10-03T21:36:08.352Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9d/09d06f36ce71fa276e1d9453fb4b04250a7038292b13b8c273a5a1a8f7c0/scikit_image-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be79d7493f320a964f8fcf603121595ba82f84720de999db0fcca002266a549a", size = 14954814, upload_time = "2023-10-03T21:36:11.871Z" }, + { url = "https://files.pythonhosted.org/packages/dc/35/e6327ae498c6f557cb0a7c3fc284effe7958d2d1c43fb61cd77804fc2c4f/scikit_image-0.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:722b970aa5da725dca55252c373b18bbea7858c1cdb406e19f9b01a4a73b30b2", size = 25004857, upload_time = "2023-10-03T21:36:15.457Z" }, ] [[package]] @@ -2291,23 +2349,23 @@ dependencies = [ { name = "scipy" }, { name = "threadpoolctl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/88/00/835e3d280fdd7784e76bdef91dd9487582d7951a7254f59fc8004fc8b213/scikit-learn-1.3.2.tar.gz", hash = "sha256:a2f54c76accc15a34bfb9066e6c7a56c1e7235dda5762b990792330b52ccfb05", size = 7510251 } +sdist = { url = "https://files.pythonhosted.org/packages/88/00/835e3d280fdd7784e76bdef91dd9487582d7951a7254f59fc8004fc8b213/scikit-learn-1.3.2.tar.gz", hash = "sha256:a2f54c76accc15a34bfb9066e6c7a56c1e7235dda5762b990792330b52ccfb05", size = 7510251, upload_time = "2023-10-23T13:47:55.287Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/53/570b55a6e10b8694ac1e3024d2df5cd443f1b4ff6d28430845da8b9019b3/scikit_learn-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e326c0eb5cf4d6ba40f93776a20e9a7a69524c4db0757e7ce24ba222471ee8a1", size = 10209999 }, - { url = "https://files.pythonhosted.org/packages/70/d0/50ace22129f79830e3cf682d0a2bd4843ef91573299d43112d52790163a8/scikit_learn-1.3.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:535805c2a01ccb40ca4ab7d081d771aea67e535153e35a1fd99418fcedd1648a", size = 9479353 }, - { url = "https://files.pythonhosted.org/packages/8f/46/fcc35ed7606c50d3072eae5a107a45cfa5b7f5fa8cc48610edd8cc8e8550/scikit_learn-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1215e5e58e9880b554b01187b8c9390bf4dc4692eedeaf542d3273f4785e342c", size = 10304705 }, - { url = "https://files.pythonhosted.org/packages/d0/0b/26ad95cf0b747be967b15fb71a06f5ac67aba0fd2f9cd174de6edefc4674/scikit_learn-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ee107923a623b9f517754ea2f69ea3b62fc898a3641766cb7deb2f2ce450161", size = 10827807 }, - { url = "https://files.pythonhosted.org/packages/69/8a/cf17d6443f5f537e099be81535a56ab68a473f9393fbffda38cd19899fc8/scikit_learn-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:35a22e8015048c628ad099da9df5ab3004cdbf81edc75b396fd0cff8699ac58c", size = 9255427 }, - { url = "https://files.pythonhosted.org/packages/08/5d/e5acecd6e99a6b656e42e7a7b18284e2f9c9f512e8ed6979e1e75d25f05f/scikit_learn-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6fb6bc98f234fda43163ddbe36df8bcde1d13ee176c6dc9b92bb7d3fc842eb66", size = 10116376 }, - { url = "https://files.pythonhosted.org/packages/40/c6/2e91eefb757822e70d351e02cc38d07c137212ae7c41ac12746415b4860a/scikit_learn-1.3.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:18424efee518a1cde7b0b53a422cde2f6625197de6af36da0b57ec502f126157", size = 9383415 }, - { url = "https://files.pythonhosted.org/packages/fa/fd/b3637639e73bb72b12803c5245f2a7299e09b2acd85a0f23937c53369a1c/scikit_learn-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3271552a5eb16f208a6f7f617b8cc6d1f137b52c8a1ef8edf547db0259b2c9fb", size = 10279163 }, - { url = "https://files.pythonhosted.org/packages/0c/2a/d3ff6091406bc2207e0adb832ebd15e40ac685811c7e2e3b432bfd969b71/scikit_learn-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4144a5004a676d5022b798d9e573b05139e77f271253a4703eed295bde0433", size = 10884422 }, - { url = "https://files.pythonhosted.org/packages/4e/ba/ce9bd1cd4953336a0e213b29cb80bb11816f2a93de8c99f88ef0b446ad0c/scikit_learn-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:67f37d708f042a9b8d59551cf94d30431e01374e00dc2645fa186059c6c5d78b", size = 9207060 }, - { url = "https://files.pythonhosted.org/packages/26/7e/2c3b82c8c29aa384c8bf859740419278627d2cdd0050db503c8840e72477/scikit_learn-1.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8db94cd8a2e038b37a80a04df8783e09caac77cbe052146432e67800e430c028", size = 9979322 }, - { url = "https://files.pythonhosted.org/packages/cf/fc/6c52ffeb587259b6b893b7cac268f1eb1b5426bcce1aa20e53523bfe6944/scikit_learn-1.3.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:61a6efd384258789aa89415a410dcdb39a50e19d3d8410bd29be365bcdd512d5", size = 9270688 }, - { url = "https://files.pythonhosted.org/packages/e5/a7/6f4ae76f72ae9de162b97acbf1f53acbe404c555f968d13da21e4112a002/scikit_learn-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb06f8dce3f5ddc5dee1715a9b9f19f20d295bed8e3cd4fa51e1d050347de525", size = 10280398 }, - { url = "https://files.pythonhosted.org/packages/5d/b7/ee35904c07a0666784349529412fbb9814a56382b650d30fd9d6be5e5054/scikit_learn-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b2de18d86f630d68fe1f87af690d451388bb186480afc719e5f770590c2ef6c", size = 10796478 }, - { url = "https://files.pythonhosted.org/packages/fe/6b/db949ed5ac367987b1f250f070f340b7715d22f0c9c965bdf07de6ca75a3/scikit_learn-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:0402638c9a7c219ee52c94cbebc8fcb5eb9fe9c773717965c1f4185588ad3107", size = 9133979 }, + { url = "https://files.pythonhosted.org/packages/0d/53/570b55a6e10b8694ac1e3024d2df5cd443f1b4ff6d28430845da8b9019b3/scikit_learn-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e326c0eb5cf4d6ba40f93776a20e9a7a69524c4db0757e7ce24ba222471ee8a1", size = 10209999, upload_time = "2023-10-23T13:46:30.373Z" }, + { url = "https://files.pythonhosted.org/packages/70/d0/50ace22129f79830e3cf682d0a2bd4843ef91573299d43112d52790163a8/scikit_learn-1.3.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:535805c2a01ccb40ca4ab7d081d771aea67e535153e35a1fd99418fcedd1648a", size = 9479353, upload_time = "2023-10-23T13:46:34.368Z" }, + { url = "https://files.pythonhosted.org/packages/8f/46/fcc35ed7606c50d3072eae5a107a45cfa5b7f5fa8cc48610edd8cc8e8550/scikit_learn-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1215e5e58e9880b554b01187b8c9390bf4dc4692eedeaf542d3273f4785e342c", size = 10304705, upload_time = "2023-10-23T13:46:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/d0/0b/26ad95cf0b747be967b15fb71a06f5ac67aba0fd2f9cd174de6edefc4674/scikit_learn-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ee107923a623b9f517754ea2f69ea3b62fc898a3641766cb7deb2f2ce450161", size = 10827807, upload_time = "2023-10-23T13:46:41.59Z" }, + { url = "https://files.pythonhosted.org/packages/69/8a/cf17d6443f5f537e099be81535a56ab68a473f9393fbffda38cd19899fc8/scikit_learn-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:35a22e8015048c628ad099da9df5ab3004cdbf81edc75b396fd0cff8699ac58c", size = 9255427, upload_time = "2023-10-23T13:46:44.826Z" }, + { url = "https://files.pythonhosted.org/packages/08/5d/e5acecd6e99a6b656e42e7a7b18284e2f9c9f512e8ed6979e1e75d25f05f/scikit_learn-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6fb6bc98f234fda43163ddbe36df8bcde1d13ee176c6dc9b92bb7d3fc842eb66", size = 10116376, upload_time = "2023-10-23T13:46:48.147Z" }, + { url = "https://files.pythonhosted.org/packages/40/c6/2e91eefb757822e70d351e02cc38d07c137212ae7c41ac12746415b4860a/scikit_learn-1.3.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:18424efee518a1cde7b0b53a422cde2f6625197de6af36da0b57ec502f126157", size = 9383415, upload_time = "2023-10-23T13:46:51.324Z" }, + { url = "https://files.pythonhosted.org/packages/fa/fd/b3637639e73bb72b12803c5245f2a7299e09b2acd85a0f23937c53369a1c/scikit_learn-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3271552a5eb16f208a6f7f617b8cc6d1f137b52c8a1ef8edf547db0259b2c9fb", size = 10279163, upload_time = "2023-10-23T13:46:54.642Z" }, + { url = "https://files.pythonhosted.org/packages/0c/2a/d3ff6091406bc2207e0adb832ebd15e40ac685811c7e2e3b432bfd969b71/scikit_learn-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4144a5004a676d5022b798d9e573b05139e77f271253a4703eed295bde0433", size = 10884422, upload_time = "2023-10-23T13:46:58.087Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ba/ce9bd1cd4953336a0e213b29cb80bb11816f2a93de8c99f88ef0b446ad0c/scikit_learn-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:67f37d708f042a9b8d59551cf94d30431e01374e00dc2645fa186059c6c5d78b", size = 9207060, upload_time = "2023-10-23T13:47:00.948Z" }, + { url = "https://files.pythonhosted.org/packages/26/7e/2c3b82c8c29aa384c8bf859740419278627d2cdd0050db503c8840e72477/scikit_learn-1.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8db94cd8a2e038b37a80a04df8783e09caac77cbe052146432e67800e430c028", size = 9979322, upload_time = "2023-10-23T13:47:03.977Z" }, + { url = "https://files.pythonhosted.org/packages/cf/fc/6c52ffeb587259b6b893b7cac268f1eb1b5426bcce1aa20e53523bfe6944/scikit_learn-1.3.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:61a6efd384258789aa89415a410dcdb39a50e19d3d8410bd29be365bcdd512d5", size = 9270688, upload_time = "2023-10-23T13:47:07.316Z" }, + { url = "https://files.pythonhosted.org/packages/e5/a7/6f4ae76f72ae9de162b97acbf1f53acbe404c555f968d13da21e4112a002/scikit_learn-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb06f8dce3f5ddc5dee1715a9b9f19f20d295bed8e3cd4fa51e1d050347de525", size = 10280398, upload_time = "2023-10-23T13:47:10.796Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b7/ee35904c07a0666784349529412fbb9814a56382b650d30fd9d6be5e5054/scikit_learn-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b2de18d86f630d68fe1f87af690d451388bb186480afc719e5f770590c2ef6c", size = 10796478, upload_time = "2023-10-23T13:47:14.077Z" }, + { url = "https://files.pythonhosted.org/packages/fe/6b/db949ed5ac367987b1f250f070f340b7715d22f0c9c965bdf07de6ca75a3/scikit_learn-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:0402638c9a7c219ee52c94cbebc8fcb5eb9fe9c773717965c1f4185588ad3107", size = 9133979, upload_time = "2023-10-23T13:47:17.389Z" }, ] [[package]] @@ -2317,53 +2375,65 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6e/1f/91144ba78dccea567a6466262922786ffc97be1e9b06ed9574ef0edc11e1/scipy-1.11.4.tar.gz", hash = "sha256:90a2b78e7f5733b9de748f589f09225013685f9b218275257f8a8168ededaeaa", size = 56336202 } +sdist = { url = "https://files.pythonhosted.org/packages/6e/1f/91144ba78dccea567a6466262922786ffc97be1e9b06ed9574ef0edc11e1/scipy-1.11.4.tar.gz", hash = "sha256:90a2b78e7f5733b9de748f589f09225013685f9b218275257f8a8168ededaeaa", size = 56336202, upload_time = "2023-11-18T21:06:08.277Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/34/c6/a32add319475d21f89733c034b99c81b3a7c6c7c19f96f80c7ca3ff1bbd4/scipy-1.11.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc9a714581f561af0848e6b69947fda0614915f072dfd14142ed1bfe1b806710", size = 37293259 }, - { url = "https://files.pythonhosted.org/packages/de/0d/4fa68303568c70fd56fbf40668b6c6807cfee4cad975f07d80bdd26d013e/scipy-1.11.4-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:cf00bd2b1b0211888d4dc75656c0412213a8b25e80d73898083f402b50f47e41", size = 29760656 }, - { url = "https://files.pythonhosted.org/packages/13/e5/8012be7857db6cbbbdbeea8a154dbacdfae845e95e1e19c028e82236d4a0/scipy-1.11.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9999c008ccf00e8fbcce1236f85ade5c569d13144f77a1946bef8863e8f6eb4", size = 32922489 }, - { url = "https://files.pythonhosted.org/packages/e0/9e/80e2205d138960a49caea391f3710600895dd8292b6868dc9aff7aa593f9/scipy-1.11.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:933baf588daa8dc9a92c20a0be32f56d43faf3d1a60ab11b3f08c356430f6e56", size = 36442040 }, - { url = "https://files.pythonhosted.org/packages/69/60/30a9c3fbe5066a3a93eefe3e2d44553df13587e6f792e1bff20dfed3d17e/scipy-1.11.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8fce70f39076a5aa62e92e69a7f62349f9574d8405c0a5de6ed3ef72de07f446", size = 36643257 }, - { url = "https://files.pythonhosted.org/packages/f8/ec/b46756f80e3f4c5f0989f6e4492c2851f156d9c239d554754a3c8cffd4e2/scipy-1.11.4-cp310-cp310-win_amd64.whl", hash = "sha256:6550466fbeec7453d7465e74d4f4b19f905642c89a7525571ee91dd7adabb5a3", size = 44149285 }, - { url = "https://files.pythonhosted.org/packages/b8/f2/1aefbd5e54ebd8c6163ccf7f73e5d17bc8cb38738d312befc524fce84bb4/scipy-1.11.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f313b39a7e94f296025e3cffc2c567618174c0b1dde173960cf23808f9fae4be", size = 37159197 }, - { url = "https://files.pythonhosted.org/packages/4b/48/20e77ddb1f473d4717a7d4d3fc8d15557f406f7708496054c59f635b7734/scipy-1.11.4-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:1b7c3dca977f30a739e0409fb001056484661cb2541a01aba0bb0029f7b68db8", size = 29675057 }, - { url = "https://files.pythonhosted.org/packages/75/2e/a781862190d0e7e76afa74752ef363488a9a9d6ea86e46d5e5506cee8df6/scipy-1.11.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00150c5eae7b610c32589dda259eacc7c4f1665aedf25d921907f4d08a951b1c", size = 32882747 }, - { url = "https://files.pythonhosted.org/packages/6b/d4/d62ce38ba00dc67d7ec4ec5cc19d36958d8ed70e63778715ad626bcbc796/scipy-1.11.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:530f9ad26440e85766509dbf78edcfe13ffd0ab7fec2560ee5c36ff74d6269ff", size = 36402732 }, - { url = "https://files.pythonhosted.org/packages/88/86/827b56aea1ed04adbb044a675672a73c84d81076a350092bbfcfc1ae723b/scipy-1.11.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5e347b14fe01003d3b78e196e84bd3f48ffe4c8a7b8a1afbcb8f5505cb710993", size = 36622138 }, - { url = "https://files.pythonhosted.org/packages/43/d0/f3cd75b62e1b90f48dbf091261b2fc7ceec14a700e308c50f6a69c83d337/scipy-1.11.4-cp311-cp311-win_amd64.whl", hash = "sha256:acf8ed278cc03f5aff035e69cb511741e0418681d25fbbb86ca65429c4f4d9cd", size = 44095631 }, - { url = "https://files.pythonhosted.org/packages/df/64/8a690570485b636da614acff35fd725fcbc487f8b1fa9bdb12871b77412f/scipy-1.11.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:028eccd22e654b3ea01ee63705681ee79933652b2d8f873e7949898dda6d11b6", size = 37053653 }, - { url = "https://files.pythonhosted.org/packages/5e/43/abf331745a7e5f4af51f13d40e2a72f516048db41ecbcf3ac6f86ada54a3/scipy-1.11.4-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2c6ff6ef9cc27f9b3db93a6f8b38f97387e6e0591600369a297a50a8e96e835d", size = 29641601 }, - { url = "https://files.pythonhosted.org/packages/47/9b/62d0ec086dd2871009da8769c504bec6e39b80f4c182c6ead0fcebd8b323/scipy-1.11.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b030c6674b9230d37c5c60ab456e2cf12f6784596d15ce8da9365e70896effc4", size = 32272137 }, - { url = "https://files.pythonhosted.org/packages/08/77/f90f7306d755ac68bd159c50bb86fffe38400e533e8c609dd8484bd0f172/scipy-1.11.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad669df80528aeca5f557712102538f4f37e503f0c5b9541655016dd0932ca79", size = 35777534 }, - { url = "https://files.pythonhosted.org/packages/00/de/b9f6938090c37b5092969ba1c67118e9114e8e6ef9d197251671444e839c/scipy-1.11.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ce7fff2e23ab2cc81ff452a9444c215c28e6305f396b2ba88343a567feec9660", size = 35963721 }, - { url = "https://files.pythonhosted.org/packages/c6/a1/357e4cd43af2748e1e0407ae0e9a5ea8aaaa6b702833c81be11670dcbad8/scipy-1.11.4-cp312-cp312-win_amd64.whl", hash = "sha256:36750b7733d960d7994888f0d148d31ea3017ac15eef664194b4ef68d36a4a97", size = 43730653 }, + { url = "https://files.pythonhosted.org/packages/34/c6/a32add319475d21f89733c034b99c81b3a7c6c7c19f96f80c7ca3ff1bbd4/scipy-1.11.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc9a714581f561af0848e6b69947fda0614915f072dfd14142ed1bfe1b806710", size = 37293259, upload_time = "2023-11-18T21:01:18.805Z" }, + { url = "https://files.pythonhosted.org/packages/de/0d/4fa68303568c70fd56fbf40668b6c6807cfee4cad975f07d80bdd26d013e/scipy-1.11.4-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:cf00bd2b1b0211888d4dc75656c0412213a8b25e80d73898083f402b50f47e41", size = 29760656, upload_time = "2023-11-18T21:01:41.815Z" }, + { url = "https://files.pythonhosted.org/packages/13/e5/8012be7857db6cbbbdbeea8a154dbacdfae845e95e1e19c028e82236d4a0/scipy-1.11.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9999c008ccf00e8fbcce1236f85ade5c569d13144f77a1946bef8863e8f6eb4", size = 32922489, upload_time = "2023-11-18T21:01:50.637Z" }, + { url = "https://files.pythonhosted.org/packages/e0/9e/80e2205d138960a49caea391f3710600895dd8292b6868dc9aff7aa593f9/scipy-1.11.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:933baf588daa8dc9a92c20a0be32f56d43faf3d1a60ab11b3f08c356430f6e56", size = 36442040, upload_time = "2023-11-18T21:02:00.119Z" }, + { url = "https://files.pythonhosted.org/packages/69/60/30a9c3fbe5066a3a93eefe3e2d44553df13587e6f792e1bff20dfed3d17e/scipy-1.11.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8fce70f39076a5aa62e92e69a7f62349f9574d8405c0a5de6ed3ef72de07f446", size = 36643257, upload_time = "2023-11-18T21:02:06.798Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ec/b46756f80e3f4c5f0989f6e4492c2851f156d9c239d554754a3c8cffd4e2/scipy-1.11.4-cp310-cp310-win_amd64.whl", hash = "sha256:6550466fbeec7453d7465e74d4f4b19f905642c89a7525571ee91dd7adabb5a3", size = 44149285, upload_time = "2023-11-18T21:02:15.592Z" }, + { url = "https://files.pythonhosted.org/packages/b8/f2/1aefbd5e54ebd8c6163ccf7f73e5d17bc8cb38738d312befc524fce84bb4/scipy-1.11.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f313b39a7e94f296025e3cffc2c567618174c0b1dde173960cf23808f9fae4be", size = 37159197, upload_time = "2023-11-18T21:02:21.959Z" }, + { url = "https://files.pythonhosted.org/packages/4b/48/20e77ddb1f473d4717a7d4d3fc8d15557f406f7708496054c59f635b7734/scipy-1.11.4-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:1b7c3dca977f30a739e0409fb001056484661cb2541a01aba0bb0029f7b68db8", size = 29675057, upload_time = "2023-11-18T21:02:28.169Z" }, + { url = "https://files.pythonhosted.org/packages/75/2e/a781862190d0e7e76afa74752ef363488a9a9d6ea86e46d5e5506cee8df6/scipy-1.11.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00150c5eae7b610c32589dda259eacc7c4f1665aedf25d921907f4d08a951b1c", size = 32882747, upload_time = "2023-11-18T21:02:33.683Z" }, + { url = "https://files.pythonhosted.org/packages/6b/d4/d62ce38ba00dc67d7ec4ec5cc19d36958d8ed70e63778715ad626bcbc796/scipy-1.11.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:530f9ad26440e85766509dbf78edcfe13ffd0ab7fec2560ee5c36ff74d6269ff", size = 36402732, upload_time = "2023-11-18T21:02:39.762Z" }, + { url = "https://files.pythonhosted.org/packages/88/86/827b56aea1ed04adbb044a675672a73c84d81076a350092bbfcfc1ae723b/scipy-1.11.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5e347b14fe01003d3b78e196e84bd3f48ffe4c8a7b8a1afbcb8f5505cb710993", size = 36622138, upload_time = "2023-11-18T21:02:45.968Z" }, + { url = "https://files.pythonhosted.org/packages/43/d0/f3cd75b62e1b90f48dbf091261b2fc7ceec14a700e308c50f6a69c83d337/scipy-1.11.4-cp311-cp311-win_amd64.whl", hash = "sha256:acf8ed278cc03f5aff035e69cb511741e0418681d25fbbb86ca65429c4f4d9cd", size = 44095631, upload_time = "2023-11-18T21:02:52.859Z" }, + { url = "https://files.pythonhosted.org/packages/df/64/8a690570485b636da614acff35fd725fcbc487f8b1fa9bdb12871b77412f/scipy-1.11.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:028eccd22e654b3ea01ee63705681ee79933652b2d8f873e7949898dda6d11b6", size = 37053653, upload_time = "2023-11-18T21:03:00.107Z" }, + { url = "https://files.pythonhosted.org/packages/5e/43/abf331745a7e5f4af51f13d40e2a72f516048db41ecbcf3ac6f86ada54a3/scipy-1.11.4-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2c6ff6ef9cc27f9b3db93a6f8b38f97387e6e0591600369a297a50a8e96e835d", size = 29641601, upload_time = "2023-11-18T21:03:06.708Z" }, + { url = "https://files.pythonhosted.org/packages/47/9b/62d0ec086dd2871009da8769c504bec6e39b80f4c182c6ead0fcebd8b323/scipy-1.11.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b030c6674b9230d37c5c60ab456e2cf12f6784596d15ce8da9365e70896effc4", size = 32272137, upload_time = "2023-11-18T21:03:14.877Z" }, + { url = "https://files.pythonhosted.org/packages/08/77/f90f7306d755ac68bd159c50bb86fffe38400e533e8c609dd8484bd0f172/scipy-1.11.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad669df80528aeca5f557712102538f4f37e503f0c5b9541655016dd0932ca79", size = 35777534, upload_time = "2023-11-18T21:03:21.451Z" }, + { url = "https://files.pythonhosted.org/packages/00/de/b9f6938090c37b5092969ba1c67118e9114e8e6ef9d197251671444e839c/scipy-1.11.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ce7fff2e23ab2cc81ff452a9444c215c28e6305f396b2ba88343a567feec9660", size = 35963721, upload_time = "2023-11-18T21:03:27.85Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a1/357e4cd43af2748e1e0407ae0e9a5ea8aaaa6b702833c81be11670dcbad8/scipy-1.11.4-cp312-cp312-win_amd64.whl", hash = "sha256:36750b7733d960d7994888f0d148d31ea3017ac15eef664194b4ef68d36a4a97", size = 43730653, upload_time = "2023-11-18T21:03:34.758Z" }, ] [[package]] name = "setuptools" version = "70.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/d8/10a70e86f6c28ae59f101a9de6d77bf70f147180fbf40c3af0f64080adc3/setuptools-70.3.0.tar.gz", hash = "sha256:f171bab1dfbc86b132997f26a119f6056a57950d058587841a0082e8830f9dc5", size = 2333112 } +sdist = { url = "https://files.pythonhosted.org/packages/65/d8/10a70e86f6c28ae59f101a9de6d77bf70f147180fbf40c3af0f64080adc3/setuptools-70.3.0.tar.gz", hash = "sha256:f171bab1dfbc86b132997f26a119f6056a57950d058587841a0082e8830f9dc5", size = 2333112, upload_time = "2024-07-09T16:08:06.251Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/15/88e46eb9387e905704b69849618e699dc2f54407d8953cc4ec4b8b46528d/setuptools-70.3.0-py3-none-any.whl", hash = "sha256:fe384da74336c398e0d956d1cae0669bc02eed936cdb1d49b57de1990dc11ffc", size = 931070 }, + { url = "https://files.pythonhosted.org/packages/ef/15/88e46eb9387e905704b69849618e699dc2f54407d8953cc4ec4b8b46528d/setuptools-70.3.0-py3-none-any.whl", hash = "sha256:fe384da74336c398e0d956d1cae0669bc02eed936cdb1d49b57de1990dc11ffc", size = 931070, upload_time = "2024-07-09T16:07:58.829Z" }, +] + +[[package]] +name = "simple-websocket" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wsproto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b0/d4/bfa032f961103eba93de583b161f0e6a5b63cebb8f2c7d0c6e6efe1e3d2e/simple_websocket-1.1.0.tar.gz", hash = "sha256:7939234e7aa067c534abdab3a9ed933ec9ce4691b0713c78acb195560aa52ae4", size = 17300, upload_time = "2024-10-10T22:39:31.412Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/59/0782e51887ac6b07ffd1570e0364cf901ebc36345fea669969d2084baebb/simple_websocket-1.1.0-py3-none-any.whl", hash = "sha256:4af6069630a38ed6c561010f0e11a5bc0d4ca569b36306eb257cd9a192497c8c", size = 13842, upload_time = "2024-10-10T22:39:29.645Z" }, ] [[package]] name = "six" version = "1.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 } +sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041, upload_time = "2021-05-05T14:18:18.379Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, + { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053, upload_time = "2021-05-05T14:18:17.237Z" }, ] [[package]] name = "sniffio" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/50/d49c388cae4ec10e8109b1b833fd265511840706808576df3ada99ecb0ac/sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101", size = 17103 } +sdist = { url = "https://files.pythonhosted.org/packages/cd/50/d49c388cae4ec10e8109b1b833fd265511840706808576df3ada99ecb0ac/sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101", size = 17103, upload_time = "2022-09-01T12:31:36.968Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/a0/5dba8ed157b0136607c7f2151db695885606968d1fae123dc3391e0cfdbf/sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384", size = 10165 }, + { url = "https://files.pythonhosted.org/packages/c3/a0/5dba8ed157b0136607c7f2151db695885606968d1fae123dc3391e0cfdbf/sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384", size = 10165, upload_time = "2022-09-01T12:31:34.186Z" }, ] [[package]] @@ -2373,9 +2443,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3e/da/1fb4bdb72ae12b834becd7e1e7e47001d32f91ec0ce8d7bc1b618d9f0bd9/starlette-0.41.2.tar.gz", hash = "sha256:9834fd799d1a87fd346deb76158668cfa0b0d56f85caefe8268e2d97c3468b62", size = 2573867 } +sdist = { url = "https://files.pythonhosted.org/packages/3e/da/1fb4bdb72ae12b834becd7e1e7e47001d32f91ec0ce8d7bc1b618d9f0bd9/starlette-0.41.2.tar.gz", hash = "sha256:9834fd799d1a87fd346deb76158668cfa0b0d56f85caefe8268e2d97c3468b62", size = 2573867, upload_time = "2024-10-27T08:20:02.818Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/43/f185bfd0ca1d213beb4293bed51d92254df23d8ceaf6c0e17146d508a776/starlette-0.41.2-py3-none-any.whl", hash = "sha256:fbc189474b4731cf30fcef52f18a8d070e3f3b46c6a04c97579e85e6ffca942d", size = 73259 }, + { url = "https://files.pythonhosted.org/packages/54/43/f185bfd0ca1d213beb4293bed51d92254df23d8ceaf6c0e17146d508a776/starlette-0.41.2-py3-none-any.whl", hash = "sha256:fbc189474b4731cf30fcef52f18a8d070e3f3b46c6a04c97579e85e6ffca942d", size = 73259, upload_time = "2024-10-27T08:20:00.052Z" }, ] [[package]] @@ -2385,18 +2455,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mpmath" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e5/57/3485a1a3dff51bfd691962768b14310dae452431754bfc091250be50dd29/sympy-1.12.tar.gz", hash = "sha256:ebf595c8dac3e0fdc4152c51878b498396ec7f30e7a914d6071e674d49420fb8", size = 6722203 } +sdist = { url = "https://files.pythonhosted.org/packages/e5/57/3485a1a3dff51bfd691962768b14310dae452431754bfc091250be50dd29/sympy-1.12.tar.gz", hash = "sha256:ebf595c8dac3e0fdc4152c51878b498396ec7f30e7a914d6071e674d49420fb8", size = 6722203, upload_time = "2023-05-10T18:23:00.378Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/05/e6600db80270777c4a64238a98d442f0fd07cc8915be2a1c16da7f2b9e74/sympy-1.12-py3-none-any.whl", hash = "sha256:c3588cd4295d0c0f603d0f2ae780587e64e2efeedb3521e46b9bb1d08d184fa5", size = 5742435 }, + { url = "https://files.pythonhosted.org/packages/d2/05/e6600db80270777c4a64238a98d442f0fd07cc8915be2a1c16da7f2b9e74/sympy-1.12-py3-none-any.whl", hash = "sha256:c3588cd4295d0c0f603d0f2ae780587e64e2efeedb3521e46b9bb1d08d184fa5", size = 5742435, upload_time = "2023-05-10T18:22:14.76Z" }, ] [[package]] name = "threadpoolctl" version = "3.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/8a/c05f7831beb32aff70f808766224f11c650f7edfd49b27a8fc6666107006/threadpoolctl-3.2.0.tar.gz", hash = "sha256:c96a0ba3bdddeaca37dc4cc7344aafad41cdb8c313f74fdfe387a867bba93355", size = 36266 } +sdist = { url = "https://files.pythonhosted.org/packages/47/8a/c05f7831beb32aff70f808766224f11c650f7edfd49b27a8fc6666107006/threadpoolctl-3.2.0.tar.gz", hash = "sha256:c96a0ba3bdddeaca37dc4cc7344aafad41cdb8c313f74fdfe387a867bba93355", size = 36266, upload_time = "2023-07-13T14:53:53.299Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/12/fd4dea011af9d69e1cad05c75f3f7202cdcbeac9b712eea58ca779a72865/threadpoolctl-3.2.0-py3-none-any.whl", hash = "sha256:2b7818516e423bdaebb97c723f86a7c6b0a83d3f3b0970328d66f4d9104dc032", size = 15539 }, + { url = "https://files.pythonhosted.org/packages/81/12/fd4dea011af9d69e1cad05c75f3f7202cdcbeac9b712eea58ca779a72865/threadpoolctl-3.2.0-py3-none-any.whl", hash = "sha256:2b7818516e423bdaebb97c723f86a7c6b0a83d3f3b0970328d66f4d9104dc032", size = 15539, upload_time = "2023-07-13T14:53:39.336Z" }, ] [[package]] @@ -2406,9 +2476,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/a4/6c0eadea1ccfcda27e6cce400c366098b5b082138a073f4252fe399f4148/tifffile-2023.12.9.tar.gz", hash = "sha256:9dd1da91180a6453018a241ff219e1905f169384355cd89c9ef4034c1b46cdb8", size = 353467 } +sdist = { url = "https://files.pythonhosted.org/packages/f8/a4/6c0eadea1ccfcda27e6cce400c366098b5b082138a073f4252fe399f4148/tifffile-2023.12.9.tar.gz", hash = "sha256:9dd1da91180a6453018a241ff219e1905f169384355cd89c9ef4034c1b46cdb8", size = 353467, upload_time = "2023-12-09T20:46:29.203Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/a4/569fc717831969cf48bced350bdaf070cdeab06918d179429899e144358d/tifffile-2023.12.9-py3-none-any.whl", hash = "sha256:9b066e4b1a900891ea42ffd33dab8ba34c537935618b9893ddef42d7d422692f", size = 223627 }, + { url = "https://files.pythonhosted.org/packages/54/a4/569fc717831969cf48bced350bdaf070cdeab06918d179429899e144358d/tifffile-2023.12.9-py3-none-any.whl", hash = "sha256:9b066e4b1a900891ea42ffd33dab8ba34c537935618b9893ddef42d7d422692f", size = 223627, upload_time = "2023-12-09T20:46:26.569Z" }, ] [[package]] @@ -2418,31 +2488,31 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/92/76/5ac0c97f1117b91b7eb7323dcd61af80d72f790b4df71249a7850c195f30/tokenizers-0.21.1.tar.gz", hash = "sha256:a1bb04dc5b448985f86ecd4b05407f5a8d97cb2c0532199b2a302a604a0165ab", size = 343256 } +sdist = { url = "https://files.pythonhosted.org/packages/92/76/5ac0c97f1117b91b7eb7323dcd61af80d72f790b4df71249a7850c195f30/tokenizers-0.21.1.tar.gz", hash = "sha256:a1bb04dc5b448985f86ecd4b05407f5a8d97cb2c0532199b2a302a604a0165ab", size = 343256, upload_time = "2025-03-13T10:51:18.189Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/1f/328aee25f9115bf04262e8b4e5a2050b7b7cf44b59c74e982db7270c7f30/tokenizers-0.21.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e78e413e9e668ad790a29456e677d9d3aa50a9ad311a40905d6861ba7692cf41", size = 2780767 }, - { url = "https://files.pythonhosted.org/packages/ae/1a/4526797f3719b0287853f12c5ad563a9be09d446c44ac784cdd7c50f76ab/tokenizers-0.21.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:cd51cd0a91ecc801633829fcd1fda9cf8682ed3477c6243b9a095539de4aecf3", size = 2650555 }, - { url = "https://files.pythonhosted.org/packages/4d/7a/a209b29f971a9fdc1da86f917fe4524564924db50d13f0724feed37b2a4d/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28da6b72d4fb14ee200a1bd386ff74ade8992d7f725f2bde2c495a9a98cf4d9f", size = 2937541 }, - { url = "https://files.pythonhosted.org/packages/3c/1e/b788b50ffc6191e0b1fc2b0d49df8cff16fe415302e5ceb89f619d12c5bc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34d8cfde551c9916cb92014e040806122295a6800914bab5865deb85623931cf", size = 2819058 }, - { url = "https://files.pythonhosted.org/packages/36/aa/3626dfa09a0ecc5b57a8c58eeaeb7dd7ca9a37ad9dd681edab5acd55764c/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaa852d23e125b73d283c98f007e06d4595732104b65402f46e8ef24b588d9f8", size = 3133278 }, - { url = "https://files.pythonhosted.org/packages/a4/4d/8fbc203838b3d26269f944a89459d94c858f5b3f9a9b6ee9728cdcf69161/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a21a15d5c8e603331b8a59548bbe113564136dc0f5ad8306dd5033459a226da0", size = 3144253 }, - { url = "https://files.pythonhosted.org/packages/d8/1b/2bd062adeb7c7511b847b32e356024980c0ffcf35f28947792c2d8ad2288/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2fdbd4c067c60a0ac7eca14b6bd18a5bebace54eb757c706b47ea93204f7a37c", size = 3398225 }, - { url = "https://files.pythonhosted.org/packages/8a/63/38be071b0c8e06840bc6046991636bcb30c27f6bb1e670f4f4bc87cf49cc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd9a0061e403546f7377df940e866c3e678d7d4e9643d0461ea442b4f89e61a", size = 3038874 }, - { url = "https://files.pythonhosted.org/packages/ec/83/afa94193c09246417c23a3c75a8a0a96bf44ab5630a3015538d0c316dd4b/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:db9484aeb2e200c43b915a1a0150ea885e35f357a5a8fabf7373af333dcc8dbf", size = 9014448 }, - { url = "https://files.pythonhosted.org/packages/ae/b3/0e1a37d4f84c0f014d43701c11eb8072704f6efe8d8fc2dcdb79c47d76de/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed248ab5279e601a30a4d67bdb897ecbe955a50f1e7bb62bd99f07dd11c2f5b6", size = 8937877 }, - { url = "https://files.pythonhosted.org/packages/ac/33/ff08f50e6d615eb180a4a328c65907feb6ded0b8f990ec923969759dc379/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:9ac78b12e541d4ce67b4dfd970e44c060a2147b9b2a21f509566d556a509c67d", size = 9186645 }, - { url = "https://files.pythonhosted.org/packages/5f/aa/8ae85f69a9f6012c6f8011c6f4aa1c96154c816e9eea2e1b758601157833/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e5a69c1a4496b81a5ee5d2c1f3f7fbdf95e90a0196101b0ee89ed9956b8a168f", size = 9384380 }, - { url = "https://files.pythonhosted.org/packages/e8/5b/a5d98c89f747455e8b7a9504910c865d5e51da55e825a7ae641fb5ff0a58/tokenizers-0.21.1-cp39-abi3-win32.whl", hash = "sha256:1039a3a5734944e09de1d48761ade94e00d0fa760c0e0551151d4dd851ba63e3", size = 2239506 }, - { url = "https://files.pythonhosted.org/packages/e6/b6/072a8e053ae600dcc2ac0da81a23548e3b523301a442a6ca900e92ac35be/tokenizers-0.21.1-cp39-abi3-win_amd64.whl", hash = "sha256:0f0dcbcc9f6e13e675a66d7a5f2f225a736745ce484c1a4e07476a89ccdad382", size = 2435481 }, + { url = "https://files.pythonhosted.org/packages/a5/1f/328aee25f9115bf04262e8b4e5a2050b7b7cf44b59c74e982db7270c7f30/tokenizers-0.21.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e78e413e9e668ad790a29456e677d9d3aa50a9ad311a40905d6861ba7692cf41", size = 2780767, upload_time = "2025-03-13T10:51:09.459Z" }, + { url = "https://files.pythonhosted.org/packages/ae/1a/4526797f3719b0287853f12c5ad563a9be09d446c44ac784cdd7c50f76ab/tokenizers-0.21.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:cd51cd0a91ecc801633829fcd1fda9cf8682ed3477c6243b9a095539de4aecf3", size = 2650555, upload_time = "2025-03-13T10:51:07.692Z" }, + { url = "https://files.pythonhosted.org/packages/4d/7a/a209b29f971a9fdc1da86f917fe4524564924db50d13f0724feed37b2a4d/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28da6b72d4fb14ee200a1bd386ff74ade8992d7f725f2bde2c495a9a98cf4d9f", size = 2937541, upload_time = "2025-03-13T10:50:56.679Z" }, + { url = "https://files.pythonhosted.org/packages/3c/1e/b788b50ffc6191e0b1fc2b0d49df8cff16fe415302e5ceb89f619d12c5bc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34d8cfde551c9916cb92014e040806122295a6800914bab5865deb85623931cf", size = 2819058, upload_time = "2025-03-13T10:50:59.525Z" }, + { url = "https://files.pythonhosted.org/packages/36/aa/3626dfa09a0ecc5b57a8c58eeaeb7dd7ca9a37ad9dd681edab5acd55764c/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaa852d23e125b73d283c98f007e06d4595732104b65402f46e8ef24b588d9f8", size = 3133278, upload_time = "2025-03-13T10:51:04.678Z" }, + { url = "https://files.pythonhosted.org/packages/a4/4d/8fbc203838b3d26269f944a89459d94c858f5b3f9a9b6ee9728cdcf69161/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a21a15d5c8e603331b8a59548bbe113564136dc0f5ad8306dd5033459a226da0", size = 3144253, upload_time = "2025-03-13T10:51:01.261Z" }, + { url = "https://files.pythonhosted.org/packages/d8/1b/2bd062adeb7c7511b847b32e356024980c0ffcf35f28947792c2d8ad2288/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2fdbd4c067c60a0ac7eca14b6bd18a5bebace54eb757c706b47ea93204f7a37c", size = 3398225, upload_time = "2025-03-13T10:51:03.243Z" }, + { url = "https://files.pythonhosted.org/packages/8a/63/38be071b0c8e06840bc6046991636bcb30c27f6bb1e670f4f4bc87cf49cc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd9a0061e403546f7377df940e866c3e678d7d4e9643d0461ea442b4f89e61a", size = 3038874, upload_time = "2025-03-13T10:51:06.235Z" }, + { url = "https://files.pythonhosted.org/packages/ec/83/afa94193c09246417c23a3c75a8a0a96bf44ab5630a3015538d0c316dd4b/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:db9484aeb2e200c43b915a1a0150ea885e35f357a5a8fabf7373af333dcc8dbf", size = 9014448, upload_time = "2025-03-13T10:51:10.927Z" }, + { url = "https://files.pythonhosted.org/packages/ae/b3/0e1a37d4f84c0f014d43701c11eb8072704f6efe8d8fc2dcdb79c47d76de/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed248ab5279e601a30a4d67bdb897ecbe955a50f1e7bb62bd99f07dd11c2f5b6", size = 8937877, upload_time = "2025-03-13T10:51:12.688Z" }, + { url = "https://files.pythonhosted.org/packages/ac/33/ff08f50e6d615eb180a4a328c65907feb6ded0b8f990ec923969759dc379/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:9ac78b12e541d4ce67b4dfd970e44c060a2147b9b2a21f509566d556a509c67d", size = 9186645, upload_time = "2025-03-13T10:51:14.723Z" }, + { url = "https://files.pythonhosted.org/packages/5f/aa/8ae85f69a9f6012c6f8011c6f4aa1c96154c816e9eea2e1b758601157833/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e5a69c1a4496b81a5ee5d2c1f3f7fbdf95e90a0196101b0ee89ed9956b8a168f", size = 9384380, upload_time = "2025-03-13T10:51:16.526Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5b/a5d98c89f747455e8b7a9504910c865d5e51da55e825a7ae641fb5ff0a58/tokenizers-0.21.1-cp39-abi3-win32.whl", hash = "sha256:1039a3a5734944e09de1d48761ade94e00d0fa760c0e0551151d4dd851ba63e3", size = 2239506, upload_time = "2025-03-13T10:51:20.643Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b6/072a8e053ae600dcc2ac0da81a23548e3b523301a442a6ca900e92ac35be/tokenizers-0.21.1-cp39-abi3-win_amd64.whl", hash = "sha256:0f0dcbcc9f6e13e675a66d7a5f2f225a736745ce484c1a4e07476a89ccdad382", size = 2435481, upload_time = "2025-03-13T10:51:19.243Z" }, ] [[package]] name = "tomli" version = "2.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f", size = 15164 } +sdist = { url = "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f", size = 15164, upload_time = "2022-02-08T10:54:04.006Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", size = 12757 }, + { url = "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", size = 12757, upload_time = "2022-02-08T10:54:02.017Z" }, ] [[package]] @@ -2452,18 +2522,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/03/00/6a9b3aedb0b60a80995ade30f718f1a9902612f22a1aaf531c85a02987f7/tqdm-4.66.3.tar.gz", hash = "sha256:23097a41eba115ba99ecae40d06444c15d1c0c698d527a01c6c8bd1c5d0647e5", size = 169551 } +sdist = { url = "https://files.pythonhosted.org/packages/03/00/6a9b3aedb0b60a80995ade30f718f1a9902612f22a1aaf531c85a02987f7/tqdm-4.66.3.tar.gz", hash = "sha256:23097a41eba115ba99ecae40d06444c15d1c0c698d527a01c6c8bd1c5d0647e5", size = 169551, upload_time = "2024-05-02T21:44:05.084Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/ad/7d47bbf2cae78ff79f29db0bed5016ec9c56b212a93fca624bb88b551a7c/tqdm-4.66.3-py3-none-any.whl", hash = "sha256:4f41d54107ff9a223dca80b53efe4fb654c67efaba7f47bada3ee9d50e05bd53", size = 78374 }, + { url = "https://files.pythonhosted.org/packages/d1/ad/7d47bbf2cae78ff79f29db0bed5016ec9c56b212a93fca624bb88b551a7c/tqdm-4.66.3-py3-none-any.whl", hash = "sha256:4f41d54107ff9a223dca80b53efe4fb654c67efaba7f47bada3ee9d50e05bd53", size = 78374, upload_time = "2024-05-02T21:44:01.541Z" }, ] [[package]] name = "types-pyyaml" version = "6.0.12.20250402" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/68/609eed7402f87c9874af39d35942744e39646d1ea9011765ec87b01b2a3c/types_pyyaml-6.0.12.20250402.tar.gz", hash = "sha256:d7c13c3e6d335b6af4b0122a01ff1d270aba84ab96d1a1a1063ecba3e13ec075", size = 17282 } +sdist = { url = "https://files.pythonhosted.org/packages/2d/68/609eed7402f87c9874af39d35942744e39646d1ea9011765ec87b01b2a3c/types_pyyaml-6.0.12.20250402.tar.gz", hash = "sha256:d7c13c3e6d335b6af4b0122a01ff1d270aba84ab96d1a1a1063ecba3e13ec075", size = 17282, upload_time = "2025-04-02T02:56:00.235Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/56/1fe61db05685fbb512c07ea9323f06ea727125951f1eb4dff110b3311da3/types_pyyaml-6.0.12.20250402-py3-none-any.whl", hash = "sha256:652348fa9e7a203d4b0d21066dfb00760d3cbd5a15ebb7cf8d33c88a49546681", size = 20329 }, + { url = "https://files.pythonhosted.org/packages/ed/56/1fe61db05685fbb512c07ea9323f06ea727125951f1eb4dff110b3311da3/types_pyyaml-6.0.12.20250402-py3-none-any.whl", hash = "sha256:652348fa9e7a203d4b0d21066dfb00760d3cbd5a15ebb7cf8d33c88a49546681", size = 20329, upload_time = "2025-04-02T02:55:59.382Z" }, ] [[package]] @@ -2473,9 +2543,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/7d/eb174f74e3f5634eaacb38031bbe467dfe2e545bc255e5c90096ec46bc46/types_requests-2.32.0.20250328.tar.gz", hash = "sha256:c9e67228ea103bd811c96984fac36ed2ae8da87a36a633964a21f199d60baf32", size = 22995 } +sdist = { url = "https://files.pythonhosted.org/packages/00/7d/eb174f74e3f5634eaacb38031bbe467dfe2e545bc255e5c90096ec46bc46/types_requests-2.32.0.20250328.tar.gz", hash = "sha256:c9e67228ea103bd811c96984fac36ed2ae8da87a36a633964a21f199d60baf32", size = 22995, upload_time = "2025-03-28T02:55:13.271Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/15/3700282a9d4ea3b37044264d3e4d1b1f0095a4ebf860a99914fd544e3be3/types_requests-2.32.0.20250328-py3-none-any.whl", hash = "sha256:72ff80f84b15eb3aa7a8e2625fffb6a93f2ad5a0c20215fc1dcfa61117bcb2a2", size = 20663 }, + { url = "https://files.pythonhosted.org/packages/cc/15/3700282a9d4ea3b37044264d3e4d1b1f0095a4ebf860a99914fd544e3be3/types_requests-2.32.0.20250328-py3-none-any.whl", hash = "sha256:72ff80f84b15eb3aa7a8e2625fffb6a93f2ad5a0c20215fc1dcfa61117bcb2a2", size = 20663, upload_time = "2025-03-28T02:55:11.946Z" }, ] [[package]] @@ -2485,36 +2555,36 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "setuptools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b8/0f/2d1d000c2be3919bcdea15e5da48456bf1e55c18d02c5509ea59dade1408/types_setuptools-76.0.0.20250313.tar.gz", hash = "sha256:b2be66f550f95f3cad2a7d46177b273c7e9c80df7d257fa57addbbcfc8126a9e", size = 43627 } +sdist = { url = "https://files.pythonhosted.org/packages/b8/0f/2d1d000c2be3919bcdea15e5da48456bf1e55c18d02c5509ea59dade1408/types_setuptools-76.0.0.20250313.tar.gz", hash = "sha256:b2be66f550f95f3cad2a7d46177b273c7e9c80df7d257fa57addbbcfc8126a9e", size = 43627, upload_time = "2025-03-13T02:51:28.3Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/89/ea9669a0a76b160ffb312d0b02b15bad053c1bc81d2a54e42e3a402ca754/types_setuptools-76.0.0.20250313-py3-none-any.whl", hash = "sha256:bf454b2a49b8cfd7ebcf5844d4dd5fe4c8666782df1e3663c5866fd51a47460e", size = 65845 }, + { url = "https://files.pythonhosted.org/packages/ca/89/ea9669a0a76b160ffb312d0b02b15bad053c1bc81d2a54e42e3a402ca754/types_setuptools-76.0.0.20250313-py3-none-any.whl", hash = "sha256:bf454b2a49b8cfd7ebcf5844d4dd5fe4c8666782df1e3663c5866fd51a47460e", size = 65845, upload_time = "2025-03-13T02:51:27.055Z" }, ] [[package]] name = "types-simplejson" version = "3.20.0.20250326" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/14/e26fc55e1ea56f9ea470917d3e2f8240e6d043ca914181021d04115ae0f7/types_simplejson-3.20.0.20250326.tar.gz", hash = "sha256:b2689bc91e0e672d7a5a947b4cb546b76ae7ddc2899c6678e72a10bf96cd97d2", size = 10489 } +sdist = { url = "https://files.pythonhosted.org/packages/af/14/e26fc55e1ea56f9ea470917d3e2f8240e6d043ca914181021d04115ae0f7/types_simplejson-3.20.0.20250326.tar.gz", hash = "sha256:b2689bc91e0e672d7a5a947b4cb546b76ae7ddc2899c6678e72a10bf96cd97d2", size = 10489, upload_time = "2025-03-26T02:53:35.825Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/bf/d3f3a5ba47fd18115e8446d39f025b85905d2008677c29ee4d03b4cddd57/types_simplejson-3.20.0.20250326-py3-none-any.whl", hash = "sha256:db1ddea7b8f7623b27a137578f22fc6c618db8c83ccfb1828ca0d2f0ec11efa7", size = 10462 }, + { url = "https://files.pythonhosted.org/packages/76/bf/d3f3a5ba47fd18115e8446d39f025b85905d2008677c29ee4d03b4cddd57/types_simplejson-3.20.0.20250326-py3-none-any.whl", hash = "sha256:db1ddea7b8f7623b27a137578f22fc6c618db8c83ccfb1828ca0d2f0ec11efa7", size = 10462, upload_time = "2025-03-26T02:53:35.036Z" }, ] [[package]] name = "types-ujson" version = "5.10.0.20250326" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/5c/c974451c4babdb4ae3588925487edde492d59a8403010b4642a554d09954/types_ujson-5.10.0.20250326.tar.gz", hash = "sha256:5469e05f2c31ecb3c4c0267cc8fe41bcd116826fbb4ded69801a645c687dd014", size = 8340 } +sdist = { url = "https://files.pythonhosted.org/packages/cc/5c/c974451c4babdb4ae3588925487edde492d59a8403010b4642a554d09954/types_ujson-5.10.0.20250326.tar.gz", hash = "sha256:5469e05f2c31ecb3c4c0267cc8fe41bcd116826fbb4ded69801a645c687dd014", size = 8340, upload_time = "2025-03-26T02:53:39.197Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/c9/8a73a5f8fa6e70fc02eed506d5ac0ae9ceafbd2b8c9ad34a7de0f29900d6/types_ujson-5.10.0.20250326-py3-none-any.whl", hash = "sha256:acc0913f569def62ef6a892c8a47703f65d05669a3252391a97765cf207dca5b", size = 7644 }, + { url = "https://files.pythonhosted.org/packages/3e/c9/8a73a5f8fa6e70fc02eed506d5ac0ae9ceafbd2b8c9ad34a7de0f29900d6/types_ujson-5.10.0.20250326-py3-none-any.whl", hash = "sha256:acc0913f569def62ef6a892c8a47703f65d05669a3252391a97765cf207dca5b", size = 7644, upload_time = "2025-03-26T02:53:38.2Z" }, ] [[package]] name = "typing-extensions" version = "4.12.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321, upload_time = "2024-06-07T18:52:15.995Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438, upload_time = "2024-06-07T18:52:13.582Z" }, ] [[package]] @@ -2524,32 +2594,32 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 } +sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222, upload_time = "2025-02-25T17:27:59.638Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 }, + { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125, upload_time = "2025-02-25T17:27:57.754Z" }, ] [[package]] name = "urllib3" version = "2.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/36/dd/a6b232f449e1bc71802a5b7950dc3675d32c6dbc2a1bd6d71f065551adb6/urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54", size = 263900 } +sdist = { url = "https://files.pythonhosted.org/packages/36/dd/a6b232f449e1bc71802a5b7950dc3675d32c6dbc2a1bd6d71f065551adb6/urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54", size = 263900, upload_time = "2023-11-13T12:29:45.049Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/94/c31f58c7a7f470d5665935262ebd7455c7e4c7782eb525658d3dbf4b9403/urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3", size = 104579 }, + { url = "https://files.pythonhosted.org/packages/96/94/c31f58c7a7f470d5665935262ebd7455c7e4c7782eb525658d3dbf4b9403/urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3", size = 104579, upload_time = "2023-11-13T12:29:42.719Z" }, ] [[package]] name = "uvicorn" -version = "0.34.0" +version = "0.34.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568 } +sdist = { url = "https://files.pythonhosted.org/packages/a6/ae/9bbb19b9e1c450cf9ecaef06463e40234d98d95bf572fab11b4f19ae5ded/uvicorn-0.34.2.tar.gz", hash = "sha256:0e929828f6186353a80b58ea719861d2629d766293b6d19baf086ba31d4f3328", size = 76815, upload_time = "2025-04-19T06:02:50.101Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 }, + { url = "https://files.pythonhosted.org/packages/b1/4b/4cef6ce21a2aaca9d852a6e84ef4f135d99fcd74fa75105e2fc0c8308acd/uvicorn-0.34.2-py3-none-any.whl", hash = "sha256:deb49af569084536d269fe0a6d67e3754f104cf03aba7c11c40f01aadf33c403", size = 62483, upload_time = "2025-04-19T06:02:48.42Z" }, ] [package.optional-dependencies] @@ -2567,26 +2637,26 @@ standard = [ name = "uvloop" version = "0.19.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9c/16/728cc5dde368e6eddb299c5aec4d10eaf25335a5af04e8c0abd68e2e9d32/uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd", size = 2318492 } +sdist = { url = "https://files.pythonhosted.org/packages/9c/16/728cc5dde368e6eddb299c5aec4d10eaf25335a5af04e8c0abd68e2e9d32/uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd", size = 2318492, upload_time = "2023-10-22T22:03:57.665Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/c2/27bf858a576b1fa35b5c2c2029c8cec424a8789e87545ed2f25466d1f21d/uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e", size = 1443484 }, - { url = "https://files.pythonhosted.org/packages/4e/35/05b6064b93f4113412d1fd92bdcb6018607e78ae94d1712e63e533f9b2fa/uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428", size = 793850 }, - { url = "https://files.pythonhosted.org/packages/aa/56/b62ab4e10458ce96bb30c98d327c127f989d3bb4ef899e4c410c739f7ef6/uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8", size = 3418601 }, - { url = "https://files.pythonhosted.org/packages/ab/ed/12729fba5e3b7e02ee70b3ea230b88e60a50375cf63300db22607694d2f0/uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849", size = 3416731 }, - { url = "https://files.pythonhosted.org/packages/a2/23/80381a2d728d2a0c36e2eef202f5b77428990004d8fbdd3865558ff49fa5/uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957", size = 4128572 }, - { url = "https://files.pythonhosted.org/packages/6b/23/1ee41a15e1ad15182e2bd12cbfd37bcb6802f01d6bbcaddf6ca136cbb308/uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd", size = 4129235 }, - { url = "https://files.pythonhosted.org/packages/41/2a/608ad69f27f51280098abee440c33e921d3ad203e2c86f7262e241e49c99/uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef", size = 1357681 }, - { url = "https://files.pythonhosted.org/packages/13/00/d0923d66d80c8717983493a4d7af747ce47f1c2147d82df057a846ba6bff/uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2", size = 746421 }, - { url = "https://files.pythonhosted.org/packages/1f/c7/e494c367b0c6e6453f9bed5a78548f5b2ff49add36302cd915a91d347d88/uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1", size = 3481000 }, - { url = "https://files.pythonhosted.org/packages/86/cc/1829b3f740e4cb1baefff8240a1c6fc8db9e3caac7b93169aec7d4386069/uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24", size = 3476361 }, - { url = "https://files.pythonhosted.org/packages/7a/4c/ca87e8f5a30629ffa2038c20907c8ab455c5859ff10e810227b76e60d927/uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533", size = 4169571 }, - { url = "https://files.pythonhosted.org/packages/d2/a9/f947a00c47b1c87c937cac2423243a41ba08f0fb76d04eb0d1d170606e0a/uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12", size = 4170459 }, - { url = "https://files.pythonhosted.org/packages/85/57/6736733bb0e86a4b5380d04082463b289c0baecaa205934ba81e8a1d5ea4/uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650", size = 1355376 }, - { url = "https://files.pythonhosted.org/packages/eb/0c/51339463da912ed34b48d470538d98a91660749b2db56902f23db9b42fdd/uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec", size = 745031 }, - { url = "https://files.pythonhosted.org/packages/e6/fc/f0daaf19f5b2116a2d26eb9f98c4a45084aea87bf03c33bcca7aa1ff36e5/uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc", size = 4077630 }, - { url = "https://files.pythonhosted.org/packages/fd/96/fdc318ffe82ae567592b213ec2fcd8ecedd927b5da068cf84d56b28c51a4/uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6", size = 4159957 }, - { url = "https://files.pythonhosted.org/packages/71/bc/092068ae7fc16dcf20f3e389126ba7800cee75ffba83f78bf1d167aee3cd/uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593", size = 4014951 }, - { url = "https://files.pythonhosted.org/packages/a6/f2/6ce1e73933eb038c89f929e26042e64b2cb8d4453410153eed14918ca9a8/uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3", size = 4100911 }, + { url = "https://files.pythonhosted.org/packages/36/c2/27bf858a576b1fa35b5c2c2029c8cec424a8789e87545ed2f25466d1f21d/uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e", size = 1443484, upload_time = "2023-10-22T22:02:54.169Z" }, + { url = "https://files.pythonhosted.org/packages/4e/35/05b6064b93f4113412d1fd92bdcb6018607e78ae94d1712e63e533f9b2fa/uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428", size = 793850, upload_time = "2023-10-22T22:02:56.311Z" }, + { url = "https://files.pythonhosted.org/packages/aa/56/b62ab4e10458ce96bb30c98d327c127f989d3bb4ef899e4c410c739f7ef6/uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8", size = 3418601, upload_time = "2023-10-22T22:02:58.717Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ed/12729fba5e3b7e02ee70b3ea230b88e60a50375cf63300db22607694d2f0/uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849", size = 3416731, upload_time = "2023-10-22T22:03:01.043Z" }, + { url = "https://files.pythonhosted.org/packages/a2/23/80381a2d728d2a0c36e2eef202f5b77428990004d8fbdd3865558ff49fa5/uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957", size = 4128572, upload_time = "2023-10-22T22:03:02.874Z" }, + { url = "https://files.pythonhosted.org/packages/6b/23/1ee41a15e1ad15182e2bd12cbfd37bcb6802f01d6bbcaddf6ca136cbb308/uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd", size = 4129235, upload_time = "2023-10-22T22:03:05.361Z" }, + { url = "https://files.pythonhosted.org/packages/41/2a/608ad69f27f51280098abee440c33e921d3ad203e2c86f7262e241e49c99/uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef", size = 1357681, upload_time = "2023-10-22T22:03:07.158Z" }, + { url = "https://files.pythonhosted.org/packages/13/00/d0923d66d80c8717983493a4d7af747ce47f1c2147d82df057a846ba6bff/uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2", size = 746421, upload_time = "2023-10-22T22:03:09.4Z" }, + { url = "https://files.pythonhosted.org/packages/1f/c7/e494c367b0c6e6453f9bed5a78548f5b2ff49add36302cd915a91d347d88/uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1", size = 3481000, upload_time = "2023-10-22T22:03:11.755Z" }, + { url = "https://files.pythonhosted.org/packages/86/cc/1829b3f740e4cb1baefff8240a1c6fc8db9e3caac7b93169aec7d4386069/uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24", size = 3476361, upload_time = "2023-10-22T22:03:13.841Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4c/ca87e8f5a30629ffa2038c20907c8ab455c5859ff10e810227b76e60d927/uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533", size = 4169571, upload_time = "2023-10-22T22:03:15.618Z" }, + { url = "https://files.pythonhosted.org/packages/d2/a9/f947a00c47b1c87c937cac2423243a41ba08f0fb76d04eb0d1d170606e0a/uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12", size = 4170459, upload_time = "2023-10-22T22:03:17.988Z" }, + { url = "https://files.pythonhosted.org/packages/85/57/6736733bb0e86a4b5380d04082463b289c0baecaa205934ba81e8a1d5ea4/uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650", size = 1355376, upload_time = "2023-10-22T22:03:20.075Z" }, + { url = "https://files.pythonhosted.org/packages/eb/0c/51339463da912ed34b48d470538d98a91660749b2db56902f23db9b42fdd/uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec", size = 745031, upload_time = "2023-10-22T22:03:21.404Z" }, + { url = "https://files.pythonhosted.org/packages/e6/fc/f0daaf19f5b2116a2d26eb9f98c4a45084aea87bf03c33bcca7aa1ff36e5/uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc", size = 4077630, upload_time = "2023-10-22T22:03:23.568Z" }, + { url = "https://files.pythonhosted.org/packages/fd/96/fdc318ffe82ae567592b213ec2fcd8ecedd927b5da068cf84d56b28c51a4/uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6", size = 4159957, upload_time = "2023-10-22T22:03:25.278Z" }, + { url = "https://files.pythonhosted.org/packages/71/bc/092068ae7fc16dcf20f3e389126ba7800cee75ffba83f78bf1d167aee3cd/uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593", size = 4014951, upload_time = "2023-10-22T22:03:27.055Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f2/6ce1e73933eb038c89f929e26042e64b2cb8d4453410153eed14918ca9a8/uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3", size = 4100911, upload_time = "2023-10-22T22:03:29.39Z" }, ] [[package]] @@ -2596,106 +2666,115 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/79/0ee412e1228aaf6f9568aa180b43cb482472de52560fbd7c283c786534af/watchfiles-0.21.0.tar.gz", hash = "sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3", size = 37098 } +sdist = { url = "https://files.pythonhosted.org/packages/66/79/0ee412e1228aaf6f9568aa180b43cb482472de52560fbd7c283c786534af/watchfiles-0.21.0.tar.gz", hash = "sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3", size = 37098, upload_time = "2023-10-13T13:06:39.809Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/85/ea2a035b7d86bf0a29ee1c32bc2df8ad4da77e6602806e679d9735ff28cb/watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa", size = 428182 }, - { url = "https://files.pythonhosted.org/packages/b5/e5/240e5eb3ff0ee3da3b028ac5be2019c407bdd0f3fdb02bd75fdf3bd10aff/watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e", size = 418275 }, - { url = "https://files.pythonhosted.org/packages/5b/79/ecd0dfb04443a1900cd3952d7ea6493bf655c2db9a0d3736a5d98a15da39/watchfiles-0.21.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03", size = 1379785 }, - { url = "https://files.pythonhosted.org/packages/41/0e/3333b986b1889bb71f0e44b3fac0591824a679619b8b8ddd70ff8858edc4/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124", size = 1349374 }, - { url = "https://files.pythonhosted.org/packages/18/c4/ad5ad16cad900a29aaa792e0ed121ff70d76f74062b051661090d88c6dfd/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab", size = 1348033 }, - { url = "https://files.pythonhosted.org/packages/4e/d2/769254ff04ba88ceb179a6e892606ac4da17338eb010e85ca7a9c3339234/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303", size = 1464393 }, - { url = "https://files.pythonhosted.org/packages/14/d0/662800e778ca20e7664dd5df57751aa79ef18b6abb92224b03c8c2e852a6/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d", size = 1542953 }, - { url = "https://files.pythonhosted.org/packages/f7/4b/b90dcdc3bbaf3bb2db733e1beea2d01566b601c15fcf8e71dfcc8686c097/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c", size = 1346961 }, - { url = "https://files.pythonhosted.org/packages/92/ff/75cc1b30c5abcad13a2a72e75625ec619c7a393028a111d7d24dba578d5e/watchfiles-0.21.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9", size = 1464393 }, - { url = "https://files.pythonhosted.org/packages/9a/65/12cbeb363bf220482a559c48107edfd87f09248f55e1ac315a36c2098a0f/watchfiles-0.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9", size = 1463409 }, - { url = "https://files.pythonhosted.org/packages/f2/08/92e28867c66f0d9638bb131feca739057efc48dbcd391fd7f0a55507e470/watchfiles-0.21.0-cp310-none-win32.whl", hash = "sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293", size = 268101 }, - { url = "https://files.pythonhosted.org/packages/4b/ea/80527adf1ad51488a96fc201715730af5879f4dfeccb5e2069ff82d890d4/watchfiles-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235", size = 279675 }, - { url = "https://files.pythonhosted.org/packages/57/b9/2667286003dd305b81d3a3aa824d3dfc63dacbf2a96faae09e72d953c430/watchfiles-0.21.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7", size = 428210 }, - { url = "https://files.pythonhosted.org/packages/a3/87/6793ac60d2e20c9c1883aec7431c2e7b501ee44a839f6da1b747c13baa23/watchfiles-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef", size = 418196 }, - { url = "https://files.pythonhosted.org/packages/5d/12/e1d1d220c5b99196eea38c9a878964f30a2b55ec9d72fd713191725b35e8/watchfiles-0.21.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586", size = 1380287 }, - { url = "https://files.pythonhosted.org/packages/0e/cf/126f0a8683f326d190c3539a769e45e747a80a5fcbf797de82e738c946ae/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317", size = 1349653 }, - { url = "https://files.pythonhosted.org/packages/20/6e/6cffd795ec65dbc82f15d95b73d3042c1ddaffc4dd25f6c8240bfcf0640f/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b", size = 1348844 }, - { url = "https://files.pythonhosted.org/packages/d5/2a/f9633279d8937ad84c532997405dd106fa6100e8d2b83e364f1c87561f96/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1", size = 1464343 }, - { url = "https://files.pythonhosted.org/packages/d7/49/9b2199bbf3c89e7c8dd795fced9dac29f201be8a28a5df0c8ff625737df6/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d", size = 1542858 }, - { url = "https://files.pythonhosted.org/packages/35/e0/e8a9c1fe30e98c5b3507ad381abc4d9ee2c3b9c0ae62ffe9c164a5838186/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7", size = 1347464 }, - { url = "https://files.pythonhosted.org/packages/ba/66/873739dd7defdfaee4b880114de9463fae18ba13ae2ddd784806b0ee33b6/watchfiles-0.21.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0", size = 1464343 }, - { url = "https://files.pythonhosted.org/packages/bd/51/d7539aa258d8f0e5d7b870af8b9b8964b4f88a1e4517eeb8a2efb838e9b3/watchfiles-0.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365", size = 1463338 }, - { url = "https://files.pythonhosted.org/packages/ee/92/219c539a2a93b6870fa7b84eace946983126b20a7e15c6c034d8d0472682/watchfiles-0.21.0-cp311-none-win32.whl", hash = "sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400", size = 267658 }, - { url = "https://files.pythonhosted.org/packages/f3/dc/2a8a447b783f5059c4bf7a6bad8fe59375a5a9ce872774763b25c21c2860/watchfiles-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe", size = 280113 }, - { url = "https://files.pythonhosted.org/packages/22/15/e4085181cf0210a6ec6eb29fee0c6088de867ee33d81555076a4a2726e8b/watchfiles-0.21.0-cp311-none-win_arm64.whl", hash = "sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078", size = 268688 }, - { url = "https://files.pythonhosted.org/packages/a1/fd/2f009eb17809afd32a143b442856628585c9ce3a9c6d5c1841e44e35a16c/watchfiles-0.21.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a", size = 426902 }, - { url = "https://files.pythonhosted.org/packages/e0/62/a2605f212a136e06f2d056ee7491ede9935ba0f1d5ceafd1f7da2a0c8625/watchfiles-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1", size = 417300 }, - { url = "https://files.pythonhosted.org/packages/69/0e/29f158fa22eb2cc1f188b5ec20fb5c0a64eb801e3901ad5b7ad546cbaed0/watchfiles-0.21.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a", size = 1378126 }, - { url = "https://files.pythonhosted.org/packages/e8/f3/c67865cb5a174201c52d34e870cc7956b8408ee83ce9a02909d6a2a93a14/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915", size = 1348275 }, - { url = "https://files.pythonhosted.org/packages/d7/eb/b6f1184d1c7b9670f5bd1e184e4c221ecf25fd817cf2fcac6adc387882b5/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360", size = 1347255 }, - { url = "https://files.pythonhosted.org/packages/c8/27/e534e4b3fe739f4bf8bd5dc4c26cbc5d3baa427125d8ef78a6556acd6ff4/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6", size = 1462845 }, - { url = "https://files.pythonhosted.org/packages/b0/ba/a0d1c1c55f75e7e47c8f79f2314f7ec670b5177596f6d27764aecc7048cd/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7", size = 1528957 }, - { url = "https://files.pythonhosted.org/packages/1c/3a/4e38518c4dff58090c01fc8cc051fa08ac9ae00b361c855075809b0058ce/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c", size = 1345542 }, - { url = "https://files.pythonhosted.org/packages/9f/b7/783097f8137a710d5cd9ccbfcd92e4b453d38dab05cfcb5dbd2c587752e5/watchfiles-0.21.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235", size = 1462238 }, - { url = "https://files.pythonhosted.org/packages/6b/4c/b741eb38f2c408ae9c5a25235f6506b1dda43486ae0fdb4c462ef75bce11/watchfiles-0.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7", size = 1462406 }, - { url = "https://files.pythonhosted.org/packages/77/e4/8d2b3c67364671b0e1c0ce383895a5415f45ecb3e8586982deff4a8e85c9/watchfiles-0.21.0-cp312-none-win32.whl", hash = "sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3", size = 266789 }, - { url = "https://files.pythonhosted.org/packages/da/f2/6b1de38aeb21eb9dac1ae6a1ee4521566e79690117032036c737cfab52fa/watchfiles-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094", size = 280292 }, - { url = "https://files.pythonhosted.org/packages/5a/a5/7aba9435beb863c2490bae3173a45f42044ac7a48155d3dd42ab49cfae45/watchfiles-0.21.0-cp312-none-win_arm64.whl", hash = "sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6", size = 268026 }, - { url = "https://files.pythonhosted.org/packages/62/66/7463ceb43eabc6deaa795c7969ff4d4fd938de54e655035483dfd1e97c84/watchfiles-0.21.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994", size = 429092 }, - { url = "https://files.pythonhosted.org/packages/fe/a3/42686af3a089f34aba35c39abac852869661938dae7025c1a0580dfe0fbf/watchfiles-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f", size = 419188 }, - { url = "https://files.pythonhosted.org/packages/37/17/4825999346f15d650f4c69093efa64fb040fbff4f706a20e8c4745f64070/watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c", size = 1350366 }, - { url = "https://files.pythonhosted.org/packages/70/76/8d124e14cf51af4d6bba926c7473f253c6efd1539ba62577f079a2d71537/watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc", size = 1346270 }, + { url = "https://files.pythonhosted.org/packages/6e/85/ea2a035b7d86bf0a29ee1c32bc2df8ad4da77e6602806e679d9735ff28cb/watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa", size = 428182, upload_time = "2023-10-13T13:04:34.803Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e5/240e5eb3ff0ee3da3b028ac5be2019c407bdd0f3fdb02bd75fdf3bd10aff/watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e", size = 418275, upload_time = "2023-10-13T13:04:36.632Z" }, + { url = "https://files.pythonhosted.org/packages/5b/79/ecd0dfb04443a1900cd3952d7ea6493bf655c2db9a0d3736a5d98a15da39/watchfiles-0.21.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03", size = 1379785, upload_time = "2023-10-13T13:04:38.641Z" }, + { url = "https://files.pythonhosted.org/packages/41/0e/3333b986b1889bb71f0e44b3fac0591824a679619b8b8ddd70ff8858edc4/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124", size = 1349374, upload_time = "2023-10-13T13:04:41.711Z" }, + { url = "https://files.pythonhosted.org/packages/18/c4/ad5ad16cad900a29aaa792e0ed121ff70d76f74062b051661090d88c6dfd/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab", size = 1348033, upload_time = "2023-10-13T13:04:43.324Z" }, + { url = "https://files.pythonhosted.org/packages/4e/d2/769254ff04ba88ceb179a6e892606ac4da17338eb010e85ca7a9c3339234/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303", size = 1464393, upload_time = "2023-10-13T13:04:44.818Z" }, + { url = "https://files.pythonhosted.org/packages/14/d0/662800e778ca20e7664dd5df57751aa79ef18b6abb92224b03c8c2e852a6/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d", size = 1542953, upload_time = "2023-10-13T13:04:46.714Z" }, + { url = "https://files.pythonhosted.org/packages/f7/4b/b90dcdc3bbaf3bb2db733e1beea2d01566b601c15fcf8e71dfcc8686c097/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c", size = 1346961, upload_time = "2023-10-13T13:04:48.072Z" }, + { url = "https://files.pythonhosted.org/packages/92/ff/75cc1b30c5abcad13a2a72e75625ec619c7a393028a111d7d24dba578d5e/watchfiles-0.21.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9", size = 1464393, upload_time = "2023-10-13T13:04:49.638Z" }, + { url = "https://files.pythonhosted.org/packages/9a/65/12cbeb363bf220482a559c48107edfd87f09248f55e1ac315a36c2098a0f/watchfiles-0.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9", size = 1463409, upload_time = "2023-10-13T13:04:51.762Z" }, + { url = "https://files.pythonhosted.org/packages/f2/08/92e28867c66f0d9638bb131feca739057efc48dbcd391fd7f0a55507e470/watchfiles-0.21.0-cp310-none-win32.whl", hash = "sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293", size = 268101, upload_time = "2023-10-13T13:04:53.78Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ea/80527adf1ad51488a96fc201715730af5879f4dfeccb5e2069ff82d890d4/watchfiles-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235", size = 279675, upload_time = "2023-10-13T13:04:55.113Z" }, + { url = "https://files.pythonhosted.org/packages/57/b9/2667286003dd305b81d3a3aa824d3dfc63dacbf2a96faae09e72d953c430/watchfiles-0.21.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7", size = 428210, upload_time = "2023-10-13T13:04:56.894Z" }, + { url = "https://files.pythonhosted.org/packages/a3/87/6793ac60d2e20c9c1883aec7431c2e7b501ee44a839f6da1b747c13baa23/watchfiles-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef", size = 418196, upload_time = "2023-10-13T13:04:58.19Z" }, + { url = "https://files.pythonhosted.org/packages/5d/12/e1d1d220c5b99196eea38c9a878964f30a2b55ec9d72fd713191725b35e8/watchfiles-0.21.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586", size = 1380287, upload_time = "2023-10-13T13:04:59.923Z" }, + { url = "https://files.pythonhosted.org/packages/0e/cf/126f0a8683f326d190c3539a769e45e747a80a5fcbf797de82e738c946ae/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317", size = 1349653, upload_time = "2023-10-13T13:05:01.622Z" }, + { url = "https://files.pythonhosted.org/packages/20/6e/6cffd795ec65dbc82f15d95b73d3042c1ddaffc4dd25f6c8240bfcf0640f/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b", size = 1348844, upload_time = "2023-10-13T13:05:03.805Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2a/f9633279d8937ad84c532997405dd106fa6100e8d2b83e364f1c87561f96/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1", size = 1464343, upload_time = "2023-10-13T13:05:05.248Z" }, + { url = "https://files.pythonhosted.org/packages/d7/49/9b2199bbf3c89e7c8dd795fced9dac29f201be8a28a5df0c8ff625737df6/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d", size = 1542858, upload_time = "2023-10-13T13:05:06.791Z" }, + { url = "https://files.pythonhosted.org/packages/35/e0/e8a9c1fe30e98c5b3507ad381abc4d9ee2c3b9c0ae62ffe9c164a5838186/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7", size = 1347464, upload_time = "2023-10-13T13:05:08.622Z" }, + { url = "https://files.pythonhosted.org/packages/ba/66/873739dd7defdfaee4b880114de9463fae18ba13ae2ddd784806b0ee33b6/watchfiles-0.21.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0", size = 1464343, upload_time = "2023-10-13T13:05:10.584Z" }, + { url = "https://files.pythonhosted.org/packages/bd/51/d7539aa258d8f0e5d7b870af8b9b8964b4f88a1e4517eeb8a2efb838e9b3/watchfiles-0.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365", size = 1463338, upload_time = "2023-10-13T13:05:12.671Z" }, + { url = "https://files.pythonhosted.org/packages/ee/92/219c539a2a93b6870fa7b84eace946983126b20a7e15c6c034d8d0472682/watchfiles-0.21.0-cp311-none-win32.whl", hash = "sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400", size = 267658, upload_time = "2023-10-13T13:05:13.972Z" }, + { url = "https://files.pythonhosted.org/packages/f3/dc/2a8a447b783f5059c4bf7a6bad8fe59375a5a9ce872774763b25c21c2860/watchfiles-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe", size = 280113, upload_time = "2023-10-13T13:05:15.289Z" }, + { url = "https://files.pythonhosted.org/packages/22/15/e4085181cf0210a6ec6eb29fee0c6088de867ee33d81555076a4a2726e8b/watchfiles-0.21.0-cp311-none-win_arm64.whl", hash = "sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078", size = 268688, upload_time = "2023-10-13T13:05:17.144Z" }, + { url = "https://files.pythonhosted.org/packages/a1/fd/2f009eb17809afd32a143b442856628585c9ce3a9c6d5c1841e44e35a16c/watchfiles-0.21.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a", size = 426902, upload_time = "2023-10-13T13:05:18.828Z" }, + { url = "https://files.pythonhosted.org/packages/e0/62/a2605f212a136e06f2d056ee7491ede9935ba0f1d5ceafd1f7da2a0c8625/watchfiles-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1", size = 417300, upload_time = "2023-10-13T13:05:20.116Z" }, + { url = "https://files.pythonhosted.org/packages/69/0e/29f158fa22eb2cc1f188b5ec20fb5c0a64eb801e3901ad5b7ad546cbaed0/watchfiles-0.21.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a", size = 1378126, upload_time = "2023-10-13T13:05:21.508Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f3/c67865cb5a174201c52d34e870cc7956b8408ee83ce9a02909d6a2a93a14/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915", size = 1348275, upload_time = "2023-10-13T13:05:22.995Z" }, + { url = "https://files.pythonhosted.org/packages/d7/eb/b6f1184d1c7b9670f5bd1e184e4c221ecf25fd817cf2fcac6adc387882b5/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360", size = 1347255, upload_time = "2023-10-13T13:05:24.618Z" }, + { url = "https://files.pythonhosted.org/packages/c8/27/e534e4b3fe739f4bf8bd5dc4c26cbc5d3baa427125d8ef78a6556acd6ff4/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6", size = 1462845, upload_time = "2023-10-13T13:05:26.531Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ba/a0d1c1c55f75e7e47c8f79f2314f7ec670b5177596f6d27764aecc7048cd/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7", size = 1528957, upload_time = "2023-10-13T13:05:28.365Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3a/4e38518c4dff58090c01fc8cc051fa08ac9ae00b361c855075809b0058ce/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c", size = 1345542, upload_time = "2023-10-13T13:05:29.862Z" }, + { url = "https://files.pythonhosted.org/packages/9f/b7/783097f8137a710d5cd9ccbfcd92e4b453d38dab05cfcb5dbd2c587752e5/watchfiles-0.21.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235", size = 1462238, upload_time = "2023-10-13T13:05:32.245Z" }, + { url = "https://files.pythonhosted.org/packages/6b/4c/b741eb38f2c408ae9c5a25235f6506b1dda43486ae0fdb4c462ef75bce11/watchfiles-0.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7", size = 1462406, upload_time = "2023-10-13T13:05:34.339Z" }, + { url = "https://files.pythonhosted.org/packages/77/e4/8d2b3c67364671b0e1c0ce383895a5415f45ecb3e8586982deff4a8e85c9/watchfiles-0.21.0-cp312-none-win32.whl", hash = "sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3", size = 266789, upload_time = "2023-10-13T13:05:35.606Z" }, + { url = "https://files.pythonhosted.org/packages/da/f2/6b1de38aeb21eb9dac1ae6a1ee4521566e79690117032036c737cfab52fa/watchfiles-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094", size = 280292, upload_time = "2023-10-13T13:05:37.357Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a5/7aba9435beb863c2490bae3173a45f42044ac7a48155d3dd42ab49cfae45/watchfiles-0.21.0-cp312-none-win_arm64.whl", hash = "sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6", size = 268026, upload_time = "2023-10-13T13:05:38.591Z" }, + { url = "https://files.pythonhosted.org/packages/62/66/7463ceb43eabc6deaa795c7969ff4d4fd938de54e655035483dfd1e97c84/watchfiles-0.21.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994", size = 429092, upload_time = "2023-10-13T13:06:21.419Z" }, + { url = "https://files.pythonhosted.org/packages/fe/a3/42686af3a089f34aba35c39abac852869661938dae7025c1a0580dfe0fbf/watchfiles-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f", size = 419188, upload_time = "2023-10-13T13:06:22.934Z" }, + { url = "https://files.pythonhosted.org/packages/37/17/4825999346f15d650f4c69093efa64fb040fbff4f706a20e8c4745f64070/watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c", size = 1350366, upload_time = "2023-10-13T13:06:24.254Z" }, + { url = "https://files.pythonhosted.org/packages/70/76/8d124e14cf51af4d6bba926c7473f253c6efd1539ba62577f079a2d71537/watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc", size = 1346270, upload_time = "2023-10-13T13:06:25.742Z" }, ] [[package]] name = "wcwidth" version = "0.2.13" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 } +sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301, upload_time = "2024-01-06T02:10:57.829Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, + { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166, upload_time = "2024-01-06T02:10:55.763Z" }, +] + +[[package]] +name = "websocket-client" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da", size = 54648, upload_time = "2024-04-23T22:16:16.976Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826, upload_time = "2024-04-23T22:16:14.422Z" }, ] [[package]] name = "websockets" version = "12.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2e/62/7a7874b7285413c954a4cca3c11fd851f11b2fe5b4ae2d9bee4f6d9bdb10/websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b", size = 104994 } +sdist = { url = "https://files.pythonhosted.org/packages/2e/62/7a7874b7285413c954a4cca3c11fd851f11b2fe5b4ae2d9bee4f6d9bdb10/websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b", size = 104994, upload_time = "2023-10-21T14:21:11.88Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/b9/360b86ded0920a93bff0db4e4b0aa31370b0208ca240b2e98d62aad8d082/websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374", size = 124025 }, - { url = "https://files.pythonhosted.org/packages/bb/d3/1eca0d8fb6f0665c96f0dc7c0d0ec8aa1a425e8c003e0c18e1451f65d177/websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be", size = 121261 }, - { url = "https://files.pythonhosted.org/packages/4e/e1/f6c3ecf7f1bfd9209e13949db027d7fdea2faf090c69b5f2d17d1d796d96/websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547", size = 121328 }, - { url = "https://files.pythonhosted.org/packages/74/4d/f88eeceb23cb587c4aeca779e3f356cf54817af2368cb7f2bd41f93c8360/websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2", size = 130925 }, - { url = "https://files.pythonhosted.org/packages/16/17/f63d9ee6ffd9afbeea021d5950d6e8db84cd4aead306c6c2ca523805699e/websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558", size = 129930 }, - { url = "https://files.pythonhosted.org/packages/9a/12/c7a7504f5bf74d6ee0533f6fc7d30d8f4b79420ab179d1df2484b07602eb/websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480", size = 130245 }, - { url = "https://files.pythonhosted.org/packages/e4/6a/3600c7771eb31116d2e77383d7345618b37bb93709d041e328c08e2a8eb3/websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c", size = 134966 }, - { url = "https://files.pythonhosted.org/packages/22/26/df77c4b7538caebb78c9b97f43169ef742a4f445e032a5ea1aaef88f8f46/websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8", size = 134196 }, - { url = "https://files.pythonhosted.org/packages/e5/18/18ce9a4a08203c8d0d3d561e3ea4f453daf32f099601fc831e60c8a9b0f2/websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603", size = 134822 }, - { url = "https://files.pythonhosted.org/packages/45/51/1f823a341fc20a880e67ae62f6c38c4880a24a4b60fbe544a38f516f39a1/websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f", size = 124454 }, - { url = "https://files.pythonhosted.org/packages/41/b0/5ec054cfcf23adfc88d39359b85e81d043af8a141e3ac8ce40f45a5ce5f4/websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf", size = 124974 }, - { url = "https://files.pythonhosted.org/packages/02/73/9c1e168a2e7fdf26841dc98f5f5502e91dea47428da7690a08101f616169/websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4", size = 124047 }, - { url = "https://files.pythonhosted.org/packages/e4/2d/9a683359ad2ed11b2303a7a94800db19c61d33fa3bde271df09e99936022/websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f", size = 121282 }, - { url = "https://files.pythonhosted.org/packages/95/aa/75fa3b893142d6d98a48cb461169bd268141f2da8bfca97392d6462a02eb/websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3", size = 121325 }, - { url = "https://files.pythonhosted.org/packages/6e/a4/51a25e591d645df71ee0dc3a2c880b28e5514c00ce752f98a40a87abcd1e/websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c", size = 131502 }, - { url = "https://files.pythonhosted.org/packages/cd/ea/0ceeea4f5b87398fe2d9f5bcecfa00a1bcd542e2bfcac2f2e5dd612c4e9e/websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45", size = 130491 }, - { url = "https://files.pythonhosted.org/packages/e3/05/f52a60b66d9faf07a4f7d71dc056bffafe36a7e98c4eb5b78f04fe6e4e85/websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04", size = 130872 }, - { url = "https://files.pythonhosted.org/packages/ac/4e/c7361b2d7b964c40fea924d64881145164961fcd6c90b88b7e3ab2c4f431/websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447", size = 136318 }, - { url = "https://files.pythonhosted.org/packages/0a/31/337bf35ae5faeaf364c9cddec66681cdf51dc4414ee7a20f92a18e57880f/websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca", size = 135594 }, - { url = "https://files.pythonhosted.org/packages/95/aa/1ac767825c96f9d7e43c4c95683757d4ef28cf11fa47a69aca42428d3e3a/websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53", size = 136191 }, - { url = "https://files.pythonhosted.org/packages/28/4b/344ec5cfeb6bc417da097f8253607c3aed11d9a305fb58346f506bf556d8/websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402", size = 124453 }, - { url = "https://files.pythonhosted.org/packages/d1/40/6b169cd1957476374f51f4486a3e85003149e62a14e6b78a958c2222337a/websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b", size = 124971 }, - { url = "https://files.pythonhosted.org/packages/a9/6d/23cc898647c8a614a0d9ca703695dd04322fb5135096a20c2684b7c852b6/websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df", size = 124061 }, - { url = "https://files.pythonhosted.org/packages/39/34/364f30fdf1a375e4002a26ee3061138d1571dfda6421126127d379d13930/websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc", size = 121296 }, - { url = "https://files.pythonhosted.org/packages/2e/00/96ae1c9dcb3bc316ef683f2febd8c97dde9f254dc36c3afc65c7645f734c/websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b", size = 121326 }, - { url = "https://files.pythonhosted.org/packages/af/f1/bba1e64430685dd456c1a1fd6b0c791ae33104967b928aefeff261761e8d/websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb", size = 131807 }, - { url = "https://files.pythonhosted.org/packages/62/3b/98ee269712f37d892b93852ce07b3e6d7653160ca4c0d4f8c8663f8021f8/websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92", size = 130751 }, - { url = "https://files.pythonhosted.org/packages/f1/00/d6f01ca2b191f8b0808e4132ccd2e7691f0453cbd7d0f72330eb97453c3a/websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed", size = 131176 }, - { url = "https://files.pythonhosted.org/packages/af/9c/703ff3cd8109dcdee6152bae055d852ebaa7750117760ded697ab836cbcf/websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5", size = 136246 }, - { url = "https://files.pythonhosted.org/packages/0b/a5/1a38fb85a456b9dc874ec984f3ff34f6550eafd17a3da28753cd3c1628e8/websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2", size = 135466 }, - { url = "https://files.pythonhosted.org/packages/3c/98/1261f289dff7e65a38d59d2f591de6ed0a2580b729aebddec033c4d10881/websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113", size = 136083 }, - { url = "https://files.pythonhosted.org/packages/a9/1c/f68769fba63ccb9c13fe0a25b616bd5aebeef1c7ddebc2ccc32462fb784d/websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d", size = 124460 }, - { url = "https://files.pythonhosted.org/packages/20/52/8915f51f9aaef4e4361c89dd6cf69f72a0159f14e0d25026c81b6ad22525/websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f", size = 124985 }, - { url = "https://files.pythonhosted.org/packages/43/8b/554a8a8bb6da9dd1ce04c44125e2192af7b7beebf6e3dbfa5d0e285cc20f/websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd", size = 121110 }, - { url = "https://files.pythonhosted.org/packages/b0/8e/58b8812940d746ad74d395fb069497255cb5ef50748dfab1e8b386b1f339/websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870", size = 123216 }, - { url = "https://files.pythonhosted.org/packages/81/ee/272cb67ace1786ce6d9f39d47b3c55b335e8b75dd1972a7967aad39178b6/websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077", size = 122821 }, - { url = "https://files.pythonhosted.org/packages/a8/03/387fc902b397729df166763e336f4e5cec09fe7b9d60f442542c94a21be1/websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b", size = 122768 }, - { url = "https://files.pythonhosted.org/packages/50/f0/5939fbc9bc1979d79a774ce5b7c4b33c0cefe99af22fb70f7462d0919640/websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30", size = 125009 }, - { url = "https://files.pythonhosted.org/packages/79/4d/9cc401e7b07e80532ebc8c8e993f42541534da9e9249c59ee0139dcb0352/websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e", size = 118370 }, + { url = "https://files.pythonhosted.org/packages/b1/b9/360b86ded0920a93bff0db4e4b0aa31370b0208ca240b2e98d62aad8d082/websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374", size = 124025, upload_time = "2023-10-21T14:19:28.387Z" }, + { url = "https://files.pythonhosted.org/packages/bb/d3/1eca0d8fb6f0665c96f0dc7c0d0ec8aa1a425e8c003e0c18e1451f65d177/websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be", size = 121261, upload_time = "2023-10-21T14:19:30.203Z" }, + { url = "https://files.pythonhosted.org/packages/4e/e1/f6c3ecf7f1bfd9209e13949db027d7fdea2faf090c69b5f2d17d1d796d96/websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547", size = 121328, upload_time = "2023-10-21T14:19:31.765Z" }, + { url = "https://files.pythonhosted.org/packages/74/4d/f88eeceb23cb587c4aeca779e3f356cf54817af2368cb7f2bd41f93c8360/websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2", size = 130925, upload_time = "2023-10-21T14:19:33.36Z" }, + { url = "https://files.pythonhosted.org/packages/16/17/f63d9ee6ffd9afbeea021d5950d6e8db84cd4aead306c6c2ca523805699e/websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558", size = 129930, upload_time = "2023-10-21T14:19:35.109Z" }, + { url = "https://files.pythonhosted.org/packages/9a/12/c7a7504f5bf74d6ee0533f6fc7d30d8f4b79420ab179d1df2484b07602eb/websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480", size = 130245, upload_time = "2023-10-21T14:19:36.761Z" }, + { url = "https://files.pythonhosted.org/packages/e4/6a/3600c7771eb31116d2e77383d7345618b37bb93709d041e328c08e2a8eb3/websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c", size = 134966, upload_time = "2023-10-21T14:19:38.481Z" }, + { url = "https://files.pythonhosted.org/packages/22/26/df77c4b7538caebb78c9b97f43169ef742a4f445e032a5ea1aaef88f8f46/websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8", size = 134196, upload_time = "2023-10-21T14:19:40.264Z" }, + { url = "https://files.pythonhosted.org/packages/e5/18/18ce9a4a08203c8d0d3d561e3ea4f453daf32f099601fc831e60c8a9b0f2/websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603", size = 134822, upload_time = "2023-10-21T14:19:41.836Z" }, + { url = "https://files.pythonhosted.org/packages/45/51/1f823a341fc20a880e67ae62f6c38c4880a24a4b60fbe544a38f516f39a1/websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f", size = 124454, upload_time = "2023-10-21T14:19:43.639Z" }, + { url = "https://files.pythonhosted.org/packages/41/b0/5ec054cfcf23adfc88d39359b85e81d043af8a141e3ac8ce40f45a5ce5f4/websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf", size = 124974, upload_time = "2023-10-21T14:19:44.934Z" }, + { url = "https://files.pythonhosted.org/packages/02/73/9c1e168a2e7fdf26841dc98f5f5502e91dea47428da7690a08101f616169/websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4", size = 124047, upload_time = "2023-10-21T14:19:46.519Z" }, + { url = "https://files.pythonhosted.org/packages/e4/2d/9a683359ad2ed11b2303a7a94800db19c61d33fa3bde271df09e99936022/websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f", size = 121282, upload_time = "2023-10-21T14:19:47.739Z" }, + { url = "https://files.pythonhosted.org/packages/95/aa/75fa3b893142d6d98a48cb461169bd268141f2da8bfca97392d6462a02eb/websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3", size = 121325, upload_time = "2023-10-21T14:19:49.4Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a4/51a25e591d645df71ee0dc3a2c880b28e5514c00ce752f98a40a87abcd1e/websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c", size = 131502, upload_time = "2023-10-21T14:19:50.683Z" }, + { url = "https://files.pythonhosted.org/packages/cd/ea/0ceeea4f5b87398fe2d9f5bcecfa00a1bcd542e2bfcac2f2e5dd612c4e9e/websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45", size = 130491, upload_time = "2023-10-21T14:19:51.835Z" }, + { url = "https://files.pythonhosted.org/packages/e3/05/f52a60b66d9faf07a4f7d71dc056bffafe36a7e98c4eb5b78f04fe6e4e85/websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04", size = 130872, upload_time = "2023-10-21T14:19:53.071Z" }, + { url = "https://files.pythonhosted.org/packages/ac/4e/c7361b2d7b964c40fea924d64881145164961fcd6c90b88b7e3ab2c4f431/websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447", size = 136318, upload_time = "2023-10-21T14:19:54.41Z" }, + { url = "https://files.pythonhosted.org/packages/0a/31/337bf35ae5faeaf364c9cddec66681cdf51dc4414ee7a20f92a18e57880f/websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca", size = 135594, upload_time = "2023-10-21T14:19:55.982Z" }, + { url = "https://files.pythonhosted.org/packages/95/aa/1ac767825c96f9d7e43c4c95683757d4ef28cf11fa47a69aca42428d3e3a/websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53", size = 136191, upload_time = "2023-10-21T14:19:57.349Z" }, + { url = "https://files.pythonhosted.org/packages/28/4b/344ec5cfeb6bc417da097f8253607c3aed11d9a305fb58346f506bf556d8/websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402", size = 124453, upload_time = "2023-10-21T14:19:59.11Z" }, + { url = "https://files.pythonhosted.org/packages/d1/40/6b169cd1957476374f51f4486a3e85003149e62a14e6b78a958c2222337a/websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b", size = 124971, upload_time = "2023-10-21T14:20:00.243Z" }, + { url = "https://files.pythonhosted.org/packages/a9/6d/23cc898647c8a614a0d9ca703695dd04322fb5135096a20c2684b7c852b6/websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df", size = 124061, upload_time = "2023-10-21T14:20:02.221Z" }, + { url = "https://files.pythonhosted.org/packages/39/34/364f30fdf1a375e4002a26ee3061138d1571dfda6421126127d379d13930/websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc", size = 121296, upload_time = "2023-10-21T14:20:03.591Z" }, + { url = "https://files.pythonhosted.org/packages/2e/00/96ae1c9dcb3bc316ef683f2febd8c97dde9f254dc36c3afc65c7645f734c/websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b", size = 121326, upload_time = "2023-10-21T14:20:04.956Z" }, + { url = "https://files.pythonhosted.org/packages/af/f1/bba1e64430685dd456c1a1fd6b0c791ae33104967b928aefeff261761e8d/websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb", size = 131807, upload_time = "2023-10-21T14:20:06.153Z" }, + { url = "https://files.pythonhosted.org/packages/62/3b/98ee269712f37d892b93852ce07b3e6d7653160ca4c0d4f8c8663f8021f8/websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92", size = 130751, upload_time = "2023-10-21T14:20:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/f1/00/d6f01ca2b191f8b0808e4132ccd2e7691f0453cbd7d0f72330eb97453c3a/websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed", size = 131176, upload_time = "2023-10-21T14:20:09.212Z" }, + { url = "https://files.pythonhosted.org/packages/af/9c/703ff3cd8109dcdee6152bae055d852ebaa7750117760ded697ab836cbcf/websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5", size = 136246, upload_time = "2023-10-21T14:20:10.423Z" }, + { url = "https://files.pythonhosted.org/packages/0b/a5/1a38fb85a456b9dc874ec984f3ff34f6550eafd17a3da28753cd3c1628e8/websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2", size = 135466, upload_time = "2023-10-21T14:20:11.826Z" }, + { url = "https://files.pythonhosted.org/packages/3c/98/1261f289dff7e65a38d59d2f591de6ed0a2580b729aebddec033c4d10881/websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113", size = 136083, upload_time = "2023-10-21T14:20:13.451Z" }, + { url = "https://files.pythonhosted.org/packages/a9/1c/f68769fba63ccb9c13fe0a25b616bd5aebeef1c7ddebc2ccc32462fb784d/websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d", size = 124460, upload_time = "2023-10-21T14:20:14.719Z" }, + { url = "https://files.pythonhosted.org/packages/20/52/8915f51f9aaef4e4361c89dd6cf69f72a0159f14e0d25026c81b6ad22525/websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f", size = 124985, upload_time = "2023-10-21T14:20:15.817Z" }, + { url = "https://files.pythonhosted.org/packages/43/8b/554a8a8bb6da9dd1ce04c44125e2192af7b7beebf6e3dbfa5d0e285cc20f/websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd", size = 121110, upload_time = "2023-10-21T14:20:48.335Z" }, + { url = "https://files.pythonhosted.org/packages/b0/8e/58b8812940d746ad74d395fb069497255cb5ef50748dfab1e8b386b1f339/websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870", size = 123216, upload_time = "2023-10-21T14:20:50.083Z" }, + { url = "https://files.pythonhosted.org/packages/81/ee/272cb67ace1786ce6d9f39d47b3c55b335e8b75dd1972a7967aad39178b6/websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077", size = 122821, upload_time = "2023-10-21T14:20:51.237Z" }, + { url = "https://files.pythonhosted.org/packages/a8/03/387fc902b397729df166763e336f4e5cec09fe7b9d60f442542c94a21be1/websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b", size = 122768, upload_time = "2023-10-21T14:20:52.59Z" }, + { url = "https://files.pythonhosted.org/packages/50/f0/5939fbc9bc1979d79a774ce5b7c4b33c0cefe99af22fb70f7462d0919640/websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30", size = 125009, upload_time = "2023-10-21T14:20:54.419Z" }, + { url = "https://files.pythonhosted.org/packages/79/4d/9cc401e7b07e80532ebc8c8e993f42541534da9e9249c59ee0139dcb0352/websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e", size = 118370, upload_time = "2023-10-21T14:21:10.075Z" }, ] [[package]] @@ -2705,9 +2784,21 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/51/2e0fc149e7a810d300422ab543f87f2bcf64d985eb6f1228c4efd6e4f8d4/werkzeug-3.0.3.tar.gz", hash = "sha256:097e5bfda9f0aba8da6b8545146def481d06aa7d3266e7448e2cccf67dd8bd18", size = 803342 } +sdist = { url = "https://files.pythonhosted.org/packages/02/51/2e0fc149e7a810d300422ab543f87f2bcf64d985eb6f1228c4efd6e4f8d4/werkzeug-3.0.3.tar.gz", hash = "sha256:097e5bfda9f0aba8da6b8545146def481d06aa7d3266e7448e2cccf67dd8bd18", size = 803342, upload_time = "2024-05-05T23:10:31.999Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/6e/e792999e816d19d7fcbfa94c730936750036d65656a76a5a688b57a656c4/werkzeug-3.0.3-py3-none-any.whl", hash = "sha256:fc9645dc43e03e4d630d23143a04a7f947a9a3b5727cd535fdfe155a17cc48c8", size = 227274 }, + { url = "https://files.pythonhosted.org/packages/9d/6e/e792999e816d19d7fcbfa94c730936750036d65656a76a5a688b57a656c4/werkzeug-3.0.3-py3-none-any.whl", hash = "sha256:fc9645dc43e03e4d630d23143a04a7f947a9a3b5727cd535fdfe155a17cc48c8", size = 227274, upload_time = "2024-05-05T23:10:29.567Z" }, +] + +[[package]] +name = "wsproto" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/4a/44d3c295350d776427904d73c189e10aeae66d7f555bb2feee16d1e4ba5a/wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065", size = 53425, upload_time = "2022-08-23T19:58:21.447Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/58/e860788190eba3bcce367f74d29c4675466ce8dddfba85f7827588416f01/wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736", size = 24226, upload_time = "2022-08-23T19:58:19.96Z" }, ] [[package]] @@ -2717,9 +2808,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "setuptools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/46/c2/427f1867bb96555d1d34342f1dd97f8c420966ab564d58d18469a1db8736/zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd", size = 17350 } +sdist = { url = "https://files.pythonhosted.org/packages/46/c2/427f1867bb96555d1d34342f1dd97f8c420966ab564d58d18469a1db8736/zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd", size = 17350, upload_time = "2023-06-23T06:28:35.709Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/42/f8dbc2b9ad59e927940325a22d6d3931d630c3644dae7e2369ef5d9ba230/zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26", size = 6824 }, + { url = "https://files.pythonhosted.org/packages/fe/42/f8dbc2b9ad59e927940325a22d6d3931d630c3644dae7e2369ef5d9ba230/zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26", size = 6824, upload_time = "2023-06-23T06:28:32.652Z" }, ] [[package]] @@ -2729,24 +2820,24 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "setuptools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/87/03/6b85c1df2dca1b9acca38b423d1e226d8ffdf30ebd78bcb398c511de8b54/zope.interface-6.1.tar.gz", hash = "sha256:2fdc7ccbd6eb6b7df5353012fbed6c3c5d04ceaca0038f75e601060e95345309", size = 293914 } +sdist = { url = "https://files.pythonhosted.org/packages/87/03/6b85c1df2dca1b9acca38b423d1e226d8ffdf30ebd78bcb398c511de8b54/zope.interface-6.1.tar.gz", hash = "sha256:2fdc7ccbd6eb6b7df5353012fbed6c3c5d04ceaca0038f75e601060e95345309", size = 293914, upload_time = "2023-10-05T11:24:38.943Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/ec/c1e7ce928dc10bfe02c6da7e964342d941aaf168f96f8084636167ea50d2/zope.interface-6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:43b576c34ef0c1f5a4981163b551a8781896f2a37f71b8655fd20b5af0386abb", size = 202417 }, - { url = "https://files.pythonhosted.org/packages/f7/0b/12f269ad049fc40a7a3ab85445d7855b6bc6f1e774c5ca9dd6f5c32becb3/zope.interface-6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:67be3ca75012c6e9b109860820a8b6c9a84bfb036fbd1076246b98e56951ca92", size = 202528 }, - { url = "https://files.pythonhosted.org/packages/7f/85/3a35144509eb4a5a2208b48ae8d116a969d67de62cc6513d85602144d9cd/zope.interface-6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b9bc671626281f6045ad61d93a60f52fd5e8209b1610972cf0ef1bbe6d808e3", size = 247532 }, - { url = "https://files.pythonhosted.org/packages/50/d6/6176aaa1f6588378f5a5a4a9c6ad50a36824e902b2f844ca8de7f1b0c4a7/zope.interface-6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbe81def9cf3e46f16ce01d9bfd8bea595e06505e51b7baf45115c77352675fd", size = 241703 }, - { url = "https://files.pythonhosted.org/packages/4f/20/94d4f221989b4bbdd09004b2afb329958e776b7015b7ea8bc915327e195a/zope.interface-6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dc998f6de015723196a904045e5a2217f3590b62ea31990672e31fbc5370b41", size = 247078 }, - { url = "https://files.pythonhosted.org/packages/97/7e/b790b4ab9605010816a91df26a715f163e228d60eb36c947c3118fb65190/zope.interface-6.1-cp310-cp310-win_amd64.whl", hash = "sha256:239a4a08525c080ff833560171d23b249f7f4d17fcbf9316ef4159f44997616f", size = 204155 }, - { url = "https://files.pythonhosted.org/packages/4a/0b/1d8817b8a3631384a26ff7faa4c1f3e6726f7e4950c3442721cfef2c95eb/zope.interface-6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9ffdaa5290422ac0f1688cb8adb1b94ca56cee3ad11f29f2ae301df8aecba7d1", size = 202441 }, - { url = "https://files.pythonhosted.org/packages/3e/1f/43557bb2b6e8537002a5a26af9b899171e26ddfcdf17a00ff729b00c036b/zope.interface-6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34c15ca9248f2e095ef2e93af2d633358c5f048c49fbfddf5fdfc47d5e263736", size = 202530 }, - { url = "https://files.pythonhosted.org/packages/37/a1/5d2b265f4b7371630cad5873d0873965e35ca3de993d11b9336c720f7259/zope.interface-6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b012d023b4fb59183909b45d7f97fb493ef7a46d2838a5e716e3155081894605", size = 249584 }, - { url = "https://files.pythonhosted.org/packages/8b/6d/547bfa7465e5b296adba0aff5c7ace1150f2a9e429fbf6c33d6618275162/zope.interface-6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97806e9ca3651588c1baaebb8d0c5ee3db95430b612db354c199b57378312ee8", size = 243737 }, - { url = "https://files.pythonhosted.org/packages/db/5f/46946b588c43eb28efe0e46f4cf455b1ed8b2d1ea62a21b0001c6610662f/zope.interface-6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fddbab55a2473f1d3b8833ec6b7ac31e8211b0aa608df5ab09ce07f3727326de", size = 249104 }, - { url = "https://files.pythonhosted.org/packages/6c/9c/9d3c0e7e5362ea59da3c42b3b2b9fc073db433a0fe3bc6cae0809ccec395/zope.interface-6.1-cp311-cp311-win_amd64.whl", hash = "sha256:a0da79117952a9a41253696ed3e8b560a425197d4e41634a23b1507efe3273f1", size = 204155 }, - { url = "https://files.pythonhosted.org/packages/3c/91/68a0bbc97c2554f87d39572091954e94d043bcd83897cd6a779ca85cb5cc/zope.interface-6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8bb9c990ca9027b4214fa543fd4025818dc95f8b7abce79d61dc8a2112b561a", size = 202757 }, - { url = "https://files.pythonhosted.org/packages/e1/84/850092a8ab7e87a3ea615daf3f822f7196c52592e3e92f264621b4cfe5a2/zope.interface-6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b51b64432eed4c0744241e9ce5c70dcfecac866dff720e746d0a9c82f371dfa7", size = 202654 }, - { url = "https://files.pythonhosted.org/packages/57/23/508f7f79619ae4e025f5b264a9283efc3c805ed4c0ad75cb28c091179ced/zope.interface-6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa6fd016e9644406d0a61313e50348c706e911dca29736a3266fc9e28ec4ca6d", size = 254400 }, - { url = "https://files.pythonhosted.org/packages/7c/0d/db0ccf0d12767015f23b302aebe98d5eca218aaadc70c2e3908b85fecd2a/zope.interface-6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c8cf55261e15590065039696607f6c9c1aeda700ceee40c70478552d323b3ff", size = 248853 }, - { url = "https://files.pythonhosted.org/packages/fd/4f/8e80173ebcdefe0ff4164444c22b171cf8bd72533026befc2adf079f3ac8/zope.interface-6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e30506bcb03de8983f78884807e4fd95d8db6e65b69257eea05d13d519b83ac0", size = 255127 }, - { url = "https://files.pythonhosted.org/packages/0f/d5/81f9789311d9773a02ed048af7452fc6cedce059748dba956c1dc040340a/zope.interface-6.1-cp312-cp312-win_amd64.whl", hash = "sha256:e33e86fd65f369f10608b08729c8f1c92ec7e0e485964670b4d2633a4812d36b", size = 204268 }, + { url = "https://files.pythonhosted.org/packages/3c/ec/c1e7ce928dc10bfe02c6da7e964342d941aaf168f96f8084636167ea50d2/zope.interface-6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:43b576c34ef0c1f5a4981163b551a8781896f2a37f71b8655fd20b5af0386abb", size = 202417, upload_time = "2023-10-05T11:24:25.141Z" }, + { url = "https://files.pythonhosted.org/packages/f7/0b/12f269ad049fc40a7a3ab85445d7855b6bc6f1e774c5ca9dd6f5c32becb3/zope.interface-6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:67be3ca75012c6e9b109860820a8b6c9a84bfb036fbd1076246b98e56951ca92", size = 202528, upload_time = "2023-10-05T11:24:27.336Z" }, + { url = "https://files.pythonhosted.org/packages/7f/85/3a35144509eb4a5a2208b48ae8d116a969d67de62cc6513d85602144d9cd/zope.interface-6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b9bc671626281f6045ad61d93a60f52fd5e8209b1610972cf0ef1bbe6d808e3", size = 247532, upload_time = "2023-10-05T11:49:20.587Z" }, + { url = "https://files.pythonhosted.org/packages/50/d6/6176aaa1f6588378f5a5a4a9c6ad50a36824e902b2f844ca8de7f1b0c4a7/zope.interface-6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbe81def9cf3e46f16ce01d9bfd8bea595e06505e51b7baf45115c77352675fd", size = 241703, upload_time = "2023-10-05T11:25:33.542Z" }, + { url = "https://files.pythonhosted.org/packages/4f/20/94d4f221989b4bbdd09004b2afb329958e776b7015b7ea8bc915327e195a/zope.interface-6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dc998f6de015723196a904045e5a2217f3590b62ea31990672e31fbc5370b41", size = 247078, upload_time = "2023-10-05T11:25:48.235Z" }, + { url = "https://files.pythonhosted.org/packages/97/7e/b790b4ab9605010816a91df26a715f163e228d60eb36c947c3118fb65190/zope.interface-6.1-cp310-cp310-win_amd64.whl", hash = "sha256:239a4a08525c080ff833560171d23b249f7f4d17fcbf9316ef4159f44997616f", size = 204155, upload_time = "2023-10-05T11:37:56.715Z" }, + { url = "https://files.pythonhosted.org/packages/4a/0b/1d8817b8a3631384a26ff7faa4c1f3e6726f7e4950c3442721cfef2c95eb/zope.interface-6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9ffdaa5290422ac0f1688cb8adb1b94ca56cee3ad11f29f2ae301df8aecba7d1", size = 202441, upload_time = "2023-10-05T11:24:20.414Z" }, + { url = "https://files.pythonhosted.org/packages/3e/1f/43557bb2b6e8537002a5a26af9b899171e26ddfcdf17a00ff729b00c036b/zope.interface-6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34c15ca9248f2e095ef2e93af2d633358c5f048c49fbfddf5fdfc47d5e263736", size = 202530, upload_time = "2023-10-05T11:24:22.975Z" }, + { url = "https://files.pythonhosted.org/packages/37/a1/5d2b265f4b7371630cad5873d0873965e35ca3de993d11b9336c720f7259/zope.interface-6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b012d023b4fb59183909b45d7f97fb493ef7a46d2838a5e716e3155081894605", size = 249584, upload_time = "2023-10-05T11:49:22.978Z" }, + { url = "https://files.pythonhosted.org/packages/8b/6d/547bfa7465e5b296adba0aff5c7ace1150f2a9e429fbf6c33d6618275162/zope.interface-6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97806e9ca3651588c1baaebb8d0c5ee3db95430b612db354c199b57378312ee8", size = 243737, upload_time = "2023-10-05T11:25:35.439Z" }, + { url = "https://files.pythonhosted.org/packages/db/5f/46946b588c43eb28efe0e46f4cf455b1ed8b2d1ea62a21b0001c6610662f/zope.interface-6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fddbab55a2473f1d3b8833ec6b7ac31e8211b0aa608df5ab09ce07f3727326de", size = 249104, upload_time = "2023-10-05T11:25:51.355Z" }, + { url = "https://files.pythonhosted.org/packages/6c/9c/9d3c0e7e5362ea59da3c42b3b2b9fc073db433a0fe3bc6cae0809ccec395/zope.interface-6.1-cp311-cp311-win_amd64.whl", hash = "sha256:a0da79117952a9a41253696ed3e8b560a425197d4e41634a23b1507efe3273f1", size = 204155, upload_time = "2023-10-05T11:39:39.139Z" }, + { url = "https://files.pythonhosted.org/packages/3c/91/68a0bbc97c2554f87d39572091954e94d043bcd83897cd6a779ca85cb5cc/zope.interface-6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8bb9c990ca9027b4214fa543fd4025818dc95f8b7abce79d61dc8a2112b561a", size = 202757, upload_time = "2023-10-05T11:25:05.865Z" }, + { url = "https://files.pythonhosted.org/packages/e1/84/850092a8ab7e87a3ea615daf3f822f7196c52592e3e92f264621b4cfe5a2/zope.interface-6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b51b64432eed4c0744241e9ce5c70dcfecac866dff720e746d0a9c82f371dfa7", size = 202654, upload_time = "2023-10-05T11:25:08.347Z" }, + { url = "https://files.pythonhosted.org/packages/57/23/508f7f79619ae4e025f5b264a9283efc3c805ed4c0ad75cb28c091179ced/zope.interface-6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa6fd016e9644406d0a61313e50348c706e911dca29736a3266fc9e28ec4ca6d", size = 254400, upload_time = "2023-10-05T11:49:25.326Z" }, + { url = "https://files.pythonhosted.org/packages/7c/0d/db0ccf0d12767015f23b302aebe98d5eca218aaadc70c2e3908b85fecd2a/zope.interface-6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c8cf55261e15590065039696607f6c9c1aeda700ceee40c70478552d323b3ff", size = 248853, upload_time = "2023-10-05T11:25:37.37Z" }, + { url = "https://files.pythonhosted.org/packages/fd/4f/8e80173ebcdefe0ff4164444c22b171cf8bd72533026befc2adf079f3ac8/zope.interface-6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e30506bcb03de8983f78884807e4fd95d8db6e65b69257eea05d13d519b83ac0", size = 255127, upload_time = "2023-10-05T11:25:53.819Z" }, + { url = "https://files.pythonhosted.org/packages/0f/d5/81f9789311d9773a02ed048af7452fc6cedce059748dba956c1dc040340a/zope.interface-6.1-cp312-cp312-win_amd64.whl", hash = "sha256:e33e86fd65f369f10608b08729c8f1c92ec7e0e485964670b4d2633a4812d36b", size = 204268, upload_time = "2023-10-05T11:41:22.778Z" }, ] diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml index 58d7f0655a..eb81dc267b 100644 --- a/mobile/android/app/src/main/AndroidManifest.xml +++ b/mobile/android/app/src/main/AndroidManifest.xml @@ -6,7 +6,6 @@ android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> - <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" /> <uses-permission android:name="android.permission.MANAGE_MEDIA" /> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> @@ -125,4 +124,4 @@ <data android:scheme="geo" /> </intent> </queries> -</manifest> +</manifest> \ No newline at end of file diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/BackgroundServicePlugin.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/BackgroundServicePlugin.kt index e7f787e8d8..ae2ec22a71 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/BackgroundServicePlugin.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/BackgroundServicePlugin.kt @@ -1,17 +1,17 @@ package app.alextran.immich +import android.app.Activity import android.content.ContentResolver import android.content.ContentUris -import android.content.ContentValues import android.content.Context import android.content.Intent import android.net.Uri import android.os.Build import android.os.Bundle -import android.os.Environment import android.provider.MediaStore import android.provider.Settings import android.util.Log +import androidx.annotation.RequiresApi import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding @@ -23,6 +23,7 @@ import io.flutter.plugin.common.PluginRegistry import java.security.MessageDigest import java.io.FileInputStream import kotlinx.coroutines.* +import androidx.core.net.toUri /** * Android plugin for Dart `BackgroundService` and file trash operations @@ -33,7 +34,8 @@ class BackgroundServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler, private var fileTrashChannel: MethodChannel? = null private var context: Context? = null private var pendingResult: Result? = null - private val PERMISSION_REQUEST_CODE = 1001 + private val permissionRequestCode = 1001 + private val trashRequestCode = 1002 private var activityBinding: ActivityPluginBinding? = null override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { @@ -138,36 +140,35 @@ class BackgroundServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler, // File Trash methods moved from MainActivity "moveToTrash" -> { - val fileName = call.argument<String>("fileName") - if (fileName != null) { - if (hasManageStoragePermission()) { - val success = moveToTrash(fileName) - result.success(success) + val mediaUrls = call.argument<List<String>>("mediaUrls") + if (mediaUrls != null) { + if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) && hasManageMediaPermission()) { + moveToTrash(mediaUrls, result) } else { - result.error("PERMISSION_DENIED", "Storage permission required", null) + result.error("PERMISSION_DENIED", "Media permission required", null) } } else { - result.error("INVALID_NAME", "The file name is not specified.", null) + result.error("INVALID_NAME", "The mediaUrls is not specified.", null) } } "restoreFromTrash" -> { val fileName = call.argument<String>("fileName") - if (fileName != null) { - if (hasManageStoragePermission()) { - val success = untrashImage(fileName) - result.success(success) + val type = call.argument<Int>("type") + if (fileName != null && type != null) { + if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) && hasManageMediaPermission()) { + restoreFromTrash(fileName, type, result) } else { - result.error("PERMISSION_DENIED", "Storage permission required", null) + result.error("PERMISSION_DENIED", "Media permission required", null) } } else { result.error("INVALID_NAME", "The file name is not specified.", null) } } - "requestManageStoragePermission" -> { - if (!hasManageStoragePermission()) { - requestManageStoragePermission(result) + "requestManageMediaPermission" -> { + if (!hasManageMediaPermission()) { + requestManageMediaPermission(result) } else { Log.e("Manage storage permission", "Permission already granted") result.success(true) @@ -178,100 +179,98 @@ class BackgroundServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler, } } - // File Trash methods moved from MainActivity - private fun hasManageStoragePermission(): Boolean { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - Environment.isExternalStorageManager() - } else { - true + private fun hasManageMediaPermission(): Boolean { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + MediaStore.canManageMedia(context!!); + } else { + false } } - private fun requestManageStoragePermission(result: Result) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + private fun requestManageMediaPermission(result: Result) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { pendingResult = result // Store the result callback val activity = activityBinding?.activity ?: return - val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) - intent.data = Uri.parse("package:${activity.packageName}") - activity.startActivityForResult(intent, PERMISSION_REQUEST_CODE) + val intent = Intent(Settings.ACTION_REQUEST_MANAGE_MEDIA) + intent.data = "package:${activity.packageName}".toUri() + activity.startActivityForResult(intent, permissionRequestCode) } else { - result.success(true) + result.success(false) } } - private fun moveToTrash(fileName: String): Boolean { - val contentResolver = context?.contentResolver ?: return false - val uri = getFileUri(fileName) + @RequiresApi(Build.VERSION_CODES.R) + private fun moveToTrash(mediaUrls: List<String>, result: Result) { + val urisToTrash = mediaUrls.map { it.toUri() } + if (urisToTrash.isEmpty()) { + result.error("INVALID_ARGS", "No valid URIs provided", null) + return + } + + toggleTrash(urisToTrash, true, result); + } + + @RequiresApi(Build.VERSION_CODES.R) + private fun restoreFromTrash(name: String, type: Int, result: Result) { + val uri = getTrashedFileUri(name, type) + if (uri == null) { + Log.e("TrashError", "Asset Uri cannot be found obtained") + result.error("TrashError", "Asset Uri cannot be found obtained", null) + return + } Log.e("FILE_URI", uri.toString()) - return uri?.let { moveToTrash(it) } ?: false + uri.let { toggleTrash(listOf(it), false, result) } } - private fun moveToTrash(contentUri: Uri): Boolean { - val contentResolver = context?.contentResolver ?: return false - return try { - val values = ContentValues().apply { - put(MediaStore.MediaColumns.IS_TRASHED, 1) // Move to trash + @RequiresApi(Build.VERSION_CODES.R) + private fun toggleTrash(contentUris: List<Uri>, isTrashed: Boolean, result: Result) { + val activity = activityBinding?.activity + val contentResolver = context?.contentResolver + if (activity == null || contentResolver == null) { + result.error("TrashError", "Activity or ContentResolver not available", null) + return } - val updated = contentResolver.update(contentUri, values, null, null) - updated > 0 - } catch (e: Exception) { - Log.e("TrashError", "Error moving to trash", e) - false + + try { + val pendingIntent = MediaStore.createTrashRequest(contentResolver, contentUris, isTrashed) + pendingResult = result // Store for onActivityResult + activity.startIntentSenderForResult( + pendingIntent.intentSender, + trashRequestCode, + null, 0, 0, 0 + ) + } catch (e: Exception) { + Log.e("TrashError", "Error creating or starting trash request", e) + result.error("TrashError", "Error creating or starting trash request", null) } } - private fun getFileUri(fileName: String): Uri? { + @RequiresApi(Build.VERSION_CODES.R) + private fun getTrashedFileUri(fileName: String, type: Int): Uri? { val contentResolver = context?.contentResolver ?: return null - val contentUri = MediaStore.Files.getContentUri("external") - val projection = arrayOf(MediaStore.Images.Media._ID) - val selection = "${MediaStore.Images.Media.DISPLAY_NAME} = ?" - val selectionArgs = arrayOf(fileName) - var fileUri: Uri? = null - - contentResolver.query(contentUri, projection, selection, selectionArgs, null)?.use { cursor -> - if (cursor.moveToFirst()) { - val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)) - fileUri = ContentUris.withAppendedId(contentUri, id) - } - } - return fileUri - } - - private fun untrashImage(name: String): Boolean { - val contentResolver = context?.contentResolver ?: return false - val uri = getTrashedFileUri(contentResolver, name) - Log.e("FILE_URI", uri.toString()) - return uri?.let { untrashImage(it) } ?: false - } - - private fun untrashImage(contentUri: Uri): Boolean { - val contentResolver = context?.contentResolver ?: return false - return try { - val values = ContentValues().apply { - put(MediaStore.MediaColumns.IS_TRASHED, 0) // Restore file - } - val updated = contentResolver.update(contentUri, values, null, null) - updated > 0 - } catch (e: Exception) { - Log.e("TrashError", "Error restoring file", e) - false - } - } - - private fun getTrashedFileUri(contentResolver: ContentResolver, fileName: String): Uri? { - val contentUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL) + val queryUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL) val projection = arrayOf(MediaStore.Files.FileColumns._ID) val queryArgs = Bundle().apply { - putString(ContentResolver.QUERY_ARG_SQL_SELECTION, "${MediaStore.Files.FileColumns.DISPLAY_NAME} = ?") + putString( + ContentResolver.QUERY_ARG_SQL_SELECTION, + "${MediaStore.Files.FileColumns.DISPLAY_NAME} = ?" + ) putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS, arrayOf(fileName)) putInt(MediaStore.QUERY_ARG_MATCH_TRASHED, MediaStore.MATCH_ONLY) } - contentResolver.query(contentUri, projection, queryArgs, null)?.use { cursor -> + contentResolver.query(queryUri, projection, queryArgs, null)?.use { cursor -> if (cursor.moveToFirst()) { val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID)) + // same order as AssetType from dart + val contentUri = when (type) { + 1 -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI + 2 -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI + 3 -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + else -> queryUri + } return ContentUris.withAppendedId(contentUri, id) } } @@ -301,12 +300,19 @@ class BackgroundServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler, // ActivityResultListener implementation override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { - if (requestCode == PERMISSION_REQUEST_CODE) { - val granted = hasManageStoragePermission() + if (requestCode == permissionRequestCode) { + val granted = hasManageMediaPermission() pendingResult?.success(granted) pendingResult = null return true } + + if (requestCode == trashRequestCode) { + val approved = resultCode == Activity.RESULT_OK + pendingResult?.success(approved) + pendingResult = null + return true + } return false } } diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index 612e5084d2..a0b08bb316 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" => 193, - "android.injected.version.name" => "1.131.3", + "android.injected.version.code" => 197, + "android.injected.version.name" => "1.132.3", } ) 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/drift_schemas/main/drift_schema_v1.json b/mobile/drift_schemas/main/drift_schema_v1.json index 448691afd0..836a926b03 100644 --- a/mobile/drift_schemas/main/drift_schema_v1.json +++ b/mobile/drift_schemas/main/drift_schema_v1.json @@ -1 +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":"blob","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":"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":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"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":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"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":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"blob","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":"preferences","getter_name":"preferences","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userPreferenceConverter","dart_type_name":"UserPreferences"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"blob","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":"blob","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":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>(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":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"local_id","getter_name":"localId","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":"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":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["local_id"]}},{"id":4,"references":[3],"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":"asset_count","getter_name":"assetCount","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"thumbnail_id","getter_name":"thumbnailId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (local_id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (local_id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"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>(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_all","getter_name":"isAll","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_all\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_all\" 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":["id"]}},{"id":5,"references":[3,4],"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 (local_id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (local_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"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"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>(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":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"remote_id","getter_name":"remoteId","moor_type":"blob","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,"defaultConstraints":"UNIQUE","dialectAwareDefaultConstraints":{"sqlite":"UNIQUE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"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":"blob","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":"thumbhash","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":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["remote_id"]}},{"id":7,"references":[6],"type":"table","data":{"name":"exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"blob","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (remote_id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (remote_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":"int","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":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"int","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":"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":8,"references":[3],"type":"index","data":{"on":3,"name":"local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":9,"references":[6],"type":"index","data":{"on":6,"name":"remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}}]} \ No newline at end of file +{"_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":"blob","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":"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":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"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":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"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":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"blob","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":"preferences","getter_name":"preferences","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userPreferenceConverter","dart_type_name":"UserPreferences"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"blob","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":"blob","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":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>(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":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"local_id","getter_name":"localId","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":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["local_id"]}},{"id":4,"references":[3],"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":"thumbnail_id","getter_name":"thumbnailId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (local_id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (local_id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"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>(BackupSelection.values)","dart_type_name":"BackupSelection"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[3,4],"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 (local_id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (local_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"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"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>(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":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"remote_id","getter_name":"remoteId","moor_type":"blob","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,"defaultConstraints":"UNIQUE","dialectAwareDefaultConstraints":{"sqlite":"UNIQUE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"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":"blob","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":"thumbhash","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":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["remote_id"]}},{"id":7,"references":[6],"type":"table","data":{"name":"exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"blob","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (remote_id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (remote_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":"int","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":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"int","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":"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":8,"references":[3],"type":"index","data":{"on":3,"name":"local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":9,"references":[6],"type":"index","data":{"on":6,"name":"remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}}]} \ No newline at end of file diff --git a/mobile/ios/Podfile b/mobile/ios/Podfile index a98032db20..ca0166a382 100644 --- a/mobile/ios/Podfile +++ b/mobile/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '12.0' +platform :ios, '14.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -45,7 +45,7 @@ post_install do |installer| installer.generated_projects.each do |project| project.targets.each do |target| target.build_configurations.each do |config| - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0' + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.0' end end end diff --git a/mobile/ios/Podfile.lock b/mobile/ios/Podfile.lock index 908ee84aed..9740d6aa52 100644 --- a/mobile/ios/Podfile.lock +++ b/mobile/ios/Podfile.lock @@ -224,7 +224,7 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/wakelock_plus/ios" SPEC CHECKSUMS: - background_downloader: b42a56120f5348bff70e74222f0e9e6f7f1a1537 + background_downloader: 50e91d979067b82081aba359d7d916b3ba5fadad connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c @@ -261,6 +261,6 @@ SPEC CHECKSUMS: url_launcher_ios: 694010445543906933d732453a59da0a173ae33d wakelock_plus: 04623e3f525556020ebd4034310f20fe7fda8b49 -PODFILE CHECKSUM: 03b7eead4ee77b9e778179eeb0f3b5513617451c +PODFILE CHECKSUM: 7ce312f2beab01395db96f6969d90a447279cf45 COCOAPODS: 1.16.2 diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj index 83c231d741..744ddc053b 100644 --- a/mobile/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/ios/Runner.xcodeproj/project.pbxproj @@ -261,9 +261,11 @@ 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; }; FAC6F88F2D287C890078CB2F = { CreatedOnToolsVersion = 16.0; + ProvisioningStyle = Automatic; }; }; }; @@ -541,12 +543,12 @@ CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 201; + CURRENT_PROJECT_VERSION = 205; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -685,12 +687,12 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 201; + CURRENT_PROJECT_VERSION = 205; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -715,12 +717,12 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 201; + CURRENT_PROJECT_VERSION = 205; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -748,7 +750,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 201; + CURRENT_PROJECT_VERSION = 205; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -769,6 +771,7 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.vdebug.ShareExtension; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -791,7 +794,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 201; + CURRENT_PROJECT_VERSION = 205; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -811,6 +814,7 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.ShareExtension; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -831,7 +835,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 201; + CURRENT_PROJECT_VERSION = 205; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -851,6 +855,7 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.profile.ShareExtension; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; diff --git a/mobile/ios/Runner/Info.plist b/mobile/ios/Runner/Info.plist index bad1ea42f2..38394f0f1b 100644 --- a/mobile/ios/Runner/Info.plist +++ b/mobile/ios/Runner/Info.plist @@ -78,7 +78,7 @@ <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> - <string>1.131.3</string> + <string>1.132.3</string> <key>CFBundleSignature</key> <string>????</string> <key>CFBundleURLTypes</key> @@ -93,7 +93,7 @@ </dict> </array> <key>CFBundleVersion</key> - <string>201</string> + <string>205</string> <key>FLTEnableImpeller</key> <true/> <key>ITSAppUsesNonExemptEncryption</key> diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index 4853e9be43..3306fef1e2 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -18,8 +18,11 @@ default_platform(:ios) platform :ios do desc "iOS Release" lane :release do + enable_automatic_code_signing( + path: "./Runner.xcodeproj", + ) increment_version_number( - version_number: "1.131.3" + version_number: "1.132.3" ) increment_build_number( build_number: latest_testflight_build_number + 1, diff --git a/mobile/lib/domain/interfaces/album_media.interface.dart b/mobile/lib/domain/interfaces/album_media.interface.dart index 6257de9f17..feb3adeb25 100644 --- a/mobile/lib/domain/interfaces/album_media.interface.dart +++ b/mobile/lib/domain/interfaces/album_media.interface.dart @@ -1,4 +1,4 @@ -import 'package:immich_mobile/domain/models/asset/asset.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/local_album.model.dart'; abstract interface class IAlbumMediaRepository { diff --git a/mobile/lib/domain/interfaces/local_album.interface.dart b/mobile/lib/domain/interfaces/local_album.interface.dart index 85fff14893..611527d08a 100644 --- a/mobile/lib/domain/interfaces/local_album.interface.dart +++ b/mobile/lib/domain/interfaces/local_album.interface.dart @@ -1,9 +1,9 @@ import 'package:immich_mobile/domain/interfaces/db.interface.dart'; -import 'package:immich_mobile/domain/models/asset/asset.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/local_album.model.dart'; abstract interface class ILocalAlbumRepository implements IDatabaseRepository { - Future<void> insert(LocalAlbum localAlbum, Iterable<LocalAsset> assets); + Future<void> insert(LocalAlbum album, Iterable<LocalAsset> assets); Future<void> addAssets(String albumId, Iterable<LocalAsset> assets); @@ -11,7 +11,7 @@ abstract interface class ILocalAlbumRepository implements IDatabaseRepository { Future<List<LocalAsset>> getAssetsForAlbum(String albumId); - Future<void> update(LocalAlbum localAlbum); + Future<void> update(LocalAlbum album); Future<void> delete(String albumId); diff --git a/mobile/lib/domain/interfaces/local_asset.interface.dart b/mobile/lib/domain/interfaces/local_asset.interface.dart index 7cf05a55be..2f9fbd143f 100644 --- a/mobile/lib/domain/interfaces/local_asset.interface.dart +++ b/mobile/lib/domain/interfaces/local_asset.interface.dart @@ -1,5 +1,5 @@ import 'package:immich_mobile/domain/interfaces/db.interface.dart'; -import 'package:immich_mobile/domain/models/asset/asset.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; abstract interface class ILocalAssetRepository implements IDatabaseRepository { Future<LocalAsset> get(String assetId); diff --git a/mobile/lib/domain/models/asset/asset.model.dart b/mobile/lib/domain/models/asset/asset.model.dart index 36f8ada253..211d24985f 100644 --- a/mobile/lib/domain/models/asset/asset.model.dart +++ b/mobile/lib/domain/models/asset/asset.model.dart @@ -1,64 +1,46 @@ -import 'package:immich_mobile/utils/nullable_value.dart'; +part of 'base_asset.model.dart'; -part 'local_asset.model.dart'; -part 'merged_asset.model.dart'; -part 'remote_asset.model.dart'; - -enum AssetType { - // do not change this order! - other, - image, - video, - audio, -} - -sealed class Asset { - final String name; - final String? checksum; - final AssetType type; - final DateTime createdAt; - final DateTime updatedAt; - final int? durationInSeconds; +class Asset extends BaseAsset { + final String id; + final String? localId; const Asset({ - required this.name, - required this.checksum, - required this.type, - required this.createdAt, - required this.updatedAt, - this.durationInSeconds, + required this.id, + this.localId, + required super.name, + required super.checksum, + required super.type, + required super.createdAt, + required super.updatedAt, + super.width, + super.height, + super.durationInSeconds, + super.isFavorite = false, }); @override String toString() { return '''Asset { - name: $name, - type: $type, - createdAt: $createdAt, - updatedAt: $updatedAt, - durationInSeconds: ${durationInSeconds ?? "<NA>"} -}'''; + id: $id, + name: $name, + type: $type, + createdAt: $createdAt, + updatedAt: $updatedAt, + width: ${width ?? "<NA>"}, + height: ${height ?? "<NA>"}, + durationInSeconds: ${durationInSeconds ?? "<NA>"}, + localId: ${localId ?? "<NA>"}, + isFavorite: $isFavorite, + }'''; } @override bool operator ==(Object other) { + if (other is! Asset) return false; if (identical(this, other)) return true; - if (other is Asset) { - return name == other.name && - type == other.type && - createdAt == other.createdAt && - updatedAt == other.updatedAt && - durationInSeconds == other.durationInSeconds; - } - return false; + return super == other && id == other.id && localId == other.localId; } @override - int get hashCode { - return name.hashCode ^ - type.hashCode ^ - createdAt.hashCode ^ - updatedAt.hashCode ^ - durationInSeconds.hashCode; - } + int get hashCode => super.hashCode ^ id.hashCode ^ localId.hashCode; } diff --git a/mobile/lib/domain/models/asset/base_asset.model.dart b/mobile/lib/domain/models/asset/base_asset.model.dart new file mode 100644 index 0000000000..fb95437659 --- /dev/null +++ b/mobile/lib/domain/models/asset/base_asset.model.dart @@ -0,0 +1,76 @@ +part 'asset.model.dart'; +part 'local_asset.model.dart'; + +enum AssetType { + // do not change this order! + other, + image, + video, + audio, +} + +sealed class BaseAsset { + final String name; + final String? checksum; + final AssetType type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final bool isFavorite; + + const BaseAsset({ + required this.name, + required this.checksum, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + this.isFavorite = false, + }); + + @override + String toString() { + return '''BaseAsset { + name: $name, + type: $type, + createdAt: $createdAt, + updatedAt: $updatedAt, + width: ${width ?? "<NA>"}, + height: ${height ?? "<NA>"}, + durationInSeconds: ${durationInSeconds ?? "<NA>"}, + isFavorite: $isFavorite, +}'''; + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other is BaseAsset) { + return name == other.name && + type == other.type && + createdAt == other.createdAt && + updatedAt == other.updatedAt && + width == other.width && + height == other.height && + durationInSeconds == other.durationInSeconds && + isFavorite == other.isFavorite; + } + return false; + } + + @override + int get hashCode { + return name.hashCode ^ + type.hashCode ^ + createdAt.hashCode ^ + updatedAt.hashCode ^ + width.hashCode ^ + height.hashCode ^ + durationInSeconds.hashCode ^ + isFavorite.hashCode; + } +} diff --git a/mobile/lib/domain/models/asset/local_asset.model.dart b/mobile/lib/domain/models/asset/local_asset.model.dart index 28f3d8c9fb..25e617d8ed 100644 --- a/mobile/lib/domain/models/asset/local_asset.model.dart +++ b/mobile/lib/domain/models/asset/local_asset.model.dart @@ -1,33 +1,36 @@ -part of 'asset.model.dart'; +part of 'base_asset.model.dart'; -class LocalAsset extends Asset { - final String localId; - final int? width; - final int? height; +class LocalAsset extends BaseAsset { + final String id; + final String? remoteId; const LocalAsset({ - required this.localId, + required this.id, + this.remoteId, required super.name, super.checksum, required super.type, required super.createdAt, required super.updatedAt, - this.width, - this.height, + super.width, + super.height, super.durationInSeconds, + super.isFavorite = false, }); @override String toString() { return '''LocalAsset { - localId: $localId, + id: $id, name: $name, type: $type, createdAt: $createdAt, updatedAt: $updatedAt, width: ${width ?? "<NA>"}, height: ${height ?? "<NA>"}, - durationInSeconds: ${durationInSeconds ?? "<NA>"} + durationInSeconds: ${durationInSeconds ?? "<NA>"}, + remoteId: ${remoteId ?? "<NA>"} + isFavorite: $isFavorite, }'''; } @@ -35,38 +38,37 @@ class LocalAsset extends Asset { bool operator ==(Object other) { if (other is! LocalAsset) return false; if (identical(this, other)) return true; - return super == other && - localId == other.localId && - width == other.width && - height == other.height; + return super == other && id == other.id && remoteId == other.remoteId; } @override - int get hashCode { - return super.hashCode ^ localId.hashCode; - } + int get hashCode => super.hashCode ^ id.hashCode ^ remoteId.hashCode; LocalAsset copyWith({ - String? localId, + String? id, + String? remoteId, String? name, String? checksum, AssetType? type, DateTime? createdAt, DateTime? updatedAt, - NullableValue<int> width = const NullableValue.absent(), - NullableValue<int> height = const NullableValue.absent(), - NullableValue<int> durationInSeconds = const NullableValue.absent(), + int? width, + int? height, + int? durationInSeconds, + bool? isFavorite, }) { return LocalAsset( - localId: localId ?? this.localId, + id: id ?? this.id, + remoteId: remoteId ?? this.remoteId, name: name ?? this.name, checksum: checksum ?? this.checksum, type: type ?? this.type, createdAt: createdAt ?? this.createdAt, updatedAt: updatedAt ?? this.updatedAt, - width: width.getOrDefault(this.width), - height: height.getOrDefault(this.height), - durationInSeconds: durationInSeconds.getOrDefault(this.durationInSeconds), + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + isFavorite: isFavorite ?? this.isFavorite, ); } } diff --git a/mobile/lib/domain/models/asset/merged_asset.model.dart b/mobile/lib/domain/models/asset/merged_asset.model.dart deleted file mode 100644 index e02cdfcb89..0000000000 --- a/mobile/lib/domain/models/asset/merged_asset.model.dart +++ /dev/null @@ -1,44 +0,0 @@ -part of 'asset.model.dart'; - -class MergedAsset extends Asset { - final String remoteId; - final String localId; - - const MergedAsset({ - required this.remoteId, - required this.localId, - required super.name, - required super.checksum, - required super.type, - required super.createdAt, - required super.updatedAt, - super.durationInSeconds, - }); - - @override - String toString() { - return '''MergedAsset { - remoteId: $remoteId, - localId: $localId, - name: $name, - type: $type, - createdAt: $createdAt, - updatedAt: $updatedAt, - durationInSeconds: ${durationInSeconds ?? "<NA>"} - }'''; - } - - @override - bool operator ==(Object other) { - if (other is! MergedAsset) return false; - if (identical(this, other)) return true; - return super == other && - remoteId == other.remoteId && - localId == other.localId; - } - - @override - int get hashCode { - return super.hashCode ^ remoteId.hashCode ^ localId.hashCode; - } -} diff --git a/mobile/lib/domain/models/asset/remote_asset.model.dart b/mobile/lib/domain/models/asset/remote_asset.model.dart deleted file mode 100644 index 30c1fa2158..0000000000 --- a/mobile/lib/domain/models/asset/remote_asset.model.dart +++ /dev/null @@ -1,65 +0,0 @@ -part of 'asset.model.dart'; - -class RemoteAsset extends Asset { - final String remoteId; - final bool isFavorite; - final String ownerId; - final DateTime localDateTime; - final String? thumbhash; - final DateTime? deletedAt; - - const RemoteAsset({ - required this.remoteId, - required this.ownerId, - required super.name, - required super.checksum, - required super.type, - required this.isFavorite, - required this.localDateTime, - required super.createdAt, - required super.updatedAt, - this.deletedAt, - this.thumbhash, - super.durationInSeconds, - }); - - @override - String toString() { - return '''RemoteAsset { - remoteId: $remoteId, - ownerId: $ownerId, - name: $name, - type: $type, - isFavorite: $isFavorite, - createdAt: $createdAt, - updatedAt: $updatedAt, - localDateTime: $localDateTime, - deletedAt: ${deletedAt ?? "<NA>"}, - durationInSeconds: ${durationInSeconds ?? "<NA>"}, - }'''; - } - - @override - bool operator ==(Object other) { - if (other is! RemoteAsset) return false; - if (identical(this, other)) return true; - return super == other && - remoteId == other.remoteId && - isFavorite == other.isFavorite && - ownerId == other.ownerId && - localDateTime == other.localDateTime && - deletedAt == other.deletedAt && - thumbhash == other.thumbhash; - } - - @override - int get hashCode { - return super.hashCode ^ - remoteId.hashCode ^ - isFavorite.hashCode ^ - ownerId.hashCode ^ - localDateTime.hashCode ^ - deletedAt.hashCode ^ - thumbhash.hashCode; - } -} diff --git a/mobile/lib/domain/models/local_album.model.dart b/mobile/lib/domain/models/local_album.model.dart index eb005ee43a..a33584d579 100644 --- a/mobile/lib/domain/models/local_album.model.dart +++ b/mobile/lib/domain/models/local_album.model.dart @@ -11,8 +11,6 @@ class LocalAlbum { final String name; final DateTime updatedAt; - /// Whether the album contains all photos (i.e, the virtual "Recent" album) - final bool isAll; final int assetCount; final String? thumbnailId; final BackupSelection backupSelection; @@ -24,7 +22,6 @@ class LocalAlbum { this.assetCount = 0, this.thumbnailId, this.backupSelection = BackupSelection.none, - this.isAll = false, }); LocalAlbum copyWith({ @@ -34,7 +31,6 @@ class LocalAlbum { int? assetCount, NullableValue<String> thumbnailId = const NullableValue.absent(), BackupSelection? backupSelection, - bool? isAll, }) { return LocalAlbum( id: id ?? this.id, @@ -43,7 +39,6 @@ class LocalAlbum { assetCount: assetCount ?? this.assetCount, thumbnailId: thumbnailId.getOrDefault(this.thumbnailId), backupSelection: backupSelection ?? this.backupSelection, - isAll: isAll ?? this.isAll, ); } @@ -56,7 +51,6 @@ class LocalAlbum { other.name == name && other.updatedAt == updatedAt && other.assetCount == assetCount && - other.isAll == isAll && other.thumbnailId == thumbnailId && other.backupSelection == backupSelection; } @@ -67,7 +61,6 @@ class LocalAlbum { name.hashCode ^ updatedAt.hashCode ^ assetCount.hashCode ^ - isAll.hashCode ^ thumbnailId.hashCode ^ backupSelection.hashCode; } @@ -81,7 +74,6 @@ updatedAt: $updatedAt, assetCount: $assetCount, thumbnailId: ${thumbnailId ?? '<NA>'}, backupSelection: $backupSelection, -isAll: $isAll }'''; } } diff --git a/mobile/lib/domain/services/device_sync.service.dart b/mobile/lib/domain/services/device_sync.service.dart index 09f71fe76e..4a7b1c7c07 100644 --- a/mobile/lib/domain/services/device_sync.service.dart +++ b/mobile/lib/domain/services/device_sync.service.dart @@ -5,7 +5,7 @@ import 'package:flutter/widgets.dart'; import 'package:immich_mobile/domain/interfaces/album_media.interface.dart'; import 'package:immich_mobile/domain/interfaces/local_album.interface.dart'; import 'package:immich_mobile/domain/interfaces/local_asset.interface.dart'; -import 'package:immich_mobile/domain/models/asset/asset.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/local_album.model.dart'; import 'package:immich_mobile/utils/diff.dart'; import 'package:immich_mobile/utils/nullable_value.dart'; @@ -34,6 +34,7 @@ class DeviceSyncService { // and not the albums. final deviceAlbums = (await _albumMediaRepository.getAll()).sortedBy((a) => a.id); + final dbAlbums = await _localAlbumRepository.getAll(sortBy: SortLocalAlbumsBy.id); @@ -64,7 +65,7 @@ class DeviceSyncService { final album = deviceAlbum.copyWith( // The below assumes the list is already sorted by createdDate from the filter - thumbnailId: NullableValue.valueOrEmpty(assets.firstOrNull?.localId), + thumbnailId: NullableValue.valueOrEmpty(assets.firstOrNull?.id), ); await _localAlbumRepository.insert(album, assets); @@ -100,7 +101,7 @@ class DeviceSyncService { return false; } - _log.fine("Device album ${dbAlbum.name} has changed. Syncing..."); + _log.info("Device album ${dbAlbum.name} has changed. Syncing..."); // Faster path - only new assets added if (await checkAddition(dbAlbum, deviceAlbum)) { @@ -157,13 +158,13 @@ class DeviceSyncService { String? thumbnailId = dbAlbum.thumbnailId; if (thumbnailId == null || newAssets.isNotEmpty) { if (thumbnailId == null) { - thumbnailId = newAssets.firstOrNull?.localId; + thumbnailId = newAssets.firstOrNull?.id; } else if (newAssets.isNotEmpty) { // The below assumes the list is already sorted by createdDate from the filter final oldThumbAsset = await _localAssetRepository.get(thumbnailId); if (oldThumbAsset.createdAt .isBefore(newAssets.firstOrNull!.createdAt)) { - thumbnailId = newAssets.firstOrNull?.localId; + thumbnailId = newAssets.firstOrNull?.id; } } } @@ -205,14 +206,14 @@ class DeviceSyncService { thumbnailId: const NullableValue.empty(), backupSelection: dbAlbum.backupSelection, ), - assetIdsToDelete: assetsInDb.map((a) => a.localId), + assetIdsToDelete: assetsInDb.map((a) => a.id), ); return true; } // The below assumes the list is already sorted by createdDate from the filter String? thumbnailId = assetsInDevice.isNotEmpty - ? assetsInDevice.firstOrNull?.localId + ? assetsInDevice.firstOrNull?.id : dbAlbum.thumbnailId; final updatedDeviceAlbum = deviceAlbum.copyWith( @@ -228,8 +229,8 @@ class DeviceSyncService { return true; } - assert(assetsInDb.isSortedBy((a) => a.localId)); - assetsInDevice.sort((a, b) => a.localId.compareTo(b.localId)); + assert(assetsInDb.isSortedBy((a) => a.id)); + assetsInDevice.sort((a, b) => a.id.compareTo(b.id)); final assetsToUpsert = <LocalAsset>[]; final assetsToDelete = <String>[]; @@ -237,7 +238,7 @@ class DeviceSyncService { diffSortedListsSync( assetsInDb, assetsInDevice, - compare: (a, b) => a.localId.compareTo(b.localId), + compare: (a, b) => a.id.compareTo(b.id), both: (dbAsset, deviceAsset) { // Custom comparison to check if the asset has been modified without // comparing the checksum @@ -247,7 +248,7 @@ class DeviceSyncService { } return false; }, - onlyFirst: (dbAsset) => assetsToDelete.add(dbAsset.localId), + onlyFirst: (dbAsset) => assetsToDelete.add(dbAsset.id), onlySecond: (deviceAsset) => assetsToUpsert.add(deviceAsset), ); diff --git a/mobile/lib/infrastructure/entities/local_album.entity.dart b/mobile/lib/infrastructure/entities/local_album.entity.dart index 5d5f766fd4..4eff2a1154 100644 --- a/mobile/lib/infrastructure/entities/local_album.entity.dart +++ b/mobile/lib/infrastructure/entities/local_album.entity.dart @@ -10,12 +10,10 @@ class LocalAlbumEntity extends Table with DriftDefaultsMixin { TextColumn get id => text()(); TextColumn get name => text()(); DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); - IntColumn get assetCount => integer().withDefault(const Constant(0))(); TextColumn get thumbnailId => text() .nullable() .references(LocalAssetEntity, #localId, onDelete: KeyAction.setNull)(); IntColumn get backupSelection => intEnum<BackupSelection>()(); - BoolColumn get isAll => boolean().withDefault(const Constant(false))(); @override Set<Column> get primaryKey => {id}; @@ -30,7 +28,6 @@ extension LocalAlbumEntityX on LocalAlbumEntityData { assetCount: assetCount, thumbnailId: thumbnailId, backupSelection: backupSelection, - isAll: isAll, ); } } diff --git a/mobile/lib/infrastructure/entities/local_album.entity.drift.dart b/mobile/lib/infrastructure/entities/local_album.entity.drift.dart index 19ada47e87..ee6c5b8c61 100644 --- a/mobile/lib/infrastructure/entities/local_album.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/local_album.entity.drift.dart @@ -16,20 +16,16 @@ typedef $$LocalAlbumEntityTableCreateCompanionBuilder required String id, required String name, i0.Value<DateTime> updatedAt, - i0.Value<int> assetCount, i0.Value<String?> thumbnailId, required i2.BackupSelection backupSelection, - i0.Value<bool> isAll, }); typedef $$LocalAlbumEntityTableUpdateCompanionBuilder = i1.LocalAlbumEntityCompanion Function({ i0.Value<String> id, i0.Value<String> name, i0.Value<DateTime> updatedAt, - i0.Value<int> assetCount, i0.Value<String?> thumbnailId, i0.Value<i2.BackupSelection> backupSelection, - i0.Value<bool> isAll, }); final class $$LocalAlbumEntityTableReferences extends i0.BaseReferences< @@ -82,17 +78,11 @@ class $$LocalAlbumEntityTableFilterComposer i0.ColumnFilters<DateTime> get updatedAt => $composableBuilder( column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); - i0.ColumnFilters<int> get assetCount => $composableBuilder( - column: $table.assetCount, builder: (column) => i0.ColumnFilters(column)); - i0.ColumnWithTypeConverterFilters<i2.BackupSelection, i2.BackupSelection, int> get backupSelection => $composableBuilder( column: $table.backupSelection, builder: (column) => i0.ColumnWithTypeConverterFilters(column)); - i0.ColumnFilters<bool> get isAll => $composableBuilder( - column: $table.isAll, builder: (column) => i0.ColumnFilters(column)); - i5.$$LocalAssetEntityTableFilterComposer get thumbnailId { final i5.$$LocalAssetEntityTableFilterComposer composer = $composerBuilder( composer: this, @@ -135,17 +125,10 @@ class $$LocalAlbumEntityTableOrderingComposer column: $table.updatedAt, builder: (column) => i0.ColumnOrderings(column)); - i0.ColumnOrderings<int> get assetCount => $composableBuilder( - column: $table.assetCount, - builder: (column) => i0.ColumnOrderings(column)); - i0.ColumnOrderings<int> get backupSelection => $composableBuilder( column: $table.backupSelection, builder: (column) => i0.ColumnOrderings(column)); - i0.ColumnOrderings<bool> get isAll => $composableBuilder( - column: $table.isAll, builder: (column) => i0.ColumnOrderings(column)); - i5.$$LocalAssetEntityTableOrderingComposer get thumbnailId { final i5.$$LocalAssetEntityTableOrderingComposer composer = $composerBuilder( @@ -189,16 +172,10 @@ class $$LocalAlbumEntityTableAnnotationComposer i0.GeneratedColumn<DateTime> get updatedAt => $composableBuilder(column: $table.updatedAt, builder: (column) => column); - i0.GeneratedColumn<int> get assetCount => $composableBuilder( - column: $table.assetCount, builder: (column) => column); - i0.GeneratedColumnWithTypeConverter<i2.BackupSelection, int> get backupSelection => $composableBuilder( column: $table.backupSelection, builder: (column) => column); - i0.GeneratedColumn<bool> get isAll => - $composableBuilder(column: $table.isAll, builder: (column) => column); - i5.$$LocalAssetEntityTableAnnotationComposer get thumbnailId { final i5.$$LocalAssetEntityTableAnnotationComposer composer = $composerBuilder( @@ -252,38 +229,30 @@ class $$LocalAlbumEntityTableTableManager extends i0.RootTableManager< i0.Value<String> id = const i0.Value.absent(), i0.Value<String> name = const i0.Value.absent(), i0.Value<DateTime> updatedAt = const i0.Value.absent(), - i0.Value<int> assetCount = const i0.Value.absent(), i0.Value<String?> thumbnailId = const i0.Value.absent(), i0.Value<i2.BackupSelection> backupSelection = const i0.Value.absent(), - i0.Value<bool> isAll = const i0.Value.absent(), }) => i1.LocalAlbumEntityCompanion( id: id, name: name, updatedAt: updatedAt, - assetCount: assetCount, thumbnailId: thumbnailId, backupSelection: backupSelection, - isAll: isAll, ), createCompanionCallback: ({ required String id, required String name, i0.Value<DateTime> updatedAt = const i0.Value.absent(), - i0.Value<int> assetCount = const i0.Value.absent(), i0.Value<String?> thumbnailId = const i0.Value.absent(), required i2.BackupSelection backupSelection, - i0.Value<bool> isAll = const i0.Value.absent(), }) => i1.LocalAlbumEntityCompanion.insert( id: id, name: name, updatedAt: updatedAt, - assetCount: assetCount, thumbnailId: thumbnailId, backupSelection: backupSelection, - isAll: isAll, ), withReferenceMapper: (p0) => p0 .map((e) => ( @@ -368,14 +337,6 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity type: i0.DriftSqlType.dateTime, requiredDuringInsert: false, defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _assetCountMeta = - const i0.VerificationMeta('assetCount'); - @override - late final i0.GeneratedColumn<int> assetCount = i0.GeneratedColumn<int>( - 'asset_count', aliasedName, false, - type: i0.DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const i4.Constant(0)); static const i0.VerificationMeta _thumbnailIdMeta = const i0.VerificationMeta('thumbnailId'); @override @@ -392,19 +353,9 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity type: i0.DriftSqlType.int, requiredDuringInsert: true) .withConverter<i2.BackupSelection>( i1.$LocalAlbumEntityTable.$converterbackupSelection); - static const i0.VerificationMeta _isAllMeta = - const i0.VerificationMeta('isAll'); - @override - late final i0.GeneratedColumn<bool> isAll = i0.GeneratedColumn<bool>( - 'is_all', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - i0.GeneratedColumn.constraintIsAlways('CHECK ("is_all" IN (0, 1))'), - defaultValue: const i4.Constant(false)); @override List<i0.GeneratedColumn> get $columns => - [id, name, updatedAt, assetCount, thumbnailId, backupSelection, isAll]; + [id, name, updatedAt, thumbnailId, backupSelection]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -431,22 +382,12 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity context.handle(_updatedAtMeta, updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); } - if (data.containsKey('asset_count')) { - context.handle( - _assetCountMeta, - assetCount.isAcceptableOrUnknown( - data['asset_count']!, _assetCountMeta)); - } if (data.containsKey('thumbnail_id')) { context.handle( _thumbnailIdMeta, thumbnailId.isAcceptableOrUnknown( data['thumbnail_id']!, _thumbnailIdMeta)); } - if (data.containsKey('is_all')) { - context.handle( - _isAllMeta, isAll.isAcceptableOrUnknown(data['is_all']!, _isAllMeta)); - } return context; } @@ -463,15 +404,11 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, updatedAt: attachedDatabase.typeMapping.read( i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - assetCount: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}asset_count'])!, thumbnailId: attachedDatabase.typeMapping .read(i0.DriftSqlType.string, data['${effectivePrefix}thumbnail_id']), backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection .fromSql(attachedDatabase.typeMapping.read(i0.DriftSqlType.int, data['${effectivePrefix}backup_selection'])!), - isAll: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_all'])!, ); } @@ -495,25 +432,20 @@ class LocalAlbumEntityData extends i0.DataClass final String id; final String name; final DateTime updatedAt; - final int assetCount; final String? thumbnailId; final i2.BackupSelection backupSelection; - final bool isAll; const LocalAlbumEntityData( {required this.id, required this.name, required this.updatedAt, - required this.assetCount, this.thumbnailId, - required this.backupSelection, - required this.isAll}); + required this.backupSelection}); @override Map<String, i0.Expression> toColumns(bool nullToAbsent) { final map = <String, i0.Expression>{}; map['id'] = i0.Variable<String>(id); map['name'] = i0.Variable<String>(name); map['updated_at'] = i0.Variable<DateTime>(updatedAt); - map['asset_count'] = i0.Variable<int>(assetCount); if (!nullToAbsent || thumbnailId != null) { map['thumbnail_id'] = i0.Variable<String>(thumbnailId); } @@ -522,7 +454,6 @@ class LocalAlbumEntityData extends i0.DataClass .$LocalAlbumEntityTable.$converterbackupSelection .toSql(backupSelection)); } - map['is_all'] = i0.Variable<bool>(isAll); return map; } @@ -533,11 +464,9 @@ class LocalAlbumEntityData extends i0.DataClass id: serializer.fromJson<String>(json['id']), name: serializer.fromJson<String>(json['name']), updatedAt: serializer.fromJson<DateTime>(json['updatedAt']), - assetCount: serializer.fromJson<int>(json['assetCount']), thumbnailId: serializer.fromJson<String?>(json['thumbnailId']), backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection .fromJson(serializer.fromJson<int>(json['backupSelection'])), - isAll: serializer.fromJson<bool>(json['isAll']), ); } @override @@ -547,12 +476,10 @@ class LocalAlbumEntityData extends i0.DataClass 'id': serializer.toJson<String>(id), 'name': serializer.toJson<String>(name), 'updatedAt': serializer.toJson<DateTime>(updatedAt), - 'assetCount': serializer.toJson<int>(assetCount), 'thumbnailId': serializer.toJson<String?>(thumbnailId), 'backupSelection': serializer.toJson<int>(i1 .$LocalAlbumEntityTable.$converterbackupSelection .toJson(backupSelection)), - 'isAll': serializer.toJson<bool>(isAll), }; } @@ -560,32 +487,25 @@ class LocalAlbumEntityData extends i0.DataClass {String? id, String? name, DateTime? updatedAt, - int? assetCount, i0.Value<String?> thumbnailId = const i0.Value.absent(), - i2.BackupSelection? backupSelection, - bool? isAll}) => + i2.BackupSelection? backupSelection}) => i1.LocalAlbumEntityData( id: id ?? this.id, name: name ?? this.name, updatedAt: updatedAt ?? this.updatedAt, - assetCount: assetCount ?? this.assetCount, thumbnailId: thumbnailId.present ? thumbnailId.value : this.thumbnailId, backupSelection: backupSelection ?? this.backupSelection, - isAll: isAll ?? this.isAll, ); LocalAlbumEntityData copyWithCompanion(i1.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, - assetCount: - data.assetCount.present ? data.assetCount.value : this.assetCount, thumbnailId: data.thumbnailId.present ? data.thumbnailId.value : this.thumbnailId, backupSelection: data.backupSelection.present ? data.backupSelection.value : this.backupSelection, - isAll: data.isAll.present ? data.isAll.value : this.isAll, ); } @@ -595,17 +515,15 @@ class LocalAlbumEntityData extends i0.DataClass ..write('id: $id, ') ..write('name: $name, ') ..write('updatedAt: $updatedAt, ') - ..write('assetCount: $assetCount, ') ..write('thumbnailId: $thumbnailId, ') - ..write('backupSelection: $backupSelection, ') - ..write('isAll: $isAll') + ..write('backupSelection: $backupSelection') ..write(')')) .toString(); } @override - int get hashCode => Object.hash( - id, name, updatedAt, assetCount, thumbnailId, backupSelection, isAll); + int get hashCode => + Object.hash(id, name, updatedAt, thumbnailId, backupSelection); @override bool operator ==(Object other) => identical(this, other) || @@ -613,10 +531,8 @@ class LocalAlbumEntityData extends i0.DataClass other.id == this.id && other.name == this.name && other.updatedAt == this.updatedAt && - other.assetCount == this.assetCount && other.thumbnailId == this.thumbnailId && - other.backupSelection == this.backupSelection && - other.isAll == this.isAll); + other.backupSelection == this.backupSelection); } class LocalAlbumEntityCompanion @@ -624,27 +540,21 @@ class LocalAlbumEntityCompanion final i0.Value<String> id; final i0.Value<String> name; final i0.Value<DateTime> updatedAt; - final i0.Value<int> assetCount; final i0.Value<String?> thumbnailId; final i0.Value<i2.BackupSelection> backupSelection; - final i0.Value<bool> isAll; const LocalAlbumEntityCompanion({ this.id = const i0.Value.absent(), this.name = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(), - this.assetCount = const i0.Value.absent(), this.thumbnailId = const i0.Value.absent(), this.backupSelection = const i0.Value.absent(), - this.isAll = const i0.Value.absent(), }); LocalAlbumEntityCompanion.insert({ required String id, required String name, this.updatedAt = const i0.Value.absent(), - this.assetCount = const i0.Value.absent(), this.thumbnailId = const i0.Value.absent(), required i2.BackupSelection backupSelection, - this.isAll = const i0.Value.absent(), }) : id = i0.Value(id), name = i0.Value(name), backupSelection = i0.Value(backupSelection); @@ -652,19 +562,15 @@ class LocalAlbumEntityCompanion i0.Expression<String>? id, i0.Expression<String>? name, i0.Expression<DateTime>? updatedAt, - i0.Expression<int>? assetCount, i0.Expression<String>? thumbnailId, i0.Expression<int>? backupSelection, - i0.Expression<bool>? isAll, }) { return i0.RawValuesInsertable({ if (id != null) 'id': id, if (name != null) 'name': name, if (updatedAt != null) 'updated_at': updatedAt, - if (assetCount != null) 'asset_count': assetCount, if (thumbnailId != null) 'thumbnail_id': thumbnailId, if (backupSelection != null) 'backup_selection': backupSelection, - if (isAll != null) 'is_all': isAll, }); } @@ -672,18 +578,14 @@ class LocalAlbumEntityCompanion {i0.Value<String>? id, i0.Value<String>? name, i0.Value<DateTime>? updatedAt, - i0.Value<int>? assetCount, i0.Value<String?>? thumbnailId, - i0.Value<i2.BackupSelection>? backupSelection, - i0.Value<bool>? isAll}) { + i0.Value<i2.BackupSelection>? backupSelection}) { return i1.LocalAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, updatedAt: updatedAt ?? this.updatedAt, - assetCount: assetCount ?? this.assetCount, thumbnailId: thumbnailId ?? this.thumbnailId, backupSelection: backupSelection ?? this.backupSelection, - isAll: isAll ?? this.isAll, ); } @@ -699,9 +601,6 @@ class LocalAlbumEntityCompanion if (updatedAt.present) { map['updated_at'] = i0.Variable<DateTime>(updatedAt.value); } - if (assetCount.present) { - map['asset_count'] = i0.Variable<int>(assetCount.value); - } if (thumbnailId.present) { map['thumbnail_id'] = i0.Variable<String>(thumbnailId.value); } @@ -710,9 +609,6 @@ class LocalAlbumEntityCompanion .$LocalAlbumEntityTable.$converterbackupSelection .toSql(backupSelection.value)); } - if (isAll.present) { - map['is_all'] = i0.Variable<bool>(isAll.value); - } return map; } @@ -722,10 +618,8 @@ class LocalAlbumEntityCompanion ..write('id: $id, ') ..write('name: $name, ') ..write('updatedAt: $updatedAt, ') - ..write('assetCount: $assetCount, ') ..write('thumbnailId: $thumbnailId, ') - ..write('backupSelection: $backupSelection, ') - ..write('isAll: $isAll') + ..write('backupSelection: $backupSelection') ..write(')')) .toString(); } diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.dart b/mobile/lib/infrastructure/entities/local_asset.entity.dart index 92ff3f2c62..2127ab1713 100644 --- a/mobile/lib/infrastructure/entities/local_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/local_asset.entity.dart @@ -1,5 +1,5 @@ import 'package:drift/drift.dart'; -import 'package:immich_mobile/domain/models/asset/asset.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; @@ -12,9 +12,8 @@ class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { TextColumn get checksum => text().nullable()(); - IntColumn get width => integer().nullable()(); - - IntColumn get height => integer().nullable()(); + // Only used during backup to mirror the favorite status of the asset in the server + BoolColumn get isFavorite => boolean().withDefault(const Constant(false))(); @override Set<Column> get primaryKey => {localId}; @@ -23,15 +22,14 @@ class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { extension LocalAssetEntityX on LocalAssetEntityData { LocalAsset toDto() { return LocalAsset( - localId: localId, + id: localId, name: name, checksum: checksum, type: type, createdAt: createdAt, updatedAt: updatedAt, - width: width, - height: height, durationInSeconds: durationInSeconds, + isFavorite: isFavorite, ); } } diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart index d8635097b1..e6a567d033 100644 --- a/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart @@ -3,7 +3,7 @@ import 'package:drift/drift.dart' as i0; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart' as i1; -import 'package:immich_mobile/domain/models/asset/asset.model.dart' as i2; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart' as i2; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart' as i3; import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4; @@ -17,8 +17,7 @@ typedef $$LocalAssetEntityTableCreateCompanionBuilder i0.Value<int?> durationInSeconds, required String localId, i0.Value<String?> checksum, - i0.Value<int?> width, - i0.Value<int?> height, + i0.Value<bool> isFavorite, }); typedef $$LocalAssetEntityTableUpdateCompanionBuilder = i1.LocalAssetEntityCompanion Function({ @@ -29,8 +28,7 @@ typedef $$LocalAssetEntityTableUpdateCompanionBuilder i0.Value<int?> durationInSeconds, i0.Value<String> localId, i0.Value<String?> checksum, - i0.Value<int?> width, - i0.Value<int?> height, + i0.Value<bool> isFavorite, }); class $$LocalAssetEntityTableFilterComposer @@ -66,11 +64,8 @@ class $$LocalAssetEntityTableFilterComposer i0.ColumnFilters<String> get checksum => $composableBuilder( column: $table.checksum, builder: (column) => i0.ColumnFilters(column)); - i0.ColumnFilters<int> get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnFilters(column)); - - i0.ColumnFilters<int> get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnFilters(column)); + i0.ColumnFilters<bool> get isFavorite => $composableBuilder( + column: $table.isFavorite, builder: (column) => i0.ColumnFilters(column)); } class $$LocalAssetEntityTableOrderingComposer @@ -106,11 +101,9 @@ class $$LocalAssetEntityTableOrderingComposer i0.ColumnOrderings<String> get checksum => $composableBuilder( column: $table.checksum, builder: (column) => i0.ColumnOrderings(column)); - i0.ColumnOrderings<int> get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnOrderings(column)); - - i0.ColumnOrderings<int> get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnOrderings(column)); + i0.ColumnOrderings<bool> get isFavorite => $composableBuilder( + column: $table.isFavorite, + builder: (column) => i0.ColumnOrderings(column)); } class $$LocalAssetEntityTableAnnotationComposer @@ -143,11 +136,8 @@ class $$LocalAssetEntityTableAnnotationComposer i0.GeneratedColumn<String> get checksum => $composableBuilder(column: $table.checksum, builder: (column) => column); - i0.GeneratedColumn<int> get width => - $composableBuilder(column: $table.width, builder: (column) => column); - - i0.GeneratedColumn<int> get height => - $composableBuilder(column: $table.height, builder: (column) => column); + i0.GeneratedColumn<bool> get isFavorite => $composableBuilder( + column: $table.isFavorite, builder: (column) => column); } class $$LocalAssetEntityTableTableManager extends i0.RootTableManager< @@ -186,8 +176,7 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager< i0.Value<int?> durationInSeconds = const i0.Value.absent(), i0.Value<String> localId = const i0.Value.absent(), i0.Value<String?> checksum = const i0.Value.absent(), - i0.Value<int?> width = const i0.Value.absent(), - i0.Value<int?> height = const i0.Value.absent(), + i0.Value<bool> isFavorite = const i0.Value.absent(), }) => i1.LocalAssetEntityCompanion( name: name, @@ -197,8 +186,7 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager< durationInSeconds: durationInSeconds, localId: localId, checksum: checksum, - width: width, - height: height, + isFavorite: isFavorite, ), createCompanionCallback: ({ required String name, @@ -208,8 +196,7 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager< i0.Value<int?> durationInSeconds = const i0.Value.absent(), required String localId, i0.Value<String?> checksum = const i0.Value.absent(), - i0.Value<int?> width = const i0.Value.absent(), - i0.Value<int?> height = const i0.Value.absent(), + i0.Value<bool> isFavorite = const i0.Value.absent(), }) => i1.LocalAssetEntityCompanion.insert( name: name, @@ -219,8 +206,7 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager< durationInSeconds: durationInSeconds, localId: localId, checksum: checksum, - width: width, - height: height, + isFavorite: isFavorite, ), withReferenceMapper: (p0) => p0 .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) @@ -300,18 +286,16 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity late final i0.GeneratedColumn<String> checksum = i0.GeneratedColumn<String>( 'checksum', aliasedName, true, type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _widthMeta = - const i0.VerificationMeta('width'); + static const i0.VerificationMeta _isFavoriteMeta = + const i0.VerificationMeta('isFavorite'); @override - late final i0.GeneratedColumn<int> width = i0.GeneratedColumn<int>( - 'width', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _heightMeta = - const i0.VerificationMeta('height'); - @override - late final i0.GeneratedColumn<int> height = i0.GeneratedColumn<int>( - 'height', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + late final i0.GeneratedColumn<bool> isFavorite = i0.GeneratedColumn<bool>( + 'is_favorite', aliasedName, false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))'), + defaultValue: const i4.Constant(false)); @override List<i0.GeneratedColumn> get $columns => [ name, @@ -321,8 +305,7 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity durationInSeconds, localId, checksum, - width, - height + isFavorite ]; @override String get aliasedName => _alias ?? actualTableName; @@ -365,13 +348,11 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity context.handle(_checksumMeta, checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta)); } - if (data.containsKey('width')) { + if (data.containsKey('is_favorite')) { context.handle( - _widthMeta, width.isAcceptableOrUnknown(data['width']!, _widthMeta)); - } - if (data.containsKey('height')) { - context.handle(_heightMeta, - height.isAcceptableOrUnknown(data['height']!, _heightMeta)); + _isFavoriteMeta, + isFavorite.isAcceptableOrUnknown( + data['is_favorite']!, _isFavoriteMeta)); } return context; } @@ -398,10 +379,8 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity .read(i0.DriftSqlType.string, data['${effectivePrefix}local_id'])!, checksum: attachedDatabase.typeMapping .read(i0.DriftSqlType.string, data['${effectivePrefix}checksum']), - width: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}height']), + isFavorite: attachedDatabase.typeMapping + .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, ); } @@ -427,8 +406,7 @@ class LocalAssetEntityData extends i0.DataClass final int? durationInSeconds; final String localId; final String? checksum; - final int? width; - final int? height; + final bool isFavorite; const LocalAssetEntityData( {required this.name, required this.type, @@ -437,8 +415,7 @@ class LocalAssetEntityData extends i0.DataClass this.durationInSeconds, required this.localId, this.checksum, - this.width, - this.height}); + required this.isFavorite}); @override Map<String, i0.Expression> toColumns(bool nullToAbsent) { final map = <String, i0.Expression>{}; @@ -456,12 +433,7 @@ class LocalAssetEntityData extends i0.DataClass if (!nullToAbsent || checksum != null) { map['checksum'] = i0.Variable<String>(checksum); } - if (!nullToAbsent || width != null) { - map['width'] = i0.Variable<int>(width); - } - if (!nullToAbsent || height != null) { - map['height'] = i0.Variable<int>(height); - } + map['is_favorite'] = i0.Variable<bool>(isFavorite); return map; } @@ -477,8 +449,7 @@ class LocalAssetEntityData extends i0.DataClass durationInSeconds: serializer.fromJson<int?>(json['durationInSeconds']), localId: serializer.fromJson<String>(json['localId']), checksum: serializer.fromJson<String?>(json['checksum']), - width: serializer.fromJson<int?>(json['width']), - height: serializer.fromJson<int?>(json['height']), + isFavorite: serializer.fromJson<bool>(json['isFavorite']), ); } @override @@ -493,8 +464,7 @@ class LocalAssetEntityData extends i0.DataClass 'durationInSeconds': serializer.toJson<int?>(durationInSeconds), 'localId': serializer.toJson<String>(localId), 'checksum': serializer.toJson<String?>(checksum), - 'width': serializer.toJson<int?>(width), - 'height': serializer.toJson<int?>(height), + 'isFavorite': serializer.toJson<bool>(isFavorite), }; } @@ -506,8 +476,7 @@ class LocalAssetEntityData extends i0.DataClass i0.Value<int?> durationInSeconds = const i0.Value.absent(), String? localId, i0.Value<String?> checksum = const i0.Value.absent(), - i0.Value<int?> width = const i0.Value.absent(), - i0.Value<int?> height = const i0.Value.absent()}) => + bool? isFavorite}) => i1.LocalAssetEntityData( name: name ?? this.name, type: type ?? this.type, @@ -518,8 +487,7 @@ class LocalAssetEntityData extends i0.DataClass : this.durationInSeconds, localId: localId ?? this.localId, checksum: checksum.present ? checksum.value : this.checksum, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, + isFavorite: isFavorite ?? this.isFavorite, ); LocalAssetEntityData copyWithCompanion(i1.LocalAssetEntityCompanion data) { return LocalAssetEntityData( @@ -532,8 +500,8 @@ class LocalAssetEntityData extends i0.DataClass : this.durationInSeconds, localId: data.localId.present ? data.localId.value : this.localId, checksum: data.checksum.present ? data.checksum.value : this.checksum, - width: data.width.present ? data.width.value : this.width, - height: data.height.present ? data.height.value : this.height, + isFavorite: + data.isFavorite.present ? data.isFavorite.value : this.isFavorite, ); } @@ -547,15 +515,14 @@ class LocalAssetEntityData extends i0.DataClass ..write('durationInSeconds: $durationInSeconds, ') ..write('localId: $localId, ') ..write('checksum: $checksum, ') - ..write('width: $width, ') - ..write('height: $height') + ..write('isFavorite: $isFavorite') ..write(')')) .toString(); } @override int get hashCode => Object.hash(name, type, createdAt, updatedAt, - durationInSeconds, localId, checksum, width, height); + durationInSeconds, localId, checksum, isFavorite); @override bool operator ==(Object other) => identical(this, other) || @@ -567,8 +534,7 @@ class LocalAssetEntityData extends i0.DataClass other.durationInSeconds == this.durationInSeconds && other.localId == this.localId && other.checksum == this.checksum && - other.width == this.width && - other.height == this.height); + other.isFavorite == this.isFavorite); } class LocalAssetEntityCompanion @@ -580,8 +546,7 @@ class LocalAssetEntityCompanion final i0.Value<int?> durationInSeconds; final i0.Value<String> localId; final i0.Value<String?> checksum; - final i0.Value<int?> width; - final i0.Value<int?> height; + final i0.Value<bool> isFavorite; const LocalAssetEntityCompanion({ this.name = const i0.Value.absent(), this.type = const i0.Value.absent(), @@ -590,8 +555,7 @@ class LocalAssetEntityCompanion this.durationInSeconds = const i0.Value.absent(), this.localId = const i0.Value.absent(), this.checksum = const i0.Value.absent(), - this.width = const i0.Value.absent(), - this.height = const i0.Value.absent(), + this.isFavorite = const i0.Value.absent(), }); LocalAssetEntityCompanion.insert({ required String name, @@ -601,8 +565,7 @@ class LocalAssetEntityCompanion this.durationInSeconds = const i0.Value.absent(), required String localId, this.checksum = const i0.Value.absent(), - this.width = const i0.Value.absent(), - this.height = const i0.Value.absent(), + this.isFavorite = const i0.Value.absent(), }) : name = i0.Value(name), type = i0.Value(type), localId = i0.Value(localId); @@ -614,8 +577,7 @@ class LocalAssetEntityCompanion i0.Expression<int>? durationInSeconds, i0.Expression<String>? localId, i0.Expression<String>? checksum, - i0.Expression<int>? width, - i0.Expression<int>? height, + i0.Expression<bool>? isFavorite, }) { return i0.RawValuesInsertable({ if (name != null) 'name': name, @@ -625,8 +587,7 @@ class LocalAssetEntityCompanion if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, if (localId != null) 'local_id': localId, if (checksum != null) 'checksum': checksum, - if (width != null) 'width': width, - if (height != null) 'height': height, + if (isFavorite != null) 'is_favorite': isFavorite, }); } @@ -638,8 +599,7 @@ class LocalAssetEntityCompanion i0.Value<int?>? durationInSeconds, i0.Value<String>? localId, i0.Value<String?>? checksum, - i0.Value<int?>? width, - i0.Value<int?>? height}) { + i0.Value<bool>? isFavorite}) { return i1.LocalAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -648,8 +608,7 @@ class LocalAssetEntityCompanion durationInSeconds: durationInSeconds ?? this.durationInSeconds, localId: localId ?? this.localId, checksum: checksum ?? this.checksum, - width: width ?? this.width, - height: height ?? this.height, + isFavorite: isFavorite ?? this.isFavorite, ); } @@ -678,11 +637,8 @@ class LocalAssetEntityCompanion if (checksum.present) { map['checksum'] = i0.Variable<String>(checksum.value); } - if (width.present) { - map['width'] = i0.Variable<int>(width.value); - } - if (height.present) { - map['height'] = i0.Variable<int>(height.value); + if (isFavorite.present) { + map['is_favorite'] = i0.Variable<bool>(isFavorite.value); } return map; } @@ -697,8 +653,7 @@ class LocalAssetEntityCompanion ..write('durationInSeconds: $durationInSeconds, ') ..write('localId: $localId, ') ..write('checksum: $checksum, ') - ..write('width: $width, ') - ..write('height: $height') + ..write('isFavorite: $isFavorite') ..write(')')) .toString(); } diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart index 4f036150b3..b868709c5b 100644 --- a/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart @@ -3,7 +3,7 @@ import 'package:drift/drift.dart' as i0; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' as i1; -import 'package:immich_mobile/domain/models/asset/asset.model.dart' as i2; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart' as i2; import 'dart:typed_data' as i3; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart' as i4; diff --git a/mobile/lib/infrastructure/repositories/album_media.repository.dart b/mobile/lib/infrastructure/repositories/album_media.repository.dart index c834169852..0a2da332fc 100644 --- a/mobile/lib/infrastructure/repositories/album_media.repository.dart +++ b/mobile/lib/infrastructure/repositories/album_media.repository.dart @@ -1,11 +1,15 @@ import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/interfaces/album_media.interface.dart'; -import 'package:immich_mobile/domain/models/asset/asset.model.dart' as asset; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart' + as asset; import 'package:immich_mobile/domain/models/local_album.model.dart'; import 'package:photo_manager/photo_manager.dart'; +import 'package:platform/platform.dart'; class AlbumMediaRepository implements IAlbumMediaRepository { - const AlbumMediaRepository(); + final Platform _platform; + const AlbumMediaRepository({Platform platform = const LocalPlatform()}) + : _platform = platform; PMFilter _getAlbumFilter({ withAssetTitle = false, @@ -42,7 +46,12 @@ class AlbumMediaRepository implements IAlbumMediaRepository { ); return PhotoManager.getAssetPathList(hasAll: true, filterOption: filter) - .then((e) => e.toDtoList()); + .then((e) { + if (_platform.isAndroid) { + e.removeWhere((a) => a.isAll); + } + return e.toDtoList(); + }); } @override @@ -85,7 +94,7 @@ class AlbumMediaRepository implements IAlbumMediaRepository { extension on AssetEntity { Future<asset.LocalAsset> toDto() async => asset.LocalAsset( - localId: id, + id: id, name: title ?? await titleAsync, type: switch (type) { AssetType.other => asset.AssetType.other, @@ -114,7 +123,6 @@ extension on AssetPathEntity { // the assetCountAsync call is expensive for larger albums with several thousand assets assetCount: withAssetCount ? await assetCountAsync : 0, backupSelection: BackupSelection.none, - isAll: isAll, ); } diff --git a/mobile/lib/infrastructure/repositories/local_album.repository.dart b/mobile/lib/infrastructure/repositories/local_album.repository.dart index 557c264409..0ae0d0daf7 100644 --- a/mobile/lib/infrastructure/repositories/local_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_album.repository.dart @@ -1,6 +1,6 @@ import 'package:drift/drift.dart'; import 'package:immich_mobile/domain/interfaces/local_album.interface.dart'; -import 'package:immich_mobile/domain/models/asset/asset.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/local_album.model.dart'; import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'; @@ -137,10 +137,8 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository id: localAlbum.id, name: localAlbum.name, updatedAt: Value(localAlbum.updatedAt), - assetCount: Value(localAlbum.assetCount), thumbnailId: Value.absentIfNull(localAlbum.thumbnailId), backupSelection: localAlbum.backupSelection, - isAll: Value(localAlbum.isAll), ); return _db.localAlbumEntity @@ -160,7 +158,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository _db.localAlbumAssetEntity, assets.map( (a) => LocalAlbumAssetEntityCompanion.insert( - assetId: a.localId, + assetId: a.id, albumId: albumId, ), ), @@ -220,10 +218,8 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository type: a.type, createdAt: Value(a.createdAt), updatedAt: Value(a.updatedAt), - width: Value.absentIfNull(a.width), - height: Value.absentIfNull(a.height), durationInSeconds: Value.absentIfNull(a.durationInSeconds), - localId: a.localId, + localId: a.id, checksum: Value.absentIfNull(a.checksum), ), ), diff --git a/mobile/lib/infrastructure/repositories/local_asset.repository.dart b/mobile/lib/infrastructure/repositories/local_asset.repository.dart index c77e997d23..cb9119b653 100644 --- a/mobile/lib/infrastructure/repositories/local_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_asset.repository.dart @@ -1,5 +1,5 @@ import 'package:immich_mobile/domain/interfaces/local_asset.interface.dart'; -import 'package:immich_mobile/domain/models/asset/asset.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; diff --git a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart index cc6c1e3e7a..03a3cdff4a 100644 --- a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart @@ -1,6 +1,6 @@ import 'package:drift/drift.dart'; import 'package:immich_mobile/domain/interfaces/sync_stream.interface.dart'; -import 'package:immich_mobile/domain/models/asset/asset.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/entities/exif.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'; diff --git a/mobile/lib/infrastructure/utils/asset.mixin.dart b/mobile/lib/infrastructure/utils/asset.mixin.dart index 4221ae27dd..8649550826 100644 --- a/mobile/lib/infrastructure/utils/asset.mixin.dart +++ b/mobile/lib/infrastructure/utils/asset.mixin.dart @@ -1,5 +1,5 @@ import 'package:drift/drift.dart'; -import 'package:immich_mobile/domain/models/asset/asset.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; mixin AssetEntityMixin on Table { TextColumn get name => text()(); diff --git a/mobile/lib/interfaces/asset.interface.dart b/mobile/lib/interfaces/asset.interface.dart index 76744c9172..ca9e9d64fb 100644 --- a/mobile/lib/interfaces/asset.interface.dart +++ b/mobile/lib/interfaces/asset.interface.dart @@ -61,7 +61,7 @@ abstract interface class IAssetRepository implements IDatabaseRepository { Future<List<Asset>> getTrashAssets(String userId); - Future<List<Asset>> getRecentlyAddedAssets(String userId); + Future<List<Asset>> getRecentlyTakenAssets(String userId); Future<List<Asset>> getMotionAssets(String userId); } diff --git a/mobile/lib/interfaces/local_files_manager.interface.dart b/mobile/lib/interfaces/local_files_manager.interface.dart index c8b83a7c93..07274b7e29 100644 --- a/mobile/lib/interfaces/local_files_manager.interface.dart +++ b/mobile/lib/interfaces/local_files_manager.interface.dart @@ -1,5 +1,5 @@ abstract interface class ILocalFilesManager { - Future<bool> moveToTrash(String fileName); - Future<bool> restoreFromTrash(String fileName); - Future<bool> requestManageStoragePermission(); + Future<bool> moveToTrash(List<String> mediaUrls); + Future<bool> restoreFromTrash(String fileName, int type); + Future<bool> requestManageMediaPermission(); } diff --git a/mobile/lib/pages/common/gallery_viewer.page.dart b/mobile/lib/pages/common/gallery_viewer.page.dart index 7392a4d340..420b699730 100644 --- a/mobile/lib/pages/common/gallery_viewer.page.dart +++ b/mobile/lib/pages/common/gallery_viewer.page.dart @@ -63,9 +63,12 @@ class GalleryViewerPage extends HookConsumerWidget { final loadAsset = renderList.loadAsset; final isPlayingMotionVideo = ref.watch(isPlayingMotionVideoProvider); - // This key is to prevent the video player from being re-initialized during - // hero animation or device rotation. - final videoPlayerKey = useMemoized(() => GlobalKey()); + final videoPlayerKeys = useRef<Map<int, GlobalKey>>({}); + + GlobalKey getVideoPlayerKey(int id) { + videoPlayerKeys.value.putIfAbsent(id, () => GlobalKey()); + return videoPlayerKeys.value[id]!; + } Future<void> precacheNextImage(int index) async { if (!context.mounted) { @@ -243,7 +246,7 @@ class GalleryViewerPage extends HookConsumerWidget { width: context.width, height: context.height, child: NativeVideoViewerPage( - key: videoPlayerKey, + key: getVideoPlayerKey(asset.id), asset: asset, image: Image( key: ValueKey(asset), diff --git a/mobile/lib/pages/library/library.page.dart b/mobile/lib/pages/library/library.page.dart index 1852fb7877..1dc336d204 100644 --- a/mobile/lib/pages/library/library.page.dart +++ b/mobile/lib/pages/library/library.page.dart @@ -297,32 +297,34 @@ class LocalAlbumsCollectionCard extends HookConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( + SizedBox( height: size, width: size, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(30), - context.colorScheme.primary.withAlpha(25), - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, + child: DecoratedBox( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(20)), + gradient: LinearGradient( + colors: [ + context.colorScheme.primary.withAlpha(30), + context.colorScheme.primary.withAlpha(25), + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: GridView.count( + crossAxisCount: 2, + padding: const EdgeInsets.all(12), + crossAxisSpacing: 8, + mainAxisSpacing: 8, + physics: const NeverScrollableScrollPhysics(), + children: albums.take(4).map((album) { + return AlbumThumbnailCard( + album: album, + showTitle: false, + ); + }).toList(), ), - ), - child: GridView.count( - crossAxisCount: 2, - padding: const EdgeInsets.all(12), - crossAxisSpacing: 8, - mainAxisSpacing: 8, - physics: const NeverScrollableScrollPhysics(), - children: albums.take(4).map((album) { - return AlbumThumbnailCard( - album: album, - showTitle: false, - ); - }).toList(), ), ), Padding( @@ -354,27 +356,35 @@ class PlacesCollectionCard extends StatelessWidget { final size = context.width * widthFactor - 20.0; return GestureDetector( - onTap: () => context.pushRoute(const PlacesCollectionRoute()), + onTap: () => context.pushRoute( + PlacesCollectionRoute( + currentLocation: null, + ), + ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( + SizedBox( height: size, width: size, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - color: context.colorScheme.secondaryContainer.withAlpha(100), - ), - child: IgnorePointer( - child: MapThumbnail( - zoom: 8, - centre: const LatLng( - 21.44950, - -157.91959, + child: DecoratedBox( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(20)), + color: + context.colorScheme.secondaryContainer.withAlpha(100), + ), + child: IgnorePointer( + child: MapThumbnail( + zoom: 8, + centre: const LatLng( + 21.44950, + -157.91959, + ), + showAttribution: false, + themeMode: context.isDarkTheme + ? ThemeMode.dark + : ThemeMode.light, ), - showAttribution: false, - themeMode: - context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), ), ), diff --git a/mobile/lib/pages/library/places/places_collection.page.dart b/mobile/lib/pages/library/places/places_collection.page.dart index f9a2d4292c..5f2dea0dec 100644 --- a/mobile/lib/pages/library/places/places_collection.page.dart +++ b/mobile/lib/pages/library/places/places_collection.page.dart @@ -19,7 +19,8 @@ import 'package:maplibre_gl/maplibre_gl.dart'; @RoutePage() class PlacesCollectionPage extends HookConsumerWidget { - const PlacesCollectionPage({super.key}); + const PlacesCollectionPage({super.key, this.currentLocation}); + final LatLng? currentLocation; @override Widget build(BuildContext context, WidgetRef ref) { final places = ref.watch(getAllPlacesProvider); @@ -58,12 +59,14 @@ class PlacesCollectionPage extends HookConsumerWidget { height: 200, width: context.width, child: MapThumbnail( - onTap: (_, __) => context.pushRoute(const MapRoute()), + onTap: (_, __) => context + .pushRoute(MapRoute(initialLocation: currentLocation)), zoom: 8, - centre: const LatLng( - 21.44950, - -157.91959, - ), + centre: currentLocation ?? + const LatLng( + 21.44950, + -157.91959, + ), showAttribution: false, themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, diff --git a/mobile/lib/pages/onboarding/permission_onboarding.page.dart b/mobile/lib/pages/onboarding/permission_onboarding.page.dart index a6768cc207..b0a1b34b06 100644 --- a/mobile/lib/pages/onboarding/permission_onboarding.page.dart +++ b/mobile/lib/pages/onboarding/permission_onboarding.page.dart @@ -44,7 +44,7 @@ class PermissionOnboardingPage extends HookConsumerWidget { } }), child: const Text( - 'grant_permission', + 'continue', ).tr(), ), ], diff --git a/mobile/lib/pages/search/map/map.page.dart b/mobile/lib/pages/search/map/map.page.dart index 0e64759241..b80b96f94f 100644 --- a/mobile/lib/pages/search/map/map.page.dart +++ b/mobile/lib/pages/search/map/map.page.dart @@ -34,7 +34,8 @@ import 'package:maplibre_gl/maplibre_gl.dart'; @RoutePage() class MapPage extends HookConsumerWidget { - const MapPage({super.key}); + const MapPage({super.key, this.initialLocation}); + final LatLng? initialLocation; @override Widget build(BuildContext context, WidgetRef ref) { @@ -235,7 +236,8 @@ class MapPage extends HookConsumerWidget { } void onZoomToLocation() async { - final (location, error) = await MapUtils.checkPermAndGetLocation(context); + final (location, error) = + await MapUtils.checkPermAndGetLocation(context: context); if (error != null) { if (error == LocationPermission.unableToDetermine && context.mounted) { ImmichToast.show( @@ -272,6 +274,7 @@ class MapPage extends HookConsumerWidget { body: Stack( children: [ _MapWithMarker( + initialLocation: initialLocation, style: style, selectedMarker: selectedMarker, onMapCreated: onMapCreated, @@ -303,6 +306,7 @@ class MapPage extends HookConsumerWidget { body: Stack( children: [ _MapWithMarker( + initialLocation: initialLocation, style: style, selectedMarker: selectedMarker, onMapCreated: onMapCreated, @@ -368,6 +372,7 @@ class _MapWithMarker extends StatelessWidget { final OnStyleLoadedCallback onStyleLoaded; final Function()? onMarkerTapped; final ValueNotifier<_AssetMarkerMeta?> selectedMarker; + final LatLng? initialLocation; const _MapWithMarker({ required this.style, @@ -377,6 +382,7 @@ class _MapWithMarker extends StatelessWidget { required this.onStyleLoaded, required this.selectedMarker, this.onMarkerTapped, + this.initialLocation, }); @override @@ -389,8 +395,10 @@ class _MapWithMarker extends StatelessWidget { children: [ style.widgetWhen( onData: (style) => MapLibreMap( - initialCameraPosition: - const CameraPosition(target: LatLng(0, 0)), + initialCameraPosition: CameraPosition( + target: initialLocation ?? const LatLng(0, 0), + zoom: initialLocation != null ? 12 : 0, + ), styleString: style, // This is needed to update the selectedMarker's position on map camera updates // The changes are notified through the mapController ValueListener which is added in [onMapCreated] diff --git a/mobile/lib/pages/search/map/map_location_picker.page.dart b/mobile/lib/pages/search/map/map_location_picker.page.dart index 9d526d8080..f27deae052 100644 --- a/mobile/lib/pages/search/map/map_location_picker.page.dart +++ b/mobile/lib/pages/search/map/map_location_picker.page.dart @@ -46,7 +46,7 @@ class MapLocationPickerPage extends HookConsumerWidget { Future<void> getCurrentLocation() async { var (currentLocation, _) = - await MapUtils.checkPermAndGetLocation(context); + await MapUtils.checkPermAndGetLocation(context: context); if (currentLocation == null) { return; diff --git a/mobile/lib/pages/search/recently_added.page.dart b/mobile/lib/pages/search/recently_taken.page.dart similarity index 74% rename from mobile/lib/pages/search/recently_added.page.dart rename to mobile/lib/pages/search/recently_taken.page.dart index b79527e222..cc1eb7086e 100644 --- a/mobile/lib/pages/search/recently_added.page.dart +++ b/mobile/lib/pages/search/recently_taken.page.dart @@ -4,19 +4,19 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart'; -import 'package:immich_mobile/providers/search/recently_added_asset.provider.dart'; +import 'package:immich_mobile/providers/search/recently_taken_asset.provider.dart'; @RoutePage() -class RecentlyAddedPage extends HookConsumerWidget { - const RecentlyAddedPage({super.key}); +class RecentlyTakenPage extends HookConsumerWidget { + const RecentlyTakenPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final recents = ref.watch(recentlyAddedAssetProvider); + final recents = ref.watch(recentlyTakenAssetProvider); return Scaffold( appBar: AppBar( - title: const Text('recently_added_page_title').tr(), + title: const Text('recently_taken_page_title').tr(), leading: IconButton( onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded), diff --git a/mobile/lib/pages/search/search.page.dart b/mobile/lib/pages/search/search.page.dart index 62a62d6c98..f7a87803de 100644 --- a/mobile/lib/pages/search/search.page.dart +++ b/mobile/lib/pages/search/search.page.dart @@ -843,10 +843,10 @@ class QuickLinkList extends StatelessWidget { physics: const NeverScrollableScrollPhysics(), children: [ QuickLink( - title: 'recently_added'.tr(), + title: 'recently_taken'.tr(), icon: Icons.schedule_outlined, isTop: true, - onTap: () => context.pushRoute(const RecentlyAddedRoute()), + onTap: () => context.pushRoute(const RecentlyTakenRoute()), ), QuickLink( title: 'videos'.tr(), diff --git a/mobile/lib/providers/image/immich_local_image_provider.dart b/mobile/lib/providers/image/immich_local_image_provider.dart index 36fd3334b9..4c77ee4b56 100644 --- a/mobile/lib/providers/image/immich_local_image_provider.dart +++ b/mobile/lib/providers/image/immich_local_image_provider.dart @@ -53,50 +53,35 @@ class ImmichLocalImageProvider extends ImageProvider<ImmichLocalImageProvider> { ImageDecoderCallback decode, StreamController<ImageChunkEvent> chunkEvents, ) async* { - ui.ImmutableBuffer? buffer; try { final local = asset.local; if (local == null) { throw StateError('Asset ${asset.fileName} has no local data'); } - var thumbBytes = await local - .thumbnailDataWithSize(const ThumbnailSize.square(256), quality: 80); - if (thumbBytes == null) { - throw StateError("Loading thumbnail for ${asset.fileName} failed"); - } - buffer = await ui.ImmutableBuffer.fromUint8List(thumbBytes); - thumbBytes = null; - yield await decode(buffer); - buffer = null; - switch (asset.type) { case AssetType.image: final File? file = await local.originFile; if (file == null) { throw StateError("Opening file for asset ${asset.fileName} failed"); } - buffer = await ui.ImmutableBuffer.fromFilePath(file.path); + final buffer = await ui.ImmutableBuffer.fromFilePath(file.path); yield await decode(buffer); - buffer = null; break; case AssetType.video: final size = ThumbnailSize(width.ceil(), height.ceil()); - thumbBytes = await local.thumbnailDataWithSize(size); + final thumbBytes = await local.thumbnailDataWithSize(size); if (thumbBytes == null) { throw StateError("Failed to load preview for ${asset.fileName}"); } - buffer = await ui.ImmutableBuffer.fromUint8List(thumbBytes); - thumbBytes = null; + final buffer = await ui.ImmutableBuffer.fromUint8List(thumbBytes); yield await decode(buffer); - buffer = null; break; default: throw StateError('Unsupported asset type ${asset.type}'); } } catch (error, stack) { log.severe('Error loading local image ${asset.fileName}', error, stack); - buffer?.dispose(); } finally { chunkEvents.close(); } @@ -106,12 +91,11 @@ class ImmichLocalImageProvider extends ImageProvider<ImmichLocalImageProvider> { bool operator ==(Object other) { if (identical(this, other)) return true; if (other is ImmichLocalImageProvider) { - return asset == other.asset; + return asset.id == other.asset.id && asset.localId == other.asset.localId; } - return false; } @override - int get hashCode => asset.hashCode; + int get hashCode => Object.hash(asset.id, asset.localId); } diff --git a/mobile/lib/providers/image/immich_local_thumbnail_provider.dart b/mobile/lib/providers/image/immich_local_thumbnail_provider.dart index 69cdb105c0..edcf8a9458 100644 --- a/mobile/lib/providers/image/immich_local_thumbnail_provider.dart +++ b/mobile/lib/providers/image/immich_local_thumbnail_provider.dart @@ -2,11 +2,14 @@ import 'dart:async'; import 'dart:ui' as ui; import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; +import 'package:immich_mobile/providers/image/cache/thumbnail_image_cache_manager.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/painting.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:photo_manager/photo_manager.dart' show ThumbnailSize; +import 'package:logging/logging.dart'; /// The local image provider for an asset /// Only viable @@ -15,11 +18,16 @@ class ImmichLocalThumbnailProvider final Asset asset; final int height; final int width; + final CacheManager? cacheManager; + final Logger log = Logger("ImmichLocalThumbnailProvider"); + final String? userId; ImmichLocalThumbnailProvider({ required this.asset, this.height = 256, this.width = 256, + this.cacheManager, + this.userId, }) : assert(asset.local != null, 'Only usable when asset.local is set'); /// Converts an [ImageProvider]'s settings plus an [ImageConfiguration] to a key @@ -36,57 +44,62 @@ class ImmichLocalThumbnailProvider ImmichLocalThumbnailProvider key, ImageDecoderCallback decode, ) { - final chunkEvents = StreamController<ImageChunkEvent>(); + final cache = cacheManager ?? ThumbnailImageCacheManager(); return MultiImageStreamCompleter( - codec: _codec(key.asset, decode, chunkEvents), + codec: _codec(key.asset, cache, decode), scale: 1.0, - chunkEvents: chunkEvents.stream, informationCollector: () sync* { - yield ErrorDescription(asset.fileName); + yield ErrorDescription(key.asset.fileName); }, ); } // Streams in each stage of the image as we ask for it Stream<ui.Codec> _codec( - Asset key, + Asset assetData, + CacheManager cache, ImageDecoderCallback decode, - StreamController<ImageChunkEvent> chunkEvents, ) async* { - // Load a small thumbnail - final thumbBytes = await asset.local?.thumbnailDataWithSize( - const ThumbnailSize.square(32), - quality: 75, - ); - if (thumbBytes != null) { - final buffer = await ui.ImmutableBuffer.fromUint8List(thumbBytes); - final codec = await decode(buffer); - yield codec; - } else { - debugPrint("Loading thumb for ${asset.fileName} failed"); + final cacheKey = + '$userId${assetData.localId}${assetData.checksum}$width$height'; + final fileFromCache = await cache.getFileFromCache(cacheKey); + if (fileFromCache != null) { + try { + final buffer = + await ui.ImmutableBuffer.fromFilePath(fileFromCache.file.path); + final codec = await decode(buffer); + yield codec; + return; + } catch (error) { + log.severe('Found thumbnail in cache, but loading it failed', error); + } } - final normalThumbBytes = - await asset.local?.thumbnailDataWithSize(ThumbnailSize(width, height)); - if (normalThumbBytes == null) { + final thumbnailBytes = await assetData.local?.thumbnailDataWithSize( + ThumbnailSize(width, height), + quality: 80, + ); + if (thumbnailBytes == null) { throw StateError( - "Loading thumb for local photo ${asset.fileName} failed", + "Loading thumb for local photo ${assetData.fileName} failed", ); } - final buffer = await ui.ImmutableBuffer.fromUint8List(normalThumbBytes); + + final buffer = await ui.ImmutableBuffer.fromUint8List(thumbnailBytes); final codec = await decode(buffer); yield codec; - - chunkEvents.close(); + await cache.putFile(cacheKey, thumbnailBytes); } @override bool operator ==(Object other) { - if (other is! ImmichLocalThumbnailProvider) return false; if (identical(this, other)) return true; - return asset == other.asset; + if (other is ImmichLocalThumbnailProvider) { + return asset.id == other.asset.id && asset.localId == other.asset.localId; + } + return false; } @override - int get hashCode => asset.hashCode; + int get hashCode => Object.hash(asset.id, asset.localId); } diff --git a/mobile/lib/providers/image/immich_remote_image_provider.dart b/mobile/lib/providers/image/immich_remote_image_provider.dart index 9e1d8aa120..d5189fa4fc 100644 --- a/mobile/lib/providers/image/immich_remote_image_provider.dart +++ b/mobile/lib/providers/image/immich_remote_image_provider.dart @@ -57,12 +57,6 @@ class ImmichRemoteImageProvider AppSettingsEnum.loadOriginal.defaultValue, ); - /// Whether to load the preview thumbnail first or not - bool get _loadPreview => Store.get( - AppSettingsEnum.loadPreview.storeKey, - AppSettingsEnum.loadPreview.defaultValue, - ); - // Streams in each stage of the image as we ask for it Stream<ui.Codec> _codec( ImmichRemoteImageProvider key, @@ -70,21 +64,6 @@ class ImmichRemoteImageProvider ImageDecoderCallback decode, StreamController<ImageChunkEvent> chunkEvents, ) async* { - // Load a preview to the chunk events - if (_loadPreview) { - final preview = getThumbnailUrlForRemoteId( - key.assetId, - type: api.AssetMediaSize.thumbnail, - ); - - yield await ImageLoader.loadImageFromCache( - preview, - cache: cache, - decode: decode, - chunkEvents: chunkEvents, - ); - } - // Load the higher resolution version of the image final url = getThumbnailUrlForRemoteId( key.assetId, diff --git a/mobile/lib/providers/search/recently_added_asset.provider.dart b/mobile/lib/providers/search/recently_taken_asset.provider.dart similarity index 68% rename from mobile/lib/providers/search/recently_added_asset.provider.dart rename to mobile/lib/providers/search/recently_taken_asset.provider.dart index c4819d9d44..157e7c2a74 100644 --- a/mobile/lib/providers/search/recently_added_asset.provider.dart +++ b/mobile/lib/providers/search/recently_taken_asset.provider.dart @@ -2,8 +2,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/services/asset.service.dart'; -final recentlyAddedAssetProvider = FutureProvider<List<Asset>>((ref) async { +final recentlyTakenAssetProvider = FutureProvider<List<Asset>>((ref) async { final assetService = ref.read(assetServiceProvider); - return assetService.getRecentlyAddedAssets(); + return assetService.getRecentlyTakenAssets(); }); diff --git a/mobile/lib/repositories/asset.repository.dart b/mobile/lib/repositories/asset.repository.dart index cda2b25e4d..60e5d09bcd 100644 --- a/mobile/lib/repositories/asset.repository.dart +++ b/mobile/lib/repositories/asset.repository.dart @@ -225,7 +225,7 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository { } @override - Future<List<Asset>> getRecentlyAddedAssets(String userId) { + Future<List<Asset>> getRecentlyTakenAssets(String userId) { return db.assets .where() .ownerIdEqualToAnyChecksum(fastHash(userId)) diff --git a/mobile/lib/repositories/local_files_manager.repository.dart b/mobile/lib/repositories/local_files_manager.repository.dart index 522d7e7a05..c2e234d14d 100644 --- a/mobile/lib/repositories/local_files_manager.repository.dart +++ b/mobile/lib/repositories/local_files_manager.repository.dart @@ -3,21 +3,23 @@ import 'package:immich_mobile/interfaces/local_files_manager.interface.dart'; import 'package:immich_mobile/utils/local_files_manager.dart'; final localFilesManagerRepositoryProvider = - Provider((ref) => LocalFilesManagerRepository()); + Provider((ref) => const LocalFilesManagerRepository()); class LocalFilesManagerRepository implements ILocalFilesManager { + const LocalFilesManagerRepository(); + @override - Future<bool> moveToTrash(String fileName) async { - return await LocalFilesManager.moveToTrash(fileName); + Future<bool> moveToTrash(List<String> mediaUrls) async { + return await LocalFilesManager.moveToTrash(mediaUrls); } @override - Future<bool> restoreFromTrash(String fileName) async { - return await LocalFilesManager.restoreFromTrash(fileName); + Future<bool> restoreFromTrash(String fileName, int type) async { + return await LocalFilesManager.restoreFromTrash(fileName, type); } @override - Future<bool> requestManageStoragePermission() async { - return await LocalFilesManager.requestManageStoragePermission(); + Future<bool> requestManageMediaPermission() async { + return await LocalFilesManager.requestManageMediaPermission(); } } diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index 5b771d3acc..b6229b9963 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -58,7 +58,7 @@ import 'package:immich_mobile/pages/search/all_videos.page.dart'; import 'package:immich_mobile/pages/search/map/map.page.dart'; import 'package:immich_mobile/pages/search/map/map_location_picker.page.dart'; import 'package:immich_mobile/pages/search/person_result.page.dart'; -import 'package:immich_mobile/pages/search/recently_added.page.dart'; +import 'package:immich_mobile/pages/search/recently_taken.page.dart'; import 'package:immich_mobile/pages/search/search.page.dart'; import 'package:immich_mobile/pages/share_intent/share_intent.page.dart'; import 'package:immich_mobile/presentation/pages/feat_in_development.page.dart'; @@ -161,7 +161,7 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _duplicateGuard], ), AutoRoute( - page: RecentlyAddedRoute.page, + page: RecentlyTakenRoute.page, guards: [_authGuard, _duplicateGuard], ), CustomRoute( diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index fbc730fed2..40025e8597 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -1043,10 +1043,17 @@ class MapLocationPickerRouteArgs { /// generated route for /// [MapPage] -class MapRoute extends PageRouteInfo<void> { - const MapRoute({List<PageRouteInfo>? children}) - : super( +class MapRoute extends PageRouteInfo<MapRouteArgs> { + MapRoute({ + Key? key, + LatLng? initialLocation, + List<PageRouteInfo>? children, + }) : super( MapRoute.name, + args: MapRouteArgs( + key: key, + initialLocation: initialLocation, + ), initialChildren: children, ); @@ -1055,11 +1062,32 @@ class MapRoute extends PageRouteInfo<void> { static PageInfo page = PageInfo( name, builder: (data) { - return const MapPage(); + final args = + data.argsAs<MapRouteArgs>(orElse: () => const MapRouteArgs()); + return MapPage( + key: args.key, + initialLocation: args.initialLocation, + ); }, ); } +class MapRouteArgs { + const MapRouteArgs({ + this.key, + this.initialLocation, + }); + + final Key? key; + + final LatLng? initialLocation; + + @override + String toString() { + return 'MapRouteArgs{key: $key, initialLocation: $initialLocation}'; + } +} + /// generated route for /// [MemoryPage] class MemoryRoute extends PageRouteInfo<MemoryRouteArgs> { @@ -1352,10 +1380,17 @@ class PhotosRoute extends PageRouteInfo<void> { /// generated route for /// [PlacesCollectionPage] -class PlacesCollectionRoute extends PageRouteInfo<void> { - const PlacesCollectionRoute({List<PageRouteInfo>? children}) - : super( +class PlacesCollectionRoute extends PageRouteInfo<PlacesCollectionRouteArgs> { + PlacesCollectionRoute({ + Key? key, + LatLng? currentLocation, + List<PageRouteInfo>? children, + }) : super( PlacesCollectionRoute.name, + args: PlacesCollectionRouteArgs( + key: key, + currentLocation: currentLocation, + ), initialChildren: children, ); @@ -1364,26 +1399,47 @@ class PlacesCollectionRoute extends PageRouteInfo<void> { static PageInfo page = PageInfo( name, builder: (data) { - return const PlacesCollectionPage(); + final args = data.argsAs<PlacesCollectionRouteArgs>( + orElse: () => const PlacesCollectionRouteArgs()); + return PlacesCollectionPage( + key: args.key, + currentLocation: args.currentLocation, + ); }, ); } +class PlacesCollectionRouteArgs { + const PlacesCollectionRouteArgs({ + this.key, + this.currentLocation, + }); + + final Key? key; + + final LatLng? currentLocation; + + @override + String toString() { + return 'PlacesCollectionRouteArgs{key: $key, currentLocation: $currentLocation}'; + } +} + /// generated route for -/// [RecentlyAddedPage] -class RecentlyAddedRoute extends PageRouteInfo<void> { - const RecentlyAddedRoute({List<PageRouteInfo>? children}) +/// [RecentlyTakenPage] +class RecentlyTakenRoute extends PageRouteInfo<void> { + const RecentlyTakenRoute({List<PageRouteInfo>? children}) : super( - RecentlyAddedRoute.name, + RecentlyTakenRoute.name, initialChildren: children, ); - static const String name = 'RecentlyAddedRoute'; + static const String name = 'RecentlyTakenRoute'; static PageInfo page = PageInfo( name, builder: (data) { - return const RecentlyAddedPage(); + return const RecentlyTakenPage(); }, ); } diff --git a/mobile/lib/services/asset.service.dart b/mobile/lib/services/asset.service.dart index d187284d07..4bf62eca31 100644 --- a/mobile/lib/services/asset.service.dart +++ b/mobile/lib/services/asset.service.dart @@ -514,9 +514,9 @@ class AssetService { return _assetRepository.watchAsset(id, fireImmediately: fireImmediately); } - Future<List<Asset>> getRecentlyAddedAssets() { + Future<List<Asset>> getRecentlyTakenAssets() { final me = _userService.getMyUser(); - return _assetRepository.getRecentlyAddedAssets(me.id); + return _assetRepository.getRecentlyTakenAssets(me.id); } Future<List<Asset>> getMotionAssets() { diff --git a/mobile/lib/services/oauth.service.dart b/mobile/lib/services/oauth.service.dart index ddd97522f8..9a54a8d7c9 100644 --- a/mobile/lib/services/oauth.service.dart +++ b/mobile/lib/services/oauth.service.dart @@ -13,6 +13,8 @@ class OAuthService { Future<String?> getOAuthServerUrl( String serverUrl, + String state, + String codeChallenge, ) async { // Resolve API server endpoint from user provided serverUrl await _apiService.resolveAndSetEndpoint(serverUrl); @@ -22,7 +24,11 @@ class OAuthService { ); final dto = await _apiService.oAuthApi.startOAuth( - OAuthConfigDto(redirectUri: redirectUri), + OAuthConfigDto( + redirectUri: redirectUri, + state: state, + codeChallenge: codeChallenge, + ), ); final authUrl = dto?.url; @@ -31,7 +37,11 @@ class OAuthService { return authUrl; } - Future<LoginResponseDto?> oAuthLogin(String oauthUrl) async { + Future<LoginResponseDto?> oAuthLogin( + String oauthUrl, + String state, + String codeVerifier, + ) async { String result = await FlutterWebAuth2.authenticate( url: oauthUrl, callbackUrlScheme: callbackUrlScheme, @@ -49,6 +59,8 @@ class OAuthService { return await _apiService.oAuthApi.finishOAuth( OAuthCallbackDto( url: result, + state: state, + codeVerifier: codeVerifier, ), ); } diff --git a/mobile/lib/services/sync.service.dart b/mobile/lib/services/sync.service.dart index 547e49c1a0..80950d8c00 100644 --- a/mobile/lib/services/sync.service.dart +++ b/mobile/lib/services/sync.service.dart @@ -255,9 +255,12 @@ class SyncService { .where((asset) => idsToDelete.contains(asset.remoteId)) .toList(); - for (var asset in matchedAssets) { - _localFilesManager.moveToTrash(asset.fileName); - } + final mediaUrls = await Future.wait( + matchedAssets + .map((asset) => asset.local?.getMediaUrl() ?? Future.value(null)), + ); + + await _localFilesManager.moveToTrash(mediaUrls.nonNulls.toList()); } /// Deletes remote-only assets, updates merged assets to be local-only @@ -819,13 +822,29 @@ class SyncService { } Future<void> _toggleTrashStatusForAssets(List<Asset> assetsList) async { - for (var asset in assetsList) { + final trashMediaUrls = <String>[]; + + for (final asset in assetsList) { if (asset.isTrashed) { - _localFilesManager.moveToTrash(asset.fileName); + final mediaUrl = await asset.local?.getMediaUrl(); + if (mediaUrl == null) { + _log.warning( + "Failed to get media URL for asset ${asset.name} while moving to trash", + ); + continue; + } + trashMediaUrls.add(mediaUrl); } else { - _localFilesManager.restoreFromTrash(asset.fileName); + await _localFilesManager.restoreFromTrash( + asset.fileName, + asset.type.index, + ); } } + + if (trashMediaUrls.isNotEmpty) { + await _localFilesManager.moveToTrash(trashMediaUrls); + } } /// Inserts or updates the assets in the database with their ExifInfo (if any) diff --git a/mobile/lib/utils/local_files_manager.dart b/mobile/lib/utils/local_files_manager.dart index da9308c3cf..a4cf41a6e6 100644 --- a/mobile/lib/utils/local_files_manager.dart +++ b/mobile/lib/utils/local_files_manager.dart @@ -1,38 +1,37 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; +import 'package:logging/logging.dart'; -class LocalFilesManager { +abstract final class LocalFilesManager { + static final Logger _logger = Logger('LocalFilesManager'); static const MethodChannel _channel = MethodChannel('file_trash'); - static Future<bool> moveToTrash(String fileName) async { + static Future<bool> moveToTrash(List<String> mediaUrls) async { try { - final bool success = - await _channel.invokeMethod('moveToTrash', {'fileName': fileName}); - return success; - } on PlatformException catch (e) { - debugPrint('Error moving to trash: ${e.message}'); + return await _channel + .invokeMethod('moveToTrash', {'mediaUrls': mediaUrls}); + } catch (e, s) { + _logger.warning('Error moving file to trash', e, s); return false; } } - static Future<bool> restoreFromTrash(String fileName) async { + static Future<bool> restoreFromTrash(String fileName, int type) async { try { - final bool success = await _channel - .invokeMethod('restoreFromTrash', {'fileName': fileName}); - return success; - } on PlatformException catch (e) { - debugPrint('Error restoring file: ${e.message}'); + return await _channel.invokeMethod( + 'restoreFromTrash', + {'fileName': fileName, 'type': type}, + ); + } catch (e, s) { + _logger.warning('Error restore file from trash', e, s); return false; } } - static Future<bool> requestManageStoragePermission() async { + static Future<bool> requestManageMediaPermission() async { try { - final bool success = - await _channel.invokeMethod('requestManageStoragePermission'); - return success; - } on PlatformException catch (e) { - debugPrint('Error requesting permission: ${e.message}'); + return await _channel.invokeMethod('requestManageMediaPermission'); + } catch (e, s) { + _logger.warning('Error requesting manage media permission', e, s); return false; } } diff --git a/mobile/lib/utils/map_utils.dart b/mobile/lib/utils/map_utils.dart index 44f7ebf271..df1ff28d8f 100644 --- a/mobile/lib/utils/map_utils.dart +++ b/mobile/lib/utils/map_utils.dart @@ -64,12 +64,13 @@ class MapUtils { 'features': markers.map(_addFeature).toList(), }; - static Future<(Position?, LocationPermission?)> checkPermAndGetLocation( - BuildContext context, - ) async { + static Future<(Position?, LocationPermission?)> checkPermAndGetLocation({ + required BuildContext context, + bool silent = false, + }) async { try { bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); - if (!serviceEnabled) { + if (!serviceEnabled && !silent) { showDialog( context: context, builder: (context) => _LocationServiceDisabledDialog(), @@ -80,7 +81,7 @@ class MapUtils { LocationPermission permission = await Geolocator.checkPermission(); bool shouldRequestPermission = false; - if (permission == LocationPermission.denied) { + if (permission == LocationPermission.denied && !silent) { shouldRequestPermission = await showDialog( context: context, builder: (context) => _LocationPermissionDisabledDialog(), @@ -94,15 +95,19 @@ class MapUtils { permission == LocationPermission.deniedForever) { // Open app settings only if you did not request for permission before if (permission == LocationPermission.deniedForever && - !shouldRequestPermission) { + !shouldRequestPermission && + !silent) { await Geolocator.openAppSettings(); } return (null, LocationPermission.deniedForever); } Position currentUserLocation = await Geolocator.getCurrentPosition( - desiredAccuracy: LocationAccuracy.medium, - timeLimit: const Duration(seconds: 5), + locationSettings: const LocationSettings( + accuracy: LocationAccuracy.high, + distanceFilter: 0, + timeLimit: Duration(seconds: 5), + ), ); return (currentUserLocation, null); } catch (error, stack) { diff --git a/mobile/lib/utils/migration.dart b/mobile/lib/utils/migration.dart index bebd7a027b..6a09f79ce2 100644 --- a/mobile/lib/utils/migration.dart +++ b/mobile/lib/utils/migration.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'dart:io'; -import 'package:flutter/widgets.dart'; +import 'package:flutter/foundation.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/android_device_asset.entity.dart'; @@ -17,6 +17,8 @@ import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/utils/diff.dart'; import 'package:isar/isar.dart'; +// ignore: import_rule_photo_manager +import 'package:photo_manager/photo_manager.dart'; const int targetVersion = 10; @@ -69,14 +71,45 @@ Future<void> _migrateDeviceAsset(Isar db) async { : (await db.iOSDeviceAssets.where().findAll()) .map((i) => _DeviceAsset(assetId: i.id, hash: i.hash)) .toList(); - final localAssets = (await db.assets - .where() - .anyOf(ids, (query, id) => query.localIdEqualTo(id.assetId)) - .findAll()) - .map((a) => _DeviceAsset(assetId: a.localId!, dateTime: a.fileModifiedAt)) - .toList(); - debugPrint("Device Asset Ids length - ${ids.length}"); - debugPrint("Local Asset Ids length - ${localAssets.length}"); + + final PermissionState ps = await PhotoManager.requestPermissionExtend(); + if (!ps.hasAccess) { + if (kDebugMode) { + debugPrint( + "[MIGRATION] Photo library permission not granted. Skipping device asset migration.", + ); + } + + return; + } + + List<_DeviceAsset> localAssets = []; + final List<AssetPathEntity> paths = + await PhotoManager.getAssetPathList(onlyAll: true); + + if (paths.isEmpty) { + localAssets = (await db.assets + .where() + .anyOf(ids, (query, id) => query.localIdEqualTo(id.assetId)) + .findAll()) + .map( + (a) => _DeviceAsset(assetId: a.localId!, dateTime: a.fileModifiedAt), + ) + .toList(); + } else { + final AssetPathEntity albumWithAll = paths.first; + final int assetCount = await albumWithAll.assetCountAsync; + + final List<AssetEntity> allDeviceAssets = + await albumWithAll.getAssetListRange(start: 0, end: assetCount); + + localAssets = allDeviceAssets + .map((a) => _DeviceAsset(assetId: a.id, dateTime: a.modifiedDateTime)) + .toList(); + } + + debugPrint("[MIGRATION] Device Asset Ids length - ${ids.length}"); + debugPrint("[MIGRATION] Local Asset Ids length - ${localAssets.length}"); ids.sort((a, b) => a.assetId.compareTo(b.assetId)); localAssets.sort((a, b) => a.assetId.compareTo(b.assetId)); final List<DeviceAssetEntity> toAdd = []; @@ -95,15 +128,27 @@ Future<void> _migrateDeviceAsset(Isar db) async { return false; }, onlyFirst: (deviceAsset) { - debugPrint( - 'DeviceAsset not found in local assets: ${deviceAsset.assetId}', - ); + if (kDebugMode) { + debugPrint( + '[MIGRATION] Local asset not found in DeviceAsset: ${deviceAsset.assetId}', + ); + } }, onlySecond: (asset) { - debugPrint('Local asset not found in DeviceAsset: ${asset.assetId}'); + if (kDebugMode) { + debugPrint( + '[MIGRATION] Local asset not found in DeviceAsset: ${asset.assetId}', + ); + } }, ); - debugPrint("Total number of device assets migrated - ${toAdd.length}"); + + if (kDebugMode) { + debugPrint( + "[MIGRATION] Total number of device assets migrated - ${toAdd.length}", + ); + } + await db.writeTxn(() async { await db.deviceAssetEntitys.putAll(toAdd); }); diff --git a/mobile/lib/utils/selection_handlers.dart b/mobile/lib/utils/selection_handlers.dart index 4a9fcc8c99..c63d819153 100644 --- a/mobile/lib/utils/selection_handlers.dart +++ b/mobile/lib/utils/selection_handlers.dart @@ -8,6 +8,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/services/asset.service.dart'; import 'package:immich_mobile/services/share.service.dart'; +import 'package:immich_mobile/utils/translation.dart'; import 'package:immich_mobile/widgets/common/date_time_picker.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:immich_mobile/widgets/common/location_picker.dart'; @@ -57,12 +58,13 @@ Future<void> handleArchiveAssets( .read(assetProvider.notifier) .toggleArchive(selection, shouldArchive); - final assetOrAssets = selection.length > 1 ? 'assets' : 'asset'; - final archiveOrLibrary = shouldArchive ? 'archive' : 'library'; + final message = shouldArchive + ? t('moved_to_archive', {'count': selection.length}) + : t('moved_to_library', {'count': selection.length}); if (context.mounted) { ImmichToast.show( context: context, - msg: 'Moved ${selection.length} $assetOrAssets to $archiveOrLibrary', + msg: message, gravity: toastGravity, ); } diff --git a/mobile/lib/utils/translation.dart b/mobile/lib/utils/translation.dart new file mode 100644 index 0000000000..461e88ead7 --- /dev/null +++ b/mobile/lib/utils/translation.dart @@ -0,0 +1,14 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:intl/message_format.dart'; + +String t(String key, [Map<String, Object>? args]) { + try { + String message = key.tr(); + if (args != null) { + return MessageFormat(message).format(args); + } + return message; + } catch (e) { + return key; + } +} diff --git a/mobile/lib/widgets/asset_grid/immich_asset_grid.dart b/mobile/lib/widgets/asset_grid/immich_asset_grid.dart index 2ec01e871f..da4c47e466 100644 --- a/mobile/lib/widgets/asset_grid/immich_asset_grid.dart +++ b/mobile/lib/widgets/asset_grid/immich_asset_grid.dart @@ -97,6 +97,7 @@ class ImmichAssetGrid extends HookConsumerWidget { ); if (7 - scaleFactor.value.toInt() != perRow.value) { perRow.value = 7 - scaleFactor.value.toInt(); + settings.setSetting(AppSettingsEnum.tilesPerRow, perRow.value); } }; }), diff --git a/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart b/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart index a7141c33b2..060898e270 100644 --- a/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart +++ b/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart @@ -755,7 +755,7 @@ class _MonthTitle extends StatelessWidget { key: Key("month-$title"), padding: const EdgeInsets.only(left: 12.0, top: 24.0), child: Text( - title, + toBeginningOfSentenceCase(title, context.locale.languageCode), style: const TextStyle( fontSize: 26, fontWeight: FontWeight.w500, @@ -786,7 +786,7 @@ class _Title extends StatelessWidget { @override Widget build(BuildContext context) { return GroupDividerTitle( - text: title, + text: toBeginningOfSentenceCase(title, context.locale.languageCode), multiselectEnabled: selectionActive, onSelect: () => selectAssets(assets), onDeselect: () => deselectAssets(assets), diff --git a/mobile/lib/widgets/common/immich_logo.dart b/mobile/lib/widgets/common/immich_logo.dart index 9f7725aa12..43987878cb 100644 --- a/mobile/lib/widgets/common/immich_logo.dart +++ b/mobile/lib/widgets/common/immich_logo.dart @@ -12,14 +12,11 @@ class ImmichLogo extends StatelessWidget { @override Widget build(BuildContext context) { - return Hero( - tag: heroTag, - child: Image( - image: const AssetImage('assets/immich-logo.png'), - width: size, - filterQuality: FilterQuality.high, - isAntiAlias: true, - ), + return Image( + image: const AssetImage('assets/immich-logo.png'), + width: size, + filterQuality: FilterQuality.high, + isAntiAlias: true, ); } } diff --git a/mobile/lib/widgets/common/immich_thumbnail.dart b/mobile/lib/widgets/common/immich_thumbnail.dart index 2ebead0083..35729ead7b 100644 --- a/mobile/lib/widgets/common/immich_thumbnail.dart +++ b/mobile/lib/widgets/common/immich_thumbnail.dart @@ -1,7 +1,7 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/providers/image/immich_local_thumbnail_provider.dart'; import 'package:immich_mobile/providers/image/immich_remote_thumbnail_provider.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; @@ -9,8 +9,9 @@ import 'package:immich_mobile/utils/hooks/blurhash_hook.dart'; import 'package:immich_mobile/widgets/common/immich_image.dart'; import 'package:immich_mobile/widgets/common/thumbhash_placeholder.dart'; import 'package:octo_image/octo_image.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; -class ImmichThumbnail extends HookWidget { +class ImmichThumbnail extends HookConsumerWidget { const ImmichThumbnail({ this.asset, this.width = 250, @@ -31,6 +32,7 @@ class ImmichThumbnail extends HookWidget { static ImageProvider imageProvider({ Asset? asset, String? assetId, + String? userId, int thumbnailSize = 256, }) { if (asset == null && assetId == null) { @@ -48,6 +50,7 @@ class ImmichThumbnail extends HookWidget { asset: asset, height: thumbnailSize, width: thumbnailSize, + userId: userId, ); } else { return ImmichRemoteThumbnailProvider( @@ -59,8 +62,10 @@ class ImmichThumbnail extends HookWidget { } @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { Uint8List? blurhash = useBlurHashRef(asset).value; + final userId = ref.watch(currentUserProvider)?.id; + if (asset == null) { return Container( color: Colors.grey, @@ -79,6 +84,7 @@ class ImmichThumbnail extends HookWidget { octoSet: blurHashOrPlaceholder(blurhash), image: ImmichThumbnail.imageProvider( asset: asset, + userId: userId, ), width: width, height: height, diff --git a/mobile/lib/widgets/forms/login/login_form.dart b/mobile/lib/widgets/forms/login/login_form.dart index 7af52b413d..5374d1ef33 100644 --- a/mobile/lib/widgets/forms/login/login_form.dart +++ b/mobile/lib/widgets/forms/login/login_form.dart @@ -1,6 +1,9 @@ +import 'dart:convert'; import 'dart:io'; +import 'dart:math'; import 'package:auto_route/auto_route.dart'; +import 'package:crypto/crypto.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -203,13 +206,51 @@ class LoginForm extends HookConsumerWidget { } } + String generateRandomString(int length) { + const chars = + 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890'; + final random = Random.secure(); + return String.fromCharCodes( + Iterable.generate( + length, + (_) => chars.codeUnitAt(random.nextInt(chars.length)), + ), + ); + } + + List<int> randomBytes(int length) { + final random = Random.secure(); + return List<int>.generate(length, (i) => random.nextInt(256)); + } + + /// Per specification, the code verifier must be 43-128 characters long + /// and consist of characters [A-Z, a-z, 0-9, "-", ".", "_", "~"] + /// https://datatracker.ietf.org/doc/html/rfc7636#section-4.1 + String randomCodeVerifier() { + return base64Url.encode(randomBytes(42)); + } + + Future<String> generatePKCECodeChallenge(String codeVerifier) async { + var bytes = utf8.encode(codeVerifier); + var digest = sha256.convert(bytes); + return base64Url.encode(digest.bytes).replaceAll('=', ''); + } + oAuthLogin() async { var oAuthService = ref.watch(oAuthServiceProvider); String? oAuthServerUrl; + final state = generateRandomString(32); + + final codeVerifier = randomCodeVerifier(); + final codeChallenge = await generatePKCECodeChallenge(codeVerifier); + try { - oAuthServerUrl = await oAuthService - .getOAuthServerUrl(sanitizeUrl(serverEndpointController.text)); + oAuthServerUrl = await oAuthService.getOAuthServerUrl( + sanitizeUrl(serverEndpointController.text), + state, + codeChallenge, + ); isLoading.value = true; @@ -230,8 +271,11 @@ class LoginForm extends HookConsumerWidget { if (oAuthServerUrl != null) { try { - final loginResponseDto = - await oAuthService.oAuthLogin(oAuthServerUrl); + final loginResponseDto = await oAuthService.oAuthLogin( + oAuthServerUrl, + state, + codeVerifier, + ); if (loginResponseDto == null) { return; diff --git a/mobile/lib/widgets/map/map_asset_grid.dart b/mobile/lib/widgets/map/map_asset_grid.dart index 18003cf293..a9ddc86df9 100644 --- a/mobile/lib/widgets/map/map_asset_grid.dart +++ b/mobile/lib/widgets/map/map_asset_grid.dart @@ -46,12 +46,39 @@ class MapAssetGrid extends HookConsumerWidget { final gridScrollThrottler = useThrottler(interval: const Duration(milliseconds: 300)); + // Add a cache for assets we've already loaded + final assetCache = useRef<Map<String, Asset>>({}); + void handleMapEvents(MapEvent event) async { if (event is MapAssetsInBoundsUpdated) { - assetsInBounds.value = await ref - .read(dbProvider) - .assets - .getAllByRemoteId(event.assetRemoteIds); + final assetIds = event.assetRemoteIds; + final missingIds = <String>[]; + final currentAssets = <Asset>[]; + + for (final id in assetIds) { + final asset = assetCache.value[id]; + if (asset != null) { + currentAssets.add(asset); + } else { + missingIds.add(id); + } + } + + // Only fetch missing assets + if (missingIds.isNotEmpty) { + final newAssets = + await ref.read(dbProvider).assets.getAllByRemoteId(missingIds); + + // Add new assets to cache and current list + for (final asset in newAssets) { + if (asset.remoteId != null) { + assetCache.value[asset.remoteId!] = asset; + currentAssets.add(asset); + } + } + } + + assetsInBounds.value = currentAssets; return; } } @@ -124,7 +151,7 @@ class MapAssetGrid extends HookConsumerWidget { alignment: Alignment.bottomCenter, child: FractionallySizedBox( // Place it just below the drag handle - heightFactor: 0.80, + heightFactor: 0.87, child: assetsInBounds.value.isNotEmpty ? ref .watch(assetsTimelineProvider(assetsInBounds.value)) @@ -251,8 +278,18 @@ class _MapSheetDragRegion extends StatelessWidget { const SizedBox(height: 15), const CustomDraggingHandle(), const SizedBox(height: 15), - Text(assetsInBoundsText, style: context.textTheme.bodyLarge), - const Divider(height: 35), + Center( + child: Text( + assetsInBoundsText, + style: TextStyle( + fontSize: 20, + color: context.textTheme.displayLarge?.color + ?.withValues(alpha: 0.75), + fontWeight: FontWeight.w500, + ), + ), + ), + const SizedBox(height: 8), ], ), ValueListenableBuilder( @@ -260,14 +297,14 @@ class _MapSheetDragRegion extends StatelessWidget { builder: (_, value, __) => Visibility( visible: value != null, child: Positioned( - right: 15, - top: 15, + right: 18, + top: 24, child: IconButton( icon: Icon( Icons.map_outlined, color: context.textTheme.displayLarge?.color, ), - iconSize: 20, + iconSize: 24, tooltip: 'Zoom to bounds', onPressed: () => onZoomToAsset?.call(value!), ), diff --git a/mobile/lib/widgets/search/search_map_thumbnail.dart b/mobile/lib/widgets/search/search_map_thumbnail.dart index b4a12ab826..78af8f936b 100644 --- a/mobile/lib/widgets/search/search_map_thumbnail.dart +++ b/mobile/lib/widgets/search/search_map_thumbnail.dart @@ -20,7 +20,7 @@ class SearchMapThumbnail extends StatelessWidget { return ThumbnailWithInfoContainer( label: 'search_page_your_map'.tr(), onTap: () { - context.pushRoute(const MapRoute()); + context.pushRoute(MapRoute()); }, child: IgnorePointer( child: MapThumbnail( diff --git a/mobile/lib/widgets/settings/advanced_settings.dart b/mobile/lib/widgets/settings/advanced_settings.dart index 98c8728298..d65186a191 100644 --- a/mobile/lib/widgets/settings/advanced_settings.dart +++ b/mobile/lib/widgets/settings/advanced_settings.dart @@ -49,7 +49,7 @@ class AdvancedSettings extends HookConsumerWidget { DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; int sdkVersion = androidInfo.version.sdkInt; - return sdkVersion >= 30; + return sdkVersion >= 31; } return false; } @@ -74,7 +74,7 @@ class AdvancedSettings extends HookConsumerWidget { if (value) { final result = await ref .read(localFilesManagerRepositoryProvider) - .requestManageStoragePermission(); + .requestManageMediaPermission(); manageLocalMediaAndroid.value = result; } }, diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 0ae07e9efd..a4fbbf371a 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.131.3 +- API version: 1.132.3 - Generator version: 7.8.0 - Build package: org.openapitools.codegen.languages.DartClientCodegen @@ -122,9 +122,6 @@ Class | Method | HTTP request | Description *FacesApi* | [**deleteFace**](doc//FacesApi.md#deleteface) | **DELETE** /faces/{id} | *FacesApi* | [**getFaces**](doc//FacesApi.md#getfaces) | **GET** /faces | *FacesApi* | [**reassignFacesById**](doc//FacesApi.md#reassignfacesbyid) | **PUT** /faces/{id} | -*FileReportsApi* | [**fixAuditFiles**](doc//FileReportsApi.md#fixauditfiles) | **POST** /reports/fix | -*FileReportsApi* | [**getAuditFiles**](doc//FileReportsApi.md#getauditfiles) | **GET** /reports | -*FileReportsApi* | [**getFileChecksums**](doc//FileReportsApi.md#getfilechecksums) | **POST** /reports/checksum | *JobsApi* | [**createJob**](doc//JobsApi.md#createjob) | **POST** /jobs | *JobsApi* | [**getAllJobsStatus**](doc//JobsApi.md#getalljobsstatus) | **GET** /jobs | *JobsApi* | [**sendJobCommand**](doc//JobsApi.md#sendjobcommand) | **PUT** /jobs/{id} | @@ -145,8 +142,15 @@ Class | Method | HTTP request | Description *MemoriesApi* | [**removeMemoryAssets**](doc//MemoriesApi.md#removememoryassets) | **DELETE** /memories/{id}/assets | *MemoriesApi* | [**searchMemories**](doc//MemoriesApi.md#searchmemories) | **GET** /memories | *MemoriesApi* | [**updateMemory**](doc//MemoriesApi.md#updatememory) | **PUT** /memories/{id} | -*NotificationsApi* | [**getNotificationTemplate**](doc//NotificationsApi.md#getnotificationtemplate) | **POST** /notifications/templates/{name} | -*NotificationsApi* | [**sendTestEmail**](doc//NotificationsApi.md#sendtestemail) | **POST** /notifications/test-email | +*NotificationsApi* | [**deleteNotification**](doc//NotificationsApi.md#deletenotification) | **DELETE** /notifications/{id} | +*NotificationsApi* | [**deleteNotifications**](doc//NotificationsApi.md#deletenotifications) | **DELETE** /notifications | +*NotificationsApi* | [**getNotification**](doc//NotificationsApi.md#getnotification) | **GET** /notifications/{id} | +*NotificationsApi* | [**getNotifications**](doc//NotificationsApi.md#getnotifications) | **GET** /notifications | +*NotificationsApi* | [**updateNotification**](doc//NotificationsApi.md#updatenotification) | **PUT** /notifications/{id} | +*NotificationsApi* | [**updateNotifications**](doc//NotificationsApi.md#updatenotifications) | **PUT** /notifications | +*NotificationsAdminApi* | [**createNotification**](doc//NotificationsAdminApi.md#createnotification) | **POST** /admin/notifications | +*NotificationsAdminApi* | [**getNotificationTemplateAdmin**](doc//NotificationsAdminApi.md#getnotificationtemplateadmin) | **POST** /admin/notifications/templates/{name} | +*NotificationsAdminApi* | [**sendTestEmailAdmin**](doc//NotificationsAdminApi.md#sendtestemailadmin) | **POST** /admin/notifications/test-email | *OAuthApi* | [**finishOAuth**](doc//OAuthApi.md#finishoauth) | **POST** /oauth/callback | *OAuthApi* | [**linkOAuthAccount**](doc//OAuthApi.md#linkoauthaccount) | **POST** /oauth/link | *OAuthApi* | [**redirectOAuthToMobile**](doc//OAuthApi.md#redirectoauthtomobile) | **GET** /oauth/mobile-redirect | @@ -300,7 +304,6 @@ Class | Method | HTTP request | Description - [AssetStatsResponseDto](doc//AssetStatsResponseDto.md) - [AssetTypeEnum](doc//AssetTypeEnum.md) - [AudioCodec](doc//AudioCodec.md) - - [AvatarResponse](doc//AvatarResponse.md) - [AvatarUpdate](doc//AvatarUpdate.md) - [BulkIdResponseDto](doc//BulkIdResponseDto.md) - [BulkIdsDto](doc//BulkIdsDto.md) @@ -326,11 +329,6 @@ Class | Method | HTTP request | Description - [ExifResponseDto](doc//ExifResponseDto.md) - [FaceDto](doc//FaceDto.md) - [FacialRecognitionConfig](doc//FacialRecognitionConfig.md) - - [FileChecksumDto](doc//FileChecksumDto.md) - - [FileChecksumResponseDto](doc//FileChecksumResponseDto.md) - - [FileReportDto](doc//FileReportDto.md) - - [FileReportFixDto](doc//FileReportFixDto.md) - - [FileReportItemDto](doc//FileReportItemDto.md) - [FoldersResponse](doc//FoldersResponse.md) - [FoldersUpdate](doc//FoldersUpdate.md) - [ImageFormat](doc//ImageFormat.md) @@ -361,14 +359,20 @@ Class | Method | HTTP request | Description - [MemoryUpdateDto](doc//MemoryUpdateDto.md) - [MergePersonDto](doc//MergePersonDto.md) - [MetadataSearchDto](doc//MetadataSearchDto.md) + - [NotificationCreateDto](doc//NotificationCreateDto.md) + - [NotificationDeleteAllDto](doc//NotificationDeleteAllDto.md) + - [NotificationDto](doc//NotificationDto.md) + - [NotificationLevel](doc//NotificationLevel.md) + - [NotificationType](doc//NotificationType.md) + - [NotificationUpdateAllDto](doc//NotificationUpdateAllDto.md) + - [NotificationUpdateDto](doc//NotificationUpdateDto.md) - [OAuthAuthorizeResponseDto](doc//OAuthAuthorizeResponseDto.md) - [OAuthCallbackDto](doc//OAuthCallbackDto.md) - [OAuthConfigDto](doc//OAuthConfigDto.md) + - [OAuthTokenEndpointAuthMethod](doc//OAuthTokenEndpointAuthMethod.md) - [OnThisDayDto](doc//OnThisDayDto.md) - [PartnerDirection](doc//PartnerDirection.md) - [PartnerResponseDto](doc//PartnerResponseDto.md) - - [PathEntityType](doc//PathEntityType.md) - - [PathType](doc//PathType.md) - [PeopleResponse](doc//PeopleResponse.md) - [PeopleResponseDto](doc//PeopleResponseDto.md) - [PeopleUpdate](doc//PeopleUpdate.md) diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index 3986362c96..78fa31691d 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -39,12 +39,12 @@ part 'api/deprecated_api.dart'; part 'api/download_api.dart'; part 'api/duplicates_api.dart'; part 'api/faces_api.dart'; -part 'api/file_reports_api.dart'; part 'api/jobs_api.dart'; part 'api/libraries_api.dart'; part 'api/map_api.dart'; part 'api/memories_api.dart'; part 'api/notifications_api.dart'; +part 'api/notifications_admin_api.dart'; part 'api/o_auth_api.dart'; part 'api/partners_api.dart'; part 'api/people_api.dart'; @@ -107,7 +107,6 @@ part 'model/asset_stack_response_dto.dart'; part 'model/asset_stats_response_dto.dart'; part 'model/asset_type_enum.dart'; part 'model/audio_codec.dart'; -part 'model/avatar_response.dart'; part 'model/avatar_update.dart'; part 'model/bulk_id_response_dto.dart'; part 'model/bulk_ids_dto.dart'; @@ -133,11 +132,6 @@ part 'model/email_notifications_update.dart'; part 'model/exif_response_dto.dart'; part 'model/face_dto.dart'; part 'model/facial_recognition_config.dart'; -part 'model/file_checksum_dto.dart'; -part 'model/file_checksum_response_dto.dart'; -part 'model/file_report_dto.dart'; -part 'model/file_report_fix_dto.dart'; -part 'model/file_report_item_dto.dart'; part 'model/folders_response.dart'; part 'model/folders_update.dart'; part 'model/image_format.dart'; @@ -168,14 +162,20 @@ part 'model/memory_type.dart'; part 'model/memory_update_dto.dart'; part 'model/merge_person_dto.dart'; part 'model/metadata_search_dto.dart'; +part 'model/notification_create_dto.dart'; +part 'model/notification_delete_all_dto.dart'; +part 'model/notification_dto.dart'; +part 'model/notification_level.dart'; +part 'model/notification_type.dart'; +part 'model/notification_update_all_dto.dart'; +part 'model/notification_update_dto.dart'; part 'model/o_auth_authorize_response_dto.dart'; part 'model/o_auth_callback_dto.dart'; part 'model/o_auth_config_dto.dart'; +part 'model/o_auth_token_endpoint_auth_method.dart'; part 'model/on_this_day_dto.dart'; part 'model/partner_direction.dart'; part 'model/partner_response_dto.dart'; -part 'model/path_entity_type.dart'; -part 'model/path_type.dart'; part 'model/people_response.dart'; part 'model/people_response_dto.dart'; part 'model/people_update.dart'; diff --git a/mobile/openapi/lib/api/file_reports_api.dart b/mobile/openapi/lib/api/notifications_admin_api.dart similarity index 51% rename from mobile/openapi/lib/api/file_reports_api.dart rename to mobile/openapi/lib/api/notifications_admin_api.dart index 73b3feaedb..409683a950 100644 --- a/mobile/openapi/lib/api/file_reports_api.dart +++ b/mobile/openapi/lib/api/notifications_admin_api.dart @@ -11,21 +11,21 @@ part of openapi.api; -class FileReportsApi { - FileReportsApi([ApiClient? apiClient]) : apiClient = apiClient ?? defaultApiClient; +class NotificationsAdminApi { + NotificationsAdminApi([ApiClient? apiClient]) : apiClient = apiClient ?? defaultApiClient; final ApiClient apiClient; - /// Performs an HTTP 'POST /reports/fix' operation and returns the [Response]. + /// Performs an HTTP 'POST /admin/notifications' operation and returns the [Response]. /// Parameters: /// - /// * [FileReportFixDto] fileReportFixDto (required): - Future<Response> fixAuditFilesWithHttpInfo(FileReportFixDto fileReportFixDto,) async { + /// * [NotificationCreateDto] notificationCreateDto (required): + Future<Response> createNotificationWithHttpInfo(NotificationCreateDto notificationCreateDto,) async { // ignore: prefer_const_declarations - final apiPath = r'/reports/fix'; + final apiPath = r'/admin/notifications'; // ignore: prefer_final_locals - Object? postBody = fileReportFixDto; + Object? postBody = notificationCreateDto; final queryParams = <QueryParam>[]; final headerParams = <String, String>{}; @@ -47,42 +47,9 @@ class FileReportsApi { /// Parameters: /// - /// * [FileReportFixDto] fileReportFixDto (required): - Future<void> fixAuditFiles(FileReportFixDto fileReportFixDto,) async { - final response = await fixAuditFilesWithHttpInfo(fileReportFixDto,); - if (response.statusCode >= HttpStatus.badRequest) { - throw ApiException(response.statusCode, await _decodeBodyBytes(response)); - } - } - - /// Performs an HTTP 'GET /reports' operation and returns the [Response]. - Future<Response> getAuditFilesWithHttpInfo() async { - // ignore: prefer_const_declarations - final apiPath = r'/reports'; - - // ignore: prefer_final_locals - Object? postBody; - - final queryParams = <QueryParam>[]; - final headerParams = <String, String>{}; - final formParams = <String, String>{}; - - const contentTypes = <String>[]; - - - return apiClient.invokeAPI( - apiPath, - 'GET', - queryParams, - postBody, - headerParams, - formParams, - contentTypes.isEmpty ? null : contentTypes.first, - ); - } - - Future<FileReportDto?> getAuditFiles() async { - final response = await getAuditFilesWithHttpInfo(); + /// * [NotificationCreateDto] notificationCreateDto (required): + Future<NotificationDto?> createNotification(NotificationCreateDto notificationCreateDto,) async { + final response = await createNotificationWithHttpInfo(notificationCreateDto,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -90,22 +57,25 @@ class FileReportsApi { // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" // FormatException when trying to decode an empty string. if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { - return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'FileReportDto',) as FileReportDto; + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'NotificationDto',) as NotificationDto; } return null; } - /// Performs an HTTP 'POST /reports/checksum' operation and returns the [Response]. + /// Performs an HTTP 'POST /admin/notifications/templates/{name}' operation and returns the [Response]. /// Parameters: /// - /// * [FileChecksumDto] fileChecksumDto (required): - Future<Response> getFileChecksumsWithHttpInfo(FileChecksumDto fileChecksumDto,) async { + /// * [String] name (required): + /// + /// * [TemplateDto] templateDto (required): + Future<Response> getNotificationTemplateAdminWithHttpInfo(String name, TemplateDto templateDto,) async { // ignore: prefer_const_declarations - final apiPath = r'/reports/checksum'; + final apiPath = r'/admin/notifications/templates/{name}' + .replaceAll('{name}', name); // ignore: prefer_final_locals - Object? postBody = fileChecksumDto; + Object? postBody = templateDto; final queryParams = <QueryParam>[]; final headerParams = <String, String>{}; @@ -127,9 +97,11 @@ class FileReportsApi { /// Parameters: /// - /// * [FileChecksumDto] fileChecksumDto (required): - Future<List<FileChecksumResponseDto>?> getFileChecksums(FileChecksumDto fileChecksumDto,) async { - final response = await getFileChecksumsWithHttpInfo(fileChecksumDto,); + /// * [String] name (required): + /// + /// * [TemplateDto] templateDto (required): + Future<TemplateResponseDto?> getNotificationTemplateAdmin(String name, TemplateDto templateDto,) async { + final response = await getNotificationTemplateAdminWithHttpInfo(name, templateDto,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -137,11 +109,55 @@ class FileReportsApi { // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" // FormatException when trying to decode an empty string. if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { - final responseBody = await _decodeBodyBytes(response); - return (await apiClient.deserializeAsync(responseBody, 'List<FileChecksumResponseDto>') as List) - .cast<FileChecksumResponseDto>() - .toList(growable: false); + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'TemplateResponseDto',) as TemplateResponseDto; + + } + return null; + } + /// Performs an HTTP 'POST /admin/notifications/test-email' operation and returns the [Response]. + /// Parameters: + /// + /// * [SystemConfigSmtpDto] systemConfigSmtpDto (required): + Future<Response> sendTestEmailAdminWithHttpInfo(SystemConfigSmtpDto systemConfigSmtpDto,) async { + // ignore: prefer_const_declarations + final apiPath = r'/admin/notifications/test-email'; + + // ignore: prefer_final_locals + Object? postBody = systemConfigSmtpDto; + + final queryParams = <QueryParam>[]; + final headerParams = <String, String>{}; + final formParams = <String, String>{}; + + const contentTypes = <String>['application/json']; + + + return apiClient.invokeAPI( + apiPath, + 'POST', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [SystemConfigSmtpDto] systemConfigSmtpDto (required): + Future<TestEmailResponseDto?> sendTestEmailAdmin(SystemConfigSmtpDto systemConfigSmtpDto,) async { + final response = await sendTestEmailAdminWithHttpInfo(systemConfigSmtpDto,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'TestEmailResponseDto',) as TestEmailResponseDto; + } return null; } diff --git a/mobile/openapi/lib/api/notifications_api.dart b/mobile/openapi/lib/api/notifications_api.dart index 518a1baa4a..501cc70a29 100644 --- a/mobile/openapi/lib/api/notifications_api.dart +++ b/mobile/openapi/lib/api/notifications_api.dart @@ -16,30 +16,28 @@ class NotificationsApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /notifications/templates/{name}' operation and returns the [Response]. + /// Performs an HTTP 'DELETE /notifications/{id}' operation and returns the [Response]. /// Parameters: /// - /// * [String] name (required): - /// - /// * [TemplateDto] templateDto (required): - Future<Response> getNotificationTemplateWithHttpInfo(String name, TemplateDto templateDto,) async { + /// * [String] id (required): + Future<Response> deleteNotificationWithHttpInfo(String id,) async { // ignore: prefer_const_declarations - final apiPath = r'/notifications/templates/{name}' - .replaceAll('{name}', name); + final apiPath = r'/notifications/{id}' + .replaceAll('{id}', id); // ignore: prefer_final_locals - Object? postBody = templateDto; + Object? postBody; final queryParams = <QueryParam>[]; final headerParams = <String, String>{}; final formParams = <String, String>{}; - const contentTypes = <String>['application/json']; + const contentTypes = <String>[]; return apiClient.invokeAPI( apiPath, - 'POST', + 'DELETE', queryParams, postBody, headerParams, @@ -50,34 +48,24 @@ class NotificationsApi { /// Parameters: /// - /// * [String] name (required): - /// - /// * [TemplateDto] templateDto (required): - Future<TemplateResponseDto?> getNotificationTemplate(String name, TemplateDto templateDto,) async { - final response = await getNotificationTemplateWithHttpInfo(name, templateDto,); + /// * [String] id (required): + Future<void> deleteNotification(String id,) async { + final response = await deleteNotificationWithHttpInfo(id,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } - // When a remote server returns no body with a status of 204, we shall not decode it. - // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" - // FormatException when trying to decode an empty string. - if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { - return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'TemplateResponseDto',) as TemplateResponseDto; - - } - return null; } - /// Performs an HTTP 'POST /notifications/test-email' operation and returns the [Response]. + /// Performs an HTTP 'DELETE /notifications' operation and returns the [Response]. /// Parameters: /// - /// * [SystemConfigSmtpDto] systemConfigSmtpDto (required): - Future<Response> sendTestEmailWithHttpInfo(SystemConfigSmtpDto systemConfigSmtpDto,) async { + /// * [NotificationDeleteAllDto] notificationDeleteAllDto (required): + Future<Response> deleteNotificationsWithHttpInfo(NotificationDeleteAllDto notificationDeleteAllDto,) async { // ignore: prefer_const_declarations - final apiPath = r'/notifications/test-email'; + final apiPath = r'/notifications'; // ignore: prefer_final_locals - Object? postBody = systemConfigSmtpDto; + Object? postBody = notificationDeleteAllDto; final queryParams = <QueryParam>[]; final headerParams = <String, String>{}; @@ -88,7 +76,7 @@ class NotificationsApi { return apiClient.invokeAPI( apiPath, - 'POST', + 'DELETE', queryParams, postBody, headerParams, @@ -99,9 +87,49 @@ class NotificationsApi { /// Parameters: /// - /// * [SystemConfigSmtpDto] systemConfigSmtpDto (required): - Future<TestEmailResponseDto?> sendTestEmail(SystemConfigSmtpDto systemConfigSmtpDto,) async { - final response = await sendTestEmailWithHttpInfo(systemConfigSmtpDto,); + /// * [NotificationDeleteAllDto] notificationDeleteAllDto (required): + Future<void> deleteNotifications(NotificationDeleteAllDto notificationDeleteAllDto,) async { + final response = await deleteNotificationsWithHttpInfo(notificationDeleteAllDto,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } + + /// Performs an HTTP 'GET /notifications/{id}' operation and returns the [Response]. + /// Parameters: + /// + /// * [String] id (required): + Future<Response> getNotificationWithHttpInfo(String id,) async { + // ignore: prefer_const_declarations + final apiPath = r'/notifications/{id}' + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = <QueryParam>[]; + final headerParams = <String, String>{}; + final formParams = <String, String>{}; + + const contentTypes = <String>[]; + + + return apiClient.invokeAPI( + apiPath, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [String] id (required): + Future<NotificationDto?> getNotification(String id,) async { + final response = await getNotificationWithHttpInfo(id,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -109,9 +137,175 @@ class NotificationsApi { // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" // FormatException when trying to decode an empty string. if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { - return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'TestEmailResponseDto',) as TestEmailResponseDto; + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'NotificationDto',) as NotificationDto; } return null; } + + /// Performs an HTTP 'GET /notifications' operation and returns the [Response]. + /// Parameters: + /// + /// * [String] id: + /// + /// * [NotificationLevel] level: + /// + /// * [NotificationType] type: + /// + /// * [bool] unread: + Future<Response> getNotificationsWithHttpInfo({ String? id, NotificationLevel? level, NotificationType? type, bool? unread, }) async { + // ignore: prefer_const_declarations + final apiPath = r'/notifications'; + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = <QueryParam>[]; + final headerParams = <String, String>{}; + final formParams = <String, String>{}; + + if (id != null) { + queryParams.addAll(_queryParams('', 'id', id)); + } + if (level != null) { + queryParams.addAll(_queryParams('', 'level', level)); + } + if (type != null) { + queryParams.addAll(_queryParams('', 'type', type)); + } + if (unread != null) { + queryParams.addAll(_queryParams('', 'unread', unread)); + } + + const contentTypes = <String>[]; + + + return apiClient.invokeAPI( + apiPath, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [String] id: + /// + /// * [NotificationLevel] level: + /// + /// * [NotificationType] type: + /// + /// * [bool] unread: + Future<List<NotificationDto>?> getNotifications({ String? id, NotificationLevel? level, NotificationType? type, bool? unread, }) async { + final response = await getNotificationsWithHttpInfo( id: id, level: level, type: type, unread: unread, ); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + final responseBody = await _decodeBodyBytes(response); + return (await apiClient.deserializeAsync(responseBody, 'List<NotificationDto>') as List) + .cast<NotificationDto>() + .toList(growable: false); + + } + return null; + } + + /// Performs an HTTP 'PUT /notifications/{id}' operation and returns the [Response]. + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [NotificationUpdateDto] notificationUpdateDto (required): + Future<Response> updateNotificationWithHttpInfo(String id, NotificationUpdateDto notificationUpdateDto,) async { + // ignore: prefer_const_declarations + final apiPath = r'/notifications/{id}' + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody = notificationUpdateDto; + + final queryParams = <QueryParam>[]; + final headerParams = <String, String>{}; + final formParams = <String, String>{}; + + const contentTypes = <String>['application/json']; + + + return apiClient.invokeAPI( + apiPath, + 'PUT', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [NotificationUpdateDto] notificationUpdateDto (required): + Future<NotificationDto?> updateNotification(String id, NotificationUpdateDto notificationUpdateDto,) async { + final response = await updateNotificationWithHttpInfo(id, notificationUpdateDto,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'NotificationDto',) as NotificationDto; + + } + return null; + } + + /// Performs an HTTP 'PUT /notifications' operation and returns the [Response]. + /// Parameters: + /// + /// * [NotificationUpdateAllDto] notificationUpdateAllDto (required): + Future<Response> updateNotificationsWithHttpInfo(NotificationUpdateAllDto notificationUpdateAllDto,) async { + // ignore: prefer_const_declarations + final apiPath = r'/notifications'; + + // ignore: prefer_final_locals + Object? postBody = notificationUpdateAllDto; + + final queryParams = <QueryParam>[]; + final headerParams = <String, String>{}; + final formParams = <String, String>{}; + + const contentTypes = <String>['application/json']; + + + return apiClient.invokeAPI( + apiPath, + 'PUT', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [NotificationUpdateAllDto] notificationUpdateAllDto (required): + Future<void> updateNotifications(NotificationUpdateAllDto notificationUpdateAllDto,) async { + final response = await updateNotificationsWithHttpInfo(notificationUpdateAllDto,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } } diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index 5759217f41..9d0e80e58b 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -270,8 +270,6 @@ class ApiClient { return AssetTypeEnumTypeTransformer().decode(value); case 'AudioCodec': return AudioCodecTypeTransformer().decode(value); - case 'AvatarResponse': - return AvatarResponse.fromJson(value); case 'AvatarUpdate': return AvatarUpdate.fromJson(value); case 'BulkIdResponseDto': @@ -322,16 +320,6 @@ class ApiClient { return FaceDto.fromJson(value); case 'FacialRecognitionConfig': return FacialRecognitionConfig.fromJson(value); - case 'FileChecksumDto': - return FileChecksumDto.fromJson(value); - case 'FileChecksumResponseDto': - return FileChecksumResponseDto.fromJson(value); - case 'FileReportDto': - return FileReportDto.fromJson(value); - case 'FileReportFixDto': - return FileReportFixDto.fromJson(value); - case 'FileReportItemDto': - return FileReportItemDto.fromJson(value); case 'FoldersResponse': return FoldersResponse.fromJson(value); case 'FoldersUpdate': @@ -392,22 +380,34 @@ class ApiClient { return MergePersonDto.fromJson(value); case 'MetadataSearchDto': return MetadataSearchDto.fromJson(value); + case 'NotificationCreateDto': + return NotificationCreateDto.fromJson(value); + case 'NotificationDeleteAllDto': + return NotificationDeleteAllDto.fromJson(value); + case 'NotificationDto': + return NotificationDto.fromJson(value); + case 'NotificationLevel': + return NotificationLevelTypeTransformer().decode(value); + case 'NotificationType': + return NotificationTypeTypeTransformer().decode(value); + case 'NotificationUpdateAllDto': + return NotificationUpdateAllDto.fromJson(value); + case 'NotificationUpdateDto': + return NotificationUpdateDto.fromJson(value); case 'OAuthAuthorizeResponseDto': return OAuthAuthorizeResponseDto.fromJson(value); case 'OAuthCallbackDto': return OAuthCallbackDto.fromJson(value); case 'OAuthConfigDto': return OAuthConfigDto.fromJson(value); + case 'OAuthTokenEndpointAuthMethod': + return OAuthTokenEndpointAuthMethodTypeTransformer().decode(value); case 'OnThisDayDto': return OnThisDayDto.fromJson(value); case 'PartnerDirection': return PartnerDirectionTypeTransformer().decode(value); case 'PartnerResponseDto': return PartnerResponseDto.fromJson(value); - case 'PathEntityType': - return PathEntityTypeTypeTransformer().decode(value); - case 'PathType': - return PathTypeTypeTransformer().decode(value); case 'PeopleResponse': return PeopleResponse.fromJson(value); case 'PeopleResponseDto': diff --git a/mobile/openapi/lib/api_helper.dart b/mobile/openapi/lib/api_helper.dart index 1ebf8314ad..eec991e903 100644 --- a/mobile/openapi/lib/api_helper.dart +++ b/mobile/openapi/lib/api_helper.dart @@ -100,15 +100,18 @@ String parameterToString(dynamic value) { if (value is MemoryType) { return MemoryTypeTypeTransformer().encode(value).toString(); } + if (value is NotificationLevel) { + return NotificationLevelTypeTransformer().encode(value).toString(); + } + if (value is NotificationType) { + return NotificationTypeTypeTransformer().encode(value).toString(); + } + if (value is OAuthTokenEndpointAuthMethod) { + return OAuthTokenEndpointAuthMethodTypeTransformer().encode(value).toString(); + } if (value is PartnerDirection) { return PartnerDirectionTypeTransformer().encode(value).toString(); } - if (value is PathEntityType) { - return PathEntityTypeTypeTransformer().encode(value).toString(); - } - if (value is PathType) { - return PathTypeTypeTransformer().encode(value).toString(); - } if (value is Permission) { return PermissionTypeTransformer().encode(value).toString(); } diff --git a/mobile/openapi/lib/model/file_checksum_response_dto.dart b/mobile/openapi/lib/model/file_checksum_response_dto.dart deleted file mode 100644 index 7b963c8bd5..0000000000 --- a/mobile/openapi/lib/model/file_checksum_response_dto.dart +++ /dev/null @@ -1,107 +0,0 @@ -// -// 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 FileChecksumResponseDto { - /// Returns a new [FileChecksumResponseDto] instance. - FileChecksumResponseDto({ - required this.checksum, - required this.filename, - }); - - String checksum; - - String filename; - - @override - bool operator ==(Object other) => identical(this, other) || other is FileChecksumResponseDto && - other.checksum == checksum && - other.filename == filename; - - @override - int get hashCode => - // ignore: unnecessary_parenthesis - (checksum.hashCode) + - (filename.hashCode); - - @override - String toString() => 'FileChecksumResponseDto[checksum=$checksum, filename=$filename]'; - - Map<String, dynamic> toJson() { - final json = <String, dynamic>{}; - json[r'checksum'] = this.checksum; - json[r'filename'] = this.filename; - return json; - } - - /// Returns a new [FileChecksumResponseDto] instance and imports its values from - /// [value] if it's a [Map], null otherwise. - // ignore: prefer_constructors_over_static_methods - static FileChecksumResponseDto? fromJson(dynamic value) { - upgradeDto(value, "FileChecksumResponseDto"); - if (value is Map) { - final json = value.cast<String, dynamic>(); - - return FileChecksumResponseDto( - checksum: mapValueOfType<String>(json, r'checksum')!, - filename: mapValueOfType<String>(json, r'filename')!, - ); - } - return null; - } - - static List<FileChecksumResponseDto> listFromJson(dynamic json, {bool growable = false,}) { - final result = <FileChecksumResponseDto>[]; - if (json is List && json.isNotEmpty) { - for (final row in json) { - final value = FileChecksumResponseDto.fromJson(row); - if (value != null) { - result.add(value); - } - } - } - return result.toList(growable: growable); - } - - static Map<String, FileChecksumResponseDto> mapFromJson(dynamic json) { - final map = <String, FileChecksumResponseDto>{}; - if (json is Map && json.isNotEmpty) { - json = json.cast<String, dynamic>(); // ignore: parameter_assignments - for (final entry in json.entries) { - final value = FileChecksumResponseDto.fromJson(entry.value); - if (value != null) { - map[entry.key] = value; - } - } - } - return map; - } - - // maps a json object with a list of FileChecksumResponseDto-objects as value to a dart map - static Map<String, List<FileChecksumResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = <String, List<FileChecksumResponseDto>>{}; - if (json is Map && json.isNotEmpty) { - // ignore: parameter_assignments - json = json.cast<String, dynamic>(); - for (final entry in json.entries) { - map[entry.key] = FileChecksumResponseDto.listFromJson(entry.value, growable: growable,); - } - } - return map; - } - - /// The list of required keys that must be present in a JSON. - static const requiredKeys = <String>{ - 'checksum', - 'filename', - }; -} - diff --git a/mobile/openapi/lib/model/file_report_dto.dart b/mobile/openapi/lib/model/file_report_dto.dart deleted file mode 100644 index 3dc892e5e7..0000000000 --- a/mobile/openapi/lib/model/file_report_dto.dart +++ /dev/null @@ -1,109 +0,0 @@ -// -// 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 FileReportDto { - /// Returns a new [FileReportDto] instance. - FileReportDto({ - this.extras = const [], - this.orphans = const [], - }); - - List<String> extras; - - List<FileReportItemDto> orphans; - - @override - bool operator ==(Object other) => identical(this, other) || other is FileReportDto && - _deepEquality.equals(other.extras, extras) && - _deepEquality.equals(other.orphans, orphans); - - @override - int get hashCode => - // ignore: unnecessary_parenthesis - (extras.hashCode) + - (orphans.hashCode); - - @override - String toString() => 'FileReportDto[extras=$extras, orphans=$orphans]'; - - Map<String, dynamic> toJson() { - final json = <String, dynamic>{}; - json[r'extras'] = this.extras; - json[r'orphans'] = this.orphans; - return json; - } - - /// Returns a new [FileReportDto] instance and imports its values from - /// [value] if it's a [Map], null otherwise. - // ignore: prefer_constructors_over_static_methods - static FileReportDto? fromJson(dynamic value) { - upgradeDto(value, "FileReportDto"); - if (value is Map) { - final json = value.cast<String, dynamic>(); - - return FileReportDto( - extras: json[r'extras'] is Iterable - ? (json[r'extras'] as Iterable).cast<String>().toList(growable: false) - : const [], - orphans: FileReportItemDto.listFromJson(json[r'orphans']), - ); - } - return null; - } - - static List<FileReportDto> listFromJson(dynamic json, {bool growable = false,}) { - final result = <FileReportDto>[]; - if (json is List && json.isNotEmpty) { - for (final row in json) { - final value = FileReportDto.fromJson(row); - if (value != null) { - result.add(value); - } - } - } - return result.toList(growable: growable); - } - - static Map<String, FileReportDto> mapFromJson(dynamic json) { - final map = <String, FileReportDto>{}; - if (json is Map && json.isNotEmpty) { - json = json.cast<String, dynamic>(); // ignore: parameter_assignments - for (final entry in json.entries) { - final value = FileReportDto.fromJson(entry.value); - if (value != null) { - map[entry.key] = value; - } - } - } - return map; - } - - // maps a json object with a list of FileReportDto-objects as value to a dart map - static Map<String, List<FileReportDto>> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = <String, List<FileReportDto>>{}; - if (json is Map && json.isNotEmpty) { - // ignore: parameter_assignments - json = json.cast<String, dynamic>(); - for (final entry in json.entries) { - map[entry.key] = FileReportDto.listFromJson(entry.value, growable: growable,); - } - } - return map; - } - - /// The list of required keys that must be present in a JSON. - static const requiredKeys = <String>{ - 'extras', - 'orphans', - }; -} - diff --git a/mobile/openapi/lib/model/file_report_fix_dto.dart b/mobile/openapi/lib/model/file_report_fix_dto.dart deleted file mode 100644 index d46cdeb4b7..0000000000 --- a/mobile/openapi/lib/model/file_report_fix_dto.dart +++ /dev/null @@ -1,99 +0,0 @@ -// -// 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 FileReportFixDto { - /// Returns a new [FileReportFixDto] instance. - FileReportFixDto({ - this.items = const [], - }); - - List<FileReportItemDto> items; - - @override - bool operator ==(Object other) => identical(this, other) || other is FileReportFixDto && - _deepEquality.equals(other.items, items); - - @override - int get hashCode => - // ignore: unnecessary_parenthesis - (items.hashCode); - - @override - String toString() => 'FileReportFixDto[items=$items]'; - - Map<String, dynamic> toJson() { - final json = <String, dynamic>{}; - json[r'items'] = this.items; - return json; - } - - /// Returns a new [FileReportFixDto] instance and imports its values from - /// [value] if it's a [Map], null otherwise. - // ignore: prefer_constructors_over_static_methods - static FileReportFixDto? fromJson(dynamic value) { - upgradeDto(value, "FileReportFixDto"); - if (value is Map) { - final json = value.cast<String, dynamic>(); - - return FileReportFixDto( - items: FileReportItemDto.listFromJson(json[r'items']), - ); - } - return null; - } - - static List<FileReportFixDto> listFromJson(dynamic json, {bool growable = false,}) { - final result = <FileReportFixDto>[]; - if (json is List && json.isNotEmpty) { - for (final row in json) { - final value = FileReportFixDto.fromJson(row); - if (value != null) { - result.add(value); - } - } - } - return result.toList(growable: growable); - } - - static Map<String, FileReportFixDto> mapFromJson(dynamic json) { - final map = <String, FileReportFixDto>{}; - if (json is Map && json.isNotEmpty) { - json = json.cast<String, dynamic>(); // ignore: parameter_assignments - for (final entry in json.entries) { - final value = FileReportFixDto.fromJson(entry.value); - if (value != null) { - map[entry.key] = value; - } - } - } - return map; - } - - // maps a json object with a list of FileReportFixDto-objects as value to a dart map - static Map<String, List<FileReportFixDto>> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = <String, List<FileReportFixDto>>{}; - if (json is Map && json.isNotEmpty) { - // ignore: parameter_assignments - json = json.cast<String, dynamic>(); - for (final entry in json.entries) { - map[entry.key] = FileReportFixDto.listFromJson(entry.value, growable: growable,); - } - } - return map; - } - - /// The list of required keys that must be present in a JSON. - static const requiredKeys = <String>{ - 'items', - }; -} - diff --git a/mobile/openapi/lib/model/file_report_item_dto.dart b/mobile/openapi/lib/model/file_report_item_dto.dart deleted file mode 100644 index 1ef08c2b48..0000000000 --- a/mobile/openapi/lib/model/file_report_item_dto.dart +++ /dev/null @@ -1,140 +0,0 @@ -// -// 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 FileReportItemDto { - /// Returns a new [FileReportItemDto] instance. - FileReportItemDto({ - this.checksum, - required this.entityId, - required this.entityType, - required this.pathType, - required this.pathValue, - }); - - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// - String? checksum; - - String entityId; - - PathEntityType entityType; - - PathType pathType; - - String pathValue; - - @override - bool operator ==(Object other) => identical(this, other) || other is FileReportItemDto && - other.checksum == checksum && - other.entityId == entityId && - other.entityType == entityType && - other.pathType == pathType && - other.pathValue == pathValue; - - @override - int get hashCode => - // ignore: unnecessary_parenthesis - (checksum == null ? 0 : checksum!.hashCode) + - (entityId.hashCode) + - (entityType.hashCode) + - (pathType.hashCode) + - (pathValue.hashCode); - - @override - String toString() => 'FileReportItemDto[checksum=$checksum, entityId=$entityId, entityType=$entityType, pathType=$pathType, pathValue=$pathValue]'; - - Map<String, dynamic> toJson() { - final json = <String, dynamic>{}; - if (this.checksum != null) { - json[r'checksum'] = this.checksum; - } else { - // json[r'checksum'] = null; - } - json[r'entityId'] = this.entityId; - json[r'entityType'] = this.entityType; - json[r'pathType'] = this.pathType; - json[r'pathValue'] = this.pathValue; - return json; - } - - /// Returns a new [FileReportItemDto] instance and imports its values from - /// [value] if it's a [Map], null otherwise. - // ignore: prefer_constructors_over_static_methods - static FileReportItemDto? fromJson(dynamic value) { - upgradeDto(value, "FileReportItemDto"); - if (value is Map) { - final json = value.cast<String, dynamic>(); - - return FileReportItemDto( - checksum: mapValueOfType<String>(json, r'checksum'), - entityId: mapValueOfType<String>(json, r'entityId')!, - entityType: PathEntityType.fromJson(json[r'entityType'])!, - pathType: PathType.fromJson(json[r'pathType'])!, - pathValue: mapValueOfType<String>(json, r'pathValue')!, - ); - } - return null; - } - - static List<FileReportItemDto> listFromJson(dynamic json, {bool growable = false,}) { - final result = <FileReportItemDto>[]; - if (json is List && json.isNotEmpty) { - for (final row in json) { - final value = FileReportItemDto.fromJson(row); - if (value != null) { - result.add(value); - } - } - } - return result.toList(growable: growable); - } - - static Map<String, FileReportItemDto> mapFromJson(dynamic json) { - final map = <String, FileReportItemDto>{}; - if (json is Map && json.isNotEmpty) { - json = json.cast<String, dynamic>(); // ignore: parameter_assignments - for (final entry in json.entries) { - final value = FileReportItemDto.fromJson(entry.value); - if (value != null) { - map[entry.key] = value; - } - } - } - return map; - } - - // maps a json object with a list of FileReportItemDto-objects as value to a dart map - static Map<String, List<FileReportItemDto>> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = <String, List<FileReportItemDto>>{}; - if (json is Map && json.isNotEmpty) { - // ignore: parameter_assignments - json = json.cast<String, dynamic>(); - for (final entry in json.entries) { - map[entry.key] = FileReportItemDto.listFromJson(entry.value, growable: growable,); - } - } - return map; - } - - /// The list of required keys that must be present in a JSON. - static const requiredKeys = <String>{ - 'entityId', - 'entityType', - 'pathType', - 'pathValue', - }; -} - diff --git a/mobile/openapi/lib/model/notification_create_dto.dart b/mobile/openapi/lib/model/notification_create_dto.dart new file mode 100644 index 0000000000..07985353b2 --- /dev/null +++ b/mobile/openapi/lib/model/notification_create_dto.dart @@ -0,0 +1,180 @@ +// +// 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 NotificationCreateDto { + /// Returns a new [NotificationCreateDto] instance. + NotificationCreateDto({ + this.data, + this.description, + this.level, + this.readAt, + required this.title, + this.type, + required this.userId, + }); + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + Object? data; + + String? description; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + NotificationLevel? level; + + DateTime? readAt; + + String title; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + NotificationType? type; + + String userId; + + @override + bool operator ==(Object other) => identical(this, other) || other is NotificationCreateDto && + other.data == data && + other.description == description && + other.level == level && + other.readAt == readAt && + other.title == title && + other.type == type && + other.userId == userId; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (data == null ? 0 : data!.hashCode) + + (description == null ? 0 : description!.hashCode) + + (level == null ? 0 : level!.hashCode) + + (readAt == null ? 0 : readAt!.hashCode) + + (title.hashCode) + + (type == null ? 0 : type!.hashCode) + + (userId.hashCode); + + @override + String toString() => 'NotificationCreateDto[data=$data, description=$description, level=$level, readAt=$readAt, title=$title, type=$type, userId=$userId]'; + + Map<String, dynamic> toJson() { + final json = <String, dynamic>{}; + if (this.data != null) { + json[r'data'] = this.data; + } else { + // json[r'data'] = null; + } + if (this.description != null) { + json[r'description'] = this.description; + } else { + // json[r'description'] = null; + } + if (this.level != null) { + json[r'level'] = this.level; + } else { + // json[r'level'] = null; + } + if (this.readAt != null) { + json[r'readAt'] = this.readAt!.toUtc().toIso8601String(); + } else { + // json[r'readAt'] = null; + } + json[r'title'] = this.title; + if (this.type != null) { + json[r'type'] = this.type; + } else { + // json[r'type'] = null; + } + json[r'userId'] = this.userId; + return json; + } + + /// Returns a new [NotificationCreateDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static NotificationCreateDto? fromJson(dynamic value) { + upgradeDto(value, "NotificationCreateDto"); + if (value is Map) { + final json = value.cast<String, dynamic>(); + + return NotificationCreateDto( + data: mapValueOfType<Object>(json, r'data'), + description: mapValueOfType<String>(json, r'description'), + level: NotificationLevel.fromJson(json[r'level']), + readAt: mapDateTime(json, r'readAt', r''), + title: mapValueOfType<String>(json, r'title')!, + type: NotificationType.fromJson(json[r'type']), + userId: mapValueOfType<String>(json, r'userId')!, + ); + } + return null; + } + + static List<NotificationCreateDto> listFromJson(dynamic json, {bool growable = false,}) { + final result = <NotificationCreateDto>[]; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = NotificationCreateDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map<String, NotificationCreateDto> mapFromJson(dynamic json) { + final map = <String, NotificationCreateDto>{}; + if (json is Map && json.isNotEmpty) { + json = json.cast<String, dynamic>(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = NotificationCreateDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of NotificationCreateDto-objects as value to a dart map + static Map<String, List<NotificationCreateDto>> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = <String, List<NotificationCreateDto>>{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast<String, dynamic>(); + for (final entry in json.entries) { + map[entry.key] = NotificationCreateDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = <String>{ + 'title', + 'userId', + }; +} + diff --git a/mobile/openapi/lib/model/file_checksum_dto.dart b/mobile/openapi/lib/model/notification_delete_all_dto.dart similarity index 51% rename from mobile/openapi/lib/model/file_checksum_dto.dart rename to mobile/openapi/lib/model/notification_delete_all_dto.dart index 7dc9ccdf2f..4be1b89e92 100644 --- a/mobile/openapi/lib/model/file_checksum_dto.dart +++ b/mobile/openapi/lib/model/notification_delete_all_dto.dart @@ -10,54 +10,54 @@ part of openapi.api; -class FileChecksumDto { - /// Returns a new [FileChecksumDto] instance. - FileChecksumDto({ - this.filenames = const [], +class NotificationDeleteAllDto { + /// Returns a new [NotificationDeleteAllDto] instance. + NotificationDeleteAllDto({ + this.ids = const [], }); - List<String> filenames; + List<String> ids; @override - bool operator ==(Object other) => identical(this, other) || other is FileChecksumDto && - _deepEquality.equals(other.filenames, filenames); + bool operator ==(Object other) => identical(this, other) || other is NotificationDeleteAllDto && + _deepEquality.equals(other.ids, ids); @override int get hashCode => // ignore: unnecessary_parenthesis - (filenames.hashCode); + (ids.hashCode); @override - String toString() => 'FileChecksumDto[filenames=$filenames]'; + String toString() => 'NotificationDeleteAllDto[ids=$ids]'; Map<String, dynamic> toJson() { final json = <String, dynamic>{}; - json[r'filenames'] = this.filenames; + json[r'ids'] = this.ids; return json; } - /// Returns a new [FileChecksumDto] instance and imports its values from + /// Returns a new [NotificationDeleteAllDto] instance and imports its values from /// [value] if it's a [Map], null otherwise. // ignore: prefer_constructors_over_static_methods - static FileChecksumDto? fromJson(dynamic value) { - upgradeDto(value, "FileChecksumDto"); + static NotificationDeleteAllDto? fromJson(dynamic value) { + upgradeDto(value, "NotificationDeleteAllDto"); if (value is Map) { final json = value.cast<String, dynamic>(); - return FileChecksumDto( - filenames: json[r'filenames'] is Iterable - ? (json[r'filenames'] as Iterable).cast<String>().toList(growable: false) + return NotificationDeleteAllDto( + ids: json[r'ids'] is Iterable + ? (json[r'ids'] as Iterable).cast<String>().toList(growable: false) : const [], ); } return null; } - static List<FileChecksumDto> listFromJson(dynamic json, {bool growable = false,}) { - final result = <FileChecksumDto>[]; + static List<NotificationDeleteAllDto> listFromJson(dynamic json, {bool growable = false,}) { + final result = <NotificationDeleteAllDto>[]; if (json is List && json.isNotEmpty) { for (final row in json) { - final value = FileChecksumDto.fromJson(row); + final value = NotificationDeleteAllDto.fromJson(row); if (value != null) { result.add(value); } @@ -66,12 +66,12 @@ class FileChecksumDto { return result.toList(growable: growable); } - static Map<String, FileChecksumDto> mapFromJson(dynamic json) { - final map = <String, FileChecksumDto>{}; + static Map<String, NotificationDeleteAllDto> mapFromJson(dynamic json) { + final map = <String, NotificationDeleteAllDto>{}; if (json is Map && json.isNotEmpty) { json = json.cast<String, dynamic>(); // ignore: parameter_assignments for (final entry in json.entries) { - final value = FileChecksumDto.fromJson(entry.value); + final value = NotificationDeleteAllDto.fromJson(entry.value); if (value != null) { map[entry.key] = value; } @@ -80,14 +80,14 @@ class FileChecksumDto { return map; } - // maps a json object with a list of FileChecksumDto-objects as value to a dart map - static Map<String, List<FileChecksumDto>> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = <String, List<FileChecksumDto>>{}; + // maps a json object with a list of NotificationDeleteAllDto-objects as value to a dart map + static Map<String, List<NotificationDeleteAllDto>> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = <String, List<NotificationDeleteAllDto>>{}; if (json is Map && json.isNotEmpty) { // ignore: parameter_assignments json = json.cast<String, dynamic>(); for (final entry in json.entries) { - map[entry.key] = FileChecksumDto.listFromJson(entry.value, growable: growable,); + map[entry.key] = NotificationDeleteAllDto.listFromJson(entry.value, growable: growable,); } } return map; @@ -95,7 +95,7 @@ class FileChecksumDto { /// The list of required keys that must be present in a JSON. static const requiredKeys = <String>{ - 'filenames', + 'ids', }; } diff --git a/mobile/openapi/lib/model/notification_dto.dart b/mobile/openapi/lib/model/notification_dto.dart new file mode 100644 index 0000000000..4f730b4e50 --- /dev/null +++ b/mobile/openapi/lib/model/notification_dto.dart @@ -0,0 +1,182 @@ +// +// 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 NotificationDto { + /// Returns a new [NotificationDto] instance. + NotificationDto({ + required this.createdAt, + this.data, + this.description, + required this.id, + required this.level, + this.readAt, + required this.title, + required this.type, + }); + + DateTime createdAt; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + Object? data; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? description; + + String id; + + NotificationLevel level; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + DateTime? readAt; + + String title; + + NotificationType type; + + @override + bool operator ==(Object other) => identical(this, other) || other is NotificationDto && + other.createdAt == createdAt && + other.data == data && + other.description == description && + other.id == id && + other.level == level && + other.readAt == readAt && + other.title == title && + other.type == type; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (createdAt.hashCode) + + (data == null ? 0 : data!.hashCode) + + (description == null ? 0 : description!.hashCode) + + (id.hashCode) + + (level.hashCode) + + (readAt == null ? 0 : readAt!.hashCode) + + (title.hashCode) + + (type.hashCode); + + @override + String toString() => 'NotificationDto[createdAt=$createdAt, data=$data, description=$description, id=$id, level=$level, readAt=$readAt, title=$title, type=$type]'; + + Map<String, dynamic> toJson() { + final json = <String, dynamic>{}; + json[r'createdAt'] = this.createdAt.toUtc().toIso8601String(); + if (this.data != null) { + json[r'data'] = this.data; + } else { + // json[r'data'] = null; + } + if (this.description != null) { + json[r'description'] = this.description; + } else { + // json[r'description'] = null; + } + json[r'id'] = this.id; + json[r'level'] = this.level; + if (this.readAt != null) { + json[r'readAt'] = this.readAt!.toUtc().toIso8601String(); + } else { + // json[r'readAt'] = null; + } + json[r'title'] = this.title; + json[r'type'] = this.type; + return json; + } + + /// Returns a new [NotificationDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static NotificationDto? fromJson(dynamic value) { + upgradeDto(value, "NotificationDto"); + if (value is Map) { + final json = value.cast<String, dynamic>(); + + return NotificationDto( + createdAt: mapDateTime(json, r'createdAt', r'')!, + data: mapValueOfType<Object>(json, r'data'), + description: mapValueOfType<String>(json, r'description'), + id: mapValueOfType<String>(json, r'id')!, + level: NotificationLevel.fromJson(json[r'level'])!, + readAt: mapDateTime(json, r'readAt', r''), + title: mapValueOfType<String>(json, r'title')!, + type: NotificationType.fromJson(json[r'type'])!, + ); + } + return null; + } + + static List<NotificationDto> listFromJson(dynamic json, {bool growable = false,}) { + final result = <NotificationDto>[]; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = NotificationDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map<String, NotificationDto> mapFromJson(dynamic json) { + final map = <String, NotificationDto>{}; + if (json is Map && json.isNotEmpty) { + json = json.cast<String, dynamic>(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = NotificationDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of NotificationDto-objects as value to a dart map + static Map<String, List<NotificationDto>> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = <String, List<NotificationDto>>{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast<String, dynamic>(); + for (final entry in json.entries) { + map[entry.key] = NotificationDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = <String>{ + 'createdAt', + 'id', + 'level', + 'title', + 'type', + }; +} + diff --git a/mobile/openapi/lib/model/notification_level.dart b/mobile/openapi/lib/model/notification_level.dart new file mode 100644 index 0000000000..554863ae4f --- /dev/null +++ b/mobile/openapi/lib/model/notification_level.dart @@ -0,0 +1,91 @@ +// +// 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 NotificationLevel { + /// Instantiate a new enum with the provided [value]. + const NotificationLevel._(this.value); + + /// The underlying value of this enum member. + final String value; + + @override + String toString() => value; + + String toJson() => value; + + static const success = NotificationLevel._(r'success'); + static const error = NotificationLevel._(r'error'); + static const warning = NotificationLevel._(r'warning'); + static const info = NotificationLevel._(r'info'); + + /// List of all possible values in this [enum][NotificationLevel]. + static const values = <NotificationLevel>[ + success, + error, + warning, + info, + ]; + + static NotificationLevel? fromJson(dynamic value) => NotificationLevelTypeTransformer().decode(value); + + static List<NotificationLevel> listFromJson(dynamic json, {bool growable = false,}) { + final result = <NotificationLevel>[]; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = NotificationLevel.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } +} + +/// Transformation class that can [encode] an instance of [NotificationLevel] to String, +/// and [decode] dynamic data back to [NotificationLevel]. +class NotificationLevelTypeTransformer { + factory NotificationLevelTypeTransformer() => _instance ??= const NotificationLevelTypeTransformer._(); + + const NotificationLevelTypeTransformer._(); + + String encode(NotificationLevel data) => data.value; + + /// Decodes a [dynamic value][data] to a NotificationLevel. + /// + /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, + /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] + /// cannot be decoded successfully, then an [UnimplementedError] is thrown. + /// + /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, + /// and users are still using an old app with the old code. + NotificationLevel? decode(dynamic data, {bool allowNull = true}) { + if (data != null) { + switch (data) { + case r'success': return NotificationLevel.success; + case r'error': return NotificationLevel.error; + case r'warning': return NotificationLevel.warning; + case r'info': return NotificationLevel.info; + default: + if (!allowNull) { + throw ArgumentError('Unknown enum value to decode: $data'); + } + } + } + return null; + } + + /// Singleton [NotificationLevelTypeTransformer] instance. + static NotificationLevelTypeTransformer? _instance; +} + diff --git a/mobile/openapi/lib/model/notification_type.dart b/mobile/openapi/lib/model/notification_type.dart new file mode 100644 index 0000000000..436d2d190f --- /dev/null +++ b/mobile/openapi/lib/model/notification_type.dart @@ -0,0 +1,91 @@ +// +// 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 NotificationType { + /// Instantiate a new enum with the provided [value]. + const NotificationType._(this.value); + + /// The underlying value of this enum member. + final String value; + + @override + String toString() => value; + + String toJson() => value; + + static const jobFailed = NotificationType._(r'JobFailed'); + static const backupFailed = NotificationType._(r'BackupFailed'); + static const systemMessage = NotificationType._(r'SystemMessage'); + static const custom = NotificationType._(r'Custom'); + + /// List of all possible values in this [enum][NotificationType]. + static const values = <NotificationType>[ + jobFailed, + backupFailed, + systemMessage, + custom, + ]; + + static NotificationType? fromJson(dynamic value) => NotificationTypeTypeTransformer().decode(value); + + static List<NotificationType> listFromJson(dynamic json, {bool growable = false,}) { + final result = <NotificationType>[]; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = NotificationType.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } +} + +/// Transformation class that can [encode] an instance of [NotificationType] to String, +/// and [decode] dynamic data back to [NotificationType]. +class NotificationTypeTypeTransformer { + factory NotificationTypeTypeTransformer() => _instance ??= const NotificationTypeTypeTransformer._(); + + const NotificationTypeTypeTransformer._(); + + String encode(NotificationType data) => data.value; + + /// Decodes a [dynamic value][data] to a NotificationType. + /// + /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, + /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] + /// cannot be decoded successfully, then an [UnimplementedError] is thrown. + /// + /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, + /// and users are still using an old app with the old code. + NotificationType? decode(dynamic data, {bool allowNull = true}) { + if (data != null) { + switch (data) { + case r'JobFailed': return NotificationType.jobFailed; + case r'BackupFailed': return NotificationType.backupFailed; + case r'SystemMessage': return NotificationType.systemMessage; + case r'Custom': return NotificationType.custom; + default: + if (!allowNull) { + throw ArgumentError('Unknown enum value to decode: $data'); + } + } + } + return null; + } + + /// Singleton [NotificationTypeTypeTransformer] instance. + static NotificationTypeTypeTransformer? _instance; +} + diff --git a/mobile/openapi/lib/model/notification_update_all_dto.dart b/mobile/openapi/lib/model/notification_update_all_dto.dart new file mode 100644 index 0000000000..a6393b275a --- /dev/null +++ b/mobile/openapi/lib/model/notification_update_all_dto.dart @@ -0,0 +1,112 @@ +// +// 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 NotificationUpdateAllDto { + /// Returns a new [NotificationUpdateAllDto] instance. + NotificationUpdateAllDto({ + this.ids = const [], + this.readAt, + }); + + List<String> ids; + + DateTime? readAt; + + @override + bool operator ==(Object other) => identical(this, other) || other is NotificationUpdateAllDto && + _deepEquality.equals(other.ids, ids) && + other.readAt == readAt; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (ids.hashCode) + + (readAt == null ? 0 : readAt!.hashCode); + + @override + String toString() => 'NotificationUpdateAllDto[ids=$ids, readAt=$readAt]'; + + Map<String, dynamic> toJson() { + final json = <String, dynamic>{}; + json[r'ids'] = this.ids; + if (this.readAt != null) { + json[r'readAt'] = this.readAt!.toUtc().toIso8601String(); + } else { + // json[r'readAt'] = null; + } + return json; + } + + /// Returns a new [NotificationUpdateAllDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static NotificationUpdateAllDto? fromJson(dynamic value) { + upgradeDto(value, "NotificationUpdateAllDto"); + if (value is Map) { + final json = value.cast<String, dynamic>(); + + return NotificationUpdateAllDto( + ids: json[r'ids'] is Iterable + ? (json[r'ids'] as Iterable).cast<String>().toList(growable: false) + : const [], + readAt: mapDateTime(json, r'readAt', r''), + ); + } + return null; + } + + static List<NotificationUpdateAllDto> listFromJson(dynamic json, {bool growable = false,}) { + final result = <NotificationUpdateAllDto>[]; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = NotificationUpdateAllDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map<String, NotificationUpdateAllDto> mapFromJson(dynamic json) { + final map = <String, NotificationUpdateAllDto>{}; + if (json is Map && json.isNotEmpty) { + json = json.cast<String, dynamic>(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = NotificationUpdateAllDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of NotificationUpdateAllDto-objects as value to a dart map + static Map<String, List<NotificationUpdateAllDto>> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = <String, List<NotificationUpdateAllDto>>{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast<String, dynamic>(); + for (final entry in json.entries) { + map[entry.key] = NotificationUpdateAllDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = <String>{ + 'ids', + }; +} + diff --git a/mobile/openapi/lib/model/avatar_response.dart b/mobile/openapi/lib/model/notification_update_dto.dart similarity index 51% rename from mobile/openapi/lib/model/avatar_response.dart rename to mobile/openapi/lib/model/notification_update_dto.dart index 8ce0287565..e76496eb97 100644 --- a/mobile/openapi/lib/model/avatar_response.dart +++ b/mobile/openapi/lib/model/notification_update_dto.dart @@ -10,52 +10,56 @@ part of openapi.api; -class AvatarResponse { - /// Returns a new [AvatarResponse] instance. - AvatarResponse({ - required this.color, +class NotificationUpdateDto { + /// Returns a new [NotificationUpdateDto] instance. + NotificationUpdateDto({ + this.readAt, }); - UserAvatarColor color; + DateTime? readAt; @override - bool operator ==(Object other) => identical(this, other) || other is AvatarResponse && - other.color == color; + bool operator ==(Object other) => identical(this, other) || other is NotificationUpdateDto && + other.readAt == readAt; @override int get hashCode => // ignore: unnecessary_parenthesis - (color.hashCode); + (readAt == null ? 0 : readAt!.hashCode); @override - String toString() => 'AvatarResponse[color=$color]'; + String toString() => 'NotificationUpdateDto[readAt=$readAt]'; Map<String, dynamic> toJson() { final json = <String, dynamic>{}; - json[r'color'] = this.color; + if (this.readAt != null) { + json[r'readAt'] = this.readAt!.toUtc().toIso8601String(); + } else { + // json[r'readAt'] = null; + } return json; } - /// Returns a new [AvatarResponse] instance and imports its values from + /// Returns a new [NotificationUpdateDto] instance and imports its values from /// [value] if it's a [Map], null otherwise. // ignore: prefer_constructors_over_static_methods - static AvatarResponse? fromJson(dynamic value) { - upgradeDto(value, "AvatarResponse"); + static NotificationUpdateDto? fromJson(dynamic value) { + upgradeDto(value, "NotificationUpdateDto"); if (value is Map) { final json = value.cast<String, dynamic>(); - return AvatarResponse( - color: UserAvatarColor.fromJson(json[r'color'])!, + return NotificationUpdateDto( + readAt: mapDateTime(json, r'readAt', r''), ); } return null; } - static List<AvatarResponse> listFromJson(dynamic json, {bool growable = false,}) { - final result = <AvatarResponse>[]; + static List<NotificationUpdateDto> listFromJson(dynamic json, {bool growable = false,}) { + final result = <NotificationUpdateDto>[]; if (json is List && json.isNotEmpty) { for (final row in json) { - final value = AvatarResponse.fromJson(row); + final value = NotificationUpdateDto.fromJson(row); if (value != null) { result.add(value); } @@ -64,12 +68,12 @@ class AvatarResponse { return result.toList(growable: growable); } - static Map<String, AvatarResponse> mapFromJson(dynamic json) { - final map = <String, AvatarResponse>{}; + static Map<String, NotificationUpdateDto> mapFromJson(dynamic json) { + final map = <String, NotificationUpdateDto>{}; if (json is Map && json.isNotEmpty) { json = json.cast<String, dynamic>(); // ignore: parameter_assignments for (final entry in json.entries) { - final value = AvatarResponse.fromJson(entry.value); + final value = NotificationUpdateDto.fromJson(entry.value); if (value != null) { map[entry.key] = value; } @@ -78,14 +82,14 @@ class AvatarResponse { return map; } - // maps a json object with a list of AvatarResponse-objects as value to a dart map - static Map<String, List<AvatarResponse>> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = <String, List<AvatarResponse>>{}; + // maps a json object with a list of NotificationUpdateDto-objects as value to a dart map + static Map<String, List<NotificationUpdateDto>> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = <String, List<NotificationUpdateDto>>{}; if (json is Map && json.isNotEmpty) { // ignore: parameter_assignments json = json.cast<String, dynamic>(); for (final entry in json.entries) { - map[entry.key] = AvatarResponse.listFromJson(entry.value, growable: growable,); + map[entry.key] = NotificationUpdateDto.listFromJson(entry.value, growable: growable,); } } return map; @@ -93,7 +97,6 @@ class AvatarResponse { /// The list of required keys that must be present in a JSON. static const requiredKeys = <String>{ - 'color', }; } diff --git a/mobile/openapi/lib/model/o_auth_callback_dto.dart b/mobile/openapi/lib/model/o_auth_callback_dto.dart index d0b98d5c6f..ea8cac31a0 100644 --- a/mobile/openapi/lib/model/o_auth_callback_dto.dart +++ b/mobile/openapi/lib/model/o_auth_callback_dto.dart @@ -13,25 +13,57 @@ part of openapi.api; class OAuthCallbackDto { /// Returns a new [OAuthCallbackDto] instance. OAuthCallbackDto({ + this.codeVerifier, + this.state, required this.url, }); + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? codeVerifier; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? state; + String url; @override bool operator ==(Object other) => identical(this, other) || other is OAuthCallbackDto && + other.codeVerifier == codeVerifier && + other.state == state && other.url == url; @override int get hashCode => // ignore: unnecessary_parenthesis + (codeVerifier == null ? 0 : codeVerifier!.hashCode) + + (state == null ? 0 : state!.hashCode) + (url.hashCode); @override - String toString() => 'OAuthCallbackDto[url=$url]'; + String toString() => 'OAuthCallbackDto[codeVerifier=$codeVerifier, state=$state, url=$url]'; Map<String, dynamic> toJson() { final json = <String, dynamic>{}; + if (this.codeVerifier != null) { + json[r'codeVerifier'] = this.codeVerifier; + } else { + // json[r'codeVerifier'] = null; + } + if (this.state != null) { + json[r'state'] = this.state; + } else { + // json[r'state'] = null; + } json[r'url'] = this.url; return json; } @@ -45,6 +77,8 @@ class OAuthCallbackDto { final json = value.cast<String, dynamic>(); return OAuthCallbackDto( + codeVerifier: mapValueOfType<String>(json, r'codeVerifier'), + state: mapValueOfType<String>(json, r'state'), url: mapValueOfType<String>(json, r'url')!, ); } diff --git a/mobile/openapi/lib/model/o_auth_config_dto.dart b/mobile/openapi/lib/model/o_auth_config_dto.dart index 86c79b4e04..bb3e8d448d 100644 --- a/mobile/openapi/lib/model/o_auth_config_dto.dart +++ b/mobile/openapi/lib/model/o_auth_config_dto.dart @@ -13,26 +13,58 @@ part of openapi.api; class OAuthConfigDto { /// Returns a new [OAuthConfigDto] instance. OAuthConfigDto({ + this.codeChallenge, required this.redirectUri, + this.state, }); + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? codeChallenge; + String redirectUri; + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? state; + @override bool operator ==(Object other) => identical(this, other) || other is OAuthConfigDto && - other.redirectUri == redirectUri; + other.codeChallenge == codeChallenge && + other.redirectUri == redirectUri && + other.state == state; @override int get hashCode => // ignore: unnecessary_parenthesis - (redirectUri.hashCode); + (codeChallenge == null ? 0 : codeChallenge!.hashCode) + + (redirectUri.hashCode) + + (state == null ? 0 : state!.hashCode); @override - String toString() => 'OAuthConfigDto[redirectUri=$redirectUri]'; + String toString() => 'OAuthConfigDto[codeChallenge=$codeChallenge, redirectUri=$redirectUri, state=$state]'; Map<String, dynamic> toJson() { final json = <String, dynamic>{}; + if (this.codeChallenge != null) { + json[r'codeChallenge'] = this.codeChallenge; + } else { + // json[r'codeChallenge'] = null; + } json[r'redirectUri'] = this.redirectUri; + if (this.state != null) { + json[r'state'] = this.state; + } else { + // json[r'state'] = null; + } return json; } @@ -45,7 +77,9 @@ class OAuthConfigDto { final json = value.cast<String, dynamic>(); return OAuthConfigDto( + codeChallenge: mapValueOfType<String>(json, r'codeChallenge'), redirectUri: mapValueOfType<String>(json, r'redirectUri')!, + state: mapValueOfType<String>(json, r'state'), ); } return null; diff --git a/mobile/openapi/lib/model/o_auth_token_endpoint_auth_method.dart b/mobile/openapi/lib/model/o_auth_token_endpoint_auth_method.dart new file mode 100644 index 0000000000..fc528888b3 --- /dev/null +++ b/mobile/openapi/lib/model/o_auth_token_endpoint_auth_method.dart @@ -0,0 +1,85 @@ +// +// 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 OAuthTokenEndpointAuthMethod { + /// Instantiate a new enum with the provided [value]. + const OAuthTokenEndpointAuthMethod._(this.value); + + /// The underlying value of this enum member. + final String value; + + @override + String toString() => value; + + String toJson() => value; + + static const post = OAuthTokenEndpointAuthMethod._(r'client_secret_post'); + static const basic = OAuthTokenEndpointAuthMethod._(r'client_secret_basic'); + + /// List of all possible values in this [enum][OAuthTokenEndpointAuthMethod]. + static const values = <OAuthTokenEndpointAuthMethod>[ + post, + basic, + ]; + + static OAuthTokenEndpointAuthMethod? fromJson(dynamic value) => OAuthTokenEndpointAuthMethodTypeTransformer().decode(value); + + static List<OAuthTokenEndpointAuthMethod> listFromJson(dynamic json, {bool growable = false,}) { + final result = <OAuthTokenEndpointAuthMethod>[]; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = OAuthTokenEndpointAuthMethod.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } +} + +/// Transformation class that can [encode] an instance of [OAuthTokenEndpointAuthMethod] to String, +/// and [decode] dynamic data back to [OAuthTokenEndpointAuthMethod]. +class OAuthTokenEndpointAuthMethodTypeTransformer { + factory OAuthTokenEndpointAuthMethodTypeTransformer() => _instance ??= const OAuthTokenEndpointAuthMethodTypeTransformer._(); + + const OAuthTokenEndpointAuthMethodTypeTransformer._(); + + String encode(OAuthTokenEndpointAuthMethod data) => data.value; + + /// Decodes a [dynamic value][data] to a OAuthTokenEndpointAuthMethod. + /// + /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, + /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] + /// cannot be decoded successfully, then an [UnimplementedError] is thrown. + /// + /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, + /// and users are still using an old app with the old code. + OAuthTokenEndpointAuthMethod? decode(dynamic data, {bool allowNull = true}) { + if (data != null) { + switch (data) { + case r'client_secret_post': return OAuthTokenEndpointAuthMethod.post; + case r'client_secret_basic': return OAuthTokenEndpointAuthMethod.basic; + default: + if (!allowNull) { + throw ArgumentError('Unknown enum value to decode: $data'); + } + } + } + return null; + } + + /// Singleton [OAuthTokenEndpointAuthMethodTypeTransformer] instance. + static OAuthTokenEndpointAuthMethodTypeTransformer? _instance; +} + diff --git a/mobile/openapi/lib/model/path_entity_type.dart b/mobile/openapi/lib/model/path_entity_type.dart deleted file mode 100644 index fdcdae4f1b..0000000000 --- a/mobile/openapi/lib/model/path_entity_type.dart +++ /dev/null @@ -1,88 +0,0 @@ -// -// 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 PathEntityType { - /// Instantiate a new enum with the provided [value]. - const PathEntityType._(this.value); - - /// The underlying value of this enum member. - final String value; - - @override - String toString() => value; - - String toJson() => value; - - static const asset = PathEntityType._(r'asset'); - static const person = PathEntityType._(r'person'); - static const user = PathEntityType._(r'user'); - - /// List of all possible values in this [enum][PathEntityType]. - static const values = <PathEntityType>[ - asset, - person, - user, - ]; - - static PathEntityType? fromJson(dynamic value) => PathEntityTypeTypeTransformer().decode(value); - - static List<PathEntityType> listFromJson(dynamic json, {bool growable = false,}) { - final result = <PathEntityType>[]; - if (json is List && json.isNotEmpty) { - for (final row in json) { - final value = PathEntityType.fromJson(row); - if (value != null) { - result.add(value); - } - } - } - return result.toList(growable: growable); - } -} - -/// Transformation class that can [encode] an instance of [PathEntityType] to String, -/// and [decode] dynamic data back to [PathEntityType]. -class PathEntityTypeTypeTransformer { - factory PathEntityTypeTypeTransformer() => _instance ??= const PathEntityTypeTypeTransformer._(); - - const PathEntityTypeTypeTransformer._(); - - String encode(PathEntityType data) => data.value; - - /// Decodes a [dynamic value][data] to a PathEntityType. - /// - /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, - /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] - /// cannot be decoded successfully, then an [UnimplementedError] is thrown. - /// - /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, - /// and users are still using an old app with the old code. - PathEntityType? decode(dynamic data, {bool allowNull = true}) { - if (data != null) { - switch (data) { - case r'asset': return PathEntityType.asset; - case r'person': return PathEntityType.person; - case r'user': return PathEntityType.user; - default: - if (!allowNull) { - throw ArgumentError('Unknown enum value to decode: $data'); - } - } - } - return null; - } - - /// Singleton [PathEntityTypeTypeTransformer] instance. - static PathEntityTypeTypeTransformer? _instance; -} - diff --git a/mobile/openapi/lib/model/path_type.dart b/mobile/openapi/lib/model/path_type.dart deleted file mode 100644 index 55453ed1e8..0000000000 --- a/mobile/openapi/lib/model/path_type.dart +++ /dev/null @@ -1,103 +0,0 @@ -// -// 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 PathType { - /// Instantiate a new enum with the provided [value]. - const PathType._(this.value); - - /// The underlying value of this enum member. - final String value; - - @override - String toString() => value; - - String toJson() => value; - - static const original = PathType._(r'original'); - static const fullsize = PathType._(r'fullsize'); - static const preview = PathType._(r'preview'); - static const thumbnail = PathType._(r'thumbnail'); - static const encodedVideo = PathType._(r'encoded_video'); - static const sidecar = PathType._(r'sidecar'); - static const face = PathType._(r'face'); - static const profile = PathType._(r'profile'); - - /// List of all possible values in this [enum][PathType]. - static const values = <PathType>[ - original, - fullsize, - preview, - thumbnail, - encodedVideo, - sidecar, - face, - profile, - ]; - - static PathType? fromJson(dynamic value) => PathTypeTypeTransformer().decode(value); - - static List<PathType> listFromJson(dynamic json, {bool growable = false,}) { - final result = <PathType>[]; - if (json is List && json.isNotEmpty) { - for (final row in json) { - final value = PathType.fromJson(row); - if (value != null) { - result.add(value); - } - } - } - return result.toList(growable: growable); - } -} - -/// Transformation class that can [encode] an instance of [PathType] to String, -/// and [decode] dynamic data back to [PathType]. -class PathTypeTypeTransformer { - factory PathTypeTypeTransformer() => _instance ??= const PathTypeTypeTransformer._(); - - const PathTypeTypeTransformer._(); - - String encode(PathType data) => data.value; - - /// Decodes a [dynamic value][data] to a PathType. - /// - /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, - /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] - /// cannot be decoded successfully, then an [UnimplementedError] is thrown. - /// - /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, - /// and users are still using an old app with the old code. - PathType? decode(dynamic data, {bool allowNull = true}) { - if (data != null) { - switch (data) { - case r'original': return PathType.original; - case r'fullsize': return PathType.fullsize; - case r'preview': return PathType.preview; - case r'thumbnail': return PathType.thumbnail; - case r'encoded_video': return PathType.encodedVideo; - case r'sidecar': return PathType.sidecar; - case r'face': return PathType.face; - case r'profile': return PathType.profile; - default: - if (!allowNull) { - throw ArgumentError('Unknown enum value to decode: $data'); - } - } - } - return null; - } - - /// Singleton [PathTypeTypeTransformer] instance. - static PathTypeTypeTransformer? _instance; -} - diff --git a/mobile/openapi/lib/model/permission.dart b/mobile/openapi/lib/model/permission.dart index 1244a434b6..1735bc2eb5 100644 --- a/mobile/openapi/lib/model/permission.dart +++ b/mobile/openapi/lib/model/permission.dart @@ -66,6 +66,10 @@ class Permission { static const memoryPeriodRead = Permission._(r'memory.read'); static const memoryPeriodUpdate = Permission._(r'memory.update'); static const memoryPeriodDelete = Permission._(r'memory.delete'); + static const notificationPeriodCreate = Permission._(r'notification.create'); + static const notificationPeriodRead = Permission._(r'notification.read'); + static const notificationPeriodUpdate = Permission._(r'notification.update'); + static const notificationPeriodDelete = Permission._(r'notification.delete'); static const partnerPeriodCreate = Permission._(r'partner.create'); static const partnerPeriodRead = Permission._(r'partner.read'); static const partnerPeriodUpdate = Permission._(r'partner.update'); @@ -147,6 +151,10 @@ class Permission { memoryPeriodRead, memoryPeriodUpdate, memoryPeriodDelete, + notificationPeriodCreate, + notificationPeriodRead, + notificationPeriodUpdate, + notificationPeriodDelete, partnerPeriodCreate, partnerPeriodRead, partnerPeriodUpdate, @@ -263,6 +271,10 @@ class PermissionTypeTransformer { case r'memory.read': return Permission.memoryPeriodRead; case r'memory.update': return Permission.memoryPeriodUpdate; case r'memory.delete': return Permission.memoryPeriodDelete; + case r'notification.create': return Permission.notificationPeriodCreate; + case r'notification.read': return Permission.notificationPeriodRead; + case r'notification.update': return Permission.notificationPeriodUpdate; + case r'notification.delete': return Permission.notificationPeriodDelete; case r'partner.create': return Permission.partnerPeriodCreate; case r'partner.read': return Permission.partnerPeriodRead; case r'partner.update': return Permission.partnerPeriodUpdate; diff --git a/mobile/openapi/lib/model/system_config_o_auth_dto.dart b/mobile/openapi/lib/model/system_config_o_auth_dto.dart index 9125bb7bba..24384a47b1 100644 --- a/mobile/openapi/lib/model/system_config_o_auth_dto.dart +++ b/mobile/openapi/lib/model/system_config_o_auth_dto.dart @@ -28,6 +28,8 @@ class SystemConfigOAuthDto { required this.signingAlgorithm, required this.storageLabelClaim, required this.storageQuotaClaim, + required this.timeout, + required this.tokenEndpointAuthMethod, }); bool autoLaunch; @@ -61,6 +63,11 @@ class SystemConfigOAuthDto { String storageQuotaClaim; + /// Minimum value: 1 + int timeout; + + OAuthTokenEndpointAuthMethod tokenEndpointAuthMethod; + @override bool operator ==(Object other) => identical(this, other) || other is SystemConfigOAuthDto && other.autoLaunch == autoLaunch && @@ -77,7 +84,9 @@ class SystemConfigOAuthDto { other.scope == scope && other.signingAlgorithm == signingAlgorithm && other.storageLabelClaim == storageLabelClaim && - other.storageQuotaClaim == storageQuotaClaim; + other.storageQuotaClaim == storageQuotaClaim && + other.timeout == timeout && + other.tokenEndpointAuthMethod == tokenEndpointAuthMethod; @override int get hashCode => @@ -96,10 +105,12 @@ class SystemConfigOAuthDto { (scope.hashCode) + (signingAlgorithm.hashCode) + (storageLabelClaim.hashCode) + - (storageQuotaClaim.hashCode); + (storageQuotaClaim.hashCode) + + (timeout.hashCode) + + (tokenEndpointAuthMethod.hashCode); @override - String toString() => 'SystemConfigOAuthDto[autoLaunch=$autoLaunch, autoRegister=$autoRegister, buttonText=$buttonText, clientId=$clientId, clientSecret=$clientSecret, defaultStorageQuota=$defaultStorageQuota, enabled=$enabled, issuerUrl=$issuerUrl, mobileOverrideEnabled=$mobileOverrideEnabled, mobileRedirectUri=$mobileRedirectUri, profileSigningAlgorithm=$profileSigningAlgorithm, scope=$scope, signingAlgorithm=$signingAlgorithm, storageLabelClaim=$storageLabelClaim, storageQuotaClaim=$storageQuotaClaim]'; + String toString() => 'SystemConfigOAuthDto[autoLaunch=$autoLaunch, autoRegister=$autoRegister, buttonText=$buttonText, clientId=$clientId, clientSecret=$clientSecret, defaultStorageQuota=$defaultStorageQuota, enabled=$enabled, issuerUrl=$issuerUrl, mobileOverrideEnabled=$mobileOverrideEnabled, mobileRedirectUri=$mobileRedirectUri, profileSigningAlgorithm=$profileSigningAlgorithm, scope=$scope, signingAlgorithm=$signingAlgorithm, storageLabelClaim=$storageLabelClaim, storageQuotaClaim=$storageQuotaClaim, timeout=$timeout, tokenEndpointAuthMethod=$tokenEndpointAuthMethod]'; Map<String, dynamic> toJson() { final json = <String, dynamic>{}; @@ -118,6 +129,8 @@ class SystemConfigOAuthDto { json[r'signingAlgorithm'] = this.signingAlgorithm; json[r'storageLabelClaim'] = this.storageLabelClaim; json[r'storageQuotaClaim'] = this.storageQuotaClaim; + json[r'timeout'] = this.timeout; + json[r'tokenEndpointAuthMethod'] = this.tokenEndpointAuthMethod; return json; } @@ -145,6 +158,8 @@ class SystemConfigOAuthDto { signingAlgorithm: mapValueOfType<String>(json, r'signingAlgorithm')!, storageLabelClaim: mapValueOfType<String>(json, r'storageLabelClaim')!, storageQuotaClaim: mapValueOfType<String>(json, r'storageQuotaClaim')!, + timeout: mapValueOfType<int>(json, r'timeout')!, + tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod.fromJson(json[r'tokenEndpointAuthMethod'])!, ); } return null; @@ -207,6 +222,8 @@ class SystemConfigOAuthDto { 'signingAlgorithm', 'storageLabelClaim', 'storageQuotaClaim', + 'timeout', + 'tokenEndpointAuthMethod', }; } diff --git a/mobile/openapi/lib/model/user_admin_create_dto.dart b/mobile/openapi/lib/model/user_admin_create_dto.dart index 4bd1266426..1477c82ca1 100644 --- a/mobile/openapi/lib/model/user_admin_create_dto.dart +++ b/mobile/openapi/lib/model/user_admin_create_dto.dart @@ -13,6 +13,7 @@ part of openapi.api; class UserAdminCreateDto { /// Returns a new [UserAdminCreateDto] instance. UserAdminCreateDto({ + this.avatarColor, required this.email, required this.name, this.notify, @@ -22,6 +23,8 @@ class UserAdminCreateDto { this.storageLabel, }); + UserAvatarColor? avatarColor; + String email; String name; @@ -51,6 +54,7 @@ class UserAdminCreateDto { @override bool operator ==(Object other) => identical(this, other) || other is UserAdminCreateDto && + other.avatarColor == avatarColor && other.email == email && other.name == name && other.notify == notify && @@ -62,6 +66,7 @@ class UserAdminCreateDto { @override int get hashCode => // ignore: unnecessary_parenthesis + (avatarColor == null ? 0 : avatarColor!.hashCode) + (email.hashCode) + (name.hashCode) + (notify == null ? 0 : notify!.hashCode) + @@ -71,10 +76,15 @@ class UserAdminCreateDto { (storageLabel == null ? 0 : storageLabel!.hashCode); @override - String toString() => 'UserAdminCreateDto[email=$email, name=$name, notify=$notify, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]'; + String toString() => 'UserAdminCreateDto[avatarColor=$avatarColor, email=$email, name=$name, notify=$notify, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]'; Map<String, dynamic> toJson() { final json = <String, dynamic>{}; + if (this.avatarColor != null) { + json[r'avatarColor'] = this.avatarColor; + } else { + // json[r'avatarColor'] = null; + } json[r'email'] = this.email; json[r'name'] = this.name; if (this.notify != null) { @@ -110,6 +120,7 @@ class UserAdminCreateDto { final json = value.cast<String, dynamic>(); return UserAdminCreateDto( + avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), email: mapValueOfType<String>(json, r'email')!, name: mapValueOfType<String>(json, r'name')!, notify: mapValueOfType<bool>(json, r'notify'), diff --git a/mobile/openapi/lib/model/user_admin_update_dto.dart b/mobile/openapi/lib/model/user_admin_update_dto.dart index f0478c9b4c..951ee8ce84 100644 --- a/mobile/openapi/lib/model/user_admin_update_dto.dart +++ b/mobile/openapi/lib/model/user_admin_update_dto.dart @@ -13,6 +13,7 @@ part of openapi.api; class UserAdminUpdateDto { /// Returns a new [UserAdminUpdateDto] instance. UserAdminUpdateDto({ + this.avatarColor, this.email, this.name, this.password, @@ -21,6 +22,8 @@ class UserAdminUpdateDto { this.storageLabel, }); + UserAvatarColor? avatarColor; + /// /// Please note: This property should have been non-nullable! Since the specification file /// does not include a default value (using the "default:" property), however, the generated @@ -60,6 +63,7 @@ class UserAdminUpdateDto { @override bool operator ==(Object other) => identical(this, other) || other is UserAdminUpdateDto && + other.avatarColor == avatarColor && other.email == email && other.name == name && other.password == password && @@ -70,6 +74,7 @@ class UserAdminUpdateDto { @override int get hashCode => // ignore: unnecessary_parenthesis + (avatarColor == null ? 0 : avatarColor!.hashCode) + (email == null ? 0 : email!.hashCode) + (name == null ? 0 : name!.hashCode) + (password == null ? 0 : password!.hashCode) + @@ -78,10 +83,15 @@ class UserAdminUpdateDto { (storageLabel == null ? 0 : storageLabel!.hashCode); @override - String toString() => 'UserAdminUpdateDto[email=$email, name=$name, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]'; + String toString() => 'UserAdminUpdateDto[avatarColor=$avatarColor, email=$email, name=$name, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]'; Map<String, dynamic> toJson() { final json = <String, dynamic>{}; + if (this.avatarColor != null) { + json[r'avatarColor'] = this.avatarColor; + } else { + // json[r'avatarColor'] = null; + } if (this.email != null) { json[r'email'] = this.email; } else { @@ -124,6 +134,7 @@ class UserAdminUpdateDto { final json = value.cast<String, dynamic>(); return UserAdminUpdateDto( + avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), email: mapValueOfType<String>(json, r'email'), name: mapValueOfType<String>(json, r'name'), password: mapValueOfType<String>(json, r'password'), diff --git a/mobile/openapi/lib/model/user_preferences_response_dto.dart b/mobile/openapi/lib/model/user_preferences_response_dto.dart index b244284eb0..215e691cb1 100644 --- a/mobile/openapi/lib/model/user_preferences_response_dto.dart +++ b/mobile/openapi/lib/model/user_preferences_response_dto.dart @@ -13,7 +13,6 @@ part of openapi.api; class UserPreferencesResponseDto { /// Returns a new [UserPreferencesResponseDto] instance. UserPreferencesResponseDto({ - required this.avatar, required this.download, required this.emailNotifications, required this.folders, @@ -25,8 +24,6 @@ class UserPreferencesResponseDto { required this.tags, }); - AvatarResponse avatar; - DownloadResponse download; EmailNotificationsResponse emailNotifications; @@ -47,7 +44,6 @@ class UserPreferencesResponseDto { @override bool operator ==(Object other) => identical(this, other) || other is UserPreferencesResponseDto && - other.avatar == avatar && other.download == download && other.emailNotifications == emailNotifications && other.folders == folders && @@ -61,7 +57,6 @@ class UserPreferencesResponseDto { @override int get hashCode => // ignore: unnecessary_parenthesis - (avatar.hashCode) + (download.hashCode) + (emailNotifications.hashCode) + (folders.hashCode) + @@ -73,11 +68,10 @@ class UserPreferencesResponseDto { (tags.hashCode); @override - String toString() => 'UserPreferencesResponseDto[avatar=$avatar, download=$download, emailNotifications=$emailNotifications, folders=$folders, memories=$memories, people=$people, purchase=$purchase, ratings=$ratings, sharedLinks=$sharedLinks, tags=$tags]'; + String toString() => 'UserPreferencesResponseDto[download=$download, emailNotifications=$emailNotifications, folders=$folders, memories=$memories, people=$people, purchase=$purchase, ratings=$ratings, sharedLinks=$sharedLinks, tags=$tags]'; Map<String, dynamic> toJson() { final json = <String, dynamic>{}; - json[r'avatar'] = this.avatar; json[r'download'] = this.download; json[r'emailNotifications'] = this.emailNotifications; json[r'folders'] = this.folders; @@ -99,7 +93,6 @@ class UserPreferencesResponseDto { final json = value.cast<String, dynamic>(); return UserPreferencesResponseDto( - avatar: AvatarResponse.fromJson(json[r'avatar'])!, download: DownloadResponse.fromJson(json[r'download'])!, emailNotifications: EmailNotificationsResponse.fromJson(json[r'emailNotifications'])!, folders: FoldersResponse.fromJson(json[r'folders'])!, @@ -156,7 +149,6 @@ class UserPreferencesResponseDto { /// The list of required keys that must be present in a JSON. static const requiredKeys = <String>{ - 'avatar', 'download', 'emailNotifications', 'folders', diff --git a/mobile/openapi/lib/model/user_update_me_dto.dart b/mobile/openapi/lib/model/user_update_me_dto.dart index 8f3f4df37a..779e07ffa6 100644 --- a/mobile/openapi/lib/model/user_update_me_dto.dart +++ b/mobile/openapi/lib/model/user_update_me_dto.dart @@ -13,11 +13,14 @@ part of openapi.api; class UserUpdateMeDto { /// Returns a new [UserUpdateMeDto] instance. UserUpdateMeDto({ + this.avatarColor, this.email, this.name, this.password, }); + UserAvatarColor? avatarColor; + /// /// Please note: This property should have been non-nullable! Since the specification file /// does not include a default value (using the "default:" property), however, the generated @@ -44,6 +47,7 @@ class UserUpdateMeDto { @override bool operator ==(Object other) => identical(this, other) || other is UserUpdateMeDto && + other.avatarColor == avatarColor && other.email == email && other.name == name && other.password == password; @@ -51,15 +55,21 @@ class UserUpdateMeDto { @override int get hashCode => // ignore: unnecessary_parenthesis + (avatarColor == null ? 0 : avatarColor!.hashCode) + (email == null ? 0 : email!.hashCode) + (name == null ? 0 : name!.hashCode) + (password == null ? 0 : password!.hashCode); @override - String toString() => 'UserUpdateMeDto[email=$email, name=$name, password=$password]'; + String toString() => 'UserUpdateMeDto[avatarColor=$avatarColor, email=$email, name=$name, password=$password]'; Map<String, dynamic> toJson() { final json = <String, dynamic>{}; + if (this.avatarColor != null) { + json[r'avatarColor'] = this.avatarColor; + } else { + // json[r'avatarColor'] = null; + } if (this.email != null) { json[r'email'] = this.email; } else { @@ -87,6 +97,7 @@ class UserUpdateMeDto { final json = value.cast<String, dynamic>(); return UserUpdateMeDto( + avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), email: mapValueOfType<String>(json, r'email'), name: mapValueOfType<String>(json, r'name'), password: mapValueOfType<String>(json, r'password'), diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 0c99fe19fe..d9e821d332 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -303,7 +303,7 @@ packages: source: hosted version: "0.3.4+2" crypto: - dependency: transitive + dependency: "direct main" description: name: crypto sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" @@ -696,18 +696,18 @@ packages: dependency: "direct main" description: name: geolocator - sha256: "6cb9fb6e5928b58b9a84bdf85012d757fd07aab8215c5205337021c4999bad27" + sha256: e7ebfa04ce451daf39b5499108c973189a71a919aa53c1204effda1c5b93b822 url: "https://pub.dev" source: hosted - version: "11.1.0" + version: "14.0.0" geolocator_android: dependency: transitive description: name: geolocator_android - sha256: "7aefc530db47d90d0580b552df3242440a10fe60814496a979aa67aa98b1fd47" + sha256: "114072db5d1dce0ec0b36af2697f55c133bc89a2c8dd513e137c0afe59696ed4" url: "https://pub.dev" source: hosted - version: "4.6.1" + version: "5.0.1+1" geolocator_apple: dependency: transitive description: @@ -728,10 +728,10 @@ packages: dependency: transitive description: name: geolocator_web - sha256: "49d8f846ebeb5e2b6641fe477a7e97e5dd73f03cbfef3fd5c42177b7300fb0ed" + sha256: b1ae9bdfd90f861fde8fd4f209c37b953d65e92823cb73c7dee1fa021b06f172 url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "4.1.3" geolocator_windows: dependency: transitive description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 324b7c9e9e..cf0b9b9106 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -2,7 +2,7 @@ name: immich_mobile description: Immich - selfhosted backup media file on mobile phone publish_to: 'none' -version: 1.131.3+193 +version: 1.132.3+197 environment: sdk: '>=3.3.0 <4.0.0' @@ -22,6 +22,7 @@ dependencies: collection: ^1.18.0 connectivity_plus: ^6.1.3 crop_image: ^1.0.16 + crypto: ^3.0.6 device_info_plus: ^11.3.3 dynamic_color: ^1.7.0 easy_image_viewer: ^1.5.1 @@ -35,7 +36,7 @@ dependencies: flutter_udid: ^3.0.0 flutter_web_auth_2: ^5.0.0-alpha.0 fluttertoast: ^8.2.12 - geolocator: ^11.0.0 + geolocator: ^14.0.0 hooks_riverpod: ^2.6.1 http: ^1.3.0 image_picker: ^1.1.2 diff --git a/mobile/test/domain/services/device_sync_service_test.dart b/mobile/test/domain/services/device_sync_service_test.dart index a8c5894fec..d8424f903e 100644 --- a/mobile/test/domain/services/device_sync_service_test.dart +++ b/mobile/test/domain/services/device_sync_service_test.dart @@ -3,7 +3,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:immich_mobile/domain/interfaces/album_media.interface.dart'; import 'package:immich_mobile/domain/interfaces/local_album.interface.dart'; import 'package:immich_mobile/domain/interfaces/local_asset.interface.dart'; -import 'package:immich_mobile/domain/models/asset/asset.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/local_album.model.dart'; import 'package:immich_mobile/domain/services/device_sync.service.dart'; import 'package:immich_mobile/utils/nullable_value.dart'; @@ -257,9 +257,9 @@ void main() { newAlbum.copyWith(updatedAt: DateTime(2024), assetCount: 2); final assets = [ LocalAssetStub.image1 - .copyWith(localId: "asset1", createdAt: DateTime(2024, 1, 1)), + .copyWith(id: "asset1", createdAt: DateTime(2024, 1, 1)), LocalAssetStub.image2.copyWith( - localId: "asset2", + id: "asset2", createdAt: DateTime(2024, 1, 2), ), ]; @@ -284,7 +284,7 @@ void main() { expect(capturedAlbum.id, newAlbum.id); expect(capturedAlbum.assetCount, refreshedAlbum.assetCount); expect(capturedAlbum.updatedAt, refreshedAlbum.updatedAt); - expect(capturedAlbum.thumbnailId, assets.first.localId); + expect(capturedAlbum.thumbnailId, assets.first.id); expect(listEquals(capturedAssets, assets), isTrue); }, ); @@ -354,7 +354,7 @@ void main() { when(() => mockAlbumMediaRepo.refresh(dbAlbum.id)) .thenAnswer((_) async => refreshedAlbum); - final newAsset = LocalAssetStub.image2.copyWith(localId: "new_asset"); + final newAsset = LocalAssetStub.image2.copyWith(id: "new_asset"); when( () => mockAlbumMediaRepo.getAssetsForAlbum( dbAlbum.id, @@ -388,7 +388,7 @@ void main() { (a) => a.id == dbAlbum.id && a.assetCount == 2 && - a.thumbnailId == newAsset.localId, + a.thumbnailId == newAsset.id, ), ), ), @@ -447,7 +447,7 @@ void main() { ).called(1); verify( () => mockLocalAlbumRepo - .removeAssets(dbAlbum.id, [LocalAssetStub.image1.localId]), + .removeAssets(dbAlbum.id, [LocalAssetStub.image1.id]), ).called(1); }, ); @@ -520,7 +520,7 @@ void main() { test('returns true and updates assets/metadata on success', () async { final newAsset = LocalAssetStub.image2.copyWith( - localId: "asset2", + id: "asset2", createdAt: DateTime(2024, 1, 1, 10, 30, 0), ); when( @@ -532,7 +532,7 @@ void main() { when(() => mockLocalAssetRepo.get("thumb1")).thenAnswer( (_) async => LocalAssetStub.image1.copyWith( - localId: "thumb1", + id: "thumb1", createdAt: DateTime(2024, 1, 1, 9, 0, 0), ), ); @@ -557,7 +557,7 @@ void main() { a.id == dbAlbum.id && a.assetCount == 2 && a.updatedAt == refreshedAlbum.updatedAt && - a.thumbnailId == newAsset.localId, + a.thumbnailId == newAsset.id, ), ), ), @@ -568,7 +568,7 @@ void main() { test('returns true and keeps old thumbnail if newer', () async { final newAsset = LocalAssetStub.image2.copyWith( - localId: "asset2", + id: "asset2", createdAt: DateTime(2024, 1, 1, 8, 0, 0), ); when( @@ -580,7 +580,7 @@ void main() { when(() => mockLocalAssetRepo.get("thumb1")).thenAnswer( (_) async => LocalAssetStub.image1.copyWith( - localId: "thumb1", + id: "thumb1", createdAt: DateTime(2024, 1, 1, 9, 0, 0), ), ); @@ -616,7 +616,7 @@ void main() { final dbAlbumNoThumb = dbAlbum.copyWith(thumbnailId: const NullableValue.empty()); final newAsset = LocalAssetStub.image2.copyWith( - localId: "asset2", + id: "asset2", createdAt: DateTime(2024, 1, 1, 10, 30, 0), ); when( @@ -646,7 +646,7 @@ void main() { a.id == dbAlbum.id && a.assetCount == 2 && a.updatedAt == refreshedAlbum.updatedAt && - a.thumbnailId == newAsset.localId, + a.thumbnailId == newAsset.id, ), ), ), @@ -733,22 +733,22 @@ void main() { ); final dbAsset1 = LocalAssetStub.image1.copyWith( - localId: "asset1", + id: "asset1", createdAt: DateTime(2024), updatedAt: DateTime(2024), ); final dbAsset2 = LocalAssetStub.image2.copyWith( - localId: "asset2", + id: "asset2", createdAt: DateTime(2024), updatedAt: DateTime(2024), ); // To be deleted final deviceAsset1 = LocalAssetStub.image1.copyWith( - localId: "asset1", + id: "asset1", createdAt: DateTime(2024), updatedAt: DateTime(2025), ); // Updated final deviceAsset3 = LocalAssetStub.video1.copyWith( - localId: "asset3", + id: "asset3", createdAt: DateTime(2024), updatedAt: DateTime(2024), ); // Added @@ -821,7 +821,7 @@ void main() { a.id == emptyDbAlbum.id && a.assetCount == deviceAssets.length && a.updatedAt == refreshedWithAssets.updatedAt && - a.thumbnailId == deviceAssets.first.localId, + a.thumbnailId == deviceAssets.first.id, ), ), ), @@ -835,7 +835,7 @@ void main() { final deviceAssets = [deviceAsset1, deviceAsset3]; deviceAssets.sort((a, b) => a.createdAt.compareTo(b.createdAt)); final dbAssets = [dbAsset1, dbAsset2]; - dbAssets.sort((a, b) => a.localId.compareTo(b.localId)); + dbAssets.sort((a, b) => a.id.compareTo(b.id)); when(() => mockAlbumMediaRepo.getAssetsForAlbum(dbAlbum.id)).thenAnswer( (_) async => deviceAssets, @@ -859,10 +859,10 @@ void main() { return list.length == 2 && list.any( (a) => - a.localId == "asset1" && + a.id == "asset1" && a.updatedAt == deviceAsset1.updatedAt, ) && - list.any((a) => a.localId == "asset3"); + list.any((a) => a.id == "asset3"); }), ), ), @@ -876,7 +876,7 @@ void main() { a.id == dbAlbum.id && a.assetCount == 2 && a.updatedAt == currentRefreshedAlbum.updatedAt && - a.thumbnailId == deviceAssets.first.localId, + a.thumbnailId == deviceAssets.first.id, ), ), ), @@ -894,7 +894,7 @@ void main() { final dbAssets = [dbAsset1, dbAsset2]; final deviceAssets = [dbAsset1, dbAsset2]; deviceAssets.sort((a, b) => a.createdAt.compareTo(b.createdAt)); - dbAssets.sort((a, b) => a.localId.compareTo(b.localId)); + dbAssets.sort((a, b) => a.id.compareTo(b.id)); when(() => mockAlbumMediaRepo.getAssetsForAlbum(dbAlbum.id)) .thenAnswer((_) async => deviceAssets); @@ -917,7 +917,7 @@ void main() { a.id == dbAlbum.id && a.assetCount == 2 && a.updatedAt == currentRefreshedAlbum.updatedAt && - a.thumbnailId == deviceAssets.first.localId, + a.thumbnailId == deviceAssets.first.id, ), ), ), diff --git a/mobile/test/fixtures/local_album.stub.dart b/mobile/test/fixtures/local_album.stub.dart index 32d25ce254..517bdd5189 100644 --- a/mobile/test/fixtures/local_album.stub.dart +++ b/mobile/test/fixtures/local_album.stub.dart @@ -10,7 +10,6 @@ abstract final class LocalAlbumStub { assetCount: 1, thumbnailId: null, backupSelection: BackupSelection.none, - isAll: false, ); static LocalAlbum get album2 => LocalAlbum( @@ -20,7 +19,6 @@ abstract final class LocalAlbumStub { assetCount: 2, thumbnailId: null, backupSelection: BackupSelection.selected, - isAll: true, ); static LocalAlbum get album3 => LocalAlbum( @@ -30,6 +28,5 @@ abstract final class LocalAlbumStub { assetCount: 20, thumbnailId: "123", backupSelection: BackupSelection.excluded, - isAll: false, ); } diff --git a/mobile/test/fixtures/local_asset.stub.dart b/mobile/test/fixtures/local_asset.stub.dart index a399dfdc22..1d47e7abe5 100644 --- a/mobile/test/fixtures/local_asset.stub.dart +++ b/mobile/test/fixtures/local_asset.stub.dart @@ -1,10 +1,10 @@ -import 'package:immich_mobile/domain/models/asset/asset.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; abstract final class LocalAssetStub { const LocalAssetStub(); static LocalAsset get image1 => LocalAsset( - localId: "image1", + id: "image1", name: "image1.jpg", checksum: "image1-checksum", type: AssetType.image, @@ -16,7 +16,7 @@ abstract final class LocalAssetStub { ); static LocalAsset get image2 => LocalAsset( - localId: "image2", + id: "image2", name: "image2.jpg", checksum: "image2-checksum", type: AssetType.image, @@ -28,7 +28,7 @@ abstract final class LocalAssetStub { ); static LocalAsset get video1 => LocalAsset( - localId: "video1", + id: "video1", name: "video1.mov", checksum: "video1-checksum", type: AssetType.video, diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index c322420e59..a7919f07e7 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -206,6 +206,141 @@ ] } }, + "/admin/notifications": { + "post": { + "operationId": "createNotification", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationCreateDto" + } + } + }, + "required": true + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationDto" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Notifications (Admin)" + ] + } + }, + "/admin/notifications/templates/{name}": { + "post": { + "operationId": "getNotificationTemplateAdmin", + "parameters": [ + { + "name": "name", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TemplateDto" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TemplateResponseDto" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Notifications (Admin)" + ] + } + }, + "/admin/notifications/test-email": { + "post": { + "operationId": "sendTestEmailAdmin", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SystemConfigSmtpDto" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestEmailResponseDto" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Notifications (Admin)" + ] + } + }, "/admin/users": { "get": { "operationId": "searchUsersAdmin", @@ -3485,35 +3620,86 @@ ] } }, - "/notifications/templates/{name}": { - "post": { - "operationId": "getNotificationTemplate", - "parameters": [ - { - "name": "name", - "required": true, - "in": "path", - "schema": { - "type": "string" - } - } - ], + "/notifications": { + "delete": { + "operationId": "deleteNotifications", + "parameters": [], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TemplateDto" + "$ref": "#/components/schemas/NotificationDeleteAllDto" } } }, "required": true }, + "responses": { + "200": { + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Notifications" + ] + }, + "get": { + "operationId": "getNotifications", + "parameters": [ + { + "name": "id", + "required": false, + "in": "query", + "schema": { + "format": "uuid", + "type": "string" + } + }, + { + "name": "level", + "required": false, + "in": "query", + "schema": { + "$ref": "#/components/schemas/NotificationLevel" + } + }, + { + "name": "type", + "required": false, + "in": "query", + "schema": { + "$ref": "#/components/schemas/NotificationType" + } + }, + { + "name": "unread", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + } + ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TemplateResponseDto" + "items": { + "$ref": "#/components/schemas/NotificationDto" + }, + "type": "array" } } }, @@ -3534,17 +3720,133 @@ "tags": [ "Notifications" ] - } - }, - "/notifications/test-email": { - "post": { - "operationId": "sendTestEmail", + }, + "put": { + "operationId": "updateNotifications", "parameters": [], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SystemConfigSmtpDto" + "$ref": "#/components/schemas/NotificationUpdateAllDto" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Notifications" + ] + } + }, + "/notifications/{id}": { + "delete": { + "operationId": "deleteNotification", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Notifications" + ] + }, + "get": { + "operationId": "getNotification", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationDto" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Notifications" + ] + }, + "put": { + "operationId": "updateNotification", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationUpdateDto" } } }, @@ -3555,7 +3857,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TestEmailResponseDto" + "$ref": "#/components/schemas/NotificationDto" } } }, @@ -4349,118 +4651,6 @@ ] } }, - "/reports": { - "get": { - "operationId": "getAuditFiles", - "parameters": [], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FileReportDto" - } - } - }, - "description": "" - } - }, - "security": [ - { - "bearer": [] - }, - { - "cookie": [] - }, - { - "api_key": [] - } - ], - "tags": [ - "File Reports" - ] - } - }, - "/reports/checksum": { - "post": { - "operationId": "getFileChecksums", - "parameters": [], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FileChecksumDto" - } - } - }, - "required": true - }, - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "items": { - "$ref": "#/components/schemas/FileChecksumResponseDto" - }, - "type": "array" - } - } - }, - "description": "" - } - }, - "security": [ - { - "bearer": [] - }, - { - "cookie": [] - }, - { - "api_key": [] - } - ], - "tags": [ - "File Reports" - ] - } - }, - "/reports/fix": { - "post": { - "operationId": "fixAuditFiles", - "parameters": [], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FileReportFixDto" - } - } - }, - "required": true - }, - "responses": { - "201": { - "description": "" - } - }, - "security": [ - { - "bearer": [] - }, - { - "cookie": [] - }, - { - "api_key": [] - } - ], - "tags": [ - "File Reports" - ] - } - }, "/search/cities": { "get": { "operationId": "getAssetsByCity", @@ -7656,7 +7846,7 @@ "info": { "title": "Immich", "description": "Immich API", - "version": "1.131.3", + "version": "1.132.3", "contact": {} }, "tags": [], @@ -8884,21 +9074,6 @@ ], "type": "string" }, - "AvatarResponse": { - "properties": { - "color": { - "allOf": [ - { - "$ref": "#/components/schemas/UserAvatarColor" - } - ] - } - }, - "required": [ - "color" - ], - "type": "object" - }, "AvatarUpdate": { "properties": { "color": { @@ -9462,105 +9637,6 @@ ], "type": "object" }, - "FileChecksumDto": { - "properties": { - "filenames": { - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "filenames" - ], - "type": "object" - }, - "FileChecksumResponseDto": { - "properties": { - "checksum": { - "type": "string" - }, - "filename": { - "type": "string" - } - }, - "required": [ - "checksum", - "filename" - ], - "type": "object" - }, - "FileReportDto": { - "properties": { - "extras": { - "items": { - "type": "string" - }, - "type": "array" - }, - "orphans": { - "items": { - "$ref": "#/components/schemas/FileReportItemDto" - }, - "type": "array" - } - }, - "required": [ - "extras", - "orphans" - ], - "type": "object" - }, - "FileReportFixDto": { - "properties": { - "items": { - "items": { - "$ref": "#/components/schemas/FileReportItemDto" - }, - "type": "array" - } - }, - "required": [ - "items" - ], - "type": "object" - }, - "FileReportItemDto": { - "properties": { - "checksum": { - "type": "string" - }, - "entityId": { - "format": "uuid", - "type": "string" - }, - "entityType": { - "allOf": [ - { - "$ref": "#/components/schemas/PathEntityType" - } - ] - }, - "pathType": { - "allOf": [ - { - "$ref": "#/components/schemas/PathType" - } - ] - }, - "pathValue": { - "type": "string" - } - }, - "required": [ - "entityId", - "entityType", - "pathType", - "pathValue" - ], - "type": "object" - }, "FoldersResponse": { "properties": { "enabled": { @@ -10341,6 +10417,157 @@ }, "type": "object" }, + "NotificationCreateDto": { + "properties": { + "data": { + "type": "object" + }, + "description": { + "nullable": true, + "type": "string" + }, + "level": { + "allOf": [ + { + "$ref": "#/components/schemas/NotificationLevel" + } + ] + }, + "readAt": { + "format": "date-time", + "nullable": true, + "type": "string" + }, + "title": { + "type": "string" + }, + "type": { + "allOf": [ + { + "$ref": "#/components/schemas/NotificationType" + } + ] + }, + "userId": { + "format": "uuid", + "type": "string" + } + }, + "required": [ + "title", + "userId" + ], + "type": "object" + }, + "NotificationDeleteAllDto": { + "properties": { + "ids": { + "items": { + "format": "uuid", + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "ids" + ], + "type": "object" + }, + "NotificationDto": { + "properties": { + "createdAt": { + "format": "date-time", + "type": "string" + }, + "data": { + "type": "object" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "level": { + "allOf": [ + { + "$ref": "#/components/schemas/NotificationLevel" + } + ] + }, + "readAt": { + "format": "date-time", + "type": "string" + }, + "title": { + "type": "string" + }, + "type": { + "allOf": [ + { + "$ref": "#/components/schemas/NotificationType" + } + ] + } + }, + "required": [ + "createdAt", + "id", + "level", + "title", + "type" + ], + "type": "object" + }, + "NotificationLevel": { + "enum": [ + "success", + "error", + "warning", + "info" + ], + "type": "string" + }, + "NotificationType": { + "enum": [ + "JobFailed", + "BackupFailed", + "SystemMessage", + "Custom" + ], + "type": "string" + }, + "NotificationUpdateAllDto": { + "properties": { + "ids": { + "items": { + "format": "uuid", + "type": "string" + }, + "type": "array" + }, + "readAt": { + "format": "date-time", + "nullable": true, + "type": "string" + } + }, + "required": [ + "ids" + ], + "type": "object" + }, + "NotificationUpdateDto": { + "properties": { + "readAt": { + "format": "date-time", + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, "OAuthAuthorizeResponseDto": { "properties": { "url": { @@ -10354,6 +10581,12 @@ }, "OAuthCallbackDto": { "properties": { + "codeVerifier": { + "type": "string" + }, + "state": { + "type": "string" + }, "url": { "type": "string" } @@ -10365,8 +10598,14 @@ }, "OAuthConfigDto": { "properties": { + "codeChallenge": { + "type": "string" + }, "redirectUri": { "type": "string" + }, + "state": { + "type": "string" } }, "required": [ @@ -10374,6 +10613,13 @@ ], "type": "object" }, + "OAuthTokenEndpointAuthMethod": { + "enum": [ + "client_secret_post", + "client_secret_basic" + ], + "type": "string" + }, "OnThisDayDto": { "properties": { "year": { @@ -10432,27 +10678,6 @@ ], "type": "object" }, - "PathEntityType": { - "enum": [ - "asset", - "person", - "user" - ], - "type": "string" - }, - "PathType": { - "enum": [ - "original", - "fullsize", - "preview", - "thumbnail", - "encoded_video", - "sidecar", - "face", - "profile" - ], - "type": "string" - }, "PeopleResponse": { "properties": { "enabled": { @@ -10603,6 +10828,10 @@ "memory.read", "memory.update", "memory.delete", + "notification.create", + "notification.read", + "notification.update", + "notification.delete", "partner.create", "partner.read", "partner.update", @@ -12954,6 +13183,17 @@ }, "storageQuotaClaim": { "type": "string" + }, + "timeout": { + "minimum": 1, + "type": "integer" + }, + "tokenEndpointAuthMethod": { + "allOf": [ + { + "$ref": "#/components/schemas/OAuthTokenEndpointAuthMethod" + } + ] } }, "required": [ @@ -12971,7 +13211,9 @@ "scope", "signingAlgorithm", "storageLabelClaim", - "storageQuotaClaim" + "storageQuotaClaim", + "timeout", + "tokenEndpointAuthMethod" ], "type": "object" }, @@ -13613,6 +13855,14 @@ }, "UserAdminCreateDto": { "properties": { + "avatarColor": { + "allOf": [ + { + "$ref": "#/components/schemas/UserAvatarColor" + } + ], + "nullable": true + }, "email": { "format": "email", "type": "string" @@ -13755,6 +14005,14 @@ }, "UserAdminUpdateDto": { "properties": { + "avatarColor": { + "allOf": [ + { + "$ref": "#/components/schemas/UserAvatarColor" + } + ], + "nullable": true + }, "email": { "format": "email", "type": "string" @@ -13818,9 +14076,6 @@ }, "UserPreferencesResponseDto": { "properties": { - "avatar": { - "$ref": "#/components/schemas/AvatarResponse" - }, "download": { "$ref": "#/components/schemas/DownloadResponse" }, @@ -13850,7 +14105,6 @@ } }, "required": [ - "avatar", "download", "emailNotifications", "folders", @@ -13944,6 +14198,14 @@ }, "UserUpdateMeDto": { "properties": { + "avatarColor": { + "allOf": [ + { + "$ref": "#/components/schemas/UserAvatarColor" + } + ], + "nullable": true + }, "email": { "format": "email", "type": "string" diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index 761a228de0..c102f594cf 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -1,18 +1,18 @@ { "name": "@immich/sdk", - "version": "1.131.3", + "version": "1.132.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/sdk", - "version": "1.131.3", + "version": "1.132.3", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.14.0", + "@types/node": "^22.14.1", "typescript": "^5.3.3" } }, @@ -23,9 +23,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.14.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz", - "integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==", + "version": "22.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", + "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", "dev": true, "license": "MIT", "dependencies": { diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index 29fe50dcd9..70f76512b4 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@immich/sdk", - "version": "1.131.3", + "version": "1.132.3", "description": "Auto-generated TypeScript SDK for the Immich API", "type": "module", "main": "./build/index.js", @@ -19,7 +19,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.14.0", + "@types/node": "^22.14.1", "typescript": "^5.3.3" }, "repository": { diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index f82f5bc9a7..2684d2558f 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1,6 +1,6 @@ /** * Immich - * 1.131.3 + * 1.132.3 * DO NOT MODIFY - This file has been generated using oazapfts. * See https://www.npmjs.com/package/oazapfts */ @@ -39,6 +39,48 @@ export type ActivityCreateDto = { export type ActivityStatisticsResponseDto = { comments: number; }; +export type NotificationCreateDto = { + data?: object; + description?: string | null; + level?: NotificationLevel; + readAt?: string | null; + title: string; + "type"?: NotificationType; + userId: string; +}; +export type NotificationDto = { + createdAt: string; + data?: object; + description?: string; + id: string; + level: NotificationLevel; + readAt?: string; + title: string; + "type": NotificationType; +}; +export type TemplateDto = { + template: string; +}; +export type TemplateResponseDto = { + html: string; + name: string; +}; +export type SystemConfigSmtpTransportDto = { + host: string; + ignoreCert: boolean; + password: string; + port: number; + username: string; +}; +export type SystemConfigSmtpDto = { + enabled: boolean; + "from": string; + replyTo: string; + transport: SystemConfigSmtpTransportDto; +}; +export type TestEmailResponseDto = { + messageId: string; +}; export type UserLicense = { activatedAt: string; activationKey: string; @@ -64,6 +106,7 @@ export type UserAdminResponseDto = { updatedAt: string; }; export type UserAdminCreateDto = { + avatarColor?: (UserAvatarColor) | null; email: string; name: string; notify?: boolean; @@ -76,6 +119,7 @@ export type UserAdminDeleteDto = { force?: boolean; }; export type UserAdminUpdateDto = { + avatarColor?: (UserAvatarColor) | null; email?: string; name?: string; password?: string; @@ -83,9 +127,6 @@ export type UserAdminUpdateDto = { shouldChangePassword?: boolean; storageLabel?: string | null; }; -export type AvatarResponse = { - color: UserAvatarColor; -}; export type DownloadResponse = { archiveSize: number; includeEmbeddedVideos: boolean; @@ -122,7 +163,6 @@ export type TagsResponse = { sidebarWeb: boolean; }; export type UserPreferencesResponseDto = { - avatar: AvatarResponse; download: DownloadResponse; emailNotifications: EmailNotificationsResponse; folders: FoldersResponse; @@ -663,36 +703,27 @@ export type MemoryUpdateDto = { memoryAt?: string; seenAt?: string; }; -export type TemplateDto = { - template: string; +export type NotificationDeleteAllDto = { + ids: string[]; }; -export type TemplateResponseDto = { - html: string; - name: string; +export type NotificationUpdateAllDto = { + ids: string[]; + readAt?: string | null; }; -export type SystemConfigSmtpTransportDto = { - host: string; - ignoreCert: boolean; - password: string; - port: number; - username: string; -}; -export type SystemConfigSmtpDto = { - enabled: boolean; - "from": string; - replyTo: string; - transport: SystemConfigSmtpTransportDto; -}; -export type TestEmailResponseDto = { - messageId: string; +export type NotificationUpdateDto = { + readAt?: string | null; }; export type OAuthConfigDto = { + codeChallenge?: string; redirectUri: string; + state?: string; }; export type OAuthAuthorizeResponseDto = { url: string; }; export type OAuthCallbackDto = { + codeVerifier?: string; + state?: string; url: string; }; export type PartnerResponseDto = { @@ -769,27 +800,6 @@ export type AssetFaceUpdateDto = { export type PersonStatisticsResponseDto = { assets: number; }; -export type FileReportItemDto = { - checksum?: string; - entityId: string; - entityType: PathEntityType; - pathType: PathType; - pathValue: string; -}; -export type FileReportDto = { - extras: string[]; - orphans: FileReportItemDto[]; -}; -export type FileChecksumDto = { - filenames: string[]; -}; -export type FileChecksumResponseDto = { - checksum: string; - filename: string; -}; -export type FileReportFixDto = { - items: FileReportItemDto[]; -}; export type SearchExploreItem = { data: AssetResponseDto; value: string; @@ -1284,6 +1294,8 @@ export type SystemConfigOAuthDto = { signingAlgorithm: string; storageLabelClaim: string; storageQuotaClaim: string; + timeout: number; + tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod; }; export type SystemConfigPasswordLoginDto = { enabled: boolean; @@ -1384,6 +1396,7 @@ export type TrashResponseDto = { count: number; }; export type UserUpdateMeDto = { + avatarColor?: (UserAvatarColor) | null; email?: string; name?: string; password?: string; @@ -1450,6 +1463,43 @@ export function deleteActivity({ id }: { method: "DELETE" })); } +export function createNotification({ notificationCreateDto }: { + notificationCreateDto: NotificationCreateDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 201; + data: NotificationDto; + }>("/admin/notifications", oazapfts.json({ + ...opts, + method: "POST", + body: notificationCreateDto + }))); +} +export function getNotificationTemplateAdmin({ name, templateDto }: { + name: string; + templateDto: TemplateDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: TemplateResponseDto; + }>(`/admin/notifications/templates/${encodeURIComponent(name)}`, oazapfts.json({ + ...opts, + method: "POST", + body: templateDto + }))); +} +export function sendTestEmailAdmin({ systemConfigSmtpDto }: { + systemConfigSmtpDto: SystemConfigSmtpDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: TestEmailResponseDto; + }>("/admin/notifications/test-email", oazapfts.json({ + ...opts, + method: "POST", + body: systemConfigSmtpDto + }))); +} export function searchUsersAdmin({ withDeleted }: { withDeleted?: boolean; }, opts?: Oazapfts.RequestOpts) { @@ -2318,29 +2368,71 @@ export function addMemoryAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } -export function getNotificationTemplate({ name, templateDto }: { - name: string; - templateDto: TemplateDto; +export function deleteNotifications({ notificationDeleteAllDto }: { + notificationDeleteAllDto: NotificationDeleteAllDto; }, opts?: Oazapfts.RequestOpts) { - return oazapfts.ok(oazapfts.fetchJson<{ - status: 200; - data: TemplateResponseDto; - }>(`/notifications/templates/${encodeURIComponent(name)}`, oazapfts.json({ + return oazapfts.ok(oazapfts.fetchText("/notifications", oazapfts.json({ ...opts, - method: "POST", - body: templateDto + method: "DELETE", + body: notificationDeleteAllDto }))); } -export function sendTestEmail({ systemConfigSmtpDto }: { - systemConfigSmtpDto: SystemConfigSmtpDto; +export function getNotifications({ id, level, $type, unread }: { + id?: string; + level?: NotificationLevel; + $type?: NotificationType; + unread?: boolean; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; - data: TestEmailResponseDto; - }>("/notifications/test-email", oazapfts.json({ + data: NotificationDto[]; + }>(`/notifications${QS.query(QS.explode({ + id, + level, + "type": $type, + unread + }))}`, { + ...opts + })); +} +export function updateNotifications({ notificationUpdateAllDto }: { + notificationUpdateAllDto: NotificationUpdateAllDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText("/notifications", oazapfts.json({ ...opts, - method: "POST", - body: systemConfigSmtpDto + method: "PUT", + body: notificationUpdateAllDto + }))); +} +export function deleteNotification({ id }: { + id: string; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText(`/notifications/${encodeURIComponent(id)}`, { + ...opts, + method: "DELETE" + })); +} +export function getNotification({ id }: { + id: string; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: NotificationDto; + }>(`/notifications/${encodeURIComponent(id)}`, { + ...opts + })); +} +export function updateNotification({ id, notificationUpdateDto }: { + id: string; + notificationUpdateDto: NotificationUpdateDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: NotificationDto; + }>(`/notifications/${encodeURIComponent(id)}`, oazapfts.json({ + ...opts, + method: "PUT", + body: notificationUpdateDto }))); } export function startOAuth({ oAuthConfigDto }: { @@ -2550,35 +2642,6 @@ export function getPersonThumbnail({ id }: { ...opts })); } -export function getAuditFiles(opts?: Oazapfts.RequestOpts) { - return oazapfts.ok(oazapfts.fetchJson<{ - status: 200; - data: FileReportDto; - }>("/reports", { - ...opts - })); -} -export function getFileChecksums({ fileChecksumDto }: { - fileChecksumDto: FileChecksumDto; -}, opts?: Oazapfts.RequestOpts) { - return oazapfts.ok(oazapfts.fetchJson<{ - status: 201; - data: FileChecksumResponseDto[]; - }>("/reports/checksum", oazapfts.json({ - ...opts, - method: "POST", - body: fileChecksumDto - }))); -} -export function fixAuditFiles({ fileReportFixDto }: { - fileReportFixDto: FileReportFixDto; -}, opts?: Oazapfts.RequestOpts) { - return oazapfts.ok(oazapfts.fetchText("/reports/fix", oazapfts.json({ - ...opts, - method: "POST", - body: fileReportFixDto - }))); -} export function getAssetsByCity(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3449,6 +3512,18 @@ export enum UserAvatarColor { Gray = "gray", Amber = "amber" } +export enum NotificationLevel { + Success = "success", + Error = "error", + Warning = "warning", + Info = "info" +} +export enum NotificationType { + JobFailed = "JobFailed", + BackupFailed = "BackupFailed", + SystemMessage = "SystemMessage", + Custom = "Custom" +} export enum UserStatus { Active = "active", Removing = "removing", @@ -3523,6 +3598,10 @@ export enum Permission { MemoryRead = "memory.read", MemoryUpdate = "memory.update", MemoryDelete = "memory.delete", + NotificationCreate = "notification.create", + NotificationRead = "notification.read", + NotificationUpdate = "notification.update", + NotificationDelete = "notification.delete", PartnerCreate = "partner.create", PartnerRead = "partner.read", PartnerUpdate = "partner.update", @@ -3622,21 +3701,6 @@ export enum PartnerDirection { SharedBy = "shared-by", SharedWith = "shared-with" } -export enum PathEntityType { - Asset = "asset", - Person = "person", - User = "user" -} -export enum PathType { - Original = "original", - Fullsize = "fullsize", - Preview = "preview", - Thumbnail = "thumbnail", - EncodedVideo = "encoded_video", - Sidecar = "sidecar", - Face = "face", - Profile = "profile" -} export enum SearchSuggestionType { Country = "country", State = "state", @@ -3732,6 +3796,10 @@ export enum LogLevel { Error = "error", Fatal = "fatal" } +export enum OAuthTokenEndpointAuthMethod { + ClientSecretPost = "client_secret_post", + ClientSecretBasic = "client_secret_basic" +} export enum TimeBucketSize { Day = "DAY", Month = "MONTH" diff --git a/server/Dockerfile b/server/Dockerfile index 84037031fd..5c0ef076c4 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -6,14 +6,14 @@ WORKDIR /usr/src/app COPY server/package.json server/package-lock.json ./ COPY server/patches ./patches RUN npm ci && \ - # exiftool-vendored.pl, sharp-linux-x64 and sharp-linux-arm64 are the only ones we need - # they're marked as optional dependencies, so we need to copy them manually after pruning - rm -rf node_modules/@img/sharp-libvips* && \ - rm -rf node_modules/@img/sharp-linuxmusl-x64 + # exiftool-vendored.pl, sharp-linux-x64 and sharp-linux-arm64 are the only ones we need + # they're marked as optional dependencies, so we need to copy them manually after pruning + rm -rf node_modules/@img/sharp-libvips* && \ + rm -rf node_modules/@img/sharp-linuxmusl-x64 ENV PATH="${PATH}:/usr/src/app/bin" \ - IMMICH_ENV=development \ - NVIDIA_DRIVER_CAPABILITIES=all \ - NVIDIA_VISIBLE_DEVICES=all + IMMICH_ENV=development \ + NVIDIA_DRIVER_CAPABILITIES=all \ + NVIDIA_VISIBLE_DEVICES=all ENTRYPOINT ["tini", "--", "/bin/sh"] @@ -26,7 +26,7 @@ COPY --from=dev /usr/src/app/node_modules/@img ./node_modules/@img COPY --from=dev /usr/src/app/node_modules/exiftool-vendored.pl ./node_modules/exiftool-vendored.pl # web build -FROM node:22.14.0-alpine3.20@sha256:40be979442621049f40b1d51a26b55e281246b5de4e5f51a18da7beb6e17e3f9 AS web +FROM node:22.15.0-alpine3.20@sha256:686b8892b69879ef5bfd6047589666933508f9a5451c67320df3070ba0e9807b AS web WORKDIR /usr/src/open-api/typescript-sdk COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./ @@ -47,8 +47,8 @@ FROM ghcr.io/immich-app/base-server-prod:202504081114@sha256:8353bcbdb4e6579300a WORKDIR /usr/src/app ENV NODE_ENV=production \ - NVIDIA_DRIVER_CAPABILITIES=all \ - NVIDIA_VISIBLE_DEVICES=all + NVIDIA_DRIVER_CAPABILITIES=all \ + NVIDIA_VISIBLE_DEVICES=all COPY --from=prod /usr/src/app/node_modules ./node_modules COPY --from=prod /usr/src/app/dist ./dist COPY --from=prod /usr/src/app/bin ./bin diff --git a/server/package-lock.json b/server/package-lock.json index 8045976b3c..4e451cc8e1 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich", - "version": "1.131.3", + "version": "1.132.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich", - "version": "1.131.3", + "version": "1.132.3", "hasInstallScript": true, "license": "GNU Affero General Public License version 3", "dependencies": { @@ -19,7 +19,7 @@ "@nestjs/schedule": "^5.0.0", "@nestjs/swagger": "^11.0.2", "@nestjs/websockets": "^11.0.4", - "@opentelemetry/auto-instrumentations-node": "^0.57.0", + "@opentelemetry/auto-instrumentations-node": "^0.58.0", "@opentelemetry/context-async-hooks": "^2.0.0", "@opentelemetry/exporter-prometheus": "^0.200.0", "@opentelemetry/sdk-node": "^0.200.0", @@ -32,6 +32,7 @@ "chokidar": "^3.5.3", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", + "compression": "^1.8.0", "cookie": "^1.0.2", "cookie-parser": "^1.4.7", "exiftool-vendored": "^28.3.1", @@ -52,7 +53,7 @@ "nestjs-kysely": "^1.1.0", "nestjs-otel": "^6.0.0", "nodemailer": "^6.9.13", - "openid-client": "^5.4.3", + "openid-client": "^6.3.3", "pg": "^8.11.3", "picomatch": "^4.0.2", "react": "^19.0.0", @@ -63,7 +64,7 @@ "sanitize-filename": "^1.6.3", "sanitize-html": "^2.14.0", "semver": "^7.6.2", - "sharp": "^0.33.0", + "sharp": "^0.34.0", "sirv": "^3.0.0", "tailwindcss-preset-email": "^1.3.2", "thumbhash": "^0.1.1", @@ -83,6 +84,7 @@ "@types/archiver": "^6.0.0", "@types/async-lock": "^1.4.2", "@types/bcrypt": "^5.0.0", + "@types/compression": "^1.7.5", "@types/cookie-parser": "^1.4.8", "@types/express": "^4.17.17", "@types/fluent-ffmpeg": "^2.1.21", @@ -90,7 +92,7 @@ "@types/lodash": "^4.14.197", "@types/mock-fs": "^4.13.1", "@types/multer": "^1.4.7", - "@types/node": "^22.14.0", + "@types/node": "^22.14.1", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^3.0.0", "@types/pngjs": "^6.0.5", @@ -105,8 +107,10 @@ "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^57.0.0", "globals": "^16.0.0", + "jsdom": "^26.1.0", "mock-fs": "^5.2.0", - "node-addon-api": "^8.3.0", + "node-addon-api": "^8.3.1", + "node-gyp": "^11.2.0", "patch-package": "^8.0.0", "pngjs": "^7.0.0", "prettier": "^3.0.2", @@ -171,14 +175,14 @@ } }, "node_modules/@angular-devkit/schematics-cli": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-19.2.6.tgz", - "integrity": "sha512-OCLVk1YbTWfaZwpKPnd+9A34eMAZIRjntdugGvfw21ok9dUA8gICGDhfYATSfnU8/AbVQMTPK5sgG0xhUEm3UA==", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-19.2.8.tgz", + "integrity": "sha512-RFnlyu4Ld8I4xvu/eqrhjbQ6kQTr27w79omMiTbQcQZvP3E6oUyZdBjobyih4Np+1VVQrbdEeNz76daP2iUDig==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.6", - "@angular-devkit/schematics": "19.2.6", + "@angular-devkit/core": "19.2.8", + "@angular-devkit/schematics": "19.2.8", "@inquirer/prompts": "7.3.2", "ansi-colors": "4.1.3", "symbol-observable": "4.0.0", @@ -194,9 +198,9 @@ } }, "node_modules/@angular-devkit/schematics-cli/node_modules/@angular-devkit/core": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.6.tgz", - "integrity": "sha512-WFgiYhrDMq83UNaGRAneIM7CYYdBozD+yYA9BjoU8AgBLKtrvn6S8ZcjKAk5heoHtY/u8pEb0mwDTz9gxFmJZQ==", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.8.tgz", + "integrity": "sha512-kcxUHKf5Hi98r4gAvMP3ntJV8wuQ3/i6wuU9RcMP0UKUt2Rer5Ryis3MPqT92jvVVwg6lhrLIhXsFuWJMiYjXQ==", "dev": true, "license": "MIT", "dependencies": { @@ -221,6 +225,25 @@ } } }, + "node_modules/@angular-devkit/schematics-cli/node_modules/@angular-devkit/schematics": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.8.tgz", + "integrity": "sha512-QsmFuYdAyeCyg9WF/AJBhFXDUfCwmDFTEbsv5t5KPSP6slhk0GoLNZApniiFytU2siRlSxVNpve2uATyYuAYkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.8", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "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" + } + }, "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/prompts": { "version": "7.3.2", "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.3.2.tgz", @@ -435,6 +458,20 @@ "node": ">= 8" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.1.4.tgz", + "integrity": "sha512-SeuBV4rnjpFNjI8HSgKUwteuFdkHwkboq31HWzznuqgySQir+jSTczoWVVL4jvOjKjuH80fMDG0Fvg1Sb+OJsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", @@ -720,272 +757,131 @@ "node": ">=0.1.90" } }, + "node_modules/@csstools/color-helpers": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", + "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.3.tgz", + "integrity": "sha512-XBG3talrhid44BY1x3MHzUx/aTG8+x/Zi57M4aTKK9RFB4aLlF3TTSzfzn8nWVHWL3FgAXAxmupmDd6VWww+pw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.9.tgz", + "integrity": "sha512-wILs5Zk7BU86UArYBJTPy/FMPPKVKHMj1ycCEyf3VUptol0JNRLFU/BZsJ4aiIHJEbSLiizzRrw8Pc1uAEDrXw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.0.2", + "@csstools/css-calc": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", + "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", + "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@emnapi/runtime": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.2.tgz", - "integrity": "sha512-+b+3BJl18a0LKeHvy5eLOwPkiaz10C2MUUYKQ25itZS50TlP5FuDh2Q5EiFlB++vAuCS6HnrihqVlbdcRYyp9w==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", + "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz", - "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.0.tgz", - "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz", - "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.0.tgz", - "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz", - "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz", - "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz", - "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz", - "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz", - "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz", - "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz", - "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz", - "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz", - "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz", - "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz", - "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz", - "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@esbuild/linux-x64": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz", @@ -1002,135 +898,6 @@ "node": ">=18" } }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", - "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz", - "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz", - "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz", - "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz", - "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", - "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", - "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz", - "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.0.tgz", @@ -1199,9 +966,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", + "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1249,9 +1016,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", - "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", + "version": "9.25.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.1.tgz", + "integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==", "dev": true, "license": "MIT", "engines": { @@ -1282,19 +1049,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@fastify/busboy": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", @@ -1431,9 +1185,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", - "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.1.tgz", + "integrity": "sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==", "cpu": [ "arm64" ], @@ -1449,13 +1203,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.4" + "@img/sharp-libvips-darwin-arm64": "1.1.0" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", - "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.1.tgz", + "integrity": "sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==", "cpu": [ "x64" ], @@ -1471,13 +1225,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.4" + "@img/sharp-libvips-darwin-x64": "1.1.0" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", - "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz", + "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==", "cpu": [ "arm64" ], @@ -1491,9 +1245,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", - "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", + "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", "cpu": [ "x64" ], @@ -1507,9 +1261,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", - "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz", + "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==", "cpu": [ "arm" ], @@ -1523,9 +1277,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", - "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz", + "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==", "cpu": [ "arm64" ], @@ -1538,10 +1292,26 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz", + "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", - "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz", + "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==", "cpu": [ "s390x" ], @@ -1555,9 +1325,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", - "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz", + "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==", "cpu": [ "x64" ], @@ -1571,9 +1341,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", - "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz", + "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==", "cpu": [ "arm64" ], @@ -1587,9 +1357,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", - "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz", + "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==", "cpu": [ "x64" ], @@ -1603,9 +1373,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", - "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.1.tgz", + "integrity": "sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==", "cpu": [ "arm" ], @@ -1621,13 +1391,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.5" + "@img/sharp-libvips-linux-arm": "1.1.0" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", - "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.1.tgz", + "integrity": "sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==", "cpu": [ "arm64" ], @@ -1643,13 +1413,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.4" + "@img/sharp-libvips-linux-arm64": "1.1.0" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", - "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.1.tgz", + "integrity": "sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==", "cpu": [ "s390x" ], @@ -1665,13 +1435,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.0.4" + "@img/sharp-libvips-linux-s390x": "1.1.0" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", - "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.1.tgz", + "integrity": "sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==", "cpu": [ "x64" ], @@ -1687,13 +1457,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.0.4" + "@img/sharp-libvips-linux-x64": "1.1.0" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", - "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.1.tgz", + "integrity": "sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==", "cpu": [ "arm64" ], @@ -1709,13 +1479,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", - "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.1.tgz", + "integrity": "sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==", "cpu": [ "x64" ], @@ -1731,20 +1501,20 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + "@img/sharp-libvips-linuxmusl-x64": "1.1.0" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", - "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.1.tgz", + "integrity": "sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==", "cpu": [ "wasm32" ], "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.2.0" + "@emnapi/runtime": "^1.4.0" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -1754,9 +1524,9 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", - "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.1.tgz", + "integrity": "sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==", "cpu": [ "ia32" ], @@ -1773,9 +1543,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", - "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.1.tgz", + "integrity": "sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==", "cpu": [ "x64" ], @@ -2184,6 +1954,19 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -2335,58 +2118,6 @@ "integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==", "license": "MIT" }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", - "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", - "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", - "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", - "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", @@ -2400,19 +2131,6 @@ "linux" ] }, - "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", - "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@nestjs/bull-shared": { "version": "11.0.2", "resolved": "https://registry.npmjs.org/@nestjs/bull-shared/-/bull-shared-11.0.2.tgz", @@ -2442,15 +2160,15 @@ } }, "node_modules/@nestjs/cli": { - "version": "11.0.6", - "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.6.tgz", - "integrity": "sha512-Xco8pTdWHCpTXPTYMkUGAE+C7JXvAv38oVUaQeL81o7UOAi39w8p456r+IjONN/7ekjzakWnqepDzuTtH5Xk5w==", + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.7.tgz", + "integrity": "sha512-svrP8j1R0/lQVJ8ZI3BlDtuZxmkvVJokUJSB04sr6uibunk2wHeVDDVLZvYBUorCdGU/RHJl1IufhqUBM91vAQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.6", - "@angular-devkit/schematics": "19.2.6", - "@angular-devkit/schematics-cli": "19.2.6", + "@angular-devkit/core": "19.2.8", + "@angular-devkit/schematics": "19.2.8", + "@angular-devkit/schematics-cli": "19.2.8", "@inquirer/prompts": "7.4.1", "@nestjs/schematics": "^11.0.1", "ansis": "3.17.0", @@ -2464,8 +2182,8 @@ "tree-kill": "1.2.2", "tsconfig-paths": "4.2.0", "tsconfig-paths-webpack-plugin": "4.2.0", - "typescript": "5.7.3", - "webpack": "5.98.0", + "typescript": "5.8.3", + "webpack": "5.99.6", "webpack-node-externals": "3.0.0" }, "bin": { @@ -2475,7 +2193,7 @@ "node": ">= 20.11" }, "peerDependencies": { - "@swc/cli": "^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0", + "@swc/cli": "^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0", "@swc/core": "^1.3.62" }, "peerDependenciesMeta": { @@ -2488,9 +2206,9 @@ } }, "node_modules/@nestjs/cli/node_modules/@angular-devkit/core": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.6.tgz", - "integrity": "sha512-WFgiYhrDMq83UNaGRAneIM7CYYdBozD+yYA9BjoU8AgBLKtrvn6S8ZcjKAk5heoHtY/u8pEb0mwDTz9gxFmJZQ==", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.8.tgz", + "integrity": "sha512-kcxUHKf5Hi98r4gAvMP3ntJV8wuQ3/i6wuU9RcMP0UKUt2Rer5Ryis3MPqT92jvVVwg6lhrLIhXsFuWJMiYjXQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2515,6 +2233,25 @@ } } }, + "node_modules/@nestjs/cli/node_modules/@angular-devkit/schematics": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.8.tgz", + "integrity": "sha512-QsmFuYdAyeCyg9WF/AJBhFXDUfCwmDFTEbsv5t5KPSP6slhk0GoLNZApniiFytU2siRlSxVNpve2uATyYuAYkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.8", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "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" + } + }, "node_modules/@nestjs/cli/node_modules/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", @@ -2589,27 +2326,15 @@ "node": ">= 8" } }, - "node_modules/@nestjs/cli/node_modules/typescript": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", - "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/@nestjs/common": { - "version": "11.0.17", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.0.17.tgz", - "integrity": "sha512-FwKylI/hVxaNvzBJdWMMG1LH0cLKz4Oh4jKOHet2JUVMM9j6CuodRbrSnL++KL6PJY/b2E6AY58UDPLNeCqJWw==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.0.tgz", + "integrity": "sha512-8MrajltjtIN6eW9cTpv+1IZogqz2Zsrc8YDt0LwQPUq8cSq0j50DETdQpPsNMeib+p9avkV41+NrzGk1z2o5Wg==", "license": "MIT", "dependencies": { + "file-type": "20.4.1", "iterare": "1.2.1", + "load-esm": "1.0.2", "tslib": "2.8.1", "uid": "2.0.2" }, @@ -2620,7 +2345,6 @@ "peerDependencies": { "class-transformer": "*", "class-validator": "*", - "file-type": "^20.4.1", "reflect-metadata": "^0.1.12 || ^0.2.0", "rxjs": "^7.1.0" }, @@ -2630,16 +2354,13 @@ }, "class-validator": { "optional": true - }, - "file-type": { - "optional": true } } }, "node_modules/@nestjs/core": { - "version": "11.0.17", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.0.17.tgz", - "integrity": "sha512-ImK6qNxtegKqK7EJLGTBpP5Ild/DTpcduEtAOS+WLLjZOMjK1k214G9roXvlrNQwlVt9ALAY2jcqnsasdEd7Ow==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.0.tgz", + "integrity": "sha512-IeXbTRPrr6xAVbETlDE+miSkNmYf/cPhCa9GU9gFtPO6pVNuAeG/dNrjLVc23mJtUlT/ibdsoW35TlSyHLkzEA==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -2711,9 +2432,9 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "11.0.17", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.0.17.tgz", - "integrity": "sha512-et6Ydd6dR0FlcE/WR/9VRnQoTqEpDdzBgGK+aWadA0dFJ65wlN+snJRg/9JGP4ngj90S6xwe0VKD/BbfUGj9cw==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.0.tgz", + "integrity": "sha512-lxv73GT9VdQaxndciqKcyzLsT2j3gMRX+tO6J06oa7RIfp4Dp4oMTIu57lM1gkIJ+gLGq29bob+mfPv/K8RIuw==", "license": "MIT", "dependencies": { "cors": "2.8.5", @@ -2732,9 +2453,9 @@ } }, "node_modules/@nestjs/platform-socket.io": { - "version": "11.0.17", - "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.0.17.tgz", - "integrity": "sha512-l9b8VNb7N7rB9IUwKeln2bMQDltsR9mpenzHOaYYqDkz5BtuQSiyT8NpLR2vWhxDjppxMY3DkW8fQAvXh54pMg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.1.0.tgz", + "integrity": "sha512-aCNuHln9RmT/qHkCr0/bcHxUP4rNU9hXK8O1Rd6EpDhJ9UcgMhatjkYDE95Tc7QgSgjLVscQ47pI2J8ik9b0VQ==", "license": "MIT", "dependencies": { "socket.io": "4.8.1", @@ -2887,9 +2608,9 @@ } }, "node_modules/@nestjs/swagger": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.1.3.tgz", - "integrity": "sha512-vhbW/Xu05Diti/EwYQp3Ea7Hj2M++wiakCcxqUUDA2n7NvCZC8LKsrcGynw6/x/lugdXyklYS+s2FhdAfeAikg==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.1.5.tgz", + "integrity": "sha512-qVkyUSCvEmfTVWK92hsCeOQaOODlyBGkZC4ldqb4Fi0Gg8/kOWlcPJVN6i4a9edYYSdICUkGnt6UVFgi59fSrQ==", "license": "MIT", "dependencies": { "@microsoft/tsdoc": "0.15.1", @@ -2920,9 +2641,9 @@ } }, "node_modules/@nestjs/testing": { - "version": "11.0.17", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.0.17.tgz", - "integrity": "sha512-ryEx6fCYZFCsjEBZo8jOVikQluEHMESocVqHdXWOkkG7UqMPMHimf9gT2qij0GpNnYeDAGw+i7FhSJN3Cajoug==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.0.tgz", + "integrity": "sha512-gQ+NGshkHbNrDNXMVaPiwduqZ8YHpXrnsQqhSsnyNYOcDNPdBbB+0FDq7XiiklluXqjdLAN8i+bS7MbGlZIhKw==", "dev": true, "license": "MIT", "dependencies": { @@ -2948,9 +2669,9 @@ } }, "node_modules/@nestjs/websockets": { - "version": "11.0.17", - "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.0.17.tgz", - "integrity": "sha512-2LSjxA/lUKs5hv/g5lPk555CoRNTCt/XywHFteKMSrxo09Cq3yfOQOAPwEWG929EnqAjAAsQaDVbfUHUFisFCg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.0.tgz", + "integrity": "sha512-nb96cbmk7u6XIj4yIieezX9qqDshauyQJ4SLtdg2BaxOrkeQSx2j34CQWn/DZHHoYIQimfnAj2ry3RYWET4+zw==", "license": "MIT", "dependencies": { "iterare": "1.2.1", @@ -3139,6 +2860,60 @@ "node": ">= 8" } }, + "node_modules/@npmcli/agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-3.0.0.tgz", + "integrity": "sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/agent/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@npmcli/agent/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@npmcli/fs": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-4.0.0.tgz", + "integrity": "sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/@nuxt/opencollective": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@nuxt/opencollective/-/opencollective-0.4.1.tgz", @@ -3177,9 +2952,9 @@ } }, "node_modules/@opentelemetry/auto-instrumentations-node": { - "version": "0.57.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.57.1.tgz", - "integrity": "sha512-yy+K3vYybqJ6Z4XZCXYYxEC1DtEpPrnJdwxkhI0sTtVlrVnzx49iRLqpMmdvQ4b09+PrvXSN9t0jODMCGNrs8w==", + "version": "0.58.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.58.0.tgz", + "integrity": "sha512-gtqPqkXp8TG6vrmbzAJUKjJm3nrCiVGgImlV1tj8lsVqpnKDCB1Kl7bCcXod36+Tq/O4rCeTDmW90dCHeuv9jQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/instrumentation": "^0.200.0", @@ -3192,7 +2967,7 @@ "@opentelemetry/instrumentation-cucumber": "^0.15.0", "@opentelemetry/instrumentation-dataloader": "^0.17.0", "@opentelemetry/instrumentation-dns": "^0.44.0", - "@opentelemetry/instrumentation-express": "^0.48.0", + "@opentelemetry/instrumentation-express": "^0.48.1", "@opentelemetry/instrumentation-fastify": "^0.45.0", "@opentelemetry/instrumentation-fs": "^0.20.0", "@opentelemetry/instrumentation-generic-pool": "^0.44.0", @@ -3201,7 +2976,7 @@ "@opentelemetry/instrumentation-hapi": "^0.46.0", "@opentelemetry/instrumentation-http": "^0.200.0", "@opentelemetry/instrumentation-ioredis": "^0.48.0", - "@opentelemetry/instrumentation-kafkajs": "^0.9.0", + "@opentelemetry/instrumentation-kafkajs": "^0.9.1", "@opentelemetry/instrumentation-knex": "^0.45.0", "@opentelemetry/instrumentation-koa": "^0.48.0", "@opentelemetry/instrumentation-lru-memoizer": "^0.45.0", @@ -3218,6 +2993,7 @@ "@opentelemetry/instrumentation-redis-4": "^0.47.0", "@opentelemetry/instrumentation-restify": "^0.46.0", "@opentelemetry/instrumentation-router": "^0.45.0", + "@opentelemetry/instrumentation-runtime-node": "^0.14.0", "@opentelemetry/instrumentation-socket.io": "^0.47.0", "@opentelemetry/instrumentation-tedious": "^0.19.0", "@opentelemetry/instrumentation-undici": "^0.11.0", @@ -3664,9 +3440,9 @@ } }, "node_modules/@opentelemetry/instrumentation-express": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.48.0.tgz", - "integrity": "sha512-x9L6YD7AfE+7hysSv8k0d0sFmq3Vo3zoa/5eeJBYkGWHnD92CvekKouPyqUt71oX0htmZRdIawrhrwrAi2sonQ==", + "version": "0.48.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.48.1.tgz", + "integrity": "sha512-j8NYOf9DRWtchbWor/zA0poI42TpZG9tViIKA0e1lC+6MshTqSJYtgNv8Fn1sx1Wn/TRyp+5OgSXiE4LDfvpEg==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -3812,9 +3588,9 @@ } }, "node_modules/@opentelemetry/instrumentation-kafkajs": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.9.0.tgz", - "integrity": "sha512-Uxt/LTSmrzTYtnPpPn/L2W7+tjn38+v8tSnJ7hvaE3/aRXmZA5e72n+pHv0mlCI0pVNTihiQCUE62XYWPZ4jjA==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.9.1.tgz", + "integrity": "sha512-eGl5WKBqd0unOKm7PJKjEa1G+ac9nvpDjyv870nUYuSnUkyDc/Fag5keddIjHixTJwRp3FmyP7n+AadAjh52Vw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/instrumentation": "^0.200.0", @@ -4095,6 +3871,21 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/instrumentation-runtime-node": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-runtime-node/-/instrumentation-runtime-node-0.14.0.tgz", + "integrity": "sha512-y78dGoFMKwHSz0SD113Gt1dFTcfunpPZXIJh2SzJN27Lyb9FIzuMfjc3Iu3+s/N6qNOLuS9mKnPe3/qVGG4Waw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.200.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, "node_modules/@opentelemetry/instrumentation-socket.io": { "version": "0.47.0", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.47.0.tgz", @@ -4897,216 +4688,6 @@ } } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", - "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", - "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", - "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", - "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", - "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", - "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", - "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", - "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", - "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", - "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", - "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", - "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", - "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", - "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", - "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.40.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", @@ -5135,48 +4716,6 @@ "linux" ] }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", - "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", - "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", - "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@scarf/scarf": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", @@ -5303,91 +4842,6 @@ } } }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.11.21.tgz", - "integrity": "sha512-v6gjw9YFWvKulCw3ZA1dY+LGMafYzJksm1mD4UZFZ9b36CyHFowYVYug1ajYRIRqEvvfIhHUNV660zTLoVFR8g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.11.21.tgz", - "integrity": "sha512-CUiTiqKlzskwswrx9Ve5NhNoab30L1/ScOfQwr1duvNlFvarC8fvQSgdtpw2Zh3MfnfNPpyLZnYg7ah4kbT9JQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.11.21.tgz", - "integrity": "sha512-YyBTAFM/QPqt1PscD8hDmCLnqPGKmUZpqeE25HXY8OLjl2MUs8+O4KjwPZZ+OGxpdTbwuWFyMoxjcLy80JODvg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.11.21.tgz", - "integrity": "sha512-DQD+ooJmwpNsh4acrftdkuwl5LNxxg8U4+C/RJNDd7m5FP9Wo4c0URi5U0a9Vk/6sQNh9aSGcYChDpqCDWEcBw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.11.21.tgz", - "integrity": "sha512-y1L49+snt1a1gLTYPY641slqy55QotPdtRK9Y6jMi4JBQyZwxC8swWYlQWb+MyILwxA614fi62SCNZNznB3XSA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, "node_modules/@swc/core-linux-x64-gnu": { "version": "1.11.21", "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.11.21.tgz", @@ -5422,57 +4876,6 @@ "node": ">=10" } }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.11.21.tgz", - "integrity": "sha512-DJJe9k6gXR/15ZZVLv1SKhXkFst8lYCeZRNHH99SlBodvu4slhh/MKQ6YCixINRhCwliHrpXPym8/5fOq8b7Ig==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.11.21.tgz", - "integrity": "sha512-TqEXuy6wedId7bMwLIr9byds+mKsaXVHctTN88R1UIBPwJA92Pdk0uxDgip0pEFzHB/ugU27g6d8cwUH3h2eIw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.11.21.tgz", - "integrity": "sha512-BT9BNNbMxdpUM1PPAkYtviaV0A8QcXttjs2MDtOeSqqvSJaPtyM+Fof2/+xSwQDmDEFzbGCcn75M5+xy3lGqpA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, "node_modules/@swc/counter": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", @@ -5521,6 +4924,30 @@ "testcontainers": "^10.24.2" } }, + "node_modules/@tokenizer/inflate": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", + "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "fflate": "^0.8.2", + "token-types": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT" + }, "node_modules/@turf/boolean-point-in-polygon": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-7.2.0.tgz", @@ -5617,6 +5044,16 @@ "@types/node": "*" } }, + "node_modules/@types/compression": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.5.tgz", + "integrity": "sha512-AAQvK5pxMpaT+nDvhHrsBhLSYG5yQdtkaJE1WYieSNY2mVFKAgmU4ks65rkZD5oqnGCFLyQpUr1CqI4DmUMyDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -6090,17 +5527,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", - "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.0.tgz", + "integrity": "sha512-evaQJZ/J/S4wisevDvC1KFZkPzRetH8kYZbkgcTRyql3mcKsf+ZFDV1BVWUGTCAW5pQHoqn5gK5b8kn7ou9aFQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/type-utils": "8.30.1", - "@typescript-eslint/utils": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/scope-manager": "8.31.0", + "@typescript-eslint/type-utils": "8.31.0", + "@typescript-eslint/utils": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -6120,16 +5557,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", - "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.31.0.tgz", + "integrity": "sha512-67kYYShjBR0jNI5vsf/c3WG4u+zDnCTHTPqVMQguffaWWFs7artgwKmfwdifl+r6XyM5LYLas/dInj2T0SgJyw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/typescript-estree": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/scope-manager": "8.31.0", + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/typescript-estree": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0", "debug": "^4.3.4" }, "engines": { @@ -6145,14 +5582,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", - "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.31.0.tgz", + "integrity": "sha512-knO8UyF78Nt8O/B64i7TlGXod69ko7z6vJD9uhSlm0qkAbGeRUSudcm0+K/4CrRjrpiHfBCjMWlc08Vav1xwcw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1" + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6163,14 +5600,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", - "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.31.0.tgz", + "integrity": "sha512-DJ1N1GdjI7IS7uRlzJuEDCgDQix3ZVYVtgeWEyhyn4iaoitpMBX6Ndd488mXSx0xah/cONAkEaYyylDyAeHMHg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.30.1", - "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/typescript-estree": "8.31.0", + "@typescript-eslint/utils": "8.31.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, @@ -6187,9 +5624,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", - "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.31.0.tgz", + "integrity": "sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ==", "dev": true, "license": "MIT", "engines": { @@ -6201,14 +5638,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", - "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.0.tgz", + "integrity": "sha512-xLmgn4Yl46xi6aDSZ9KkyfhhtnYI15/CvHbpOy/eR5NWhK/BK8wc709KKwhAR0m4ZKRP7h07bm4BWUYOCuRpQQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -6254,16 +5691,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", - "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.31.0.tgz", + "integrity": "sha512-qi6uPLt9cjTFxAb1zGNgTob4x9ur7xC6mHQJ8GwEzGMGE9tYniublmJaowOJ9V2jUzxrltTPfdG2nKlWsq0+Ww==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/typescript-estree": "8.30.1" + "@typescript-eslint/scope-manager": "8.31.0", + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/typescript-estree": "8.31.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6278,13 +5715,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", - "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.0.tgz", + "integrity": "sha512-QcGHmlRHWOl93o64ZUMNewCdwKGU6WItOU52H0djgNmn1EOrhVudrDzXz4OycCRSCPwFCDrE2iIt5vmuUdHxuQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/types": "8.31.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -6296,9 +5733,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.1.tgz", - "integrity": "sha512-MgV6D2dhpD6Hp/uroUoAIvFqA8AuvXEFBC2eepG3WFc1pxTfdk1LEqqkWoWhjz+rytoqrnUUCdf6Lzco3iHkLQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.2.tgz", + "integrity": "sha512-XDdaDOeaTMAMYW7N63AqoK32sYUWbXnTkC6tEbVcu3RlU1bB9of32T+PGf8KZvxqLNqeXhafDFqCkwpf2+dyaQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6311,7 +5748,7 @@ "istanbul-reports": "^3.1.7", "magic-string": "^0.30.17", "magicast": "^0.3.5", - "std-env": "^3.8.1", + "std-env": "^3.9.0", "test-exclude": "^7.0.1", "tinyrainbow": "^2.0.0" }, @@ -6319,8 +5756,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "3.1.1", - "vitest": "3.1.1" + "@vitest/browser": "3.1.2", + "vitest": "3.1.2" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -6329,14 +5766,14 @@ } }, "node_modules/@vitest/expect": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.1.tgz", - "integrity": "sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.2.tgz", + "integrity": "sha512-O8hJgr+zREopCAqWl3uCVaOdqJwZ9qaDwUP7vy3Xigad0phZe9APxKhPcDNqYYi0rX5oMvwJMSCAXY2afqeTSA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", + "@vitest/spy": "3.1.2", + "@vitest/utils": "3.1.2", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, @@ -6345,13 +5782,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.1.tgz", - "integrity": "sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.2.tgz", + "integrity": "sha512-kOtd6K2lc7SQ0mBqYv/wdGedlqPdM/B38paPY+OwJ1XiNi44w3Fpog82UfOibmHaV9Wod18A09I9SCKLyDMqgw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.1", + "@vitest/spy": "3.1.2", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -6382,9 +5819,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.1.tgz", - "integrity": "sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.2.tgz", + "integrity": "sha512-R0xAiHuWeDjTSB3kQ3OQpT8Rx3yhdOAIm/JM4axXxnG7Q/fS8XUwggv/A4xzbQA+drYRjzkMnpYnOGAc4oeq8w==", "dev": true, "license": "MIT", "dependencies": { @@ -6395,13 +5832,13 @@ } }, "node_modules/@vitest/runner": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.1.tgz", - "integrity": "sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.2.tgz", + "integrity": "sha512-bhLib9l4xb4sUMPXnThbnhX2Yi8OutBMA8Yahxa7yavQsFDtwY/jrUZwpKp2XH9DhRFJIeytlyGpXCqZ65nR+g==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.1.1", + "@vitest/utils": "3.1.2", "pathe": "^2.0.3" }, "funding": { @@ -6409,13 +5846,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.1.tgz", - "integrity": "sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.2.tgz", + "integrity": "sha512-Q1qkpazSF/p4ApZg1vfZSQ5Yw6OCQxVMVrLjslbLFA1hMDrT2uxtqMaw8Tc/jy5DLka1sNs1Y7rBcftMiaSH/Q==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.1", + "@vitest/pretty-format": "3.1.2", "magic-string": "^0.30.17", "pathe": "^2.0.3" }, @@ -6424,9 +5861,9 @@ } }, "node_modules/@vitest/spy": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.1.tgz", - "integrity": "sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.2.tgz", + "integrity": "sha512-OEc5fSXMws6sHVe4kOFyDSj/+4MSwst0ib4un0DlcYgQvRuYQ0+M2HyqGaauUMnjq87tmUaMNDxKQx7wNfVqPA==", "dev": true, "license": "MIT", "dependencies": { @@ -6437,13 +5874,13 @@ } }, "node_modules/@vitest/utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.1.tgz", - "integrity": "sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.2.tgz", + "integrity": "sha512-5GGd0ytZ7BH3H6JTj9Kw7Prn1Nbg0wZVrIvou+UWxm54d+WoXXgAgjFJ8wn3LdagWLFSEfpPeyYrByZaGEZHLg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.1", + "@vitest/pretty-format": "3.1.2", "loupe": "^3.1.3", "tinyrainbow": "^2.0.0" }, @@ -6970,12 +6407,6 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/archiver-utils/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, "node_modules/archiver-utils/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -7552,6 +6983,190 @@ "node": ">=8" } }, + "node_modules/cacache": { + "version": "19.0.1", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-19.0.1.tgz", + "integrity": "sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cacache/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/cacache/node_modules/fs-minipass": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/cacache/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cacache/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -8033,6 +7648,60 @@ "node": ">= 14" } }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz", + "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.0.2", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -8311,6 +7980,20 @@ "node": ">=4" } }, + "node_modules/cssstyle": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.3.1.tgz", + "integrity": "sha512-ZgW+Jgdd7i52AaLYCriF8Mxqft0gD/R9i9wi6RWBhs1pqdPEzPjym7rvRKi397WmQFf3SlyUsszhw+VVCbx79Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^3.1.2", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -8318,6 +8001,57 @@ "dev": true, "license": "MIT" }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/dayjs": { "version": "1.11.13", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", @@ -8353,6 +8087,13 @@ } } }, + "node_modules/decimal.js": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", + "dev": true, + "license": "MIT" + }, "node_modules/deep-eql": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", @@ -8752,6 +8493,16 @@ "node": ">= 0.8" } }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -8886,6 +8637,23 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT" + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -9014,20 +8782,20 @@ } }, "node_modules/eslint": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", - "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", + "version": "9.25.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.1.tgz", + "integrity": "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.20.0", - "@eslint/config-helpers": "^0.2.0", - "@eslint/core": "^0.12.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.13.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.24.0", - "@eslint/plugin-kit": "^0.2.7", + "@eslint/js": "9.25.1", + "@eslint/plugin-kit": "^0.2.8", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -9343,16 +9111,6 @@ "exiftool-vendored.pl": "13.0.1" } }, - "node_modules/exiftool-vendored.exe": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-13.0.0.tgz", - "integrity": "sha512-4zAMuFGgxZkOoyQIzZMHv1HlvgyJK3AkNqjAgm8A8V0UmOZO7yv3pH49cDV1OduzFJqgs6yQ6eG4OGydhKtxlg==", - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/exiftool-vendored.pl": { "version": "13.0.1", "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-13.0.1.tgz", @@ -9373,6 +9131,13 @@ "node": ">=12.0.0" } }, + "node_modules/exponential-backoff": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz", + "integrity": "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/express": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", @@ -9547,6 +9312,27 @@ "reusify": "^1.0.4" } }, + "node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -9593,6 +9379,24 @@ "stream-source": "0.3" } }, + "node_modules/file-type": { + "version": "20.4.1", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.4.1.tgz", + "integrity": "sha512-hw9gNZXUfZ02Jo0uafWLaFVPter5/k2rfcrjFJJHX/77xtSDOfJuEFb6oKlFV86FLP1SuyHMW1PSk0U9M5tKkQ==", + "license": "MIT", + "dependencies": { + "@tokenizer/inflate": "^0.2.6", + "strtok3": "^10.2.0", + "token-types": "^6.0.0", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -9921,20 +9725,6 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -10399,12 +10189,18 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", "dev": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } }, "node_modules/html-escaper": { "version": "2.0.2", @@ -10448,6 +10244,13 @@ "entities": "^4.4.0" } }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -10464,6 +10267,30 @@ "node": ">= 0.8" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -10698,6 +10525,20 @@ "url": "https://opencollective.com/ioredis" } }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -10829,6 +10670,13 @@ "node": ">=0.10.0" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", @@ -11055,9 +10903,9 @@ } }, "node_modules/jose": { - "version": "4.15.9", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", - "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.0.10.tgz", + "integrity": "sha512-skIAxZqcMkOrSwjJvplIPYrlXGpxTPnro2/QWTDCxAdWQrSTV5/KqspMWmi5WAx5+ULswASJiZ0a+1B/Lxt9cw==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -11081,6 +10929,136 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdom": { + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", + "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssstyle": "^4.2.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.5.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.1.1", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.1.1", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsdom/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsdom/node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/jsdom/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/jsdom/node_modules/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -11212,9 +11190,9 @@ } }, "node_modules/kysely": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.28.0.tgz", - "integrity": "sha512-hq8VcLy57Ww7oPTTVEOrT9ml+g8ehbbmEUkHmW4Xtubu+NHdKZi6SH6egmD4cjDhn3b/0s0h/6AjdPayOTJhNw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.28.2.tgz", + "integrity": "sha512-4YAVLoF0Sf0UTqlhgQMFU9iQECdah7n+13ANkiuVfRvlK+uI0Etbgd7bVP36dKlG+NXWbhGua8vnGt+sdhvT7A==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -11326,6 +11304,25 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "license": "MIT" }, + "node_modules/load-esm": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.2.tgz", + "integrity": "sha512-nVAvWk/jeyrWyXEAs84mpQCYccxRqgKY4OznLuJhJCa0XsPSfdOIr2zvBZEj3IHEHbX97jjscKRRV539bW0Gpw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + }, + { + "type": "buymeacoffee", + "url": "https://buymeacoffee.com/borewit" + } + ], + "license": "MIT", + "engines": { + "node": ">=13.2.0" + } + }, "node_modules/load-tsconfig": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", @@ -11423,16 +11420,10 @@ "license": "MIT" }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" }, "node_modules/luxon": { "version": "3.6.1", @@ -11489,6 +11480,29 @@ "semver": "bin/semver.js" } }, + "node_modules/make-fetch-happen": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-14.0.3.tgz", + "integrity": "sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/marked": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/marked/-/marked-7.0.4.tgz", @@ -11690,6 +11704,128 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/minipass-collect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-fetch": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-4.0.1.tgz", + "integrity": "sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-fetch/node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/minizlib": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", @@ -12055,9 +12191,9 @@ } }, "node_modules/nestjs-cls": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/nestjs-cls/-/nestjs-cls-5.4.2.tgz", - "integrity": "sha512-KQPOhD7ya82gSEc+XDwFKERPMaWK95bzV4E2pLmx8oC1hfMNuVc4dkWmEKJiu+o0hCWP/v51iWNgOGHKnJ9Raw==", + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/nestjs-cls/-/nestjs-cls-5.4.3.tgz", + "integrity": "sha512-yHEHyVoe6rsvj3XRPFonBKPXPjDREyHfKZ9PTStSLJTZAV3wey1Q89TquSj6QciqXB5387GiHv9DG+ja6iAUHw==", "license": "MIT", "engines": { "node": ">=18" @@ -12205,6 +12341,31 @@ } } }, + "node_modules/node-gyp": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.2.0.tgz", + "integrity": "sha512-T0S1zqskVUSxcsSTkAsLc7xCycrRYmtDHadDinzocrThjyQCn5kMlEBSj6H4qDbgsIOSLmmlRIeb0lZXj+UArA==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^14.0.3", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "tar": "^7.4.3", + "tinyglobby": "^0.2.12", + "which": "^5.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/node-gyp-build-optional-packages": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", @@ -12220,6 +12381,125 @@ "node-gyp-build-optional-packages-test": "build-test.js" } }, + "node_modules/node-gyp/node_modules/abbrev": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", + "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/node-gyp/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/node-gyp/node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/node-gyp/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/nopt": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", + "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/node-gyp/node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/node-gyp/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -12293,6 +12573,22 @@ "set-blocking": "^2.0.0" } }, + "node_modules/nwsapi": { + "version": "2.2.20", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz", + "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==", + "dev": true, + "license": "MIT" + }, + "node_modules/oauth4webapi": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.5.0.tgz", + "integrity": "sha512-DF3mLWNuxPkxJkHmWxbSFz4aE5CjWOsm465VBfBdWzmzX4Mg3vF8icxK+iKqfdWrIumBJ2TaoNQWx+SQc2bsPQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -12333,15 +12629,6 @@ "node": ">= 0.4" } }, - "node_modules/oidc-token-hash": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.1.0.tgz", - "integrity": "sha512-y0W+X7Ppo7oZX6eovsRkuzcSM40Bicg2JEJkDJ4irIt1wsYAP5MLSNv+QAogO8xivMffw/9OvV3um1pxXgt1uA==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || >=12.0.0" - } - }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -12405,29 +12692,18 @@ } }, "node_modules/openid-client": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", - "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.4.2.tgz", + "integrity": "sha512-4zBRTsKNRTyKxV5cFzl+LtamsYx/FsWhejjax+qgMkFNGtLj1gMtng2iSoJqqWUT0FHU3IUhO53aeBePg7Sp/g==", "license": "MIT", "dependencies": { - "jose": "^4.15.9", - "lru-cache": "^6.0.0", - "object-hash": "^2.2.0", - "oidc-token-hash": "^5.0.3" + "jose": "^6.0.10", + "oauth4webapi": "^3.4.1" }, "funding": { "url": "https://github.com/sponsors/panva" } }, - "node_modules/openid-client/node_modules/object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -12531,6 +12807,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-map": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", + "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -12573,6 +12862,32 @@ "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==", "license": "MIT" }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", + "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/parseley": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", @@ -12822,15 +13137,28 @@ "url": "https://ko-fi.com/killymxi" } }, + "node_modules/peek-readable": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-7.0.0.tgz", + "integrity": "sha512-nri2TO5JE3/mRryik9LlHFT53cgHfRK0Lt0BAZQXku/AW3E6XLt2GaY8siWi7dvW/m1z0ecn+J+bpDa9ZN3IsQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/pg": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.14.1.tgz", - "integrity": "sha512-0TdbqfjwIun9Fm/r89oB7RFQ0bLgduAhiIqIXOsyKoiC/L54DbuAAzIEN/9Op0f1Po9X7iCPXGoa/Ah+2aI8Xw==", + "version": "8.15.5", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.15.5.tgz", + "integrity": "sha512-EpAhHFQc+aH9VfeffWIVC+XXk6lmAhS9W1FxtxcPXs94yxhrI1I6w/zkWfIOII/OkBv3Be04X3xMOj0kQ78l6w==", "license": "MIT", "dependencies": { - "pg-connection-string": "^2.7.0", - "pg-pool": "^3.8.0", - "pg-protocol": "^1.8.0", + "pg-connection-string": "^2.8.5", + "pg-pool": "^3.9.5", + "pg-protocol": "^1.9.5", "pg-types": "^2.1.0", "pgpass": "1.x" }, @@ -12838,7 +13166,7 @@ "node": ">= 8.0.0" }, "optionalDependencies": { - "pg-cloudflare": "^1.1.1" + "pg-cloudflare": "^1.2.5" }, "peerDependencies": { "pg-native": ">=3.0.1" @@ -12850,16 +13178,16 @@ } }, "node_modules/pg-cloudflare": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", - "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.5.tgz", + "integrity": "sha512-OOX22Vt0vOSRrdoUPKJ8Wi2OpE/o/h9T8X1s4qSkCedbNah9ei2W2765be8iMVxQUsvgT7zIAT2eIa9fs5+vtg==", "license": "MIT", "optional": true }, "node_modules/pg-connection-string": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", - "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==", + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.8.5.tgz", + "integrity": "sha512-Ni8FuZ8yAF+sWZzojvtLE2b03cqjO5jNULcHFfM9ZZ0/JXrgom5pBREbtnAw7oxsxJqHw9Nz/XWORUEL3/IFow==", "license": "MIT" }, "node_modules/pg-int8": { @@ -12872,18 +13200,18 @@ } }, "node_modules/pg-pool": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.8.0.tgz", - "integrity": "sha512-VBw3jiVm6ZOdLBTIcXLNdSotb6Iy3uOCwDGFAksZCXmi10nyRvnP2v3jl4d+IsLYRyXf6o9hIm/ZtUzlByNUdw==", + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.9.6.tgz", + "integrity": "sha512-rFen0G7adh1YmgvrmE5IPIqbb+IgEzENUm+tzm6MLLDSlPRoZVhzU1WdML9PV2W5GOdRA9qBKURlbt1OsXOsPw==", "license": "MIT", "peerDependencies": { "pg": ">=8.0" } }, "node_modules/pg-protocol": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.8.0.tgz", - "integrity": "sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g==", + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.9.5.tgz", + "integrity": "sha512-DYTWtWpfd5FOro3UnAfwvhD8jh59r2ig8bPtc9H8Ds7MscE/9NYruUQWFAOuraRl29jwcT2kyMFQ3MxeaVjUhg==", "license": "MIT" }, "node_modules/pg-types": { @@ -13244,6 +13572,16 @@ "node": ">=6" } }, + "node_modules/proc-log": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", + "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -13259,6 +13597,20 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "license": "MIT" }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/proper-lockfile": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", @@ -13596,12 +13948,6 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/react-email/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, "node_modules/react-email/node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -14156,6 +14502,13 @@ "node": ">= 18" } }, + "node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, + "license": "MIT" + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -14233,9 +14586,9 @@ } }, "node_modules/sanitize-html": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.15.0.tgz", - "integrity": "sha512-wIjst57vJGpLyBP8ioUbg6ThwJie5SuSIjHxJg53v5Fg+kUK+AXlb7bK3RNXpp315MvwM+0OBGCV6h5pPHsVhA==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.16.0.tgz", + "integrity": "sha512-0s4caLuHHaZFVxFTG74oW91+j6vW7gKbGD6CD2+miP73CE6z6YtOBN0ArtLd2UGyi4IC7K47v3ENUbQX4jV3Mg==", "license": "MIT", "dependencies": { "deepmerge": "^4.2.2", @@ -14246,6 +14599,19 @@ "postcss": "^8.3.11" } }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.26.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", @@ -14410,15 +14776,15 @@ "license": "MIT" }, "node_modules/sharp": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", - "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.1.tgz", + "integrity": "sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", - "semver": "^7.6.3" + "semver": "^7.7.1" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -14427,25 +14793,26 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.33.5", - "@img/sharp-darwin-x64": "0.33.5", - "@img/sharp-libvips-darwin-arm64": "1.0.4", - "@img/sharp-libvips-darwin-x64": "1.0.4", - "@img/sharp-libvips-linux-arm": "1.0.5", - "@img/sharp-libvips-linux-arm64": "1.0.4", - "@img/sharp-libvips-linux-s390x": "1.0.4", - "@img/sharp-libvips-linux-x64": "1.0.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", - "@img/sharp-libvips-linuxmusl-x64": "1.0.4", - "@img/sharp-linux-arm": "0.33.5", - "@img/sharp-linux-arm64": "0.33.5", - "@img/sharp-linux-s390x": "0.33.5", - "@img/sharp-linux-x64": "0.33.5", - "@img/sharp-linuxmusl-arm64": "0.33.5", - "@img/sharp-linuxmusl-x64": "0.33.5", - "@img/sharp-wasm32": "0.33.5", - "@img/sharp-win32-ia32": "0.33.5", - "@img/sharp-win32-x64": "0.33.5" + "@img/sharp-darwin-arm64": "0.34.1", + "@img/sharp-darwin-x64": "0.34.1", + "@img/sharp-libvips-darwin-arm64": "1.1.0", + "@img/sharp-libvips-darwin-x64": "1.1.0", + "@img/sharp-libvips-linux-arm": "1.1.0", + "@img/sharp-libvips-linux-arm64": "1.1.0", + "@img/sharp-libvips-linux-ppc64": "1.1.0", + "@img/sharp-libvips-linux-s390x": "1.1.0", + "@img/sharp-libvips-linux-x64": "1.1.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", + "@img/sharp-libvips-linuxmusl-x64": "1.1.0", + "@img/sharp-linux-arm": "0.34.1", + "@img/sharp-linux-arm64": "0.34.1", + "@img/sharp-linux-s390x": "0.34.1", + "@img/sharp-linux-x64": "0.34.1", + "@img/sharp-linuxmusl-arm64": "0.34.1", + "@img/sharp-linuxmusl-x64": "0.34.1", + "@img/sharp-wasm32": "0.34.1", + "@img/sharp-win32-ia32": "0.34.1", + "@img/sharp-win32-x64": "0.34.1" } }, "node_modules/shebang-command": { @@ -14611,6 +14978,17 @@ "integrity": "sha512-YiuPbxpCj4hD9Qs06hGAz/OZhQ0eDuALN0lRWJez0eD/RevzKqGdUx1IOMUnXgpr+sXZLq3g8ERwbAH0bCb8vg==", "license": "BSD-3-Clause" }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, "node_modules/socket.io": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", @@ -14746,6 +15124,46 @@ "node": ">= 0.6" } }, + "node_modules/socks": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", + "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -14827,10 +15245,17 @@ "node": ">= 10.x" } }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/sql-formatter": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.5.2.tgz", - "integrity": "sha512-+9xZgiv1DP/c7GxkkBUHRZOf4j35gquVdwEm0rg16qKRYeFkv1+/vEeO13fsUbbz06KUotIyASJ+hyau8LM8Kg==", + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.6.1.tgz", + "integrity": "sha512-uoKbRLVbjzwa8ouY4lI9YM387zRxDv9Gg5kZBzu2iNls2wVBlDLshhudCstczddRvj7J+xOpHTTWX6Z0lRgYGA==", "dev": true, "license": "MIT", "dependencies": { @@ -14897,6 +15322,19 @@ "nan": "^2.20.0" } }, + "node_modules/ssri": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-12.0.0.tgz", + "integrity": "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -15109,6 +15547,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strtok3": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.2.2.tgz", + "integrity": "sha512-Xt18+h4s7Z8xyZ0tmBoRmzxcop97R4BAh+dXouUDCYn+Em+1P3qpkUfI5ueWLT8ynC5hZ+q4iPEmGG1urvQGBg==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", @@ -15202,13 +15657,6 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/sucrase/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC", - "peer": true - }, "node_modules/sucrase/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -15320,6 +15768,13 @@ "node": ">=0.10" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, "node_modules/synckit": { "version": "0.11.4", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.4.tgz", @@ -15752,13 +16207,6 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/test-exclude/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, "node_modules/test-exclude/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -15891,6 +16339,23 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/tinypool": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", @@ -15921,6 +16386,26 @@ "node": ">=14.0.0" } }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true, + "license": "MIT" + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -15954,6 +16439,23 @@ "node": ">=0.6" } }, + "node_modules/token-types": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.0.0.tgz", + "integrity": "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/totalist": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", @@ -15963,6 +16465,19 @@ "node": ">=6" } }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -16291,12 +16806,6 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/typeorm/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, "node_modules/typeorm/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -16356,15 +16865,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.30.1.tgz", - "integrity": "sha512-D7lC0kcehVH7Mb26MRQi64LMyRJsj3dToJxM1+JVTl53DQSV5/7oUGWQLcKl1C1KnoVHxMMU2FNQMffr7F3Row==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.31.0.tgz", + "integrity": "sha512-u+93F0sB0An8WEAPtwxVhFby573E8ckdjwUUQUj9QA4v8JAvgtoDdIyYR3XFwFHq2W1KJ1AurwJCO+w+Y1ixyQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.30.1", - "@typescript-eslint/parser": "8.30.1", - "@typescript-eslint/utils": "8.30.1" + "@typescript-eslint/eslint-plugin": "8.31.0", + "@typescript-eslint/parser": "8.31.0", + "@typescript-eslint/utils": "8.31.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -16465,6 +16974,18 @@ "node": ">= 4.0.0" } }, + "node_modules/uint8array-extras": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.4.0.tgz", + "integrity": "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/undici": { "version": "5.29.0", "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", @@ -16497,6 +17018,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/unique-filename": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-4.0.0.tgz", + "integrity": "sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/unique-slug": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-5.0.0.tgz", + "integrity": "sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -16531,9 +17078,9 @@ } }, "node_modules/unplugin-swc": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/unplugin-swc/-/unplugin-swc-1.5.1.tgz", - "integrity": "sha512-/ZLrPNjChhGx3Z95pxJ4tQgfI6rWqukgYHKflrNB4zAV1izOQuDhkTn55JWeivpBxDCoK7M/TStb2aS/14PS/g==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/unplugin-swc/-/unplugin-swc-1.5.2.tgz", + "integrity": "sha512-bf8DJO8lD1wpnwFglQpVH2XEaFfVsSU5C7yFyLwGT1gxskPtejlDeuttKxjtmHTSqrDsQrK0FCFdhw3Ny+K7hA==", "dev": true, "license": "MIT", "dependencies": { @@ -16734,9 +17281,9 @@ } }, "node_modules/vite-node": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.1.tgz", - "integrity": "sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.2.tgz", + "integrity": "sha512-/8iMryv46J3aK13iUXsei5G/A3CUlW4665THCPS+K8xAaqrVWiGB4RfXMQXCLjpK9P2eK//BczrVkn5JLAk6DA==", "dev": true, "license": "MIT", "dependencies": { @@ -16776,278 +17323,6 @@ } } }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", - "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", - "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", - "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", - "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", - "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", - "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", - "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", - "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", - "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", - "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", - "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", - "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", - "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", - "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", - "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", - "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/vite/node_modules/@esbuild/linux-x64": { "version": "0.25.2", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", @@ -17065,125 +17340,6 @@ "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", - "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", - "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", - "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", - "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", - "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", - "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", - "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/vite/node_modules/esbuild": { "version": "0.25.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", @@ -17255,31 +17411,32 @@ } }, "node_modules/vitest": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.1.tgz", - "integrity": "sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.2.tgz", + "integrity": "sha512-WaxpJe092ID1C0mr+LH9MmNrhfzi8I65EX/NRU/Ld016KqQNRgxSOlGNP1hHN+a/F8L15Mh8klwaF77zR3GeDQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "3.1.1", - "@vitest/mocker": "3.1.1", - "@vitest/pretty-format": "^3.1.1", - "@vitest/runner": "3.1.1", - "@vitest/snapshot": "3.1.1", - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", + "@vitest/expect": "3.1.2", + "@vitest/mocker": "3.1.2", + "@vitest/pretty-format": "^3.1.2", + "@vitest/runner": "3.1.2", + "@vitest/snapshot": "3.1.2", + "@vitest/spy": "3.1.2", + "@vitest/utils": "3.1.2", "chai": "^5.2.0", "debug": "^4.4.0", - "expect-type": "^1.2.0", + "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", - "std-env": "^3.8.1", + "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.13", "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.1.1", + "vite-node": "3.1.2", "why-is-node-running": "^2.3.0" }, "bin": { @@ -17295,8 +17452,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.1.1", - "@vitest/ui": "3.1.1", + "@vitest/browser": "3.1.2", + "@vitest/ui": "3.1.2", "happy-dom": "*", "jsdom": "*" }, @@ -17324,6 +17481,19 @@ } } }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/watchpack": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", @@ -17354,9 +17524,9 @@ "license": "BSD-2-Clause" }, "node_modules/webpack": { - "version": "5.98.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz", - "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==", + "version": "5.99.6", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.6.tgz", + "integrity": "sha512-TJOLrJ6oeccsGWPl7ujCYuc0pIq2cNsuD6GZDma8i5o5Npvcco/z+NKvZSFsP0/x6SShVb0+X2JK/JHUjKY9dQ==", "dev": true, "license": "MIT", "dependencies": { @@ -17549,6 +17719,29 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -17717,6 +17910,23 @@ } } }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/server/package.json b/server/package.json index 8e149d961e..33d1450a53 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "immich", - "version": "1.131.3", + "version": "1.132.3", "description": "", "author": "", "private": true, @@ -44,7 +44,7 @@ "@nestjs/schedule": "^5.0.0", "@nestjs/swagger": "^11.0.2", "@nestjs/websockets": "^11.0.4", - "@opentelemetry/auto-instrumentations-node": "^0.57.0", + "@opentelemetry/auto-instrumentations-node": "^0.58.0", "@opentelemetry/context-async-hooks": "^2.0.0", "@opentelemetry/exporter-prometheus": "^0.200.0", "@opentelemetry/sdk-node": "^0.200.0", @@ -57,6 +57,7 @@ "chokidar": "^3.5.3", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", + "compression": "^1.8.0", "cookie": "^1.0.2", "cookie-parser": "^1.4.7", "exiftool-vendored": "^28.3.1", @@ -77,7 +78,7 @@ "nestjs-kysely": "^1.1.0", "nestjs-otel": "^6.0.0", "nodemailer": "^6.9.13", - "openid-client": "^5.4.3", + "openid-client": "^6.3.3", "pg": "^8.11.3", "picomatch": "^4.0.2", "react": "^19.0.0", @@ -88,7 +89,7 @@ "sanitize-filename": "^1.6.3", "sanitize-html": "^2.14.0", "semver": "^7.6.2", - "sharp": "^0.33.0", + "sharp": "^0.34.0", "sirv": "^3.0.0", "tailwindcss-preset-email": "^1.3.2", "thumbhash": "^0.1.1", @@ -108,6 +109,7 @@ "@types/archiver": "^6.0.0", "@types/async-lock": "^1.4.2", "@types/bcrypt": "^5.0.0", + "@types/compression": "^1.7.5", "@types/cookie-parser": "^1.4.8", "@types/express": "^4.17.17", "@types/fluent-ffmpeg": "^2.1.21", @@ -115,7 +117,7 @@ "@types/lodash": "^4.14.197", "@types/mock-fs": "^4.13.1", "@types/multer": "^1.4.7", - "@types/node": "^22.14.0", + "@types/node": "^22.14.1", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^3.0.0", "@types/pngjs": "^6.0.5", @@ -130,8 +132,10 @@ "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^57.0.0", "globals": "^16.0.0", + "jsdom": "^26.1.0", "mock-fs": "^5.2.0", - "node-addon-api": "^8.3.0", + "node-addon-api": "^8.3.1", + "node-gyp": "^11.2.0", "patch-package": "^8.0.0", "pngjs": "^7.0.0", "prettier": "^3.0.2", @@ -151,5 +155,8 @@ }, "volta": { "node": "22.14.0" + }, + "overrides": { + "sharp": "^0.34.0" } } diff --git a/server/src/app.module.ts b/server/src/app.module.ts index 5720f7af0b..153b525fe5 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -17,12 +17,12 @@ import { LoggingInterceptor } from 'src/middleware/logging.interceptor'; import { repositories } from 'src/repositories'; import { ConfigRepository } from 'src/repositories/config.repository'; import { EventRepository } from 'src/repositories/event.repository'; -import { JobRepository } from 'src/repositories/job.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { teardownTelemetry, TelemetryRepository } from 'src/repositories/telemetry.repository'; import { services } from 'src/services'; import { AuthService } from 'src/services/auth.service'; import { CliService } from 'src/services/cli.service'; +import { JobService } from 'src/services/job.service'; import { getKyselyConfig } from 'src/utils/database'; const common = [...repositories, ...services, GlobalExceptionFilter]; @@ -44,7 +44,7 @@ const imports = [ BullModule.registerQueue(...bull.queues), ClsModule.forRoot(cls.config), OpenTelemetryModule.forRoot(otel), - KyselyModule.forRoot(getKyselyConfig(database.config.kysely)), + KyselyModule.forRoot(getKyselyConfig(database.config)), ]; class BaseModule implements OnModuleInit, OnModuleDestroy { @@ -52,7 +52,7 @@ class BaseModule implements OnModuleInit, OnModuleDestroy { @Inject(IWorker) private worker: ImmichWorker, logger: LoggingRepository, private eventRepository: EventRepository, - private jobRepository: JobRepository, + private jobService: JobService, private telemetryRepository: TelemetryRepository, private authService: AuthService, ) { @@ -62,10 +62,7 @@ class BaseModule implements OnModuleInit, OnModuleDestroy { async onModuleInit() { this.telemetryRepository.setup({ repositories }); - this.jobRepository.setup({ services }); - if (this.worker === ImmichWorker.MICROSERVICES) { - this.jobRepository.startWorkers(); - } + this.jobService.setServices(services); this.eventRepository.setAuthFn(async (client) => this.authService.authenticate({ diff --git a/server/src/bin/migrations.ts b/server/src/bin/migrations.ts index 2ddc6776fb..7b850f6166 100644 --- a/server/src/bin/migrations.ts +++ b/server/src/bin/migrations.ts @@ -10,7 +10,7 @@ import { DatabaseRepository } from 'src/repositories/database.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import 'src/schema'; import { schemaDiff, schemaFromCode, schemaFromDatabase } from 'src/sql-tools'; -import { getKyselyConfig } from 'src/utils/database'; +import { asPostgresConnectionConfig, getKyselyConfig } from 'src/utils/database'; const main = async () => { const command = process.argv[2]; @@ -56,7 +56,7 @@ const main = async () => { const getDatabaseClient = () => { const configRepository = new ConfigRepository(); const { database } = configRepository.getEnv(); - return new Kysely<any>(getKyselyConfig(database.config.kysely)); + return new Kysely<any>(getKyselyConfig(database.config)); }; const runQuery = async (query: string) => { @@ -105,7 +105,7 @@ const create = (path: string, up: string[], down: string[]) => { const compare = async () => { const configRepository = new ConfigRepository(); const { database } = configRepository.getEnv(); - const db = postgres(database.config.kysely); + const db = postgres(asPostgresConnectionConfig(database.config)); const source = schemaFromCode(); const target = await schemaFromDatabase(db, {}); diff --git a/server/src/bin/sync-sql.ts b/server/src/bin/sync-sql.ts index 47e6610a74..b791358a90 100644 --- a/server/src/bin/sync-sql.ts +++ b/server/src/bin/sync-sql.ts @@ -78,7 +78,7 @@ class SqlGenerator { const moduleFixture = await Test.createTestingModule({ imports: [ KyselyModule.forRoot({ - ...getKyselyConfig(database.config.kysely), + ...getKyselyConfig(database.config), log: (event) => { if (event.level === 'query') { this.sqlLogger.logQuery(event.query.sql); diff --git a/server/src/config.ts b/server/src/config.ts index 566adbd693..a9fdffbd62 100644 --- a/server/src/config.ts +++ b/server/src/config.ts @@ -5,6 +5,7 @@ import { CQMode, ImageFormat, LogLevel, + OAuthTokenEndpointAuthMethod, QueueName, ToneMapping, TranscodeHWAccel, @@ -96,6 +97,8 @@ export interface SystemConfig { scope: string; signingAlgorithm: string; profileSigningAlgorithm: string; + tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod; + timeout: number; storageLabelClaim: string; storageQuotaClaim: string; }; @@ -260,6 +263,8 @@ export const defaults = Object.freeze<SystemConfig>({ profileSigningAlgorithm: 'none', storageLabelClaim: 'preferred_username', storageQuotaClaim: 'immich_quota', + tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod.CLIENT_SECRET_POST, + timeout: 30_000, }, passwordLogin: { enabled: true, diff --git a/server/src/controllers/file-report.controller.ts b/server/src/controllers/file-report.controller.ts deleted file mode 100644 index a51a94a50e..0000000000 --- a/server/src/controllers/file-report.controller.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Body, Controller, Get, Post } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; -import { FileChecksumDto, FileChecksumResponseDto, FileReportDto, FileReportFixDto } from 'src/dtos/audit.dto'; -import { Authenticated } from 'src/middleware/auth.guard'; -import { AuditService } from 'src/services/audit.service'; - -@ApiTags('File Reports') -@Controller('reports') -export class ReportController { - constructor(private service: AuditService) {} - - @Get() - @Authenticated({ admin: true }) - getAuditFiles(): Promise<FileReportDto> { - return this.service.getFileReport(); - } - - @Post('checksum') - @Authenticated({ admin: true }) - getFileChecksums(@Body() dto: FileChecksumDto): Promise<FileChecksumResponseDto[]> { - return this.service.getChecksums(dto); - } - - @Post('fix') - @Authenticated({ admin: true }) - fixAuditFiles(@Body() dto: FileReportFixDto): Promise<void> { - return this.service.fixItems(dto.items); - } -} diff --git a/server/src/controllers/index.ts b/server/src/controllers/index.ts index c9d63f8bcd..9c39e580b6 100644 --- a/server/src/controllers/index.ts +++ b/server/src/controllers/index.ts @@ -8,11 +8,11 @@ import { AuthController } from 'src/controllers/auth.controller'; import { DownloadController } from 'src/controllers/download.controller'; import { DuplicateController } from 'src/controllers/duplicate.controller'; import { FaceController } from 'src/controllers/face.controller'; -import { ReportController } from 'src/controllers/file-report.controller'; import { JobController } from 'src/controllers/job.controller'; import { LibraryController } from 'src/controllers/library.controller'; import { MapController } from 'src/controllers/map.controller'; import { MemoryController } from 'src/controllers/memory.controller'; +import { NotificationAdminController } from 'src/controllers/notification-admin.controller'; import { NotificationController } from 'src/controllers/notification.controller'; import { OAuthController } from 'src/controllers/oauth.controller'; import { PartnerController } from 'src/controllers/partner.controller'; @@ -48,10 +48,10 @@ export const controllers = [ MapController, MemoryController, NotificationController, + NotificationAdminController, OAuthController, PartnerController, PersonController, - ReportController, SearchController, ServerController, SessionController, diff --git a/server/src/controllers/notification-admin.controller.ts b/server/src/controllers/notification-admin.controller.ts new file mode 100644 index 0000000000..9bac865bdf --- /dev/null +++ b/server/src/controllers/notification-admin.controller.ts @@ -0,0 +1,44 @@ +import { Body, Controller, HttpCode, HttpStatus, Param, Post } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { + NotificationCreateDto, + NotificationDto, + TemplateDto, + TemplateResponseDto, + TestEmailResponseDto, +} from 'src/dtos/notification.dto'; +import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto'; +import { Auth, Authenticated } from 'src/middleware/auth.guard'; +import { EmailTemplate } from 'src/repositories/email.repository'; +import { NotificationAdminService } from 'src/services/notification-admin.service'; + +@ApiTags('Notifications (Admin)') +@Controller('admin/notifications') +export class NotificationAdminController { + constructor(private service: NotificationAdminService) {} + + @Post() + @Authenticated({ admin: true }) + createNotification(@Auth() auth: AuthDto, @Body() dto: NotificationCreateDto): Promise<NotificationDto> { + return this.service.create(auth, dto); + } + + @Post('test-email') + @HttpCode(HttpStatus.OK) + @Authenticated({ admin: true }) + sendTestEmailAdmin(@Auth() auth: AuthDto, @Body() dto: SystemConfigSmtpDto): Promise<TestEmailResponseDto> { + return this.service.sendTestEmail(auth.user.id, dto); + } + + @Post('templates/:name') + @HttpCode(HttpStatus.OK) + @Authenticated({ admin: true }) + getNotificationTemplateAdmin( + @Auth() auth: AuthDto, + @Param('name') name: EmailTemplate, + @Body() dto: TemplateDto, + ): Promise<TemplateResponseDto> { + return this.service.getTemplate(name, dto.template); + } +} diff --git a/server/src/controllers/notification.controller.ts b/server/src/controllers/notification.controller.ts index 39946a9fc9..c64f786850 100644 --- a/server/src/controllers/notification.controller.ts +++ b/server/src/controllers/notification.controller.ts @@ -1,32 +1,60 @@ -import { Body, Controller, HttpCode, HttpStatus, Param, Post } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Param, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { AuthDto } from 'src/dtos/auth.dto'; -import { TemplateDto, TemplateResponseDto, TestEmailResponseDto } from 'src/dtos/notification.dto'; -import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto'; +import { + NotificationDeleteAllDto, + NotificationDto, + NotificationSearchDto, + NotificationUpdateAllDto, + NotificationUpdateDto, +} from 'src/dtos/notification.dto'; +import { Permission } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; -import { EmailTemplate } from 'src/repositories/notification.repository'; import { NotificationService } from 'src/services/notification.service'; +import { UUIDParamDto } from 'src/validation'; @ApiTags('Notifications') @Controller('notifications') export class NotificationController { constructor(private service: NotificationService) {} - @Post('test-email') - @HttpCode(HttpStatus.OK) - @Authenticated({ admin: true }) - sendTestEmail(@Auth() auth: AuthDto, @Body() dto: SystemConfigSmtpDto): Promise<TestEmailResponseDto> { - return this.service.sendTestEmail(auth.user.id, dto); + @Get() + @Authenticated({ permission: Permission.NOTIFICATION_READ }) + getNotifications(@Auth() auth: AuthDto, @Query() dto: NotificationSearchDto): Promise<NotificationDto[]> { + return this.service.search(auth, dto); } - @Post('templates/:name') - @HttpCode(HttpStatus.OK) - @Authenticated({ admin: true }) - getNotificationTemplate( + @Put() + @Authenticated({ permission: Permission.NOTIFICATION_UPDATE }) + updateNotifications(@Auth() auth: AuthDto, @Body() dto: NotificationUpdateAllDto): Promise<void> { + return this.service.updateAll(auth, dto); + } + + @Delete() + @Authenticated({ permission: Permission.NOTIFICATION_DELETE }) + deleteNotifications(@Auth() auth: AuthDto, @Body() dto: NotificationDeleteAllDto): Promise<void> { + return this.service.deleteAll(auth, dto); + } + + @Get(':id') + @Authenticated({ permission: Permission.NOTIFICATION_READ }) + getNotification(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<NotificationDto> { + return this.service.get(auth, id); + } + + @Put(':id') + @Authenticated({ permission: Permission.NOTIFICATION_UPDATE }) + updateNotification( @Auth() auth: AuthDto, - @Param('name') name: EmailTemplate, - @Body() dto: TemplateDto, - ): Promise<TemplateResponseDto> { - return this.service.getTemplate(name, dto.template); + @Param() { id }: UUIDParamDto, + @Body() dto: NotificationUpdateDto, + ): Promise<NotificationDto> { + return this.service.update(auth, id, dto); + } + + @Delete(':id') + @Authenticated({ permission: Permission.NOTIFICATION_DELETE }) + deleteNotification(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> { + return this.service.delete(auth, id); } } diff --git a/server/src/controllers/oauth.controller.ts b/server/src/controllers/oauth.controller.ts index b5b94030f2..23ddff5ddc 100644 --- a/server/src/controllers/oauth.controller.ts +++ b/server/src/controllers/oauth.controller.ts @@ -29,17 +29,35 @@ export class OAuthController { } @Post('authorize') - startOAuth(@Body() dto: OAuthConfigDto): Promise<OAuthAuthorizeResponseDto> { - return this.service.authorize(dto); + async startOAuth( + @Body() dto: OAuthConfigDto, + @Res({ passthrough: true }) res: Response, + @GetLoginDetails() loginDetails: LoginDetails, + ): Promise<OAuthAuthorizeResponseDto> { + const { url, state, codeVerifier } = await this.service.authorize(dto); + return respondWithCookie( + res, + { url }, + { + isSecure: loginDetails.isSecure, + values: [ + { key: ImmichCookie.OAUTH_STATE, value: state }, + { key: ImmichCookie.OAUTH_CODE_VERIFIER, value: codeVerifier }, + ], + }, + ); } @Post('callback') async finishOAuth( + @Req() request: Request, @Res({ passthrough: true }) res: Response, @Body() dto: OAuthCallbackDto, @GetLoginDetails() loginDetails: LoginDetails, ): Promise<LoginResponseDto> { - const body = await this.service.callback(dto, loginDetails); + const body = await this.service.callback(dto, request.headers, loginDetails); + res.clearCookie(ImmichCookie.OAUTH_STATE); + res.clearCookie(ImmichCookie.OAUTH_CODE_VERIFIER); return respondWithCookie(res, body, { isSecure: loginDetails.isSecure, values: [ @@ -52,8 +70,12 @@ export class OAuthController { @Post('link') @Authenticated() - linkOAuthAccount(@Auth() auth: AuthDto, @Body() dto: OAuthCallbackDto): Promise<UserAdminResponseDto> { - return this.service.link(auth, dto); + linkOAuthAccount( + @Req() request: Request, + @Auth() auth: AuthDto, + @Body() dto: OAuthCallbackDto, + ): Promise<UserAdminResponseDto> { + return this.service.link(auth, dto, request.headers); } @Post('unlink') diff --git a/server/src/cores/storage.core.ts b/server/src/cores/storage.core.ts index 9bd43a662d..1a8e31e86b 100644 --- a/server/src/cores/storage.core.ts +++ b/server/src/cores/storage.core.ts @@ -90,7 +90,7 @@ export class StorageCore { return StorageCore.getNestedPath(StorageFolder.THUMBNAILS, person.ownerId, `${person.id}.jpeg`); } - static getImagePath(asset: ThumbnailPathEntity, type: GeneratedImageType, format: ImageFormat) { + static getImagePath(asset: ThumbnailPathEntity, type: GeneratedImageType, format: 'jpeg' | 'webp') { return StorageCore.getNestedPath(StorageFolder.THUMBNAILS, asset.ownerId, `${asset.id}-${type}.${format}`); } diff --git a/server/src/database.ts b/server/src/database.ts index 32ecaa2a76..7ed1367f70 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -9,6 +9,7 @@ import { Permission, SharedLinkType, SourceType, + UserAvatarColor, UserStatus, } from 'src/enum'; import { OnThisDayData, UserMetadataItem } from 'src/types'; @@ -122,6 +123,7 @@ export type User = { id: string; name: string; email: string; + avatarColor: UserAvatarColor | null; profileImagePath: string; profileChangedAt: Date; }; @@ -264,7 +266,15 @@ export type AssetFace = { person?: Person | null; }; -const userColumns = ['id', 'name', 'email', 'profileImagePath', 'profileChangedAt'] as const; +const userColumns = ['id', 'name', 'email', 'avatarColor', 'profileImagePath', 'profileChangedAt'] as const; +const userWithPrefixColumns = [ + 'users.id', + 'users.name', + 'users.email', + 'users.avatarColor', + 'users.profileImagePath', + 'users.profileChangedAt', +] as const; export const columns = { asset: [ @@ -306,7 +316,7 @@ export const columns = { 'shared_links.password', ], user: userColumns, - userWithPrefix: ['users.id', 'users.name', 'users.email', 'users.profileImagePath', 'users.profileChangedAt'], + userWithPrefix: userWithPrefixColumns, userAdmin: [ ...userColumns, 'createdAt', @@ -323,6 +333,7 @@ export const columns = { ], tag: ['tags.id', 'tags.value', 'tags.createdAt', 'tags.updatedAt', 'tags.color', 'tags.parentId'], apiKey: ['id', 'name', 'userId', 'createdAt', 'updatedAt', 'permissions'], + notification: ['id', 'createdAt', 'level', 'type', 'title', 'description', 'data', 'readAt'], syncAsset: [ 'id', 'ownerId', diff --git a/server/src/db.d.ts b/server/src/db.d.ts index 4e9738ecec..85be9d5208 100644 --- a/server/src/db.d.ts +++ b/server/src/db.d.ts @@ -11,6 +11,8 @@ import { AssetStatus, AssetType, MemoryType, + NotificationLevel, + NotificationType, Permission, SharedLinkType, SourceType, @@ -263,6 +265,21 @@ export interface Memories { updateId: Generated<string>; } +export interface Notifications { + id: Generated<string>; + createdAt: Generated<Timestamp>; + updatedAt: Generated<Timestamp>; + deletedAt: Timestamp | null; + updateId: Generated<string>; + userId: string; + level: Generated<NotificationLevel>; + type: NotificationType; + title: string; + description: string | null; + data: any | null; + readAt: Timestamp | null; +} + export interface MemoriesAssetsAssets { assetsId: string; memoriesId: string; @@ -463,6 +480,7 @@ export interface DB { memories: Memories; memories_assets_assets: MemoriesAssetsAssets; migrations: Migrations; + notifications: Notifications; move_history: MoveHistory; naturalearth_countries: NaturalearthCountries; partners_audit: PartnersAudit; diff --git a/server/src/dtos/audit.dto.ts b/server/src/dtos/audit.dto.ts deleted file mode 100644 index 434da46eba..0000000000 --- a/server/src/dtos/audit.dto.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; -import { IsArray, IsEnum, IsString, IsUUID, ValidateNested } from 'class-validator'; -import { AssetPathType, EntityType, PathType, PersonPathType, UserPathType } from 'src/enum'; -import { Optional, ValidateDate, ValidateUUID } from 'src/validation'; - -const PathEnum = Object.values({ ...AssetPathType, ...PersonPathType, ...UserPathType }); - -export class AuditDeletesDto { - @ValidateDate() - after!: Date; - - @ApiProperty({ enum: EntityType, enumName: 'EntityType' }) - @IsEnum(EntityType) - entityType!: EntityType; - - @Optional() - @IsUUID('4') - @ApiProperty({ format: 'uuid' }) - userId?: string; -} - -export enum PathEntityType { - ASSET = 'asset', - PERSON = 'person', - USER = 'user', -} - -export class AuditDeletesResponseDto { - needsFullSync!: boolean; - ids!: string[]; -} - -export class FileReportDto { - orphans!: FileReportItemDto[]; - extras!: string[]; -} - -export class FileChecksumDto { - @IsString({ each: true }) - filenames!: string[]; -} - -export class FileChecksumResponseDto { - filename!: string; - checksum!: string; -} - -export class FileReportFixDto { - @IsArray() - @ValidateNested({ each: true }) - @Type(() => FileReportItemDto) - items!: FileReportItemDto[]; -} - -// used both as request and response dto -export class FileReportItemDto { - @ValidateUUID() - entityId!: string; - - @ApiProperty({ enumName: 'PathEntityType', enum: PathEntityType }) - @IsEnum(PathEntityType) - entityType!: PathEntityType; - - @ApiProperty({ enumName: 'PathType', enum: PathEnum }) - @IsEnum(PathEnum) - pathType!: PathType; - - @IsString() - pathValue!: string; - - checksum?: string; -} diff --git a/server/src/dtos/auth.dto.ts b/server/src/dtos/auth.dto.ts index 7f2ffa5878..a1978d39dd 100644 --- a/server/src/dtos/auth.dto.ts +++ b/server/src/dtos/auth.dto.ts @@ -3,11 +3,11 @@ import { Transform } from 'class-transformer'; import { IsEmail, IsNotEmpty, IsString, MinLength } from 'class-validator'; import { AuthApiKey, AuthSession, AuthSharedLink, AuthUser, UserAdmin } from 'src/database'; import { ImmichCookie } from 'src/enum'; -import { toEmail } from 'src/validation'; +import { Optional, toEmail } from 'src/validation'; export type CookieResponse = { isSecure: boolean; - values: Array<{ key: ImmichCookie; value: string }>; + values: Array<{ key: ImmichCookie; value: string | null }>; }; export class AuthDto { @@ -87,12 +87,28 @@ export class OAuthCallbackDto { @IsString() @ApiProperty() url!: string; + + @Optional() + @IsString() + state?: string; + + @Optional() + @IsString() + codeVerifier?: string; } export class OAuthConfigDto { @IsNotEmpty() @IsString() redirectUri!: string; + + @Optional() + @IsString() + state?: string; + + @Optional() + @IsString() + codeChallenge?: string; } export class OAuthAuthorizeResponseDto { diff --git a/server/src/dtos/notification.dto.ts b/server/src/dtos/notification.dto.ts index c1a09c801c..d9847cda17 100644 --- a/server/src/dtos/notification.dto.ts +++ b/server/src/dtos/notification.dto.ts @@ -1,4 +1,7 @@ -import { IsString } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsString } from 'class-validator'; +import { NotificationLevel, NotificationType } from 'src/enum'; +import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation'; export class TestEmailResponseDto { messageId!: string; @@ -11,3 +14,106 @@ export class TemplateDto { @IsString() template!: string; } + +export class NotificationDto { + id!: string; + @ValidateDate() + createdAt!: Date; + @ApiProperty({ enum: NotificationLevel, enumName: 'NotificationLevel' }) + level!: NotificationLevel; + @ApiProperty({ enum: NotificationType, enumName: 'NotificationType' }) + type!: NotificationType; + title!: string; + description?: string; + data?: any; + readAt?: Date; +} + +export class NotificationSearchDto { + @Optional() + @ValidateUUID({ optional: true }) + id?: string; + + @IsEnum(NotificationLevel) + @Optional() + @ApiProperty({ enum: NotificationLevel, enumName: 'NotificationLevel' }) + level?: NotificationLevel; + + @IsEnum(NotificationType) + @Optional() + @ApiProperty({ enum: NotificationType, enumName: 'NotificationType' }) + type?: NotificationType; + + @ValidateBoolean({ optional: true }) + unread?: boolean; +} + +export class NotificationCreateDto { + @Optional() + @IsEnum(NotificationLevel) + @ApiProperty({ enum: NotificationLevel, enumName: 'NotificationLevel' }) + level?: NotificationLevel; + + @IsEnum(NotificationType) + @Optional() + @ApiProperty({ enum: NotificationType, enumName: 'NotificationType' }) + type?: NotificationType; + + @IsString() + title!: string; + + @IsString() + @Optional({ nullable: true }) + description?: string | null; + + @Optional({ nullable: true }) + data?: any; + + @ValidateDate({ optional: true, nullable: true }) + readAt?: Date | null; + + @ValidateUUID() + userId!: string; +} + +export class NotificationUpdateDto { + @ValidateDate({ optional: true, nullable: true }) + readAt?: Date | null; +} + +export class NotificationUpdateAllDto { + @ValidateUUID({ each: true, optional: true }) + ids!: string[]; + + @ValidateDate({ optional: true, nullable: true }) + readAt?: Date | null; +} + +export class NotificationDeleteAllDto { + @ValidateUUID({ each: true }) + ids!: string[]; +} + +export type MapNotification = { + id: string; + createdAt: Date; + updateId?: string; + level: NotificationLevel; + type: NotificationType; + data: any | null; + title: string; + description: string | null; + readAt: Date | null; +}; +export const mapNotification = (notification: MapNotification): NotificationDto => { + return { + id: notification.id, + createdAt: notification.createdAt, + level: notification.level, + type: notification.type, + title: notification.title, + description: notification.description ?? undefined, + data: notification.data ?? undefined, + readAt: notification.readAt ?? undefined, + }; +}; diff --git a/server/src/dtos/system-config.dto.ts b/server/src/dtos/system-config.dto.ts index eaef40a5e1..6991baf109 100644 --- a/server/src/dtos/system-config.dto.ts +++ b/server/src/dtos/system-config.dto.ts @@ -25,6 +25,7 @@ import { Colorspace, ImageFormat, LogLevel, + OAuthTokenEndpointAuthMethod, QueueName, ToneMapping, TranscodeHWAccel, @@ -33,7 +34,7 @@ import { VideoContainer, } from 'src/enum'; import { ConcurrentQueueName } from 'src/types'; -import { IsCronExpression, ValidateBoolean } from 'src/validation'; +import { IsCronExpression, Optional, ValidateBoolean } from 'src/validation'; const isLibraryScanEnabled = (config: SystemConfigLibraryScanDto) => config.enabled; const isOAuthEnabled = (config: SystemConfigOAuthDto) => config.enabled; @@ -344,10 +345,19 @@ class SystemConfigOAuthDto { clientId!: string; @ValidateIf(isOAuthEnabled) - @IsNotEmpty() @IsString() clientSecret!: string; + @IsEnum(OAuthTokenEndpointAuthMethod) + @ApiProperty({ enum: OAuthTokenEndpointAuthMethod, enumName: 'OAuthTokenEndpointAuthMethod' }) + tokenEndpointAuthMethod!: OAuthTokenEndpointAuthMethod; + + @IsInt() + @IsPositive() + @Optional() + @ApiProperty({ type: 'integer' }) + timeout!: number; + @IsNumber() @Min(0) defaultStorageQuota!: number; diff --git a/server/src/dtos/user-preferences.dto.ts b/server/src/dtos/user-preferences.dto.ts index fe92838fdb..a9d32523ae 100644 --- a/server/src/dtos/user-preferences.dto.ts +++ b/server/src/dtos/user-preferences.dto.ts @@ -137,11 +137,6 @@ export class UserPreferencesUpdateDto { purchase?: PurchaseUpdate; } -class AvatarResponse { - @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor }) - color!: UserAvatarColor; -} - class RatingsResponse { enabled: boolean = false; } @@ -195,7 +190,6 @@ export class UserPreferencesResponseDto implements UserPreferences { ratings!: RatingsResponse; sharedLinks!: SharedLinksResponse; tags!: TagsResponse; - avatar!: AvatarResponse; emailNotifications!: EmailNotificationsResponse; download!: DownloadResponse; purchase!: PurchaseResponse; diff --git a/server/src/dtos/user.dto.ts b/server/src/dtos/user.dto.ts index 72e5c83b35..31275f9c28 100644 --- a/server/src/dtos/user.dto.ts +++ b/server/src/dtos/user.dto.ts @@ -1,10 +1,9 @@ import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; -import { IsBoolean, IsEmail, IsNotEmpty, IsNumber, IsString, Min } from 'class-validator'; +import { IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumber, IsString, Min } from 'class-validator'; import { User, UserAdmin } from 'src/database'; import { UserAvatarColor, UserMetadataKey, UserStatus } from 'src/enum'; import { UserMetadataItem } from 'src/types'; -import { getPreferences } from 'src/utils/preferences'; import { Optional, ValidateBoolean, toEmail, toSanitized } from 'src/validation'; export class UserUpdateMeDto { @@ -23,6 +22,11 @@ export class UserUpdateMeDto { @IsString() @IsNotEmpty() name?: string; + + @Optional({ nullable: true }) + @IsEnum(UserAvatarColor) + @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor }) + avatarColor?: UserAvatarColor | null; } export class UserResponseDto { @@ -41,13 +45,21 @@ export class UserLicense { activatedAt!: Date; } +const emailToAvatarColor = (email: string): UserAvatarColor => { + const values = Object.values(UserAvatarColor); + const randomIndex = Math.floor( + [...email].map((letter) => letter.codePointAt(0) ?? 0).reduce((a, b) => a + b, 0) % values.length, + ); + return values[randomIndex]; +}; + export const mapUser = (entity: User | UserAdmin): UserResponseDto => { return { id: entity.id, email: entity.email, name: entity.name, profileImagePath: entity.profileImagePath, - avatarColor: getPreferences(entity.email, (entity as UserAdmin).metadata || []).avatar.color, + avatarColor: entity.avatarColor ?? emailToAvatarColor(entity.email), profileChangedAt: entity.profileChangedAt, }; }; @@ -69,6 +81,11 @@ export class UserAdminCreateDto { @IsString() name!: string; + @Optional({ nullable: true }) + @IsEnum(UserAvatarColor) + @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor }) + avatarColor?: UserAvatarColor | null; + @Optional({ nullable: true }) @IsString() @Transform(toSanitized) @@ -104,6 +121,11 @@ export class UserAdminUpdateDto { @IsNotEmpty() name?: string; + @Optional({ nullable: true }) + @IsEnum(UserAvatarColor) + @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor }) + avatarColor?: UserAvatarColor | null; + @Optional({ nullable: true }) @IsString() @Transform(toSanitized) diff --git a/server/src/emails/album-invite.email.tsx b/server/src/emails/album-invite.email.tsx index 4bd7abc305..fdc189af97 100644 --- a/server/src/emails/album-invite.email.tsx +++ b/server/src/emails/album-invite.email.tsx @@ -2,7 +2,7 @@ import { Img, Link, Section, Text } from '@react-email/components'; import * as React from 'react'; import { ImmichButton } from 'src/emails/components/button.component'; import ImmichLayout from 'src/emails/components/immich.layout'; -import { AlbumInviteEmailProps } from 'src/repositories/notification.repository'; +import { AlbumInviteEmailProps } from 'src/repositories/email.repository'; import { replaceTemplateTags } from 'src/utils/replace-template-tags'; export const AlbumInviteEmail = ({ diff --git a/server/src/emails/album-update.email.tsx b/server/src/emails/album-update.email.tsx index 2311e896e1..3bed3a5b36 100644 --- a/server/src/emails/album-update.email.tsx +++ b/server/src/emails/album-update.email.tsx @@ -2,7 +2,7 @@ import { Img, Link, Section, Text } from '@react-email/components'; import * as React from 'react'; import { ImmichButton } from 'src/emails/components/button.component'; import ImmichLayout from 'src/emails/components/immich.layout'; -import { AlbumUpdateEmailProps } from 'src/repositories/notification.repository'; +import { AlbumUpdateEmailProps } from 'src/repositories/email.repository'; import { replaceTemplateTags } from 'src/utils/replace-template-tags'; export const AlbumUpdateEmail = ({ diff --git a/server/src/emails/test.email.tsx b/server/src/emails/test.email.tsx index ac9bdbe0ea..0d87307080 100644 --- a/server/src/emails/test.email.tsx +++ b/server/src/emails/test.email.tsx @@ -1,7 +1,7 @@ import { Link, Row, Text } from '@react-email/components'; import * as React from 'react'; import ImmichLayout from 'src/emails/components/immich.layout'; -import { TestEmailProps } from 'src/repositories/notification.repository'; +import { TestEmailProps } from 'src/repositories/email.repository'; export const TestEmail = ({ baseUrl, displayName }: TestEmailProps) => ( <ImmichLayout preview="This is a test email from Immich."> diff --git a/server/src/emails/welcome.email.tsx b/server/src/emails/welcome.email.tsx index 11a6602711..57e86ab252 100644 --- a/server/src/emails/welcome.email.tsx +++ b/server/src/emails/welcome.email.tsx @@ -2,7 +2,7 @@ import { Link, Section, Text } from '@react-email/components'; import * as React from 'react'; import { ImmichButton } from 'src/emails/components/button.component'; import ImmichLayout from 'src/emails/components/immich.layout'; -import { WelcomeEmailProps } from 'src/repositories/notification.repository'; +import { WelcomeEmailProps } from 'src/repositories/email.repository'; import { replaceTemplateTags } from 'src/utils/replace-template-tags'; export const WelcomeEmail = ({ baseUrl, displayName, username, password, customTemplate }: WelcomeEmailProps) => { diff --git a/server/src/enum.ts b/server/src/enum.ts index e5c6039be8..4e725e1c13 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -8,6 +8,8 @@ export enum ImmichCookie { AUTH_TYPE = 'immich_auth_type', IS_AUTHENTICATED = 'immich_is_authenticated', SHARED_LINK_TOKEN = 'immich_shared_link_token', + OAUTH_STATE = 'immich_oauth_state', + OAUTH_CODE_VERIFIER = 'immich_oauth_code_verifier', } export enum ImmichHeader { @@ -124,6 +126,11 @@ export enum Permission { MEMORY_UPDATE = 'memory.update', MEMORY_DELETE = 'memory.delete', + NOTIFICATION_CREATE = 'notification.create', + NOTIFICATION_READ = 'notification.read', + NOTIFICATION_UPDATE = 'notification.update', + NOTIFICATION_DELETE = 'notification.delete', + PARTNER_CREATE = 'partner.create', PARTNER_READ = 'partner.read', PARTNER_UPDATE = 'partner.update', @@ -330,6 +337,11 @@ export enum ImageFormat { WEBP = 'webp', } +export enum RawExtractedFormat { + JPEG = 'jpeg', + JXL = 'jxl', +} + export enum LogLevel { VERBOSE = 'verbose', DEBUG = 'debug', @@ -405,6 +417,8 @@ export enum DatabaseExtension { export enum BootstrapEventPriority { // Database service should be initialized before anything else, most other services need database access DatabaseService = -200, + // Other services may need to queue jobs on bootstrap. + JobService = -190, // Initialise config after other bootstrap services, stop other services from using config on bootstrap SystemConfig = 100, } @@ -511,6 +525,7 @@ export enum JobName { NOTIFY_SIGNUP = 'notify-signup', NOTIFY_ALBUM_INVITE = 'notify-album-invite', NOTIFY_ALBUM_UPDATE = 'notify-album-update', + NOTIFICATIONS_CLEANUP = 'notifications-cleanup', SEND_EMAIL = 'notification-send-email', // Version check @@ -576,3 +591,22 @@ export enum SyncEntityType { PartnerAssetDeleteV1 = 'PartnerAssetDeleteV1', PartnerAssetExifV1 = 'PartnerAssetExifV1', } + +export enum NotificationLevel { + Success = 'success', + Error = 'error', + Warning = 'warning', + Info = 'info', +} + +export enum NotificationType { + JobFailed = 'JobFailed', + BackupFailed = 'BackupFailed', + SystemMessage = 'SystemMessage', + Custom = 'Custom', +} + +export enum OAuthTokenEndpointAuthMethod { + CLIENT_SECRET_POST = 'client_secret_post', + CLIENT_SECRET_BASIC = 'client_secret_basic', +} diff --git a/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts b/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts index 993e12f822..b5d47bb8cd 100644 --- a/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts +++ b/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts @@ -1,5 +1,5 @@ -import { DatabaseExtension } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; +import { vectorIndexQuery } from 'src/utils/database'; import { MigrationInterface, QueryRunner } from 'typeorm'; const vectorExtension = new ConfigRepository().getEnv().database.vectorExtension; @@ -8,15 +8,9 @@ export class AddCLIPEmbeddingIndex1700713994428 implements MigrationInterface { name = 'AddCLIPEmbeddingIndex1700713994428'; public async up(queryRunner: QueryRunner): Promise<void> { - if (vectorExtension === DatabaseExtension.VECTORS) { - await queryRunner.query(`SET vectors.pgvector_compatibility=on`); - } await queryRunner.query(`SET search_path TO "$user", public, vectors`); - await queryRunner.query(` - CREATE INDEX IF NOT EXISTS clip_index ON smart_search - USING hnsw (embedding vector_cosine_ops) - WITH (ef_construction = 300, m = 16)`); + await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'smart_search', indexName: 'clip_index' })); } public async down(queryRunner: QueryRunner): Promise<void> { diff --git a/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts b/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts index 182aae4e42..2b41788fe4 100644 --- a/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts +++ b/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts @@ -1,5 +1,5 @@ -import { DatabaseExtension } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; +import { vectorIndexQuery } from 'src/utils/database'; import { MigrationInterface, QueryRunner } from 'typeorm'; const vectorExtension = new ConfigRepository().getEnv().database.vectorExtension; @@ -8,15 +8,9 @@ export class AddFaceEmbeddingIndex1700714033632 implements MigrationInterface { name = 'AddFaceEmbeddingIndex1700714033632'; public async up(queryRunner: QueryRunner): Promise<void> { - if (vectorExtension === DatabaseExtension.VECTORS) { - await queryRunner.query(`SET vectors.pgvector_compatibility=on`); - } await queryRunner.query(`SET search_path TO "$user", public, vectors`); - await queryRunner.query(` - CREATE INDEX IF NOT EXISTS face_index ON asset_faces - USING hnsw (embedding vector_cosine_ops) - WITH (ef_construction = 300, m = 16)`); + await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'asset_faces', indexName: 'face_index' })); } public async down(queryRunner: QueryRunner): Promise<void> { diff --git a/server/src/migrations/1718486162779-AddFaceSearchRelation.ts b/server/src/migrations/1718486162779-AddFaceSearchRelation.ts index e08bcb8e25..64849708d2 100644 --- a/server/src/migrations/1718486162779-AddFaceSearchRelation.ts +++ b/server/src/migrations/1718486162779-AddFaceSearchRelation.ts @@ -1,5 +1,6 @@ import { DatabaseExtension } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; +import { vectorIndexQuery } from 'src/utils/database'; import { MigrationInterface, QueryRunner } from 'typeorm'; const vectorExtension = new ConfigRepository().getEnv().database.vectorExtension; @@ -8,7 +9,6 @@ export class AddFaceSearchRelation1718486162779 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise<void> { if (vectorExtension === DatabaseExtension.VECTORS) { await queryRunner.query(`SET search_path TO "$user", public, vectors`); - await queryRunner.query(`SET vectors.pgvector_compatibility=on`); } const hasEmbeddings = async (tableName: string): Promise<boolean> => { @@ -47,21 +47,14 @@ export class AddFaceSearchRelation1718486162779 implements MigrationInterface { await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE real[]`); await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE vector(512)`); - await queryRunner.query(` - CREATE INDEX IF NOT EXISTS clip_index ON smart_search - USING hnsw (embedding vector_cosine_ops) - WITH (ef_construction = 300, m = 16)`); + await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'smart_search', indexName: 'clip_index' })); - await queryRunner.query(` - CREATE INDEX face_index ON face_search - USING hnsw (embedding vector_cosine_ops) - WITH (ef_construction = 300, m = 16)`); + await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'face_search', indexName: 'face_index' })); } public async down(queryRunner: QueryRunner): Promise<void> { if (vectorExtension === DatabaseExtension.VECTORS) { await queryRunner.query(`SET search_path TO "$user", public, vectors`); - await queryRunner.query(`SET vectors.pgvector_compatibility=on`); } await queryRunner.query(`ALTER TABLE asset_faces ADD COLUMN "embedding" vector(512)`); @@ -74,9 +67,6 @@ export class AddFaceSearchRelation1718486162779 implements MigrationInterface { WHERE id = fs."faceId"`); await queryRunner.query(`DROP TABLE face_search`); - await queryRunner.query(` - CREATE INDEX face_index ON asset_faces - USING hnsw (embedding vector_cosine_ops) - WITH (ef_construction = 300, m = 16)`); + await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'asset_faces', indexName: 'face_index' })); } } diff --git a/server/src/queries/access.repository.sql b/server/src/queries/access.repository.sql index dd58aebcb2..03f1af3b28 100644 --- a/server/src/queries/access.repository.sql +++ b/server/src/queries/access.repository.sql @@ -157,6 +157,15 @@ where and "memories"."ownerId" = $2 and "memories"."deletedAt" is null +-- AccessRepository.notification.checkOwnerAccess +select + "notifications"."id" +from + "notifications" +where + "notifications"."id" in ($1) + and "notifications"."userId" = $2 + -- AccessRepository.person.checkOwnerAccess select "person"."id" diff --git a/server/src/queries/activity.repository.sql b/server/src/queries/activity.repository.sql index c6e4c60a19..3040de8e03 100644 --- a/server/src/queries/activity.repository.sql +++ b/server/src/queries/activity.repository.sql @@ -13,6 +13,7 @@ from "users"."id", "users"."name", "users"."email", + "users"."avatarColor", "users"."profileImagePath", "users"."profileChangedAt" from @@ -44,6 +45,7 @@ returning "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from diff --git a/server/src/queries/album.repository.sql b/server/src/queries/album.repository.sql index b89cbfb0b9..f4eb6a9929 100644 --- a/server/src/queries/album.repository.sql +++ b/server/src/queries/album.repository.sql @@ -12,6 +12,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -36,6 +37,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -100,6 +102,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -124,6 +127,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -191,6 +195,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -215,6 +220,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -269,6 +275,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -292,6 +299,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -353,6 +361,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from diff --git a/server/src/queries/asset.job.repository.sql b/server/src/queries/asset.job.repository.sql index bf96ae80d6..d8e8430be7 100644 --- a/server/src/queries/asset.job.repository.sql +++ b/server/src/queries/asset.job.repository.sql @@ -194,6 +194,43 @@ where "asset_files"."assetId" = $1 and "asset_files"."type" = $2 +-- AssetJobRepository.streamForSearchDuplicates +select + "assets"."id" +from + "assets" + inner join "asset_job_status" as "job_status" on "assetId" = "assets"."id" +where + "assets"."isVisible" = $1 + and "assets"."deletedAt" is null + and "job_status"."previewAt" is not null + and not exists ( + select + from + "smart_search" + where + "assetId" = "assets"."id" + ) + and "job_status"."duplicatesDetectedAt" is null + +-- AssetJobRepository.streamForEncodeClip +select + "assets"."id" +from + "assets" + inner join "asset_job_status" as "job_status" on "assetId" = "assets"."id" +where + "assets"."isVisible" = $1 + and "assets"."deletedAt" is null + and "job_status"."previewAt" is not null + and not exists ( + select + from + "smart_search" + where + "assetId" = "assets"."id" + ) + -- AssetJobRepository.getForClipEncoding select "assets"."id", @@ -259,6 +296,130 @@ from where "assets"."id" = $2 +-- AssetJobRepository.getForSyncAssets +select + "assets"."id", + "assets"."isOffline", + "assets"."libraryId", + "assets"."originalPath", + "assets"."status", + "assets"."fileModifiedAt" +from + "assets" +where + "assets"."id" = any ($1::uuid[]) + +-- AssetJobRepository.getForAssetDeletion +select + "assets"."id", + "assets"."isVisible", + "assets"."libraryId", + "assets"."ownerId", + "assets"."livePhotoVideoId", + "assets"."sidecarPath", + "assets"."encodedVideoPath", + "assets"."originalPath", + to_json("exif") as "exifInfo", + ( + select + coalesce(json_agg(agg), '[]') + from + ( + select + "asset_faces".*, + "person" as "person" + from + "asset_faces" + left join lateral ( + select + "person".* + from + "person" + where + "asset_faces"."personId" = "person"."id" + ) as "person" on true + where + "asset_faces"."assetId" = "assets"."id" + and "asset_faces"."deletedAt" is null + ) as agg + ) as "faces", + ( + select + coalesce(json_agg(agg), '[]') + from + ( + select + "asset_files"."id", + "asset_files"."path", + "asset_files"."type" + from + "asset_files" + where + "asset_files"."assetId" = "assets"."id" + ) as agg + ) as "files", + to_json("stacked_assets") as "stack" +from + "assets" + left join "exif" on "assets"."id" = "exif"."assetId" + left join "asset_stack" on "asset_stack"."id" = "assets"."stackId" + left join lateral ( + select + "asset_stack"."id", + "asset_stack"."primaryAssetId", + array_agg("stacked") as "assets" + from + "assets" as "stacked" + where + "stacked"."deletedAt" is not null + and "stacked"."isArchived" = $1 + and "stacked"."stackId" = "asset_stack"."id" + group by + "asset_stack"."id" + ) as "stacked_assets" on "asset_stack"."id" is not null +where + "assets"."id" = $2 + +-- AssetJobRepository.streamForVideoConversion +select + "assets"."id" +from + "assets" +where + "assets"."type" = $1 + and ( + "assets"."encodedVideoPath" is null + or "assets"."encodedVideoPath" = $2 + ) + and "assets"."isVisible" = $3 + and "assets"."deletedAt" is null + +-- AssetJobRepository.getForVideoConversion +select + "assets"."id", + "assets"."ownerId", + "assets"."originalPath", + "assets"."encodedVideoPath" +from + "assets" +where + "assets"."id" = $1 + and "assets"."type" = $2 + +-- AssetJobRepository.streamForMetadataExtraction +select + "assets"."id" +from + "assets" + left join "asset_job_status" on "asset_job_status"."assetId" = "assets"."id" +where + ( + "asset_job_status"."metadataExtractedAt" is null + or "asset_job_status"."assetId" is null + ) + and "assets"."isVisible" = $1 + and "assets"."deletedAt" is null + -- AssetJobRepository.getForStorageTemplateJob select "assets"."id", @@ -308,3 +469,37 @@ from "assets" where "assets"."deletedAt" <= $1 + +-- AssetJobRepository.streamForSidecar +select + "assets"."id" +from + "assets" +where + ( + "assets"."sidecarPath" = $1 + or "assets"."sidecarPath" is null + ) + and "assets"."isVisible" = $2 + +-- AssetJobRepository.streamForDetectFacesJob +select + "assets"."id" +from + "assets" + inner join "asset_job_status" as "job_status" on "assetId" = "assets"."id" +where + "assets"."isVisible" = $1 + and "assets"."deletedAt" is null + and "job_status"."previewAt" is not null + and "job_status"."facesRecognizedAt" is null +order by + "assets"."createdAt" desc + +-- AssetJobRepository.streamForMigrationJob +select + "id" +from + "assets" +where + "assets"."deletedAt" is null diff --git a/server/src/queries/asset.repository.sql b/server/src/queries/asset.repository.sql index a3dcb08c1e..cb438e1c6d 100644 --- a/server/src/queries/asset.repository.sql +++ b/server/src/queries/asset.repository.sql @@ -232,25 +232,6 @@ where limit $3 --- AssetRepository.getWithout (sidecar) -select - "assets".* -from - "assets" -where - ( - "assets"."sidecarPath" = $1 - or "assets"."sidecarPath" is null - ) - and "assets"."isVisible" = $2 - and "deletedAt" is null -order by - "createdAt" -limit - $3 -offset - $4 - -- AssetRepository.getTimeBuckets with "assets" as ( diff --git a/server/src/queries/notification.repository.sql b/server/src/queries/notification.repository.sql new file mode 100644 index 0000000000..c55e00d226 --- /dev/null +++ b/server/src/queries/notification.repository.sql @@ -0,0 +1,58 @@ +-- NOTE: This file is auto generated by ./sql-generator + +-- NotificationRepository.cleanup +delete from "notifications" +where + ( + ( + "deletedAt" is not null + and "deletedAt" < $1 + ) + or ( + "readAt" > $2 + and "createdAt" < $3 + ) + or ( + "readAt" = $4 + and "createdAt" < $5 + ) + ) + +-- NotificationRepository.search +select + "id", + "createdAt", + "level", + "type", + "title", + "description", + "data", + "readAt" +from + "notifications" +where + "userId" = $1 + and "deletedAt" is null +order by + "createdAt" desc + +-- NotificationRepository.search (unread) +select + "id", + "createdAt", + "level", + "type", + "title", + "description", + "data", + "readAt" +from + "notifications" +where + ( + "userId" = $1 + and "readAt" is null + ) + and "deletedAt" is null +order by + "createdAt" desc diff --git a/server/src/queries/partner.repository.sql b/server/src/queries/partner.repository.sql index e115dc34b9..e7170f367e 100644 --- a/server/src/queries/partner.repository.sql +++ b/server/src/queries/partner.repository.sql @@ -12,6 +12,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -29,6 +30,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -61,6 +63,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -78,6 +81,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -112,6 +116,7 @@ returning "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -129,6 +134,7 @@ returning "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -156,6 +162,7 @@ returning "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -173,6 +180,7 @@ returning "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from diff --git a/server/src/queries/user.repository.sql b/server/src/queries/user.repository.sql index 1212d0f2bd..e8ab5018fc 100644 --- a/server/src/queries/user.repository.sql +++ b/server/src/queries/user.repository.sql @@ -5,6 +5,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt", "createdAt", @@ -43,6 +44,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt", "createdAt", @@ -90,6 +92,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt", "createdAt", @@ -128,6 +131,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt", "createdAt", @@ -152,6 +156,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt", "createdAt", @@ -198,6 +203,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt", "createdAt", @@ -235,6 +241,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt", "createdAt", diff --git a/server/src/repositories/access.repository.ts b/server/src/repositories/access.repository.ts index 961cccbf3e..c24209e482 100644 --- a/server/src/repositories/access.repository.ts +++ b/server/src/repositories/access.repository.ts @@ -279,6 +279,26 @@ class AuthDeviceAccess { } } +class NotificationAccess { + constructor(private db: Kysely<DB>) {} + + @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) + @ChunkedSet({ paramIndex: 1 }) + async checkOwnerAccess(userId: string, notificationIds: Set<string>) { + if (notificationIds.size === 0) { + return new Set<string>(); + } + + return this.db + .selectFrom('notifications') + .select('notifications.id') + .where('notifications.id', 'in', [...notificationIds]) + .where('notifications.userId', '=', userId) + .execute() + .then((stacks) => new Set(stacks.map((stack) => stack.id))); + } +} + class StackAccess { constructor(private db: Kysely<DB>) {} @@ -426,6 +446,7 @@ export class AccessRepository { asset: AssetAccess; authDevice: AuthDeviceAccess; memory: MemoryAccess; + notification: NotificationAccess; person: PersonAccess; partner: PartnerAccess; stack: StackAccess; @@ -438,6 +459,7 @@ export class AccessRepository { this.asset = new AssetAccess(db); this.authDevice = new AuthDeviceAccess(db); this.memory = new MemoryAccess(db); + this.notification = new NotificationAccess(db); this.person = new PersonAccess(db); this.partner = new PartnerAccess(db); this.stack = new StackAccess(db); diff --git a/server/src/repositories/asset-job.repository.ts b/server/src/repositories/asset-job.repository.ts index 4a2d52566f..1506f2997f 100644 --- a/server/src/repositories/asset-job.repository.ts +++ b/server/src/repositories/asset-job.repository.ts @@ -2,12 +2,21 @@ import { Injectable } from '@nestjs/common'; import { Kysely } from 'kysely'; import { jsonArrayFrom } from 'kysely/helpers/postgres'; import { InjectKysely } from 'nestjs-kysely'; -import { columns } from 'src/database'; +import { Asset, columns } from 'src/database'; import { DB } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; -import { AssetFileType } from 'src/enum'; +import { AssetFileType, AssetType } from 'src/enum'; import { StorageAsset } from 'src/types'; -import { anyUuid, asUuid, withExifInner, withFaces, withFiles } from 'src/utils/database'; +import { + anyUuid, + asUuid, + toJson, + withExif, + withExifInner, + withFaces, + withFacesAndPeople, + withFiles, +} from 'src/utils/database'; @Injectable() export class AssetJobRepository { @@ -126,6 +135,36 @@ export class AssetJobRepository { .execute(); } + private assetsWithPreviews() { + return this.db + .selectFrom('assets') + .where('assets.isVisible', '=', true) + .where('assets.deletedAt', 'is', null) + .innerJoin('asset_job_status as job_status', 'assetId', 'assets.id') + .where('job_status.previewAt', 'is not', null); + } + + @GenerateSql({ params: [], stream: true }) + streamForSearchDuplicates(force?: boolean) { + return this.assetsWithPreviews() + .where((eb) => eb.not((eb) => eb.exists(eb.selectFrom('smart_search').whereRef('assetId', '=', 'assets.id')))) + .$if(!force, (qb) => qb.where('job_status.duplicatesDetectedAt', 'is', null)) + .select(['assets.id']) + .stream(); + } + + @GenerateSql({ params: [], stream: true }) + streamForEncodeClip(force?: boolean) { + return this.assetsWithPreviews() + .select(['assets.id']) + .$if(!force, (qb) => + qb.where((eb) => + eb.not((eb) => eb.exists(eb.selectFrom('smart_search').whereRef('assetId', '=', 'assets.id'))), + ), + ) + .stream(); + } + @GenerateSql({ params: [DummyValue.UUID] }) getForClipEncoding(id: string) { return this.db @@ -148,6 +187,7 @@ export class AssetJobRepository { .executeTakeFirst(); } + @GenerateSql({ params: [[DummyValue.UUID]] }) getForSyncAssets(ids: string[]) { return this.db .selectFrom('assets') @@ -163,6 +203,84 @@ export class AssetJobRepository { .execute(); } + @GenerateSql({ params: [DummyValue.UUID] }) + getForAssetDeletion(id: string) { + return this.db + .selectFrom('assets') + .select([ + 'assets.id', + 'assets.isVisible', + 'assets.libraryId', + 'assets.ownerId', + 'assets.livePhotoVideoId', + 'assets.sidecarPath', + 'assets.encodedVideoPath', + 'assets.originalPath', + ]) + .$call(withExif) + .select(withFacesAndPeople) + .select(withFiles) + .leftJoin('asset_stack', 'asset_stack.id', 'assets.stackId') + .leftJoinLateral( + (eb) => + eb + .selectFrom('assets as stacked') + .select(['asset_stack.id', 'asset_stack.primaryAssetId']) + .select((eb) => eb.fn<Asset[]>('array_agg', [eb.table('stacked')]).as('assets')) + .where('stacked.deletedAt', 'is not', null) + .where('stacked.isArchived', '=', false) + .whereRef('stacked.stackId', '=', 'asset_stack.id') + .groupBy('asset_stack.id') + .as('stacked_assets'), + (join) => join.on('asset_stack.id', 'is not', null), + ) + .select((eb) => toJson(eb, 'stacked_assets').as('stack')) + .where('assets.id', '=', id) + .executeTakeFirst(); + } + + @GenerateSql({ params: [], stream: true }) + streamForVideoConversion(force?: boolean) { + return this.db + .selectFrom('assets') + .select(['assets.id']) + .where('assets.type', '=', AssetType.VIDEO) + .$if(!force, (qb) => + qb + .where((eb) => eb.or([eb('assets.encodedVideoPath', 'is', null), eb('assets.encodedVideoPath', '=', '')])) + .where('assets.isVisible', '=', true), + ) + .where('assets.deletedAt', 'is', null) + .stream(); + } + + @GenerateSql({ params: [DummyValue.UUID] }) + getForVideoConversion(id: string) { + return this.db + .selectFrom('assets') + .select(['assets.id', 'assets.ownerId', 'assets.originalPath', 'assets.encodedVideoPath']) + .where('assets.id', '=', id) + .where('assets.type', '=', AssetType.VIDEO) + .executeTakeFirst(); + } + + @GenerateSql({ params: [], stream: true }) + streamForMetadataExtraction(force?: boolean) { + return this.db + .selectFrom('assets') + .select(['assets.id']) + .$if(!force, (qb) => + qb + .leftJoin('asset_job_status', 'asset_job_status.assetId', 'assets.id') + .where((eb) => + eb.or([eb('asset_job_status.metadataExtractedAt', 'is', null), eb('asset_job_status.assetId', 'is', null)]), + ) + .where('assets.isVisible', '=', true), + ) + .where('assets.deletedAt', 'is', null) + .stream(); + } + private storageTemplateAssetQuery() { return this.db .selectFrom('assets') @@ -204,4 +322,30 @@ export class AssetJobRepository { .where('assets.deletedAt', '<=', trashedBefore) .stream(); } + + @GenerateSql({ params: [], stream: true }) + streamForSidecar(force?: boolean) { + return this.db + .selectFrom('assets') + .select(['assets.id']) + .$if(!force, (qb) => + qb.where((eb) => eb.or([eb('assets.sidecarPath', '=', ''), eb('assets.sidecarPath', 'is', null)])), + ) + .where('assets.isVisible', '=', true) + .stream(); + } + + @GenerateSql({ params: [], stream: true }) + streamForDetectFacesJob(force?: boolean) { + return this.assetsWithPreviews() + .$if(!force, (qb) => qb.where('job_status.facesRecognizedAt', 'is', null)) + .select(['assets.id']) + .orderBy('assets.createdAt', 'desc') + .stream(); + } + + @GenerateSql({ params: [DummyValue.DATE], stream: true }) + streamForMigrationJob() { + return this.db.selectFrom('assets').select(['id']).where('assets.deletedAt', 'is', null).stream(); + } } diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index 7a68ba907f..d1c08b90bc 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -7,13 +7,12 @@ import { AssetFiles, AssetJobStatus, Assets, DB, Exif } from 'src/db'; import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; import { MapAsset } from 'src/dtos/asset-response.dto'; import { AssetFileType, AssetOrder, AssetStatus, AssetType } from 'src/enum'; -import { AssetSearchOptions, SearchExploreItem, SearchExploreItemSet } from 'src/repositories/search.repository'; +import { SearchExploreItem, SearchExploreItemSet } from 'src/repositories/search.repository'; import { anyUuid, asUuid, hasPeople, removeUndefinedKeys, - searchAssetBuilder, truncatedDate, unnest, withExif, @@ -27,7 +26,6 @@ import { withTags, } from 'src/utils/database'; import { globToSqlPattern } from 'src/utils/misc'; -import { PaginationOptions, paginationHelper } from 'src/utils/pagination'; export type AssetStats = Record<AssetType, number>; @@ -45,16 +43,6 @@ export interface LivePhotoSearchOptions { type: AssetType; } -export enum WithoutProperty { - THUMBNAIL = 'thumbnail', - ENCODED_VIDEO = 'encoded-video', - EXIF = 'exif', - SMART_SEARCH = 'smart-search', - DUPLICATE = 'duplicate', - FACES = 'faces', - SIDECAR = 'sidecar', -} - export enum WithProperty { SIDECAR = 'sidecar', } @@ -336,10 +324,6 @@ export class AssetRepository { return assets.map((asset) => asset.deviceAssetId); } - getByUserId(pagination: PaginationOptions, userId: string, options: Omit<AssetSearchOptions, 'userIds'> = {}) { - return this.getAll(pagination, { ...options, userIds: [userId] }); - } - @GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] }) getByLibraryIdAndOriginalPath(libraryId: string, originalPath: string) { return this.db @@ -351,16 +335,6 @@ export class AssetRepository { .executeTakeFirst(); } - async getAll(pagination: PaginationOptions, { orderDirection, ...options }: AssetSearchOptions = {}) { - const builder = searchAssetBuilder(this.db, options) - .select(withFiles) - .orderBy('assets.createdAt', orderDirection ?? 'asc') - .limit(pagination.take + 1) - .offset(pagination.skip ?? 0); - const items = await builder.execute(); - return paginationHelper(items, pagination.take); - } - /** * Get assets by device's Id on the database * @param ownerId @@ -530,77 +504,6 @@ export class AssetRepository { .executeTakeFirst(); } - @GenerateSql( - ...Object.values(WithProperty).map((property) => ({ - name: property, - params: [DummyValue.PAGINATION, property], - })), - ) - async getWithout(pagination: PaginationOptions, property: WithoutProperty) { - const items = await this.db - .selectFrom('assets') - .selectAll('assets') - .$if(property === WithoutProperty.DUPLICATE, (qb) => - qb - .innerJoin('asset_job_status as job_status', 'assets.id', 'job_status.assetId') - .where('job_status.duplicatesDetectedAt', 'is', null) - .where('job_status.previewAt', 'is not', null) - .where((eb) => eb.exists(eb.selectFrom('smart_search').where('assetId', '=', eb.ref('assets.id')))) - .where('assets.isVisible', '=', true), - ) - .$if(property === WithoutProperty.ENCODED_VIDEO, (qb) => - qb - .where('assets.type', '=', AssetType.VIDEO) - .where((eb) => eb.or([eb('assets.encodedVideoPath', 'is', null), eb('assets.encodedVideoPath', '=', '')])), - ) - .$if(property === WithoutProperty.EXIF, (qb) => - qb - .leftJoin('asset_job_status as job_status', 'assets.id', 'job_status.assetId') - .where((eb) => eb.or([eb('job_status.metadataExtractedAt', 'is', null), eb('assetId', 'is', null)])) - .where('assets.isVisible', '=', true), - ) - .$if(property === WithoutProperty.FACES, (qb) => - qb - .innerJoin('asset_job_status as job_status', 'assetId', 'assets.id') - .where('job_status.previewAt', 'is not', null) - .where('job_status.facesRecognizedAt', 'is', null) - .where('assets.isVisible', '=', true), - ) - .$if(property === WithoutProperty.SIDECAR, (qb) => - qb - .where((eb) => eb.or([eb('assets.sidecarPath', '=', ''), eb('assets.sidecarPath', 'is', null)])) - .where('assets.isVisible', '=', true), - ) - .$if(property === WithoutProperty.SMART_SEARCH, (qb) => - qb - .innerJoin('asset_job_status as job_status', 'assetId', 'assets.id') - .where('job_status.previewAt', 'is not', null) - .where('assets.isVisible', '=', true) - .where((eb) => - eb.not((eb) => eb.exists(eb.selectFrom('smart_search').whereRef('assetId', '=', 'assets.id'))), - ), - ) - .$if(property === WithoutProperty.THUMBNAIL, (qb) => - qb - .innerJoin('asset_job_status as job_status', 'assetId', 'assets.id') - .where('assets.isVisible', '=', true) - .where((eb) => - eb.or([ - eb('job_status.previewAt', 'is', null), - eb('job_status.thumbnailAt', 'is', null), - eb('assets.thumbhash', 'is', null), - ]), - ), - ) - .where('deletedAt', 'is', null) - .limit(pagination.take + 1) - .offset(pagination.skip ?? 0) - .orderBy('createdAt') - .execute(); - - return paginationHelper(items, pagination.take); - } - getStatistics(ownerId: string, { isArchived, isFavorite, isTrashed }: AssetStatsOptions): Promise<AssetStats> { return this.db .selectFrom('assets') diff --git a/server/src/repositories/config.repository.spec.ts b/server/src/repositories/config.repository.spec.ts index 888d5c33ec..9e9ed71191 100644 --- a/server/src/repositories/config.repository.spec.ts +++ b/server/src/repositories/config.repository.spec.ts @@ -80,21 +80,12 @@ describe('getEnv', () => { const { database } = getEnv(); expect(database).toEqual({ config: { - kysely: expect.objectContaining({ - host: 'database', - port: 5432, - database: 'immich', - username: 'postgres', - password: 'postgres', - }), - typeorm: expect.objectContaining({ - type: 'postgres', - host: 'database', - port: 5432, - database: 'immich', - username: 'postgres', - password: 'postgres', - }), + connectionType: 'parts', + host: 'database', + port: 5432, + database: 'immich', + username: 'postgres', + password: 'postgres', }, skipMigrations: false, vectorExtension: 'vectors', @@ -110,88 +101,9 @@ describe('getEnv', () => { it('should use DB_URL', () => { process.env.DB_URL = 'postgres://postgres1:postgres2@database1:54320/immich'; const { database } = getEnv(); - expect(database.config.kysely).toMatchObject({ - host: 'database1', - password: 'postgres2', - user: 'postgres1', - port: 54_320, - database: 'immich', - }); - }); - - it('should handle sslmode=require', () => { - process.env.DB_URL = 'postgres://postgres1:postgres2@database1:54320/immich?sslmode=require'; - - const { database } = getEnv(); - - expect(database.config.kysely).toMatchObject({ ssl: {} }); - }); - - it('should handle sslmode=prefer', () => { - process.env.DB_URL = 'postgres://postgres1:postgres2@database1:54320/immich?sslmode=prefer'; - - const { database } = getEnv(); - - expect(database.config.kysely).toMatchObject({ ssl: {} }); - }); - - it('should handle sslmode=verify-ca', () => { - process.env.DB_URL = 'postgres://postgres1:postgres2@database1:54320/immich?sslmode=verify-ca'; - - const { database } = getEnv(); - - expect(database.config.kysely).toMatchObject({ ssl: {} }); - }); - - it('should handle sslmode=verify-full', () => { - process.env.DB_URL = 'postgres://postgres1:postgres2@database1:54320/immich?sslmode=verify-full'; - - const { database } = getEnv(); - - expect(database.config.kysely).toMatchObject({ ssl: {} }); - }); - - it('should handle sslmode=no-verify', () => { - process.env.DB_URL = 'postgres://postgres1:postgres2@database1:54320/immich?sslmode=no-verify'; - - const { database } = getEnv(); - - expect(database.config.kysely).toMatchObject({ ssl: { rejectUnauthorized: false } }); - }); - - it('should handle ssl=true', () => { - process.env.DB_URL = 'postgres://postgres1:postgres2@database1:54320/immich?ssl=true'; - - const { database } = getEnv(); - - expect(database.config.kysely).toMatchObject({ ssl: true }); - }); - - it('should reject invalid ssl', () => { - process.env.DB_URL = 'postgres://postgres1:postgres2@database1:54320/immich?ssl=invalid'; - - expect(() => getEnv()).toThrowError('Invalid ssl option: invalid'); - }); - - it('should handle socket: URLs', () => { - process.env.DB_URL = 'socket:/run/postgresql?db=database1'; - - const { database } = getEnv(); - - expect(database.config.kysely).toMatchObject({ - host: '/run/postgresql', - database: 'database1', - }); - }); - - it('should handle sockets in postgres: URLs', () => { - process.env.DB_URL = 'postgres:///database2?host=/path/to/socket'; - - const { database } = getEnv(); - - expect(database.config.kysely).toMatchObject({ - host: '/path/to/socket', - database: 'database2', + expect(database.config).toMatchObject({ + connectionType: 'url', + url: 'postgres://postgres1:postgres2@database1:54320/immich', }); }); }); diff --git a/server/src/repositories/config.repository.ts b/server/src/repositories/config.repository.ts index f689641d4f..9b88a78e6b 100644 --- a/server/src/repositories/config.repository.ts +++ b/server/src/repositories/config.repository.ts @@ -7,8 +7,7 @@ import { Request, Response } from 'express'; import { RedisOptions } from 'ioredis'; import { CLS_ID, ClsModuleOptions } from 'nestjs-cls'; import { OpenTelemetryModuleOptions } from 'nestjs-otel/lib/interfaces'; -import { join, resolve } from 'node:path'; -import { parse } from 'pg-connection-string'; +import { join } from 'node:path'; import { citiesFile, excludePaths, IWorker } from 'src/constants'; import { Telemetry } from 'src/decorators'; import { EnvDto } from 'src/dtos/env.dto'; @@ -22,9 +21,7 @@ import { QueueName, } from 'src/enum'; import { DatabaseConnectionParams, VectorExtension } from 'src/types'; -import { isValidSsl, PostgresConnectionConfig } from 'src/utils/database'; import { setDifference } from 'src/utils/set'; -import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions.js'; export interface EnvData { host?: string; @@ -59,7 +56,7 @@ export interface EnvData { }; database: { - config: { typeorm: PostgresConnectionOptions & DatabaseConnectionParams; kysely: PostgresConnectionConfig }; + config: DatabaseConnectionParams; skipMigrations: boolean; vectorExtension: VectorExtension; }; @@ -152,14 +149,10 @@ const getEnv = (): EnvData => { const isProd = environment === ImmichEnvironment.PRODUCTION; const buildFolder = dto.IMMICH_BUILD_DATA || '/build'; const folders = { - // eslint-disable-next-line unicorn/prefer-module - dist: resolve(`${__dirname}/..`), geodata: join(buildFolder, 'geodata'), web: join(buildFolder, 'www'), }; - const databaseUrl = dto.DB_URL; - let redisConfig = { host: dto.REDIS_HOSTNAME || 'redis', port: dto.REDIS_PORT || 6379, @@ -191,30 +184,16 @@ const getEnv = (): EnvData => { } } - const parts = { - connectionType: 'parts', - host: dto.DB_HOSTNAME || 'database', - port: dto.DB_PORT || 5432, - username: dto.DB_USERNAME || 'postgres', - password: dto.DB_PASSWORD || 'postgres', - database: dto.DB_DATABASE_NAME || 'immich', - } as const; - - let parsedOptions: PostgresConnectionConfig = parts; - if (dto.DB_URL) { - const parsed = parse(dto.DB_URL); - if (!isValidSsl(parsed.ssl)) { - throw new Error(`Invalid ssl option: ${parsed.ssl}`); - } - - parsedOptions = { - ...parsed, - ssl: parsed.ssl, - host: parsed.host ?? undefined, - port: parsed.port ? Number(parsed.port) : undefined, - database: parsed.database ?? undefined, - }; - } + const databaseConnection: DatabaseConnectionParams = dto.DB_URL + ? { connectionType: 'url', url: dto.DB_URL } + : { + connectionType: 'parts', + host: dto.DB_HOSTNAME || 'database', + port: dto.DB_PORT || 5432, + username: dto.DB_USERNAME || 'postgres', + password: dto.DB_PASSWORD || 'postgres', + database: dto.DB_DATABASE_NAME || 'immich', + }; return { host: dto.IMMICH_HOST, @@ -269,21 +248,7 @@ const getEnv = (): EnvData => { }, database: { - config: { - typeorm: { - type: 'postgres', - entities: [], - migrations: [`${folders.dist}/migrations` + '/*.{js,ts}'], - subscribers: [], - migrationsRun: false, - synchronize: false, - connectTimeoutMS: 10_000, // 10 seconds - parseInt8: true, - ...(databaseUrl ? { connectionType: 'url', url: databaseUrl } : parts), - }, - kysely: parsedOptions, - }, - + config: databaseConnection, skipMigrations: dto.DB_SKIP_MIGRATIONS ?? false, vectorExtension: dto.DB_VECTOR_EXTENSION === 'pgvector' ? DatabaseExtension.VECTOR : DatabaseExtension.VECTORS, }, diff --git a/server/src/repositories/database.repository.ts b/server/src/repositories/database.repository.ts index c70c2cbdd4..addf6bcff0 100644 --- a/server/src/repositories/database.repository.ts +++ b/server/src/repositories/database.repository.ts @@ -3,7 +3,7 @@ import AsyncLock from 'async-lock'; import { FileMigrationProvider, Kysely, Migrator, sql, Transaction } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; import { readdir } from 'node:fs/promises'; -import { join } from 'node:path'; +import { join, resolve } from 'node:path'; import semver from 'semver'; import { EXTENSION_NAMES, POSTGRES_VERSION_RANGE, VECTOR_VERSION_RANGE, VECTORS_VERSION_RANGE } from 'src/constants'; import { DB } from 'src/db'; @@ -12,6 +12,7 @@ import { DatabaseExtension, DatabaseLock, VectorIndex } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { ExtensionVersion, VectorExtension, VectorUpdateResult } from 'src/types'; +import { vectorIndexQuery } from 'src/utils/database'; import { isValidInteger } from 'src/validation'; import { DataSource } from 'typeorm'; @@ -119,12 +120,7 @@ export class DatabaseRepository { await sql`ALTER TABLE ${sql.raw(table)} ALTER COLUMN embedding SET DATA TYPE vector(${sql.raw(String(dimSize))})`.execute( tx, ); - await sql`SET vectors.pgvector_compatibility=on`.execute(tx); - await sql` - CREATE INDEX IF NOT EXISTS ${sql.raw(index)} ON ${sql.raw(table)} - USING hnsw (embedding vector_cosine_ops) - WITH (ef_construction = 300, m = 16) - `.execute(tx); + await sql.raw(vectorIndexQuery({ vectorExtension: this.vectorExtension, table, indexName: index })).execute(tx); }); } } @@ -205,8 +201,29 @@ export class DatabaseRepository { const { rows } = await tableExists.execute(this.db); const hasTypeOrmMigrations = !!rows[0]?.result; if (hasTypeOrmMigrations) { + // eslint-disable-next-line unicorn/prefer-module + const dist = resolve(`${__dirname}/..`); + this.logger.debug('Running typeorm migrations'); - const dataSource = new DataSource(database.config.typeorm); + const dataSource = new DataSource({ + type: 'postgres', + entities: [], + subscribers: [], + migrations: [`${dist}/migrations` + '/*.{js,ts}'], + migrationsRun: false, + synchronize: false, + connectTimeoutMS: 10_000, // 10 seconds + parseInt8: true, + ...(database.config.connectionType === 'url' + ? { url: database.config.url } + : { + host: database.config.host, + port: database.config.port, + username: database.config.username, + password: database.config.password, + database: database.config.database, + }), + }); await dataSource.initialize(); await dataSource.runMigrations(options); await dataSource.destroy(); diff --git a/server/src/repositories/notification.repository.spec.ts b/server/src/repositories/email.repository.spec.ts similarity index 87% rename from server/src/repositories/notification.repository.spec.ts rename to server/src/repositories/email.repository.spec.ts index 1d0770af6b..5640b26bf6 100644 --- a/server/src/repositories/notification.repository.spec.ts +++ b/server/src/repositories/email.repository.spec.ts @@ -1,13 +1,13 @@ +import { EmailRenderRequest, EmailRepository, EmailTemplate } from 'src/repositories/email.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; -import { EmailRenderRequest, EmailTemplate, NotificationRepository } from 'src/repositories/notification.repository'; import { automock } from 'test/utils'; -describe(NotificationRepository.name, () => { - let sut: NotificationRepository; +describe(EmailRepository.name, () => { + let sut: EmailRepository; beforeEach(() => { // eslint-disable-next-line no-sparse-arrays - sut = new NotificationRepository(automock(LoggingRepository, { args: [, { getEnv: () => ({}) }], strict: false })); + sut = new EmailRepository(automock(LoggingRepository, { args: [, { getEnv: () => ({}) }], strict: false })); }); describe('renderEmail', () => { diff --git a/server/src/repositories/email.repository.ts b/server/src/repositories/email.repository.ts new file mode 100644 index 0000000000..78c89b4a9d --- /dev/null +++ b/server/src/repositories/email.repository.ts @@ -0,0 +1,174 @@ +import { Injectable } from '@nestjs/common'; +import { render } from '@react-email/render'; +import { createTransport } from 'nodemailer'; +import React from 'react'; +import { AlbumInviteEmail } from 'src/emails/album-invite.email'; +import { AlbumUpdateEmail } from 'src/emails/album-update.email'; +import { TestEmail } from 'src/emails/test.email'; +import { WelcomeEmail } from 'src/emails/welcome.email'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { EmailImageAttachment } from 'src/types'; + +export type SendEmailOptions = { + from: string; + to: string; + replyTo?: string; + subject: string; + html: string; + text: string; + imageAttachments?: EmailImageAttachment[]; + smtp: SmtpOptions; +}; + +export type SmtpOptions = { + host: string; + port?: number; + username?: string; + password?: string; + ignoreCert?: boolean; +}; + +export enum EmailTemplate { + TEST_EMAIL = 'test', + + // AUTH + WELCOME = 'welcome', + RESET_PASSWORD = 'reset-password', + + // ALBUM + ALBUM_INVITE = 'album-invite', + ALBUM_UPDATE = 'album-update', +} + +interface BaseEmailProps { + baseUrl: string; + customTemplate?: string; +} + +export interface TestEmailProps extends BaseEmailProps { + displayName: string; +} + +export interface WelcomeEmailProps extends BaseEmailProps { + displayName: string; + username: string; + password?: string; +} + +export interface AlbumInviteEmailProps extends BaseEmailProps { + albumName: string; + albumId: string; + senderName: string; + recipientName: string; + cid?: string; +} + +export interface AlbumUpdateEmailProps extends BaseEmailProps { + albumName: string; + albumId: string; + recipientName: string; + cid?: string; +} + +export type EmailRenderRequest = + | { + template: EmailTemplate.TEST_EMAIL; + data: TestEmailProps; + customTemplate: string; + } + | { + template: EmailTemplate.WELCOME; + data: WelcomeEmailProps; + customTemplate: string; + } + | { + template: EmailTemplate.ALBUM_INVITE; + data: AlbumInviteEmailProps; + customTemplate: string; + } + | { + template: EmailTemplate.ALBUM_UPDATE; + data: AlbumUpdateEmailProps; + customTemplate: string; + }; + +export type SendEmailResponse = { + messageId: string; + response: any; +}; + +@Injectable() +export class EmailRepository { + constructor(private logger: LoggingRepository) { + this.logger.setContext(EmailRepository.name); + } + + verifySmtp(options: SmtpOptions): Promise<true> { + const transport = this.createTransport(options); + try { + return transport.verify(); + } finally { + transport.close(); + } + } + + async renderEmail(request: EmailRenderRequest): Promise<{ html: string; text: string }> { + const component = this.render(request); + const html = await render(component, { pretty: false }); + const text = await render(component, { plainText: true }); + return { html, text }; + } + + sendEmail({ to, from, subject, html, text, smtp, imageAttachments }: SendEmailOptions): Promise<SendEmailResponse> { + this.logger.debug(`Sending email to ${to} with subject: ${subject}`); + const transport = this.createTransport(smtp); + + const attachments = imageAttachments?.map((attachment) => ({ + filename: attachment.filename, + path: attachment.path, + cid: attachment.cid, + })); + + try { + return transport.sendMail({ to, from, subject, html, text, attachments }); + } finally { + transport.close(); + } + } + + private render({ template, data, customTemplate }: EmailRenderRequest): React.FunctionComponentElement<any> { + switch (template) { + case EmailTemplate.TEST_EMAIL: { + return React.createElement(TestEmail, { ...data, customTemplate }); + } + + case EmailTemplate.WELCOME: { + return React.createElement(WelcomeEmail, { ...data, customTemplate }); + } + + case EmailTemplate.ALBUM_INVITE: { + return React.createElement(AlbumInviteEmail, { ...data, customTemplate }); + } + + case EmailTemplate.ALBUM_UPDATE: { + return React.createElement(AlbumUpdateEmail, { ...data, customTemplate }); + } + } + } + + private createTransport(options: SmtpOptions) { + return createTransport({ + host: options.host, + port: options.port, + tls: { rejectUnauthorized: !options.ignoreCert }, + auth: + options.username || options.password + ? { + user: options.username, + pass: options.password, + } + : undefined, + connectionTimeout: 5000, + }); + } +} diff --git a/server/src/repositories/event.repository.ts b/server/src/repositories/event.repository.ts index 3156804d09..b41c007ef5 100644 --- a/server/src/repositories/event.repository.ts +++ b/server/src/repositories/event.repository.ts @@ -14,6 +14,7 @@ import { SystemConfig } from 'src/config'; import { EventConfig } from 'src/decorators'; import { AssetResponseDto } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; +import { NotificationDto } from 'src/dtos/notification.dto'; import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server.dto'; import { ImmichWorker, MetadataKey, QueueName } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; @@ -64,6 +65,7 @@ type EventMap = { 'assets.restore': [{ assetIds: string[]; userId: string }]; 'job.start': [QueueName, JobItem]; + 'job.failed': [{ job: JobItem; error: Error | any }]; // session events 'session.delete': [{ sessionId: string }]; @@ -104,6 +106,7 @@ export interface ClientEventMap { on_server_version: [ServerVersionResponseDto]; on_config_update: []; on_new_release: [ReleaseNotification]; + on_notification: [NotificationDto]; on_session_delete: [string]; } diff --git a/server/src/repositories/index.ts b/server/src/repositories/index.ts index ef36a2b3f8..453e515fe0 100644 --- a/server/src/repositories/index.ts +++ b/server/src/repositories/index.ts @@ -11,6 +11,7 @@ import { CronRepository } from 'src/repositories/cron.repository'; import { CryptoRepository } from 'src/repositories/crypto.repository'; import { DatabaseRepository } from 'src/repositories/database.repository'; import { DownloadRepository } from 'src/repositories/download.repository'; +import { EmailRepository } from 'src/repositories/email.repository'; import { EventRepository } from 'src/repositories/event.repository'; import { JobRepository } from 'src/repositories/job.repository'; import { LibraryRepository } from 'src/repositories/library.repository'; @@ -55,6 +56,7 @@ export const repositories = [ CryptoRepository, DatabaseRepository, DownloadRepository, + EmailRepository, EventRepository, JobRepository, LibraryRepository, diff --git a/server/src/repositories/job.repository.ts b/server/src/repositories/job.repository.ts index fd9f4c5363..0912759d1c 100644 --- a/server/src/repositories/job.repository.ts +++ b/server/src/repositories/job.repository.ts @@ -33,7 +33,7 @@ export class JobRepository { this.logger.setContext(JobRepository.name); } - setup({ services }: { services: ClassConstructor<unknown>[] }) { + setup(services: ClassConstructor<unknown>[]) { const reflector = this.moduleRef.get(Reflector, { strict: false }); // discovery diff --git a/server/src/repositories/logging.repository.ts b/server/src/repositories/logging.repository.ts index 05d2d45f4d..2ac3715a50 100644 --- a/server/src/repositories/logging.repository.ts +++ b/server/src/repositories/logging.repository.ts @@ -5,7 +5,7 @@ import { Telemetry } from 'src/decorators'; import { LogLevel } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; -type LogDetails = any[]; +type LogDetails = any; type LogFunction = () => string; const LOG_LEVELS = [LogLevel.VERBOSE, LogLevel.DEBUG, LogLevel.LOG, LogLevel.WARN, LogLevel.ERROR, LogLevel.FATAL]; diff --git a/server/src/repositories/media.repository.ts b/server/src/repositories/media.repository.ts index 1e41dd6bb2..d0ced19a6e 100644 --- a/server/src/repositories/media.repository.ts +++ b/server/src/repositories/media.repository.ts @@ -7,7 +7,7 @@ import { Writable } from 'node:stream'; import sharp from 'sharp'; import { ORIENTATION_TO_SHARP_ROTATION } from 'src/constants'; import { Exif } from 'src/database'; -import { Colorspace, LogLevel } from 'src/enum'; +import { Colorspace, LogLevel, RawExtractedFormat } from 'src/enum'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { DecodeToBufferOptions, @@ -36,34 +36,51 @@ type ProgressEvent = { percent?: number; }; +export type ExtractResult = { + buffer: Buffer; + format: RawExtractedFormat; +}; + @Injectable() export class MediaRepository { constructor(private logger: LoggingRepository) { this.logger.setContext(MediaRepository.name); } - async extract(input: string, output: string): Promise<boolean> { + /** + * + * @param input file path to the input image + * @returns ExtractResult if succeeded, or null if failed + */ + async extract(input: string): Promise<ExtractResult | null> { try { - // remove existing output file if it exists - // as exiftool-vendored does not support overwriting via "-w!" flag - // and throws "1 files could not be read" error when the output file exists - await fs.unlink(output).catch(() => null); - await exiftool.extractBinaryTag('JpgFromRaw2', input, output); - } catch { - try { - this.logger.debug('Extracting JPEG from RAW image:', input); - await exiftool.extractJpgFromRaw(input, output); - } catch (error: any) { - this.logger.debug('Could not extract JPEG from image, trying preview', error.message); - try { - await exiftool.extractPreview(input, output); - } catch (error: any) { - this.logger.debug('Could not extract preview from image', error.message); - return false; - } - } + const buffer = await exiftool.extractBinaryTagToBuffer('JpgFromRaw2', input); + return { buffer, format: RawExtractedFormat.JPEG }; + } catch (error: any) { + this.logger.debug('Could not extract JpgFromRaw2 buffer from image, trying JPEG from RAW next', error.message); + } + + try { + const buffer = await exiftool.extractBinaryTagToBuffer('JpgFromRaw', input); + return { buffer, format: RawExtractedFormat.JPEG }; + } catch (error: any) { + this.logger.debug('Could not extract JPEG buffer from image, trying PreviewJXL next', error.message); + } + + try { + const buffer = await exiftool.extractBinaryTagToBuffer('PreviewJXL', input); + return { buffer, format: RawExtractedFormat.JXL }; + } catch (error: any) { + this.logger.debug('Could not extract PreviewJXL buffer from image, trying PreviewImage next', error.message); + } + + try { + const buffer = await exiftool.extractBinaryTagToBuffer('PreviewImage', input); + return { buffer, format: RawExtractedFormat.JPEG }; + } catch (error: any) { + this.logger.debug('Could not extract preview buffer from image', error.message); + return null; } - return true; } async writeExif(tags: Partial<Exif>, output: string): Promise<boolean> { @@ -104,7 +121,7 @@ export class MediaRepository { } } - decodeImage(input: string, options: DecodeToBufferOptions) { + decodeImage(input: string | Buffer, options: DecodeToBufferOptions) { return this.getImageDecodingPipeline(input, options).raw().toBuffer({ resolveWithObject: true }); } @@ -235,7 +252,7 @@ export class MediaRepository { }); } - async getImageDimensions(input: string): Promise<ImageDimensions> { + async getImageDimensions(input: string | Buffer): Promise<ImageDimensions> { const { width = 0, height = 0 } = await sharp(input).metadata(); return { width, height }; } diff --git a/server/src/repositories/notification.repository.ts b/server/src/repositories/notification.repository.ts index 91f03b928b..112bb97e60 100644 --- a/server/src/repositories/notification.repository.ts +++ b/server/src/repositories/notification.repository.ts @@ -1,174 +1,103 @@ -import { Injectable } from '@nestjs/common'; -import { render } from '@react-email/render'; -import { createTransport } from 'nodemailer'; -import React from 'react'; -import { AlbumInviteEmail } from 'src/emails/album-invite.email'; -import { AlbumUpdateEmail } from 'src/emails/album-update.email'; -import { TestEmail } from 'src/emails/test.email'; -import { WelcomeEmail } from 'src/emails/welcome.email'; -import { LoggingRepository } from 'src/repositories/logging.repository'; -import { EmailImageAttachment } from 'src/types'; +import { Insertable, Kysely, Updateable } from 'kysely'; +import { DateTime } from 'luxon'; +import { InjectKysely } from 'nestjs-kysely'; +import { columns } from 'src/database'; +import { DB, Notifications } from 'src/db'; +import { DummyValue, GenerateSql } from 'src/decorators'; +import { NotificationSearchDto } from 'src/dtos/notification.dto'; -export type SendEmailOptions = { - from: string; - to: string; - replyTo?: string; - subject: string; - html: string; - text: string; - imageAttachments?: EmailImageAttachment[]; - smtp: SmtpOptions; -}; - -export type SmtpOptions = { - host: string; - port?: number; - username?: string; - password?: string; - ignoreCert?: boolean; -}; - -export enum EmailTemplate { - TEST_EMAIL = 'test', - - // AUTH - WELCOME = 'welcome', - RESET_PASSWORD = 'reset-password', - - // ALBUM - ALBUM_INVITE = 'album-invite', - ALBUM_UPDATE = 'album-update', -} - -interface BaseEmailProps { - baseUrl: string; - customTemplate?: string; -} - -export interface TestEmailProps extends BaseEmailProps { - displayName: string; -} - -export interface WelcomeEmailProps extends BaseEmailProps { - displayName: string; - username: string; - password?: string; -} - -export interface AlbumInviteEmailProps extends BaseEmailProps { - albumName: string; - albumId: string; - senderName: string; - recipientName: string; - cid?: string; -} - -export interface AlbumUpdateEmailProps extends BaseEmailProps { - albumName: string; - albumId: string; - recipientName: string; - cid?: string; -} - -export type EmailRenderRequest = - | { - template: EmailTemplate.TEST_EMAIL; - data: TestEmailProps; - customTemplate: string; - } - | { - template: EmailTemplate.WELCOME; - data: WelcomeEmailProps; - customTemplate: string; - } - | { - template: EmailTemplate.ALBUM_INVITE; - data: AlbumInviteEmailProps; - customTemplate: string; - } - | { - template: EmailTemplate.ALBUM_UPDATE; - data: AlbumUpdateEmailProps; - customTemplate: string; - }; - -export type SendEmailResponse = { - messageId: string; - response: any; -}; - -@Injectable() export class NotificationRepository { - constructor(private logger: LoggingRepository) { - this.logger.setContext(NotificationRepository.name); + constructor(@InjectKysely() private db: Kysely<DB>) {} + + @GenerateSql({ params: [DummyValue.UUID] }) + cleanup() { + return this.db + .deleteFrom('notifications') + .where((eb) => + eb.or([ + // remove soft-deleted notifications + eb.and([eb('deletedAt', 'is not', null), eb('deletedAt', '<', DateTime.now().minus({ days: 3 }).toJSDate())]), + + // remove old, read notifications + eb.and([ + // keep recently read messages around for a few days + eb('readAt', '>', DateTime.now().minus({ days: 2 }).toJSDate()), + eb('createdAt', '<', DateTime.now().minus({ days: 15 }).toJSDate()), + ]), + + eb.and([ + // remove super old, unread notifications + eb('readAt', '=', null), + eb('createdAt', '<', DateTime.now().minus({ days: 30 }).toJSDate()), + ]), + ]), + ) + .execute(); } - verifySmtp(options: SmtpOptions): Promise<true> { - const transport = this.createTransport(options); - try { - return transport.verify(); - } finally { - transport.close(); - } + @GenerateSql({ params: [DummyValue.UUID, {}] }, { name: 'unread', params: [DummyValue.UUID, { unread: true }] }) + search(userId: string, dto: NotificationSearchDto) { + return this.db + .selectFrom('notifications') + .select(columns.notification) + .where((qb) => + qb.and({ + userId, + id: dto.id, + level: dto.level, + type: dto.type, + readAt: dto.unread ? null : undefined, + }), + ) + .where('deletedAt', 'is', null) + .orderBy('createdAt', 'desc') + .execute(); } - async renderEmail(request: EmailRenderRequest): Promise<{ html: string; text: string }> { - const component = this.render(request); - const html = await render(component, { pretty: false }); - const text = await render(component, { plainText: true }); - return { html, text }; + create(notification: Insertable<Notifications>) { + return this.db + .insertInto('notifications') + .values(notification) + .returning(columns.notification) + .executeTakeFirstOrThrow(); } - sendEmail({ to, from, subject, html, text, smtp, imageAttachments }: SendEmailOptions): Promise<SendEmailResponse> { - this.logger.debug(`Sending email to ${to} with subject: ${subject}`); - const transport = this.createTransport(smtp); - - const attachments = imageAttachments?.map((attachment) => ({ - filename: attachment.filename, - path: attachment.path, - cid: attachment.cid, - })); - - try { - return transport.sendMail({ to, from, subject, html, text, attachments }); - } finally { - transport.close(); - } + get(id: string) { + return this.db + .selectFrom('notifications') + .select(columns.notification) + .where('id', '=', id) + .where('deletedAt', 'is not', null) + .executeTakeFirst(); } - private render({ template, data, customTemplate }: EmailRenderRequest): React.FunctionComponentElement<any> { - switch (template) { - case EmailTemplate.TEST_EMAIL: { - return React.createElement(TestEmail, { ...data, customTemplate }); - } - - case EmailTemplate.WELCOME: { - return React.createElement(WelcomeEmail, { ...data, customTemplate }); - } - - case EmailTemplate.ALBUM_INVITE: { - return React.createElement(AlbumInviteEmail, { ...data, customTemplate }); - } - - case EmailTemplate.ALBUM_UPDATE: { - return React.createElement(AlbumUpdateEmail, { ...data, customTemplate }); - } - } + update(id: string, notification: Updateable<Notifications>) { + return this.db + .updateTable('notifications') + .set(notification) + .where('deletedAt', 'is', null) + .where('id', '=', id) + .returning(columns.notification) + .executeTakeFirstOrThrow(); } - private createTransport(options: SmtpOptions) { - return createTransport({ - host: options.host, - port: options.port, - tls: { rejectUnauthorized: !options.ignoreCert }, - auth: - options.username || options.password - ? { - user: options.username, - pass: options.password, - } - : undefined, - connectionTimeout: 5000, - }); + async updateAll(ids: string[], notification: Updateable<Notifications>) { + await this.db.updateTable('notifications').set(notification).where('id', 'in', ids).execute(); + } + + async delete(id: string) { + await this.db + .updateTable('notifications') + .set({ deletedAt: DateTime.now().toJSDate() }) + .where('id', '=', id) + .execute(); + } + + async deleteAll(ids: string[]) { + await this.db + .updateTable('notifications') + .set({ deletedAt: DateTime.now().toJSDate() }) + .where('id', 'in', ids) + .execute(); } } diff --git a/server/src/repositories/oauth.repository.ts b/server/src/repositories/oauth.repository.ts index dc19a1fe01..ea9f0b1901 100644 --- a/server/src/repositories/oauth.repository.ts +++ b/server/src/repositories/oauth.repository.ts @@ -1,18 +1,21 @@ import { Injectable, InternalServerErrorException } from '@nestjs/common'; -import { custom, generators, Issuer, UserinfoResponse } from 'openid-client'; +import type { UserInfoResponse } from 'openid-client' with { 'resolution-mode': 'import' }; +import { OAuthTokenEndpointAuthMethod } from 'src/enum'; import { LoggingRepository } from 'src/repositories/logging.repository'; export type OAuthConfig = { clientId: string; - clientSecret: string; + clientSecret?: string; issuerUrl: string; mobileOverrideEnabled: boolean; mobileRedirectUri: string; profileSigningAlgorithm: string; scope: string; signingAlgorithm: string; + tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod; + timeout: number; }; -export type OAuthProfile = UserinfoResponse; +export type OAuthProfile = UserInfoResponse; @Injectable() export class OAuthRepository { @@ -20,30 +23,47 @@ export class OAuthRepository { this.logger.setContext(OAuthRepository.name); } - init() { - custom.setHttpOptionsDefaults({ timeout: 30_000 }); - } - - async authorize(config: OAuthConfig, redirectUrl: string) { + async authorize(config: OAuthConfig, redirectUrl: string, state?: string, codeChallenge?: string) { + const { buildAuthorizationUrl, randomState, randomPKCECodeVerifier, calculatePKCECodeChallenge } = await import( + 'openid-client' + ); const client = await this.getClient(config); - return client.authorizationUrl({ + state ??= randomState(); + let codeVerifier: string | null; + if (codeChallenge) { + codeVerifier = null; + } else { + codeVerifier = randomPKCECodeVerifier(); + codeChallenge = await calculatePKCECodeChallenge(codeVerifier); + } + const url = buildAuthorizationUrl(client, { redirect_uri: redirectUrl, scope: config.scope, - state: generators.state(), - }); + state, + code_challenge: codeChallenge, + code_challenge_method: 'S256', + }).toString(); + return { url, state, codeVerifier }; } async getLogoutEndpoint(config: OAuthConfig) { const client = await this.getClient(config); - return client.issuer.metadata.end_session_endpoint; + return client.serverMetadata().end_session_endpoint; } - async getProfile(config: OAuthConfig, url: string, redirectUrl: string): Promise<OAuthProfile> { + async getProfile( + config: OAuthConfig, + url: string, + expectedState: string, + codeVerifier: string, + ): Promise<OAuthProfile> { + const { authorizationCodeGrant, fetchUserInfo, ...oidc } = await import('openid-client'); const client = await this.getClient(config); - const params = client.callbackParams(url); + const pkceCodeVerifier = client.serverMetadata().supportsPKCE() ? codeVerifier : undefined; + try { - const tokens = await client.callback(redirectUrl, params, { state: params.state }); - const profile = await client.userinfo<OAuthProfile>(tokens.access_token || ''); + const tokens = await authorizationCodeGrant(client, new URL(url), { expectedState, pkceCodeVerifier }); + const profile = await fetchUserInfo(client, tokens.access_token, oidc.skipSubjectCheck); if (!profile.sub) { throw new Error('Unexpected profile response, no `sub`'); } @@ -59,7 +79,10 @@ export class OAuthRepository { ); } - throw error; + this.logger.error(`OAuth login failed: ${error.message}`); + this.logger.error(error); + + throw new Error('OAuth login failed', { cause: error }); } } @@ -81,19 +104,51 @@ export class OAuthRepository { clientSecret, profileSigningAlgorithm, signingAlgorithm, + tokenEndpointAuthMethod, + timeout, }: OAuthConfig) { try { - const issuer = await Issuer.discover(issuerUrl); - return new issuer.Client({ - client_id: clientId, - client_secret: clientSecret, - response_types: ['code'], - userinfo_signed_response_alg: profileSigningAlgorithm === 'none' ? undefined : profileSigningAlgorithm, - id_token_signed_response_alg: signingAlgorithm, - }); + const { allowInsecureRequests, discovery } = await import('openid-client'); + return await discovery( + new URL(issuerUrl), + clientId, + { + client_secret: clientSecret, + response_types: ['code'], + userinfo_signed_response_alg: profileSigningAlgorithm === 'none' ? undefined : profileSigningAlgorithm, + id_token_signed_response_alg: signingAlgorithm, + }, + await this.getTokenAuthMethod(tokenEndpointAuthMethod, clientSecret), + { + execute: [allowInsecureRequests], + timeout, + }, + ); } catch (error: any | AggregateError) { this.logger.error(`Error in OAuth discovery: ${error}`, error?.stack, error?.errors); throw new InternalServerErrorException(`Error in OAuth discovery: ${error}`, { cause: error }); } } + + private async getTokenAuthMethod(tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod, clientSecret?: string) { + const { None, ClientSecretPost, ClientSecretBasic } = await import('openid-client'); + + if (!clientSecret) { + return None(); + } + + switch (tokenEndpointAuthMethod) { + case OAuthTokenEndpointAuthMethod.CLIENT_SECRET_POST: { + return ClientSecretPost(clientSecret); + } + + case OAuthTokenEndpointAuthMethod.CLIENT_SECRET_BASIC: { + return ClientSecretBasic(clientSecret); + } + + default: { + return None(); + } + } + } } diff --git a/server/src/repositories/search.repository.ts b/server/src/repositories/search.repository.ts index 5c1ebae69d..0c958fec02 100644 --- a/server/src/repositories/search.repository.ts +++ b/server/src/repositories/search.repository.ts @@ -6,7 +6,8 @@ import { DB, Exif } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; import { MapAsset } from 'src/dtos/asset-response.dto'; import { AssetStatus, AssetType } from 'src/enum'; -import { anyUuid, asUuid, searchAssetBuilder } from 'src/utils/database'; +import { ConfigRepository } from 'src/repositories/config.repository'; +import { anyUuid, asUuid, searchAssetBuilder, vectorIndexQuery } from 'src/utils/database'; import { isValidInteger } from 'src/validation'; export interface SearchResult<T> { @@ -201,7 +202,10 @@ export interface GetCameraMakesOptions { @Injectable() export class SearchRepository { - constructor(@InjectKysely() private db: Kysely<DB>) {} + constructor( + @InjectKysely() private db: Kysely<DB>, + private configRepository: ConfigRepository, + ) {} @GenerateSql({ params: [ @@ -446,8 +450,8 @@ export class SearchRepository { async upsert(assetId: string, embedding: string): Promise<void> { await this.db .insertInto('smart_search') - .values({ assetId: asUuid(assetId), embedding } as any) - .onConflict((oc) => oc.column('assetId').doUpdateSet({ embedding } as any)) + .values({ assetId, embedding }) + .onConflict((oc) => oc.column('assetId').doUpdateSet((eb) => ({ embedding: eb.ref('excluded.embedding') }))) .execute(); } @@ -469,19 +473,32 @@ export class SearchRepository { return dimSize; } - setDimensionSize(dimSize: number): Promise<void> { + async setDimensionSize(dimSize: number): Promise<void> { if (!isValidInteger(dimSize, { min: 1, max: 2 ** 16 })) { throw new Error(`Invalid CLIP dimension size: ${dimSize}`); } - return this.db.transaction().execute(async (trx) => { - await sql`truncate ${sql.table('smart_search')}`.execute(trx); + // this is done in two transactions to handle concurrent writes + await this.db.transaction().execute(async (trx) => { + await sql`delete from ${sql.table('smart_search')}`.execute(trx); + await trx.schema.alterTable('smart_search').dropConstraint('dim_size_constraint').ifExists().execute(); + await sql`alter table ${sql.table('smart_search')} add constraint dim_size_constraint check (array_length(embedding::real[], 1) = ${sql.lit(dimSize)})`.execute( + trx, + ); + }); + + const vectorExtension = this.configRepository.getEnv().database.vectorExtension; + await this.db.transaction().execute(async (trx) => { + await sql`drop index if exists clip_index`.execute(trx); await trx.schema .alterTable('smart_search') .alterColumn('embedding', (col) => col.setDataType(sql.raw(`vector(${dimSize})`))) .execute(); - await sql`reindex index clip_index`.execute(trx); + await sql.raw(vectorIndexQuery({ vectorExtension, table: 'smart_search', indexName: 'clip_index' })).execute(trx); + await trx.schema.alterTable('smart_search').dropConstraint('dim_size_constraint').ifExists().execute(); }); + + await sql`vacuum analyze ${sql.table('smart_search')}`.execute(this.db); } async deleteAllSearchEmbeddings(): Promise<void> { diff --git a/server/src/repositories/server-info.repository.ts b/server/src/repositories/server-info.repository.ts index deb24123d0..4500094899 100644 --- a/server/src/repositories/server-info.repository.ts +++ b/server/src/repositories/server-info.repository.ts @@ -73,26 +73,54 @@ export class ServerInfoRepository { } } + buildVersions?: ServerBuildVersions; + + private async retrieveVersionFallback( + command: string, + commandTransform?: (output: string) => string, + version?: string, + ): Promise<string> { + if (!version) { + const output = await maybeFirstLine(command); + version = commandTransform ? commandTransform(output) : output; + } + return version; + } + async getBuildVersions(): Promise<ServerBuildVersions> { - const { nodeVersion, resourcePaths } = this.configRepository.getEnv(); + if (!this.buildVersions) { + const { nodeVersion, resourcePaths } = this.configRepository.getEnv(); - const [nodejsOutput, ffmpegOutput, magickOutput] = await Promise.all([ - maybeFirstLine('node --version'), - maybeFirstLine('ffmpeg -version'), - maybeFirstLine('convert --version'), - ]); + const lockfile: BuildLockfile | undefined = await readFile(resourcePaths.lockFile) + .then((buffer) => JSON.parse(buffer.toString())) + .catch(() => this.logger.warn(`Failed to read ${resourcePaths.lockFile}`)); - const lockfile = await readFile(resourcePaths.lockFile) - .then((buffer) => JSON.parse(buffer.toString())) - .catch(() => this.logger.warn(`Failed to read ${resourcePaths.lockFile}`)); + const [nodejsVersion, ffmpegVersion, magickVersion, exiftoolVersion] = await Promise.all([ + this.retrieveVersionFallback('node --version', undefined, nodeVersion), + this.retrieveVersionFallback( + 'ffmpeg -version', + (output) => output.replaceAll('ffmpeg version ', ''), + getLockfileVersion('ffmpeg', lockfile), + ), + this.retrieveVersionFallback( + 'magick --version', + (output) => output.replaceAll('Version: ImageMagick ', ''), + getLockfileVersion('imagemagick', lockfile), + ), + exiftool.version(), + ]); - return { - nodejs: nodejsOutput || nodeVersion || '', - exiftool: await exiftool.version(), - ffmpeg: getLockfileVersion('ffmpeg', lockfile) || ffmpegOutput.replaceAll('ffmpeg version', '') || '', - libvips: getLockfileVersion('libvips', lockfile) || sharp.versions.vips, - imagemagick: - getLockfileVersion('imagemagick', lockfile) || magickOutput.replaceAll('Version: ImageMagick ', '') || '', - }; + const libvipsVersion = getLockfileVersion('libvips', lockfile) || sharp.versions.vips; + + this.buildVersions = { + nodejs: nodejsVersion, + exiftool: exiftoolVersion, + ffmpeg: ffmpegVersion, + libvips: libvipsVersion, + imagemagick: magickVersion, + }; + } + + return this.buildVersions; } } diff --git a/server/src/schema/index.ts b/server/src/schema/index.ts index fe4b86d65c..c62681d049 100644 --- a/server/src/schema/index.ts +++ b/server/src/schema/index.ts @@ -28,6 +28,7 @@ import { MemoryTable } from 'src/schema/tables/memory.table'; import { MemoryAssetTable } from 'src/schema/tables/memory_asset.table'; import { MoveTable } from 'src/schema/tables/move.table'; import { NaturalEarthCountriesTable } from 'src/schema/tables/natural-earth-countries.table'; +import { NotificationTable } from 'src/schema/tables/notification.table'; import { PartnerAuditTable } from 'src/schema/tables/partner-audit.table'; import { PartnerTable } from 'src/schema/tables/partner.table'; import { PersonTable } from 'src/schema/tables/person.table'; @@ -46,14 +47,8 @@ import { UserTable } from 'src/schema/tables/user.table'; import { VersionHistoryTable } from 'src/schema/tables/version-history.table'; import { ConfigurationParameter, Database, Extensions } from 'src/sql-tools'; -@Extensions(['uuid-ossp', 'unaccent', 'cube', 'earthdistance', 'pg_trgm', 'vectors', 'plpgsql']) +@Extensions(['uuid-ossp', 'unaccent', 'cube', 'earthdistance', 'pg_trgm', 'plpgsql']) @ConfigurationParameter({ name: 'search_path', value: () => '"$user", public, vectors', scope: 'database' }) -@ConfigurationParameter({ - name: 'vectors.pgvector_compatibility', - value: () => 'on', - scope: 'user', - synchronize: false, -}) @Database({ name: 'immich' }) export class ImmichDatabase { tables = [ @@ -76,6 +71,7 @@ export class ImmichDatabase { MemoryTable, MoveTable, NaturalEarthCountriesTable, + NotificationTable, PartnerAuditTable, PartnerTable, PersonTable, diff --git a/server/src/schema/migrations/1744910873969-InitialMigration.ts b/server/src/schema/migrations/1744910873969-InitialMigration.ts index 459534a26a..ce4a37ae3b 100644 --- a/server/src/schema/migrations/1744910873969-InitialMigration.ts +++ b/server/src/schema/migrations/1744910873969-InitialMigration.ts @@ -2,6 +2,7 @@ import { Kysely, sql } from 'kysely'; import { DatabaseExtension } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; +import { vectorIndexQuery } from 'src/utils/database'; const vectorExtension = new ConfigRepository().getEnv().database.vectorExtension; const lastMigrationSql = sql<{ name: string }>`SELECT "name" FROM "migrations" ORDER BY "timestamp" DESC LIMIT 1;`; @@ -29,7 +30,7 @@ export async function up(db: Kysely<any>): Promise<void> { await sql`CREATE EXTENSION IF NOT EXISTS "cube";`.execute(db); await sql`CREATE EXTENSION IF NOT EXISTS "earthdistance";`.execute(db); await sql`CREATE EXTENSION IF NOT EXISTS "pg_trgm";`.execute(db); - await sql`CREATE EXTENSION IF NOT EXISTS "vectors";`.execute(db); + await sql`CREATE EXTENSION IF NOT EXISTS ${sql.raw(vectorExtension)}`.execute(db); await sql`CREATE OR REPLACE FUNCTION immich_uuid_v7(p_timestamp timestamp with time zone default clock_timestamp()) RETURNS uuid VOLATILE LANGUAGE SQL @@ -108,7 +109,6 @@ export async function up(db: Kysely<any>): Promise<void> { $$;`.execute(db); if (vectorExtension === DatabaseExtension.VECTORS) { await sql`SET search_path TO "$user", public, vectors`.execute(db); - await sql`SET vectors.pgvector_compatibility=on`.execute(db); } await sql`CREATE TYPE "assets_status_enum" AS ENUM ('active','trashed','deleted');`.execute(db); await sql`CREATE TYPE "sourcetype" AS ENUM ('machine-learning','exif','manual');`.execute(db); @@ -293,7 +293,7 @@ export async function up(db: Kysely<any>): Promise<void> { await sql`CREATE INDEX "IDX_live_photo_cid" ON "exif" ("livePhotoCID")`.execute(db); await sql`CREATE INDEX "IDX_auto_stack_id" ON "exif" ("autoStackId")`.execute(db); await sql`CREATE INDEX "IDX_asset_exif_update_id" ON "exif" ("updateId")`.execute(db); - await sql`CREATE INDEX "face_index" ON "face_search" USING hnsw (embedding vector_cosine_ops) WITH (ef_construction = 300, m = 16)`.execute(db); + await sql.raw(vectorIndexQuery({ vectorExtension, table: 'face_search', indexName: 'face_index' })).execute(db); await sql`CREATE INDEX "IDX_geodata_gist_earthcoord" ON "geodata_places" (ll_to_earth_public(latitude, longitude))`.execute(db); await sql`CREATE INDEX "idx_geodata_places_name" ON "geodata_places" USING gin (f_unaccent("name") gin_trgm_ops)`.execute(db); await sql`CREATE INDEX "idx_geodata_places_admin2_name" ON "geodata_places" USING gin (f_unaccent("admin2Name") gin_trgm_ops)`.execute(db); @@ -316,7 +316,7 @@ export async function up(db: Kysely<any>): Promise<void> { await sql`CREATE INDEX "IDX_sharedlink_albumId" ON "shared_links" ("albumId")`.execute(db); await sql`CREATE INDEX "IDX_5b7decce6c8d3db9593d6111a6" ON "shared_link__asset" ("assetsId")`.execute(db); await sql`CREATE INDEX "IDX_c9fab4aa97ffd1b034f3d6581a" ON "shared_link__asset" ("sharedLinksId")`.execute(db); - await sql`CREATE INDEX "clip_index" ON "smart_search" USING hnsw (embedding vector_cosine_ops) WITH (ef_construction = 300, m = 16)`.execute(db); + await sql.raw(vectorIndexQuery({ vectorExtension, table: 'smart_search', indexName: 'clip_index' })).execute(db); await sql`CREATE INDEX "IDX_d8ddd9d687816cc490432b3d4b" ON "session_sync_checkpoints" ("sessionId")`.execute(db); await sql`CREATE INDEX "IDX_session_sync_checkpoints_update_id" ON "session_sync_checkpoints" ("updateId")`.execute(db); await sql`CREATE INDEX "IDX_92e67dc508c705dd66c9461557" ON "tags" ("userId")`.execute(db); diff --git a/server/src/schema/migrations/1744991379464-AddNotificationsTable.ts b/server/src/schema/migrations/1744991379464-AddNotificationsTable.ts new file mode 100644 index 0000000000..28dca6658c --- /dev/null +++ b/server/src/schema/migrations/1744991379464-AddNotificationsTable.ts @@ -0,0 +1,22 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely<any>): Promise<void> { + await sql`CREATE TABLE "notifications" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" timestamp with time zone NOT NULL DEFAULT now(), "updatedAt" timestamp with time zone NOT NULL DEFAULT now(), "deletedAt" timestamp with time zone, "updateId" uuid NOT NULL DEFAULT immich_uuid_v7(), "userId" uuid, "level" character varying NOT NULL DEFAULT 'info', "type" character varying NOT NULL DEFAULT 'info', "data" jsonb, "title" character varying NOT NULL, "description" text, "readAt" timestamp with time zone);`.execute(db); + await sql`ALTER TABLE "notifications" ADD CONSTRAINT "PK_6a72c3c0f683f6462415e653c3a" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "notifications" ADD CONSTRAINT "FK_692a909ee0fa9383e7859f9b406" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`CREATE INDEX "IDX_notifications_update_id" ON "notifications" ("updateId")`.execute(db); + await sql`CREATE INDEX "IDX_692a909ee0fa9383e7859f9b40" ON "notifications" ("userId")`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "notifications_updated_at" + BEFORE UPDATE ON "notifications" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); +} + +export async function down(db: Kysely<any>): Promise<void> { + await sql`DROP TRIGGER "notifications_updated_at" ON "notifications";`.execute(db); + await sql`DROP INDEX "IDX_notifications_update_id";`.execute(db); + await sql`DROP INDEX "IDX_692a909ee0fa9383e7859f9b40";`.execute(db); + await sql`ALTER TABLE "notifications" DROP CONSTRAINT "PK_6a72c3c0f683f6462415e653c3a";`.execute(db); + await sql`ALTER TABLE "notifications" DROP CONSTRAINT "FK_692a909ee0fa9383e7859f9b406";`.execute(db); + await sql`DROP TABLE "notifications";`.execute(db); +} diff --git a/server/src/schema/migrations/1745244781846-AddUserAvatarColorColumn.ts b/server/src/schema/migrations/1745244781846-AddUserAvatarColorColumn.ts new file mode 100644 index 0000000000..5f3fdbedc8 --- /dev/null +++ b/server/src/schema/migrations/1745244781846-AddUserAvatarColorColumn.ts @@ -0,0 +1,14 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely<any>): Promise<void> { + await sql`ALTER TABLE "users" ADD "avatarColor" character varying;`.execute(db); + await sql` + UPDATE "users" + SET "avatarColor" = "user_metadata"."value"->'avatar'->>'color' + FROM "user_metadata" + WHERE "users"."id" = "user_metadata"."userId" AND "user_metadata"."key" = 'preferences';`.execute(db); +} + +export async function down(db: Kysely<any>): Promise<void> { + await sql`ALTER TABLE "users" DROP COLUMN "avatarColor";`.execute(db); +} diff --git a/server/src/schema/tables/notification.table.ts b/server/src/schema/tables/notification.table.ts new file mode 100644 index 0000000000..bf9b8bdf3b --- /dev/null +++ b/server/src/schema/tables/notification.table.ts @@ -0,0 +1,52 @@ +import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; +import { NotificationLevel, NotificationType } from 'src/enum'; +import { UserTable } from 'src/schema/tables/user.table'; +import { + Column, + CreateDateColumn, + DeleteDateColumn, + ForeignKeyColumn, + PrimaryGeneratedColumn, + Table, + UpdateDateColumn, +} from 'src/sql-tools'; + +@Table('notifications') +@UpdatedAtTrigger('notifications_updated_at') +export class NotificationTable { + @PrimaryGeneratedColumn() + id!: string; + + @CreateDateColumn() + createdAt!: Date; + + @UpdateDateColumn() + updatedAt!: Date; + + @DeleteDateColumn() + deletedAt?: Date; + + @UpdateIdColumn({ indexName: 'IDX_notifications_update_id' }) + updateId?: string; + + @ForeignKeyColumn(() => UserTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: true }) + userId!: string; + + @Column({ default: NotificationLevel.Info }) + level!: NotificationLevel; + + @Column({ default: NotificationLevel.Info }) + type!: NotificationType; + + @Column({ type: 'jsonb', nullable: true }) + data!: any | null; + + @Column() + title!: string; + + @Column({ type: 'text', nullable: true }) + description!: string; + + @Column({ type: 'timestamp with time zone', nullable: true }) + readAt?: Date | null; +} diff --git a/server/src/schema/tables/user.table.ts b/server/src/schema/tables/user.table.ts index eeef923796..7525a739a6 100644 --- a/server/src/schema/tables/user.table.ts +++ b/server/src/schema/tables/user.table.ts @@ -1,6 +1,6 @@ import { ColumnType } from 'kysely'; import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; -import { UserStatus } from 'src/enum'; +import { UserAvatarColor, UserStatus } from 'src/enum'; import { users_delete_audit } from 'src/schema/functions'; import { AfterDeleteTrigger, @@ -49,6 +49,9 @@ export class UserTable { @Column({ type: 'boolean', default: true }) shouldChangePassword!: Generated<boolean>; + @Column({ default: null }) + avatarColor!: UserAvatarColor | null; + @DeleteDateColumn() deletedAt!: Timestamp | null; diff --git a/server/src/services/asset.service.spec.ts b/server/src/services/asset.service.spec.ts index a3f536ac33..af4fef5bd5 100755 --- a/server/src/services/asset.service.spec.ts +++ b/server/src/services/asset.service.spec.ts @@ -565,7 +565,7 @@ describe(AssetService.name, () => { it('should remove faces', async () => { const assetWithFace = { ...assetStub.image, faces: [faceStub.face1, faceStub.mergeFace1] }; - mocks.asset.getById.mockResolvedValue(assetWithFace); + mocks.assetJob.getForAssetDeletion.mockResolvedValue(assetWithFace); await sut.handleAssetDeletion({ id: assetWithFace.id, deleteOnDisk: true }); @@ -592,7 +592,7 @@ describe(AssetService.name, () => { it('should update stack primary asset if deleted asset was primary asset in a stack', async () => { mocks.stack.update.mockResolvedValue(factory.stack() as any); - mocks.asset.getById.mockResolvedValue(assetStub.primaryImage); + mocks.assetJob.getForAssetDeletion.mockResolvedValue(assetStub.primaryImage); await sut.handleAssetDeletion({ id: assetStub.primaryImage.id, deleteOnDisk: true }); @@ -604,7 +604,7 @@ describe(AssetService.name, () => { it('should delete the entire stack if deleted asset was the primary asset and the stack would only contain one asset afterwards', async () => { mocks.stack.delete.mockResolvedValue(); - mocks.asset.getById.mockResolvedValue({ + mocks.assetJob.getForAssetDeletion.mockResolvedValue({ ...assetStub.primaryImage, stack: { ...assetStub.primaryImage.stack, assets: assetStub.primaryImage.stack!.assets.slice(0, 2) }, }); @@ -615,7 +615,7 @@ describe(AssetService.name, () => { }); it('should delete a live photo', async () => { - mocks.asset.getById.mockResolvedValue(assetStub.livePhotoStillAsset); + mocks.assetJob.getForAssetDeletion.mockResolvedValue(assetStub.livePhotoStillAsset as any); mocks.asset.getLivePhotoCount.mockResolvedValue(0); await sut.handleAssetDeletion({ @@ -653,7 +653,7 @@ describe(AssetService.name, () => { it('should not delete a live motion part if it is being used by another asset', async () => { mocks.asset.getLivePhotoCount.mockResolvedValue(2); - mocks.asset.getById.mockResolvedValue(assetStub.livePhotoStillAsset); + mocks.assetJob.getForAssetDeletion.mockResolvedValue(assetStub.livePhotoStillAsset as any); await sut.handleAssetDeletion({ id: assetStub.livePhotoStillAsset.id, @@ -680,12 +680,13 @@ describe(AssetService.name, () => { }); it('should update usage', async () => { - mocks.asset.getById.mockResolvedValue(assetStub.image); + mocks.assetJob.getForAssetDeletion.mockResolvedValue(assetStub.image); await sut.handleAssetDeletion({ id: assetStub.image.id, deleteOnDisk: true }); expect(mocks.user.updateUsage).toHaveBeenCalledWith(assetStub.image.ownerId, -5000); }); it('should fail if asset could not be found', async () => { + mocks.assetJob.getForAssetDeletion.mockResolvedValue(void 0); await expect(sut.handleAssetDeletion({ id: assetStub.image.id, deleteOnDisk: true })).resolves.toBe( JobStatus.FAILED, ); diff --git a/server/src/services/asset.service.ts b/server/src/services/asset.service.ts index 16f60907ba..3e13ed0b8e 100644 --- a/server/src/services/asset.service.ts +++ b/server/src/services/asset.service.ts @@ -189,13 +189,7 @@ export class AssetService extends BaseService { async handleAssetDeletion(job: JobOf<JobName.ASSET_DELETION>): Promise<JobStatus> { const { id, deleteOnDisk } = job; - const asset = await this.assetRepository.getById(id, { - faces: { person: true }, - library: true, - stack: { assets: true }, - exifInfo: true, - files: true, - }); + const asset = await this.assetJobRepository.getForAssetDeletion(id); if (!asset) { return JobStatus.FAILED; diff --git a/server/src/services/audit.service.spec.ts b/server/src/services/audit.service.spec.ts index 6ef139f506..381b2ec7e8 100644 --- a/server/src/services/audit.service.spec.ts +++ b/server/src/services/audit.service.spec.ts @@ -1,6 +1,4 @@ -import { BadRequestException } from '@nestjs/common'; -import { FileReportItemDto } from 'src/dtos/audit.dto'; -import { AssetFileType, AssetPathType, JobStatus, PersonPathType, UserPathType } from 'src/enum'; +import { JobStatus } from 'src/enum'; import { AuditService } from 'src/services/audit.service'; import { newTestService, ServiceMocks } from 'test/utils'; @@ -25,148 +23,4 @@ describe(AuditService.name, () => { expect(mocks.audit.removeBefore).toHaveBeenCalledWith(expect.any(Date)); }); }); - - describe('getChecksums', () => { - it('should fail if the file is not in the immich path', async () => { - await expect(sut.getChecksums({ filenames: ['foo/bar'] })).rejects.toBeInstanceOf(BadRequestException); - - expect(mocks.crypto.hashFile).not.toHaveBeenCalled(); - }); - - it('should get checksum for valid file', async () => { - await expect(sut.getChecksums({ filenames: ['./upload/my-file.jpg'] })).resolves.toEqual([ - { filename: './upload/my-file.jpg', checksum: expect.any(String) }, - ]); - - expect(mocks.crypto.hashFile).toHaveBeenCalledWith('./upload/my-file.jpg'); - }); - }); - - describe('fixItems', () => { - it('should fail if the file is not in the immich path', async () => { - await expect( - sut.fixItems([ - { entityId: 'my-id', pathType: AssetPathType.ORIGINAL, pathValue: 'foo/bar' } as FileReportItemDto, - ]), - ).rejects.toBeInstanceOf(BadRequestException); - - expect(mocks.asset.update).not.toHaveBeenCalled(); - expect(mocks.asset.upsertFile).not.toHaveBeenCalled(); - expect(mocks.person.update).not.toHaveBeenCalled(); - expect(mocks.user.update).not.toHaveBeenCalled(); - }); - - it('should update encoded video path', async () => { - await sut.fixItems([ - { - entityId: 'my-id', - pathType: AssetPathType.ENCODED_VIDEO, - pathValue: './upload/my-video.mp4', - } as FileReportItemDto, - ]); - - expect(mocks.asset.update).toHaveBeenCalledWith({ id: 'my-id', encodedVideoPath: './upload/my-video.mp4' }); - expect(mocks.asset.upsertFile).not.toHaveBeenCalled(); - expect(mocks.person.update).not.toHaveBeenCalled(); - expect(mocks.user.update).not.toHaveBeenCalled(); - }); - - it('should update preview path', async () => { - await sut.fixItems([ - { - entityId: 'my-id', - pathType: AssetPathType.PREVIEW, - pathValue: './upload/my-preview.png', - } as FileReportItemDto, - ]); - - expect(mocks.asset.upsertFile).toHaveBeenCalledWith({ - assetId: 'my-id', - type: AssetFileType.PREVIEW, - path: './upload/my-preview.png', - }); - expect(mocks.asset.update).not.toHaveBeenCalled(); - expect(mocks.person.update).not.toHaveBeenCalled(); - expect(mocks.user.update).not.toHaveBeenCalled(); - }); - - it('should update thumbnail path', async () => { - await sut.fixItems([ - { - entityId: 'my-id', - pathType: AssetPathType.THUMBNAIL, - pathValue: './upload/my-thumbnail.webp', - } as FileReportItemDto, - ]); - - expect(mocks.asset.upsertFile).toHaveBeenCalledWith({ - assetId: 'my-id', - type: AssetFileType.THUMBNAIL, - path: './upload/my-thumbnail.webp', - }); - expect(mocks.asset.update).not.toHaveBeenCalled(); - expect(mocks.person.update).not.toHaveBeenCalled(); - expect(mocks.user.update).not.toHaveBeenCalled(); - }); - - it('should update original path', async () => { - await sut.fixItems([ - { - entityId: 'my-id', - pathType: AssetPathType.ORIGINAL, - pathValue: './upload/my-original.png', - } as FileReportItemDto, - ]); - - expect(mocks.asset.update).toHaveBeenCalledWith({ id: 'my-id', originalPath: './upload/my-original.png' }); - expect(mocks.asset.upsertFile).not.toHaveBeenCalled(); - expect(mocks.person.update).not.toHaveBeenCalled(); - expect(mocks.user.update).not.toHaveBeenCalled(); - }); - - it('should update sidecar path', async () => { - await sut.fixItems([ - { - entityId: 'my-id', - pathType: AssetPathType.SIDECAR, - pathValue: './upload/my-sidecar.xmp', - } as FileReportItemDto, - ]); - - expect(mocks.asset.update).toHaveBeenCalledWith({ id: 'my-id', sidecarPath: './upload/my-sidecar.xmp' }); - expect(mocks.asset.upsertFile).not.toHaveBeenCalled(); - expect(mocks.person.update).not.toHaveBeenCalled(); - expect(mocks.user.update).not.toHaveBeenCalled(); - }); - - it('should update face path', async () => { - await sut.fixItems([ - { - entityId: 'my-id', - pathType: PersonPathType.FACE, - pathValue: './upload/my-face.jpg', - } as FileReportItemDto, - ]); - - expect(mocks.person.update).toHaveBeenCalledWith({ id: 'my-id', thumbnailPath: './upload/my-face.jpg' }); - expect(mocks.asset.update).not.toHaveBeenCalled(); - expect(mocks.asset.upsertFile).not.toHaveBeenCalled(); - expect(mocks.user.update).not.toHaveBeenCalled(); - }); - - it('should update profile path', async () => { - await sut.fixItems([ - { - entityId: 'my-id', - pathType: UserPathType.PROFILE, - pathValue: './upload/my-profile-pic.jpg', - } as FileReportItemDto, - ]); - - expect(mocks.user.update).toHaveBeenCalledWith('my-id', { profileImagePath: './upload/my-profile-pic.jpg' }); - expect(mocks.asset.update).not.toHaveBeenCalled(); - expect(mocks.asset.upsertFile).not.toHaveBeenCalled(); - expect(mocks.person.update).not.toHaveBeenCalled(); - }); - }); }); diff --git a/server/src/services/audit.service.ts b/server/src/services/audit.service.ts index a049a9c64b..7c9a070dd0 100644 --- a/server/src/services/audit.service.ts +++ b/server/src/services/audit.service.ts @@ -1,23 +1,9 @@ -import { BadRequestException, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { DateTime } from 'luxon'; -import { resolve } from 'node:path'; -import { AUDIT_LOG_MAX_DURATION, JOBS_ASSET_PAGINATION_SIZE } from 'src/constants'; -import { StorageCore } from 'src/cores/storage.core'; +import { AUDIT_LOG_MAX_DURATION } from 'src/constants'; import { OnJob } from 'src/decorators'; -import { FileChecksumDto, FileChecksumResponseDto, FileReportItemDto, PathEntityType } from 'src/dtos/audit.dto'; -import { - AssetFileType, - AssetPathType, - JobName, - JobStatus, - PersonPathType, - QueueName, - StorageFolder, - UserPathType, -} from 'src/enum'; +import { JobName, JobStatus, QueueName } from 'src/enum'; import { BaseService } from 'src/services/base.service'; -import { getAssetFiles } from 'src/utils/asset.util'; -import { usePagination } from 'src/utils/pagination'; @Injectable() export class AuditService extends BaseService { @@ -26,187 +12,4 @@ export class AuditService extends BaseService { await this.auditRepository.removeBefore(DateTime.now().minus(AUDIT_LOG_MAX_DURATION).toJSDate()); return JobStatus.SUCCESS; } - - async getChecksums(dto: FileChecksumDto) { - const results: FileChecksumResponseDto[] = []; - for (const filename of dto.filenames) { - if (!StorageCore.isImmichPath(filename)) { - throw new BadRequestException( - `Could not get the checksum of ${filename} because the file isn't accessible by Immich`, - ); - } - - const checksum = await this.cryptoRepository.hashFile(filename); - results.push({ filename, checksum: checksum.toString('base64') }); - } - return results; - } - - async fixItems(items: FileReportItemDto[]) { - for (const { entityId: id, pathType, pathValue } of items) { - if (!StorageCore.isImmichPath(pathValue)) { - throw new BadRequestException( - `Could not fix item ${id} with path ${pathValue} because the file isn't accessible by Immich`, - ); - } - - switch (pathType) { - case AssetPathType.ENCODED_VIDEO: { - await this.assetRepository.update({ id, encodedVideoPath: pathValue }); - break; - } - - case AssetPathType.PREVIEW: { - await this.assetRepository.upsertFile({ assetId: id, type: AssetFileType.PREVIEW, path: pathValue }); - break; - } - - case AssetPathType.THUMBNAIL: { - await this.assetRepository.upsertFile({ assetId: id, type: AssetFileType.THUMBNAIL, path: pathValue }); - break; - } - - case AssetPathType.ORIGINAL: { - await this.assetRepository.update({ id, originalPath: pathValue }); - break; - } - - case AssetPathType.SIDECAR: { - await this.assetRepository.update({ id, sidecarPath: pathValue }); - break; - } - - case PersonPathType.FACE: { - await this.personRepository.update({ id, thumbnailPath: pathValue }); - break; - } - - case UserPathType.PROFILE: { - await this.userRepository.update(id, { profileImagePath: pathValue }); - break; - } - } - } - } - - private fullPath(filename: string) { - return resolve(filename); - } - - async getFileReport() { - const hasFile = (items: Set<string>, filename: string) => items.has(filename) || items.has(this.fullPath(filename)); - const crawl = async (folder: StorageFolder) => - new Set( - await this.storageRepository.crawl({ - includeHidden: true, - pathsToCrawl: [StorageCore.getBaseFolder(folder)], - }), - ); - - const uploadFiles = await crawl(StorageFolder.UPLOAD); - const libraryFiles = await crawl(StorageFolder.LIBRARY); - const thumbFiles = await crawl(StorageFolder.THUMBNAILS); - const videoFiles = await crawl(StorageFolder.ENCODED_VIDEO); - const profileFiles = await crawl(StorageFolder.PROFILE); - const allFiles = new Set<string>(); - for (const list of [libraryFiles, thumbFiles, videoFiles, profileFiles, uploadFiles]) { - for (const item of list) { - allFiles.add(item); - } - } - - const track = (filename: string | null | undefined) => { - if (!filename) { - return; - } - allFiles.delete(filename); - allFiles.delete(this.fullPath(filename)); - }; - - this.logger.log( - `Found ${libraryFiles.size} original files, ${thumbFiles.size} thumbnails, ${videoFiles.size} encoded videos, ${profileFiles.size} profile files`, - ); - const pagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (options) => - this.assetRepository.getAll(options, { withDeleted: true, withArchived: true }), - ); - - let assetCount = 0; - - const orphans: FileReportItemDto[] = []; - for await (const assets of pagination) { - assetCount += assets.length; - for (const { id, files, originalPath, encodedVideoPath, isExternal, checksum } of assets) { - const { fullsizeFile, previewFile, thumbnailFile } = getAssetFiles(files); - for (const file of [ - originalPath, - fullsizeFile?.path, - previewFile?.path, - encodedVideoPath, - thumbnailFile?.path, - ]) { - track(file); - } - - const entity = { entityId: id, entityType: PathEntityType.ASSET, checksum: checksum.toString('base64') }; - if ( - originalPath && - !hasFile(libraryFiles, originalPath) && - !hasFile(uploadFiles, originalPath) && - // Android motion assets - !hasFile(videoFiles, originalPath) && - // ignore external library assets - !isExternal - ) { - orphans.push({ ...entity, pathType: AssetPathType.ORIGINAL, pathValue: originalPath }); - } - if (previewFile && !hasFile(thumbFiles, previewFile.path)) { - orphans.push({ ...entity, pathType: AssetPathType.PREVIEW, pathValue: previewFile.path }); - } - if (thumbnailFile && !hasFile(thumbFiles, thumbnailFile.path)) { - orphans.push({ ...entity, pathType: AssetPathType.THUMBNAIL, pathValue: thumbnailFile.path }); - } - if (encodedVideoPath && !hasFile(videoFiles, encodedVideoPath)) { - orphans.push({ ...entity, pathType: AssetPathType.THUMBNAIL, pathValue: encodedVideoPath }); - } - } - } - - const users = await this.userRepository.getList(); - for (const { id, profileImagePath } of users) { - track(profileImagePath); - - const entity = { entityId: id, entityType: PathEntityType.USER }; - if (profileImagePath && !hasFile(profileFiles, profileImagePath)) { - orphans.push({ ...entity, pathType: UserPathType.PROFILE, pathValue: profileImagePath }); - } - } - - let peopleCount = 0; - for await (const { id, thumbnailPath } of this.personRepository.getAll()) { - track(thumbnailPath); - const entity = { entityId: id, entityType: PathEntityType.PERSON }; - if (thumbnailPath && !hasFile(thumbFiles, thumbnailPath)) { - orphans.push({ ...entity, pathType: PersonPathType.FACE, pathValue: thumbnailPath }); - } - - if (peopleCount === JOBS_ASSET_PAGINATION_SIZE) { - this.logger.log(`Found ${assetCount} assets, ${users.length} users, ${peopleCount} people`); - peopleCount = 0; - } - } - - this.logger.log(`Found ${assetCount} assets, ${users.length} users, ${peopleCount} people`); - - const extras: string[] = []; - for (const file of allFiles) { - extras.push(file); - } - - // send as absolute paths - for (const orphan of orphans) { - orphan.pathValue = this.fullPath(orphan.pathValue); - } - - return { orphans, extras }; - } } diff --git a/server/src/services/auth.service.spec.ts b/server/src/services/auth.service.spec.ts index b1bfe00e85..75f5b8a52d 100644 --- a/server/src/services/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -55,7 +55,7 @@ describe(AuthService.name, () => { beforeEach(() => { ({ sut, mocks } = newTestService(AuthService)); - mocks.oauth.authorize.mockResolvedValue('access-token'); + mocks.oauth.authorize.mockResolvedValue({ url: 'http://test', state: 'state', codeVerifier: 'codeVerifier' }); mocks.oauth.getProfile.mockResolvedValue({ sub, email }); mocks.oauth.getLogoutEndpoint.mockResolvedValue('http://end-session-endpoint'); }); @@ -64,16 +64,6 @@ describe(AuthService.name, () => { expect(sut).toBeDefined(); }); - describe('onBootstrap', () => { - it('should init the repo', () => { - mocks.oauth.init.mockResolvedValue(); - - sut.onBootstrap(); - - expect(mocks.oauth.init).toHaveBeenCalled(); - }); - }); - describe('login', () => { it('should throw an error if password login is disabled', async () => { mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.disabled); @@ -519,16 +509,22 @@ describe(AuthService.name, () => { describe('callback', () => { it('should throw an error if OAuth is not enabled', async () => { - await expect(sut.callback({ url: '' }, loginDetails)).rejects.toBeInstanceOf(BadRequestException); + await expect( + sut.callback({ url: '', state: 'xyz789', codeVerifier: 'foo' }, {}, loginDetails), + ).rejects.toBeInstanceOf(BadRequestException); }); it('should not allow auto registering', async () => { mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.oauthEnabled); mocks.user.getByEmail.mockResolvedValue(void 0); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).rejects.toBeInstanceOf( - BadRequestException, - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foo' }, + {}, + loginDetails, + ), + ).rejects.toBeInstanceOf(BadRequestException); expect(mocks.user.getByEmail).toHaveBeenCalledTimes(1); }); @@ -541,9 +537,13 @@ describe(AuthService.name, () => { mocks.user.update.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse(user), - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foobar' }, + {}, + loginDetails, + ), + ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.getByEmail).toHaveBeenCalledTimes(1); expect(mocks.user.update).toHaveBeenCalledWith(user.id, { oauthId: sub }); @@ -557,9 +557,13 @@ describe(AuthService.name, () => { mocks.user.getAdmin.mockResolvedValue(user); mocks.user.create.mockResolvedValue(user); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).rejects.toThrow( - BadRequestException, - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foobar' }, + {}, + loginDetails, + ), + ).rejects.toThrow(BadRequestException); expect(mocks.user.update).not.toHaveBeenCalled(); expect(mocks.user.create).not.toHaveBeenCalled(); @@ -574,9 +578,13 @@ describe(AuthService.name, () => { mocks.user.create.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse(user), - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foobar' }, + {}, + loginDetails, + ), + ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.getByEmail).toHaveBeenCalledTimes(2); // second call is for domain check before create expect(mocks.user.create).toHaveBeenCalledTimes(1); @@ -592,18 +600,19 @@ describe(AuthService.name, () => { mocks.session.create.mockResolvedValue(factory.session()); mocks.oauth.getProfile.mockResolvedValue({ sub, email: undefined }); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).rejects.toBeInstanceOf( - BadRequestException, - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foobar' }, + {}, + loginDetails, + ), + ).rejects.toBeInstanceOf(BadRequestException); expect(mocks.user.getByEmail).not.toHaveBeenCalled(); expect(mocks.user.create).not.toHaveBeenCalled(); }); for (const url of [ - 'app.immich:/', - 'app.immich://', - 'app.immich:///', 'app.immich:/oauth-callback?code=abc123', 'app.immich://oauth-callback?code=abc123', 'app.immich:///oauth-callback?code=abc123', @@ -615,9 +624,14 @@ describe(AuthService.name, () => { mocks.user.getByOAuthId.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); - await sut.callback({ url }, loginDetails); + await sut.callback({ url, state: 'xyz789', codeVerifier: 'foo' }, {}, loginDetails); - expect(mocks.oauth.getProfile).toHaveBeenCalledWith(expect.objectContaining({}), url, 'http://mobile-redirect'); + expect(mocks.oauth.getProfile).toHaveBeenCalledWith( + expect.objectContaining({}), + 'http://mobile-redirect?code=abc123', + 'xyz789', + 'foo', + ); }); } @@ -630,9 +644,13 @@ describe(AuthService.name, () => { mocks.user.create.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse(user), - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foo' }, + {}, + loginDetails, + ), + ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.create).toHaveBeenCalledWith(expect.objectContaining({ quotaSizeInBytes: 1_073_741_824 })); }); @@ -647,9 +665,13 @@ describe(AuthService.name, () => { mocks.user.create.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse(user), - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foo' }, + {}, + loginDetails, + ), + ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.create).toHaveBeenCalledWith(expect.objectContaining({ quotaSizeInBytes: 1_073_741_824 })); }); @@ -664,9 +686,13 @@ describe(AuthService.name, () => { mocks.user.create.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse(user), - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foo' }, + {}, + loginDetails, + ), + ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.create).toHaveBeenCalledWith(expect.objectContaining({ quotaSizeInBytes: 1_073_741_824 })); }); @@ -681,9 +707,13 @@ describe(AuthService.name, () => { mocks.user.create.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse(user), - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foo' }, + {}, + loginDetails, + ), + ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.create).toHaveBeenCalledWith({ email: user.email, @@ -705,9 +735,13 @@ describe(AuthService.name, () => { mocks.user.create.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse(user), - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foo' }, + {}, + loginDetails, + ), + ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.create).toHaveBeenCalledWith({ email: user.email, @@ -738,9 +772,13 @@ describe(AuthService.name, () => { mocks.user.update.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse(user), - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foo' }, + {}, + loginDetails, + ), + ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.update).toHaveBeenCalledWith(user.id, { profileImagePath: `upload/profile/${user.id}/${fileId}.jpg`, @@ -762,9 +800,13 @@ describe(AuthService.name, () => { mocks.user.update.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse(user), - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foo' }, + {}, + loginDetails, + ), + ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.update).not.toHaveBeenCalled(); expect(mocks.oauth.getProfilePicture).not.toHaveBeenCalled(); @@ -779,7 +821,11 @@ describe(AuthService.name, () => { mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.enabled); mocks.user.update.mockResolvedValue(user); - await sut.link(auth, { url: 'http://immich/user-settings?code=abc123' }); + await sut.link( + auth, + { url: 'http://immich/user-settings?code=abc123', state: 'xyz789', codeVerifier: 'foo' }, + {}, + ); expect(mocks.user.update).toHaveBeenCalledWith(auth.user.id, { oauthId: sub }); }); @@ -792,9 +838,9 @@ describe(AuthService.name, () => { mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.enabled); mocks.user.getByOAuthId.mockResolvedValue({ id: 'other-user' } as UserAdmin); - await expect(sut.link(auth, { url: 'http://immich/user-settings?code=abc123' })).rejects.toBeInstanceOf( - BadRequestException, - ); + await expect( + sut.link(auth, { url: 'http://immich/user-settings?code=abc123', state: 'xyz789', codeVerifier: 'foo' }, {}), + ).rejects.toBeInstanceOf(BadRequestException); expect(mocks.user.update).not.toHaveBeenCalled(); }); diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index ee4ca4dc5d..b250b63a5e 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -7,13 +7,11 @@ import { join } from 'node:path'; import { LOGIN_URL, MOBILE_REDIRECT, SALT_ROUNDS } from 'src/constants'; import { StorageCore } from 'src/cores/storage.core'; import { UserAdmin } from 'src/database'; -import { OnEvent } from 'src/decorators'; import { AuthDto, ChangePasswordDto, LoginCredentialDto, LogoutResponseDto, - OAuthAuthorizeResponseDto, OAuthCallbackDto, OAuthConfigDto, SignUpDto, @@ -52,11 +50,6 @@ export type ValidateRequest = { @Injectable() export class AuthService extends BaseService { - @OnEvent({ name: 'app.bootstrap' }) - onBootstrap() { - this.oauthRepository.init(); - } - async login(dto: LoginCredentialDto, details: LoginDetails) { const config = await this.getConfig({ withCache: false }); if (!config.passwordLogin.enabled) { @@ -176,20 +169,35 @@ export class AuthService extends BaseService { return `${MOBILE_REDIRECT}?${url.split('?')[1] || ''}`; } - async authorize(dto: OAuthConfigDto): Promise<OAuthAuthorizeResponseDto> { + async authorize(dto: OAuthConfigDto) { const { oauth } = await this.getConfig({ withCache: false }); if (!oauth.enabled) { throw new BadRequestException('OAuth is not enabled'); } - const url = await this.oauthRepository.authorize(oauth, this.resolveRedirectUri(oauth, dto.redirectUri)); - return { url }; + return await this.oauthRepository.authorize( + oauth, + this.resolveRedirectUri(oauth, dto.redirectUri), + dto.state, + dto.codeChallenge, + ); } - async callback(dto: OAuthCallbackDto, loginDetails: LoginDetails) { + async callback(dto: OAuthCallbackDto, headers: IncomingHttpHeaders, loginDetails: LoginDetails) { + const expectedState = dto.state ?? this.getCookieOauthState(headers); + if (!expectedState?.length) { + throw new BadRequestException('OAuth state is missing'); + } + + const codeVerifier = dto.codeVerifier ?? this.getCookieCodeVerifier(headers); + if (!codeVerifier?.length) { + throw new BadRequestException('OAuth code verifier is missing'); + } + const { oauth } = await this.getConfig({ withCache: false }); - const profile = await this.oauthRepository.getProfile(oauth, dto.url, this.resolveRedirectUri(oauth, dto.url)); + const url = this.resolveRedirectUri(oauth, dto.url); + const profile = await this.oauthRepository.getProfile(oauth, url, expectedState, codeVerifier); const { autoRegister, defaultStorageQuota, storageLabelClaim, storageQuotaClaim } = oauth; this.logger.debug(`Logging in with OAuth: ${JSON.stringify(profile)}`); let user: UserAdmin | undefined = await this.userRepository.getByOAuthId(profile.sub); @@ -271,13 +279,19 @@ export class AuthService extends BaseService { } } - async link(auth: AuthDto, dto: OAuthCallbackDto): Promise<UserAdminResponseDto> { + async link(auth: AuthDto, dto: OAuthCallbackDto, headers: IncomingHttpHeaders): Promise<UserAdminResponseDto> { + const expectedState = dto.state ?? this.getCookieOauthState(headers); + if (!expectedState?.length) { + throw new BadRequestException('OAuth state is missing'); + } + + const codeVerifier = dto.codeVerifier ?? this.getCookieCodeVerifier(headers); + if (!codeVerifier?.length) { + throw new BadRequestException('OAuth code verifier is missing'); + } + const { oauth } = await this.getConfig({ withCache: false }); - const { sub: oauthId } = await this.oauthRepository.getProfile( - oauth, - dto.url, - this.resolveRedirectUri(oauth, dto.url), - ); + const { sub: oauthId } = await this.oauthRepository.getProfile(oauth, dto.url, expectedState, codeVerifier); const duplicate = await this.userRepository.getByOAuthId(oauthId); if (duplicate && duplicate.id !== auth.user.id) { this.logger.warn(`OAuth link account failed: sub is already linked to another user (${duplicate.email}).`); @@ -320,6 +334,16 @@ export class AuthService extends BaseService { return cookies[ImmichCookie.ACCESS_TOKEN] || null; } + private getCookieOauthState(headers: IncomingHttpHeaders): string | null { + const cookies = parse(headers.cookie || ''); + return cookies[ImmichCookie.OAUTH_STATE] || null; + } + + private getCookieCodeVerifier(headers: IncomingHttpHeaders): string | null { + const cookies = parse(headers.cookie || ''); + return cookies[ImmichCookie.OAUTH_CODE_VERIFIER] || null; + } + async validateSharedLink(key: string | string[]): Promise<AuthDto> { key = Array.isArray(key) ? key[0] : key; @@ -399,11 +423,9 @@ export class AuthService extends BaseService { { mobileRedirectUri, mobileOverrideEnabled }: { mobileRedirectUri: string; mobileOverrideEnabled: boolean }, url: string, ) { - const redirectUri = url.split('?')[0]; - const isMobile = redirectUri.startsWith('app.immich:/'); - if (isMobile && mobileOverrideEnabled && mobileRedirectUri) { - return mobileRedirectUri; + if (mobileOverrideEnabled && mobileRedirectUri) { + return url.replace(/app\.immich:\/+oauth-callback/, mobileRedirectUri); } - return redirectUri; + return url; } } diff --git a/server/src/services/backup.service.spec.ts b/server/src/services/backup.service.spec.ts index 704087ab05..aa72fd588a 100644 --- a/server/src/services/backup.service.spec.ts +++ b/server/src/services/backup.service.spec.ts @@ -142,52 +142,55 @@ describe(BackupService.name, () => { mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.backupEnabled); mocks.storage.createWriteStream.mockReturnValue(new PassThrough()); }); + it('should run a database backup successfully', async () => { const result = await sut.handleBackupDatabase(); expect(result).toBe(JobStatus.SUCCESS); expect(mocks.storage.createWriteStream).toHaveBeenCalled(); }); + it('should rename file on success', async () => { const result = await sut.handleBackupDatabase(); expect(result).toBe(JobStatus.SUCCESS); expect(mocks.storage.rename).toHaveBeenCalled(); }); + it('should fail if pg_dumpall fails', async () => { mocks.process.spawn.mockReturnValueOnce(mockSpawn(1, '', 'error')); - const result = await sut.handleBackupDatabase(); - expect(result).toBe(JobStatus.FAILED); + await expect(sut.handleBackupDatabase()).rejects.toThrow('Backup failed with code 1'); }); + it('should not rename file if pgdump fails and gzip succeeds', async () => { mocks.process.spawn.mockReturnValueOnce(mockSpawn(1, '', 'error')); - const result = await sut.handleBackupDatabase(); - expect(result).toBe(JobStatus.FAILED); + await expect(sut.handleBackupDatabase()).rejects.toThrow('Backup failed with code 1'); expect(mocks.storage.rename).not.toHaveBeenCalled(); }); + it('should fail if gzip fails', async () => { mocks.process.spawn.mockReturnValueOnce(mockSpawn(0, 'data', '')); mocks.process.spawn.mockReturnValueOnce(mockSpawn(1, '', 'error')); - const result = await sut.handleBackupDatabase(); - expect(result).toBe(JobStatus.FAILED); + await expect(sut.handleBackupDatabase()).rejects.toThrow('Gzip failed with code 1'); }); + it('should fail if write stream fails', async () => { mocks.storage.createWriteStream.mockImplementation(() => { throw new Error('error'); }); - const result = await sut.handleBackupDatabase(); - expect(result).toBe(JobStatus.FAILED); + await expect(sut.handleBackupDatabase()).rejects.toThrow('error'); }); + it('should fail if rename fails', async () => { mocks.storage.rename.mockRejectedValue(new Error('error')); - const result = await sut.handleBackupDatabase(); - expect(result).toBe(JobStatus.FAILED); + await expect(sut.handleBackupDatabase()).rejects.toThrow('error'); }); + it('should ignore unlink failing and still return failed job status', async () => { mocks.process.spawn.mockReturnValueOnce(mockSpawn(1, '', 'error')); mocks.storage.unlink.mockRejectedValue(new Error('error')); - const result = await sut.handleBackupDatabase(); + await expect(sut.handleBackupDatabase()).rejects.toThrow('Backup failed with code 1'); expect(mocks.storage.unlink).toHaveBeenCalled(); - expect(result).toBe(JobStatus.FAILED); }); + it.each` postgresVersion | expectedVersion ${'14.10'} | ${14} diff --git a/server/src/services/backup.service.ts b/server/src/services/backup.service.ts index dc4f71b992..10f7becc7d 100644 --- a/server/src/services/backup.service.ts +++ b/server/src/services/backup.service.ts @@ -70,7 +70,7 @@ export class BackupService extends BaseService { async handleBackupDatabase(): Promise<JobStatus> { this.logger.debug(`Database Backup Started`); const { database } = this.configRepository.getEnv(); - const config = database.config.typeorm; + const config = database.config; const isUrlConnection = config.connectionType === 'url'; @@ -174,7 +174,7 @@ export class BackupService extends BaseService { await this.storageRepository .unlink(backupFilePath) .catch((error) => this.logger.error('Failed to delete failed backup file', error)); - return JobStatus.FAILED; + throw error; } this.logger.log(`Database Backup Success`); diff --git a/server/src/services/base.service.ts b/server/src/services/base.service.ts index 2fbdd6e4c0..3381ad7222 100644 --- a/server/src/services/base.service.ts +++ b/server/src/services/base.service.ts @@ -18,6 +18,7 @@ import { CronRepository } from 'src/repositories/cron.repository'; import { CryptoRepository } from 'src/repositories/crypto.repository'; import { DatabaseRepository } from 'src/repositories/database.repository'; import { DownloadRepository } from 'src/repositories/download.repository'; +import { EmailRepository } from 'src/repositories/email.repository'; import { EventRepository } from 'src/repositories/event.repository'; import { JobRepository } from 'src/repositories/job.repository'; import { LibraryRepository } from 'src/repositories/library.repository'; @@ -70,6 +71,7 @@ export class BaseService { protected cryptoRepository: CryptoRepository, protected databaseRepository: DatabaseRepository, protected downloadRepository: DownloadRepository, + protected emailRepository: EmailRepository, protected eventRepository: EventRepository, protected jobRepository: JobRepository, protected libraryRepository: LibraryRepository, diff --git a/server/src/services/database.service.spec.ts b/server/src/services/database.service.spec.ts index 4e45ec3ae0..e0ab4a624d 100644 --- a/server/src/services/database.service.spec.ts +++ b/server/src/services/database.service.spec.ts @@ -53,22 +53,12 @@ describe(DatabaseService.name, () => { mockEnvData({ database: { config: { - kysely: { - host: 'database', - port: 5432, - user: 'postgres', - password: 'postgres', - database: 'immich', - }, - typeorm: { - connectionType: 'parts', - type: 'postgres', - host: 'database', - port: 5432, - username: 'postgres', - password: 'postgres', - database: 'immich', - }, + connectionType: 'parts', + host: 'database', + port: 5432, + username: 'postgres', + password: 'postgres', + database: 'immich', }, skipMigrations: false, vectorExtension: extension, @@ -292,22 +282,12 @@ describe(DatabaseService.name, () => { mockEnvData({ database: { config: { - kysely: { - host: 'database', - port: 5432, - user: 'postgres', - password: 'postgres', - database: 'immich', - }, - typeorm: { - connectionType: 'parts', - type: 'postgres', - host: 'database', - port: 5432, - username: 'postgres', - password: 'postgres', - database: 'immich', - }, + connectionType: 'parts', + host: 'database', + port: 5432, + username: 'postgres', + password: 'postgres', + database: 'immich', }, skipMigrations: true, vectorExtension: DatabaseExtension.VECTORS, @@ -325,22 +305,12 @@ describe(DatabaseService.name, () => { mockEnvData({ database: { config: { - kysely: { - host: 'database', - port: 5432, - user: 'postgres', - password: 'postgres', - database: 'immich', - }, - typeorm: { - connectionType: 'parts', - type: 'postgres', - host: 'database', - port: 5432, - username: 'postgres', - password: 'postgres', - database: 'immich', - }, + connectionType: 'parts', + host: 'database', + port: 5432, + username: 'postgres', + password: 'postgres', + database: 'immich', }, skipMigrations: true, vectorExtension: DatabaseExtension.VECTOR, diff --git a/server/src/services/download.service.ts b/server/src/services/download.service.ts index cb664aea32..02711b9bfd 100644 --- a/server/src/services/download.service.ts +++ b/server/src/services/download.service.ts @@ -33,7 +33,7 @@ export class DownloadService extends BaseService { const targetSize = dto.archiveSize || HumanReadableSize.GiB * 4; const metadata = await this.userRepository.getMetadata(auth.user.id); - const preferences = getPreferences(auth.user.email, metadata); + const preferences = getPreferences(metadata); const motionIds = new Set<string>(); const archives: DownloadArchiveInfo[] = []; let archive: DownloadArchiveInfo = { size: 0, assetIds: [] }; diff --git a/server/src/services/duplicate.service.spec.ts b/server/src/services/duplicate.service.spec.ts index 57ab955514..ed8f2cf177 100644 --- a/server/src/services/duplicate.service.spec.ts +++ b/server/src/services/duplicate.service.spec.ts @@ -1,10 +1,9 @@ import { AssetFileType, AssetType, JobName, JobStatus } from 'src/enum'; -import { WithoutProperty } from 'src/repositories/asset.repository'; import { DuplicateService } from 'src/services/duplicate.service'; import { SearchService } from 'src/services/search.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; -import { newTestService, ServiceMocks } from 'test/utils'; +import { makeStream, newTestService, ServiceMocks } from 'test/utils'; import { beforeEach, vitest } from 'vitest'; vitest.useFakeTimers(); @@ -113,14 +112,11 @@ describe(SearchService.name, () => { }); it('should queue missing assets', async () => { - mocks.asset.getWithout.mockResolvedValue({ - items: [assetStub.image], - hasNextPage: false, - }); + mocks.assetJob.streamForSearchDuplicates.mockReturnValue(makeStream([assetStub.image])); await sut.handleQueueSearchDuplicates({}); - expect(mocks.asset.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.DUPLICATE); + expect(mocks.assetJob.streamForSearchDuplicates).toHaveBeenCalledWith(undefined); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.DUPLICATE_DETECTION, @@ -130,14 +126,11 @@ describe(SearchService.name, () => { }); it('should queue all assets', async () => { - mocks.asset.getAll.mockResolvedValue({ - items: [assetStub.image], - hasNextPage: false, - }); + mocks.assetJob.streamForSearchDuplicates.mockReturnValue(makeStream([assetStub.image])); await sut.handleQueueSearchDuplicates({ force: true }); - expect(mocks.asset.getAll).toHaveBeenCalled(); + expect(mocks.assetJob.streamForSearchDuplicates).toHaveBeenCalledWith(true); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.DUPLICATE_DETECTION, diff --git a/server/src/services/duplicate.service.ts b/server/src/services/duplicate.service.ts index c504b1a305..41e3f13c4d 100644 --- a/server/src/services/duplicate.service.ts +++ b/server/src/services/duplicate.service.ts @@ -5,13 +5,11 @@ import { mapAsset } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { DuplicateResponseDto } from 'src/dtos/duplicate.dto'; import { AssetFileType, JobName, JobStatus, QueueName } from 'src/enum'; -import { WithoutProperty } from 'src/repositories/asset.repository'; import { AssetDuplicateResult } from 'src/repositories/search.repository'; import { BaseService } from 'src/services/base.service'; -import { JobOf } from 'src/types'; +import { JobItem, JobOf } from 'src/types'; import { getAssetFile } from 'src/utils/asset.util'; import { isDuplicateDetectionEnabled } from 'src/utils/misc'; -import { usePagination } from 'src/utils/pagination'; @Injectable() export class DuplicateService extends BaseService { @@ -30,18 +28,22 @@ export class DuplicateService extends BaseService { return JobStatus.SKIPPED; } - const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => { - return force - ? this.assetRepository.getAll(pagination, { isVisible: true }) - : this.assetRepository.getWithout(pagination, WithoutProperty.DUPLICATE); - }); + let jobs: JobItem[] = []; + const queueAll = async () => { + await this.jobRepository.queueAll(jobs); + jobs = []; + }; - for await (const assets of assetPagination) { - await this.jobRepository.queueAll( - assets.map((asset) => ({ name: JobName.DUPLICATE_DETECTION, data: { id: asset.id } })), - ); + const assets = this.assetJobRepository.streamForSearchDuplicates(force); + for await (const asset of assets) { + jobs.push({ name: JobName.DUPLICATE_DETECTION, data: { id: asset.id } }); + if (jobs.length >= JOBS_ASSET_PAGINATION_SIZE) { + await queueAll(); + } } + await queueAll(); + return JobStatus.SUCCESS; } diff --git a/server/src/services/index.ts b/server/src/services/index.ts index b214dd14f6..88b68d2c13 100644 --- a/server/src/services/index.ts +++ b/server/src/services/index.ts @@ -17,6 +17,7 @@ import { MapService } from 'src/services/map.service'; import { MediaService } from 'src/services/media.service'; import { MemoryService } from 'src/services/memory.service'; import { MetadataService } from 'src/services/metadata.service'; +import { NotificationAdminService } from 'src/services/notification-admin.service'; import { NotificationService } from 'src/services/notification.service'; import { PartnerService } from 'src/services/partner.service'; import { PersonService } from 'src/services/person.service'; @@ -60,6 +61,7 @@ export const services = [ MemoryService, MetadataService, NotificationService, + NotificationAdminService, PartnerService, PersonService, SearchService, diff --git a/server/src/services/job.service.spec.ts b/server/src/services/job.service.spec.ts index baac0af428..9acc81ceb7 100644 --- a/server/src/services/job.service.spec.ts +++ b/server/src/services/job.service.spec.ts @@ -230,7 +230,7 @@ describe(JobService.name, () => { expect(mocks.logger.error).not.toHaveBeenCalled(); }); - const tests: Array<{ item: JobItem; jobs: JobName[] }> = [ + const tests: Array<{ item: JobItem; jobs: JobName[]; stub?: any }> = [ { item: { name: JobName.SIDECAR_SYNC, data: { id: 'asset-1' } }, jobs: [JobName.METADATA_EXTRACTION], @@ -258,14 +258,22 @@ describe(JobService.name, () => { { item: { name: JobName.GENERATE_THUMBNAILS, data: { id: 'asset-1' } }, jobs: [], + stub: [assetStub.image], + }, + { + item: { name: JobName.GENERATE_THUMBNAILS, data: { id: 'asset-1' } }, + jobs: [], + stub: [assetStub.video], + }, + { + item: { name: JobName.GENERATE_THUMBNAILS, data: { id: 'asset-1', source: 'upload' } }, + jobs: [JobName.SMART_SEARCH, JobName.FACE_DETECTION], + stub: [assetStub.livePhotoStillAsset], }, { item: { name: JobName.GENERATE_THUMBNAILS, data: { id: 'asset-1', source: 'upload' } }, jobs: [JobName.SMART_SEARCH, JobName.FACE_DETECTION, JobName.VIDEO_CONVERSION], - }, - { - item: { name: JobName.GENERATE_THUMBNAILS, data: { id: 'asset-live-image', source: 'upload' } }, - jobs: [JobName.SMART_SEARCH, JobName.FACE_DETECTION, JobName.VIDEO_CONVERSION], + stub: [assetStub.video], }, { item: { name: JobName.SMART_SEARCH, data: { id: 'asset-1' } }, @@ -281,14 +289,10 @@ describe(JobService.name, () => { }, ]; - for (const { item, jobs } of tests) { + for (const { item, jobs, stub } of tests) { it(`should queue ${jobs.length} jobs when a ${item.name} job finishes successfully`, async () => { - if (item.name === JobName.GENERATE_THUMBNAILS && item.data.source === 'upload') { - if (item.data.id === 'asset-live-image') { - mocks.asset.getByIdsWithAllRelationsButStacks.mockResolvedValue([assetStub.livePhotoStillAsset as any]); - } else { - mocks.asset.getByIdsWithAllRelationsButStacks.mockResolvedValue([assetStub.livePhotoMotionAsset as any]); - } + if (stub) { + mocks.asset.getByIdsWithAllRelationsButStacks.mockResolvedValue(stub); } mocks.job.run.mockResolvedValue(JobStatus.SUCCESS); diff --git a/server/src/services/job.service.ts b/server/src/services/job.service.ts index edd018d7b1..a387e6e099 100644 --- a/server/src/services/job.service.ts +++ b/server/src/services/job.service.ts @@ -1,10 +1,12 @@ import { BadRequestException, Injectable } from '@nestjs/common'; +import { ClassConstructor } from 'class-transformer'; import { snakeCase } from 'lodash'; import { OnEvent } from 'src/decorators'; import { mapAsset } from 'src/dtos/asset-response.dto'; import { AllJobStatusResponseDto, JobCommandDto, JobCreateDto, JobStatusDto } from 'src/dtos/job.dto'; import { AssetType, + BootstrapEventPriority, ImmichWorker, JobCommand, JobName, @@ -51,6 +53,8 @@ const asJobItem = (dto: JobCreateDto): JobItem => { @Injectable() export class JobService extends BaseService { + private services: ClassConstructor<unknown>[] = []; + @OnEvent({ name: 'config.init', workers: [ImmichWorker.MICROSERVICES] }) onConfigInit({ newConfig: config }: ArgOf<'config.init'>) { this.logger.debug(`Updating queue concurrency settings`); @@ -69,6 +73,18 @@ export class JobService extends BaseService { this.onConfigInit({ newConfig: config }); } + @OnEvent({ name: 'app.bootstrap', priority: BootstrapEventPriority.JobService }) + onBootstrap() { + this.jobRepository.setup(this.services); + if (this.worker === ImmichWorker.MICROSERVICES) { + this.jobRepository.startWorkers(); + } + } + + setServices(services: ClassConstructor<unknown>[]) { + this.services = services; + } + async create(dto: JobCreateDto): Promise<void> { await this.jobRepository.queue(asJobItem(dto)); } @@ -199,11 +215,7 @@ export class JobService extends BaseService { await this.onDone(job); } } catch (error: Error | any) { - this.logger.error( - `Unable to run job handler (${queueName}/${job.name}): ${error}`, - error?.stack, - JSON.stringify(job.data), - ); + await this.eventRepository.emit('job.failed', { job, error }); } finally { this.telemetryRepository.jobs.addToGauge(queueMetric, -1); } @@ -297,8 +309,6 @@ export class JobService extends BaseService { if (asset.type === AssetType.VIDEO) { jobs.push({ name: JobName.VIDEO_CONVERSION, data: item.data }); - } else if (asset.livePhotoVideoId) { - jobs.push({ name: JobName.VIDEO_CONVERSION, data: { id: asset.livePhotoVideoId } }); } await this.jobRepository.queueAll(jobs); diff --git a/server/src/services/library.service.spec.ts b/server/src/services/library.service.spec.ts index 15b150f551..6b0817dd3b 100644 --- a/server/src/services/library.service.spec.ts +++ b/server/src/services/library.service.spec.ts @@ -273,7 +273,6 @@ describe(LibraryService.name, () => { mocks.library.get.mockResolvedValue(library); mocks.storage.walk.mockImplementation(async function* generator() {}); - mocks.asset.getAll.mockResolvedValue({ items: [assetStub.external], hasNextPage: false }); mocks.asset.getLibraryAssetCount.mockResolvedValue(1); mocks.asset.detectOfflineExternalAssets.mockResolvedValue({ numUpdatedRows: BigInt(1) }); @@ -292,7 +291,6 @@ describe(LibraryService.name, () => { mocks.library.get.mockResolvedValue(library); mocks.storage.walk.mockImplementation(async function* generator() {}); - mocks.asset.getAll.mockResolvedValue({ items: [assetStub.external], hasNextPage: false }); mocks.asset.getLibraryAssetCount.mockResolvedValue(0); mocks.asset.detectOfflineExternalAssets.mockResolvedValue({ numUpdatedRows: BigInt(1) }); diff --git a/server/src/services/media.service.spec.ts b/server/src/services/media.service.spec.ts index 8990ad86a6..d747ee0ba8 100644 --- a/server/src/services/media.service.spec.ts +++ b/server/src/services/media.service.spec.ts @@ -1,7 +1,6 @@ import { OutputInfo } from 'sharp'; import { SystemConfig } from 'src/config'; import { Exif } from 'src/database'; -import { AssetMediaSize } from 'src/dtos/asset-media.dto'; import { AssetFileType, AssetPathType, @@ -11,11 +10,11 @@ import { ImageFormat, JobName, JobStatus, + RawExtractedFormat, TranscodeHWAccel, TranscodePolicy, VideoCodec, } from 'src/enum'; -import { WithoutProperty } from 'src/repositories/asset.repository'; import { MediaService } from 'src/services/media.service'; import { JobCounts, RawImageInfo } from 'src/types'; import { assetStub } from 'test/fixtures/asset.stub'; @@ -39,10 +38,6 @@ describe(MediaService.name, () => { describe('handleQueueGenerateThumbnails', () => { it('should queue all assets', async () => { mocks.assetJob.streamForThumbnailJob.mockReturnValue(makeStream([assetStub.image])); - mocks.asset.getAll.mockResolvedValue({ - items: [assetStub.image], - hasNextPage: false, - }); mocks.person.getAll.mockReturnValue(makeStream([personStub.newThumbnail])); mocks.person.getFacesByIds.mockResolvedValue([faceStub.face1]); @@ -68,10 +63,6 @@ describe(MediaService.name, () => { it('should queue trashed assets when force is true', async () => { mocks.assetJob.streamForThumbnailJob.mockReturnValue(makeStream([assetStub.archived])); - mocks.asset.getAll.mockResolvedValue({ - items: [assetStub.trashed], - hasNextPage: false, - }); mocks.person.getAll.mockReturnValue(makeStream()); await sut.handleQueueGenerateThumbnails({ force: true }); @@ -172,7 +163,7 @@ describe(MediaService.name, () => { describe('handleQueueMigration', () => { it('should remove empty directories and queue jobs', async () => { - mocks.asset.getAll.mockResolvedValue({ hasNextPage: false, items: [assetStub.image] }); + mocks.assetJob.streamForMigrationJob.mockReturnValue(makeStream([assetStub.image])); mocks.job.getJobCounts.mockResolvedValue({ active: 1, waiting: 0 } as JobCounts); mocks.person.getAll.mockReturnValue(makeStream([personStub.withName])); @@ -232,17 +223,19 @@ describe(MediaService.name, () => { describe('handleGenerateThumbnails', () => { let rawBuffer: Buffer; let fullsizeBuffer: Buffer; + let extractedBuffer: Buffer; let rawInfo: RawImageInfo; beforeEach(() => { fullsizeBuffer = Buffer.from('embedded image data'); - rawBuffer = Buffer.from('image data'); + rawBuffer = Buffer.from('raw image data'); + extractedBuffer = Buffer.from('embedded image file'); rawInfo = { width: 100, height: 100, channels: 3 }; - mocks.media.decodeImage.mockImplementation((path) => + mocks.media.decodeImage.mockImplementation((input) => Promise.resolve( - path.includes(AssetMediaSize.FULLSIZE) - ? { data: fullsizeBuffer, info: rawInfo as OutputInfo } - : { data: rawBuffer, info: rawInfo as OutputInfo }, + typeof input === 'string' + ? { data: rawBuffer, info: rawInfo as OutputInfo } // string implies original file + : { data: fullsizeBuffer, info: rawInfo as OutputInfo }, // buffer implies embedded image extracted ), ); }); @@ -585,16 +578,15 @@ describe(MediaService.name, () => { }); it('should extract embedded image if enabled and available', async () => { - mocks.media.extract.mockResolvedValue(true); + mocks.media.extract.mockResolvedValue({ buffer: extractedBuffer, format: RawExtractedFormat.JPEG }); mocks.media.getImageDimensions.mockResolvedValue({ width: 3840, height: 2160 }); mocks.systemMetadata.get.mockResolvedValue({ image: { extractEmbedded: true } }); mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.imageDng); await sut.handleGenerateThumbnails({ id: assetStub.image.id }); - const convertedPath = mocks.media.extract.mock.lastCall?.[1].toString(); expect(mocks.media.decodeImage).toHaveBeenCalledOnce(); - expect(mocks.media.decodeImage).toHaveBeenCalledWith(convertedPath, { + expect(mocks.media.decodeImage).toHaveBeenCalledWith(extractedBuffer, { colorspace: Colorspace.P3, processInvalidImages: false, size: 1440, @@ -602,16 +594,13 @@ describe(MediaService.name, () => { }); it('should resize original image if embedded image is too small', async () => { - mocks.media.extract.mockResolvedValue(true); + mocks.media.extract.mockResolvedValue({ buffer: extractedBuffer, format: RawExtractedFormat.JPEG }); mocks.media.getImageDimensions.mockResolvedValue({ width: 1000, height: 1000 }); mocks.systemMetadata.get.mockResolvedValue({ image: { extractEmbedded: true } }); mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.imageDng); await sut.handleGenerateThumbnails({ id: assetStub.image.id }); - const extractedPath = mocks.media.extract.mock.lastCall?.[1].toString(); - expect(extractedPath).toMatch(/-fullsize\.jpeg$/); - expect(mocks.media.decodeImage).toHaveBeenCalledWith(assetStub.imageDng.originalPath, { colorspace: Colorspace.P3, processInvalidImages: false, @@ -666,38 +655,40 @@ describe(MediaService.name, () => { expect(mocks.media.generateThumbnail).toHaveBeenCalledTimes(2); expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( rawBuffer, - expect.objectContaining({ processInvalidImages: true }), + expect.objectContaining({ processInvalidImages: false }), 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', ); expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( rawBuffer, - expect.objectContaining({ processInvalidImages: true }), + expect.objectContaining({ processInvalidImages: false }), 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp', ); expect(mocks.media.generateThumbhash).toHaveBeenCalledOnce(); expect(mocks.media.generateThumbhash).toHaveBeenCalledWith( rawBuffer, - expect.objectContaining({ processInvalidImages: true }), + expect.objectContaining({ processInvalidImages: false }), ); expect(mocks.media.getImageDimensions).not.toHaveBeenCalled(); vi.unstubAllEnvs(); }); - it('should generate full-size preview using embedded JPEG from RAW images when extractEmbedded is true', async () => { - mocks.systemMetadata.get.mockResolvedValue({ image: { fullsize: { enabled: true }, extractEmbedded: true } }); - mocks.media.extract.mockResolvedValue(true); + it('should extract full-size JPEG preview from RAW', async () => { + mocks.systemMetadata.get.mockResolvedValue({ + image: { fullsize: { enabled: true, format: ImageFormat.WEBP }, extractEmbedded: true }, + }); + mocks.media.extract.mockResolvedValue({ buffer: extractedBuffer, format: RawExtractedFormat.JPEG }); mocks.media.getImageDimensions.mockResolvedValue({ width: 3840, height: 2160 }); mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.imageDng); await sut.handleGenerateThumbnails({ id: assetStub.image.id }); - const extractedPath = mocks.media.extract.mock.lastCall?.[1].toString(); expect(mocks.media.decodeImage).toHaveBeenCalledOnce(); - expect(mocks.media.decodeImage).toHaveBeenCalledWith(extractedPath, { + expect(mocks.media.decodeImage).toHaveBeenCalledWith(extractedBuffer, { colorspace: Colorspace.P3, processInvalidImages: false, + size: 1440, // capped to preview size as fullsize conversion is skipped }); expect(mocks.media.generateThumbnail).toHaveBeenCalledTimes(2); @@ -715,9 +706,51 @@ describe(MediaService.name, () => { ); }); + it('should convert full-size WEBP preview from JXL preview of RAW', async () => { + mocks.systemMetadata.get.mockResolvedValue({ + image: { fullsize: { enabled: true, format: ImageFormat.WEBP }, extractEmbedded: true }, + }); + mocks.media.extract.mockResolvedValue({ buffer: extractedBuffer, format: RawExtractedFormat.JXL }); + mocks.media.getImageDimensions.mockResolvedValue({ width: 3840, height: 2160 }); + mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.imageDng); + + await sut.handleGenerateThumbnails({ id: assetStub.image.id }); + + expect(mocks.media.decodeImage).toHaveBeenCalledOnce(); + expect(mocks.media.decodeImage).toHaveBeenCalledWith(extractedBuffer, { + colorspace: Colorspace.P3, + processInvalidImages: false, + }); + + expect(mocks.media.generateThumbnail).toHaveBeenCalledTimes(3); + expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( + fullsizeBuffer, + { + colorspace: Colorspace.P3, + format: ImageFormat.WEBP, + quality: 80, + processInvalidImages: false, + raw: rawInfo, + }, + 'upload/thumbs/user-id/as/se/asset-id-fullsize.webp', + ); + expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( + fullsizeBuffer, + { + colorspace: Colorspace.P3, + format: ImageFormat.JPEG, + size: 1440, + quality: 80, + processInvalidImages: false, + raw: rawInfo, + }, + 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + ); + }); + it('should generate full-size preview directly from RAW images when extractEmbedded is false', async () => { mocks.systemMetadata.get.mockResolvedValue({ image: { fullsize: { enabled: true }, extractEmbedded: false } }); - mocks.media.extract.mockResolvedValue(true); + mocks.media.extract.mockResolvedValue({ buffer: extractedBuffer, format: RawExtractedFormat.JPEG }); mocks.media.getImageDimensions.mockResolvedValue({ width: 3840, height: 2160 }); mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.imageDng); @@ -757,7 +790,7 @@ describe(MediaService.name, () => { it('should generate full-size preview from non-web-friendly images', async () => { mocks.systemMetadata.get.mockResolvedValue({ image: { fullsize: { enabled: true } } }); - mocks.media.extract.mockResolvedValue(true); + mocks.media.extract.mockResolvedValue({ buffer: extractedBuffer, format: RawExtractedFormat.JPEG }); mocks.media.getImageDimensions.mockResolvedValue({ width: 3840, height: 2160 }); // HEIF/HIF image taken by cameras are not web-friendly, only has limited support on Safari. mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.imageHif); @@ -786,7 +819,7 @@ describe(MediaService.name, () => { it('should skip generating full-size preview for web-friendly images', async () => { mocks.systemMetadata.get.mockResolvedValue({ image: { fullsize: { enabled: true } } }); - mocks.media.extract.mockResolvedValue(true); + mocks.media.extract.mockResolvedValue({ buffer: extractedBuffer, format: RawExtractedFormat.JPEG }); mocks.media.getImageDimensions.mockResolvedValue({ width: 3840, height: 2160 }); mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.image); @@ -811,7 +844,7 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ image: { fullsize: { enabled: true, format: ImageFormat.WEBP, quality: 90 } }, }); - mocks.media.extract.mockResolvedValue(true); + mocks.media.extract.mockResolvedValue({ buffer: extractedBuffer, format: RawExtractedFormat.JPEG }); mocks.media.getImageDimensions.mockResolvedValue({ width: 3840, height: 2160 }); // HEIF/HIF image taken by cameras are not web-friendly, only has limited support on Safari. mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.imageHif); @@ -841,16 +874,12 @@ describe(MediaService.name, () => { describe('handleQueueVideoConversion', () => { it('should queue all video assets', async () => { - mocks.asset.getAll.mockResolvedValue({ - items: [assetStub.video], - hasNextPage: false, - }); + mocks.assetJob.streamForVideoConversion.mockReturnValue(makeStream([assetStub.video])); mocks.person.getAll.mockReturnValue(makeStream()); await sut.handleQueueVideoConversion({ force: true }); - expect(mocks.asset.getAll).toHaveBeenCalledWith({ skip: 0, take: 1000 }, { type: AssetType.VIDEO }); - expect(mocks.asset.getWithout).not.toHaveBeenCalled(); + expect(mocks.assetJob.streamForVideoConversion).toHaveBeenCalledWith(true); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.VIDEO_CONVERSION, @@ -860,15 +889,11 @@ describe(MediaService.name, () => { }); it('should queue all video assets without encoded videos', async () => { - mocks.asset.getWithout.mockResolvedValue({ - items: [assetStub.video], - hasNextPage: false, - }); + mocks.assetJob.streamForVideoConversion.mockReturnValue(makeStream([assetStub.video])); await sut.handleQueueVideoConversion({}); - expect(mocks.asset.getAll).not.toHaveBeenCalled(); - expect(mocks.asset.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.ENCODED_VIDEO); + expect(mocks.assetJob.streamForVideoConversion).toHaveBeenCalledWith(void 0); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.VIDEO_CONVERSION, @@ -880,26 +905,18 @@ describe(MediaService.name, () => { describe('handleVideoConversion', () => { beforeEach(() => { - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); + mocks.assetJob.getForVideoConversion.mockResolvedValue(assetStub.video); sut.videoInterfaces = { dri: ['renderD128'], mali: true }; }); it('should skip transcoding if asset not found', async () => { - mocks.asset.getByIds.mockResolvedValue([]); + mocks.assetJob.getForVideoConversion.mockResolvedValue(void 0); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.probe).not.toHaveBeenCalled(); expect(mocks.media.transcode).not.toHaveBeenCalled(); }); - it('should skip transcoding if non-video asset', async () => { - mocks.asset.getByIds.mockResolvedValue([assetStub.image]); - await sut.handleVideoConversion({ id: assetStub.image.id }); - expect(mocks.media.probe).not.toHaveBeenCalled(); - expect(mocks.media.transcode).not.toHaveBeenCalled(); - }); - it('should transcode the longest stream', async () => { - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); mocks.logger.isLevelEnabled.mockReturnValue(false); mocks.media.probe.mockResolvedValue(probeStub.multipleVideoStreams); @@ -921,14 +938,12 @@ describe(MediaService.name, () => { it('should skip a video without any streams', async () => { mocks.media.probe.mockResolvedValue(probeStub.noVideoStreams); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).not.toHaveBeenCalled(); }); it('should skip a video without any height', async () => { mocks.media.probe.mockResolvedValue(probeStub.noHeight); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).not.toHaveBeenCalled(); }); @@ -936,7 +951,6 @@ describe(MediaService.name, () => { it('should throw an error if an unknown transcode policy is configured', async () => { mocks.media.probe.mockResolvedValue(probeStub.noAudioStreams); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: 'foo' } } as never as SystemConfig); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await expect(sut.handleVideoConversion({ id: assetStub.video.id })).rejects.toThrowError(); expect(mocks.media.transcode).not.toHaveBeenCalled(); @@ -947,7 +961,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.ALL, accel: TranscodeHWAccel.DISABLED }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); mocks.media.transcode.mockRejectedValue(new Error('Error transcoding video')); await expect(sut.handleVideoConversion({ id: assetStub.video.id })).resolves.toBe(JobStatus.FAILED); @@ -957,7 +970,6 @@ describe(MediaService.name, () => { it('should transcode when set to all', async () => { mocks.media.probe.mockResolvedValue(probeStub.multipleVideoStreams); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.ALL } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1035,7 +1047,6 @@ describe(MediaService.name, () => { it('should scale horizontally when video is horizontal', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStream2160p); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.OPTIMAL } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1051,7 +1062,6 @@ describe(MediaService.name, () => { it('should scale vertically when video is vertical', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamVertical2160p); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.OPTIMAL } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1069,7 +1079,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.ALL, targetResolution: 'original' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1087,7 +1096,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.ALL, targetResolution: 'original' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1105,7 +1113,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { targetVideoCodec: VideoCodec.HEVC, acceptedAudioCodecs: [AudioCodec.AAC] }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1127,7 +1134,6 @@ describe(MediaService.name, () => { acceptedAudioCodecs: [AudioCodec.AAC], }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1149,7 +1155,6 @@ describe(MediaService.name, () => { acceptedAudioCodecs: [AudioCodec.AAC], }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1165,7 +1170,6 @@ describe(MediaService.name, () => { it('should copy audio stream when audio matches target', async () => { mocks.media.probe.mockResolvedValue(probeStub.audioStreamAac); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.OPTIMAL } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1180,7 +1184,6 @@ describe(MediaService.name, () => { it('should remux when input is not an accepted container', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamAvi); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1204,7 +1207,6 @@ describe(MediaService.name, () => { it('should not transcode if transcoding is disabled', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStream2160p); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.DISABLED } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).not.toHaveBeenCalled(); }); @@ -1212,7 +1214,6 @@ describe(MediaService.name, () => { it('should not remux when input is not an accepted container and transcoding is disabled', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.DISABLED } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).not.toHaveBeenCalled(); }); @@ -1220,7 +1221,6 @@ describe(MediaService.name, () => { it('should not transcode if target codec is invalid', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStream2160p); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { targetVideoCodec: 'invalid' as any } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).not.toHaveBeenCalled(); }); @@ -1229,7 +1229,7 @@ describe(MediaService.name, () => { const asset = assetStub.hasEncodedVideo; mocks.media.probe.mockResolvedValue(probeStub.videoStream2160p); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.DISABLED } }); - mocks.asset.getByIds.mockResolvedValue([asset]); + mocks.assetJob.getForVideoConversion.mockResolvedValue(asset); await sut.handleVideoConversion({ id: asset.id }); @@ -1243,7 +1243,6 @@ describe(MediaService.name, () => { it('should set max bitrate if above 0', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { maxBitrate: '4500k' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1259,7 +1258,6 @@ describe(MediaService.name, () => { it('should default max bitrate to kbps if no unit is provided', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { maxBitrate: '4500' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1275,7 +1273,6 @@ describe(MediaService.name, () => { it('should transcode in two passes for h264/h265 when enabled and max bitrate is above 0', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { twoPass: true, maxBitrate: '4500k' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1291,7 +1288,6 @@ describe(MediaService.name, () => { it('should fallback to one pass for h264/h265 if two-pass is enabled but no max bitrate is set', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { twoPass: true } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1313,7 +1309,6 @@ describe(MediaService.name, () => { targetVideoCodec: VideoCodec.VP9, }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1335,7 +1330,6 @@ describe(MediaService.name, () => { targetVideoCodec: VideoCodec.VP9, }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1351,7 +1345,6 @@ describe(MediaService.name, () => { it('should configure preset for vp9', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { targetVideoCodec: VideoCodec.VP9, preset: 'slow' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1367,7 +1360,6 @@ describe(MediaService.name, () => { it('should not configure preset for vp9 if invalid', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { preset: 'invalid', targetVideoCodec: VideoCodec.VP9 } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1383,7 +1375,6 @@ describe(MediaService.name, () => { it('should configure threads if above 0', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { targetVideoCodec: VideoCodec.VP9, threads: 2 } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1399,7 +1390,6 @@ describe(MediaService.name, () => { it('should disable thread pooling for h264 if thread limit is 1', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { threads: 1 } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1415,7 +1405,6 @@ describe(MediaService.name, () => { it('should omit thread flags for h264 if thread limit is at or below 0', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { threads: 0 } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1431,7 +1420,6 @@ describe(MediaService.name, () => { it('should disable thread pooling for hevc if thread limit is 1', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamVp9); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { threads: 1, targetVideoCodec: VideoCodec.HEVC } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1447,7 +1435,6 @@ describe(MediaService.name, () => { it('should omit thread flags for hevc if thread limit is at or below 0', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamVp9); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { threads: 0, targetVideoCodec: VideoCodec.HEVC } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1463,7 +1450,6 @@ describe(MediaService.name, () => { it('should use av1 if specified', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamVp9); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { targetVideoCodec: VideoCodec.AV1 } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1489,7 +1475,6 @@ describe(MediaService.name, () => { it('should map `veryslow` preset to 4 for av1', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamVp9); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { targetVideoCodec: VideoCodec.AV1, preset: 'veryslow' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1505,7 +1490,6 @@ describe(MediaService.name, () => { it('should set max bitrate for av1 if specified', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamVp9); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { targetVideoCodec: VideoCodec.AV1, maxBitrate: '2M' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1521,7 +1505,6 @@ describe(MediaService.name, () => { it('should set threads for av1 if specified', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamVp9); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { targetVideoCodec: VideoCodec.AV1, threads: 4 } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1539,7 +1522,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { targetVideoCodec: VideoCodec.AV1, threads: 4, maxBitrate: '2M' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1561,7 +1543,6 @@ describe(MediaService.name, () => { targetResolution: '1080p', }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).not.toHaveBeenCalled(); }); @@ -1571,7 +1552,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.NVENC, targetVideoCodec: VideoCodec.VP9 }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await expect(sut.handleVideoConversion({ id: assetStub.video.id })).rejects.toThrowError(); expect(mocks.media.transcode).not.toHaveBeenCalled(); }); @@ -1579,7 +1559,6 @@ describe(MediaService.name, () => { it('should fail if hwaccel option is invalid', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: 'invalid' as any } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await expect(sut.handleVideoConversion({ id: assetStub.video.id })).rejects.toThrowError(); expect(mocks.media.transcode).not.toHaveBeenCalled(); }); @@ -1587,7 +1566,6 @@ describe(MediaService.name, () => { it('should set options for nvenc', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.NVENC } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1625,7 +1603,6 @@ describe(MediaService.name, () => { twoPass: true, }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1641,7 +1618,6 @@ describe(MediaService.name, () => { it('should set vbr options for nvenc when max bitrate is enabled', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.NVENC, maxBitrate: '10000k' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1657,7 +1633,6 @@ describe(MediaService.name, () => { it('should set cq options for nvenc when max bitrate is disabled', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.NVENC, maxBitrate: '10000k' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1673,7 +1648,6 @@ describe(MediaService.name, () => { it('should omit preset for nvenc if invalid', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.NVENC, preset: 'invalid' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1689,7 +1663,6 @@ describe(MediaService.name, () => { it('should ignore two pass for nvenc if max bitrate is disabled', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.NVENC } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1707,7 +1680,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.NVENC, accelDecode: true }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1730,7 +1702,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.NVENC, accelDecode: true }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1752,7 +1723,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.NVENC, accelDecode: true }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1768,7 +1738,6 @@ describe(MediaService.name, () => { it('should set options for qsv', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.QSV, maxBitrate: '10000k' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1809,7 +1778,6 @@ describe(MediaService.name, () => { preferredHwDevice: '/dev/dri/renderD128', }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1828,7 +1796,6 @@ describe(MediaService.name, () => { it('should omit preset for qsv if invalid', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.QSV, preset: 'invalid' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1849,7 +1816,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.QSV, targetVideoCodec: VideoCodec.VP9 }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1869,7 +1835,6 @@ describe(MediaService.name, () => { sut.videoInterfaces = { dri: [], mali: false }; mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.QSV } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await expect(sut.handleVideoConversion({ id: assetStub.video.id })).rejects.toThrowError(); @@ -1880,7 +1845,6 @@ describe(MediaService.name, () => { sut.videoInterfaces = { dri: ['card1', 'renderD129', 'card0', 'renderD128'], mali: false }; mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.QSV } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1901,7 +1865,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.QSV, accelDecode: true }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); @@ -1928,7 +1891,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.QSV, accelDecode: true }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); @@ -1958,7 +1920,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.QSV, accelDecode: true, preferredHwDevice: 'renderD129' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( @@ -1977,7 +1938,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.QSV, accelDecode: true }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); @@ -2000,7 +1960,6 @@ describe(MediaService.name, () => { it('should set options for vaapi', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2031,7 +1990,6 @@ describe(MediaService.name, () => { it('should set vbr options for vaapi when max bitrate is enabled', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI, maxBitrate: '10000k' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2056,7 +2014,6 @@ describe(MediaService.name, () => { it('should set cq options for vaapi when max bitrate is disabled', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2081,7 +2038,6 @@ describe(MediaService.name, () => { it('should omit preset for vaapi if invalid', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI, preset: 'invalid' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2101,7 +2057,6 @@ describe(MediaService.name, () => { sut.videoInterfaces = { dri: ['card1', 'renderD129', 'card0', 'renderD128'], mali: false }; mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2123,7 +2078,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI, preferredHwDevice: '/dev/dri/renderD128' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2144,7 +2098,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI, accelDecode: true }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); @@ -2170,7 +2123,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI, accelDecode: true }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); @@ -2194,7 +2146,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI, accelDecode: true }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); @@ -2215,7 +2166,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI, accelDecode: true, preferredHwDevice: 'renderD129' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( @@ -2232,7 +2182,6 @@ describe(MediaService.name, () => { it('should fallback to hw encoding and sw decoding if hw transcoding fails and hw decoding is enabled', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI, accelDecode: true } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); mocks.media.transcode.mockRejectedValueOnce(new Error('error')); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledTimes(2); @@ -2253,7 +2202,6 @@ describe(MediaService.name, () => { it('should fallback to sw decoding if fallback to sw decoding + hw encoding fails', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI, accelDecode: true } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); mocks.media.transcode.mockRejectedValueOnce(new Error('error')); mocks.media.transcode.mockRejectedValueOnce(new Error('error')); await sut.handleVideoConversion({ id: assetStub.video.id }); @@ -2272,7 +2220,6 @@ describe(MediaService.name, () => { it('should fallback to sw transcoding if hw transcoding fails and hw decoding is disabled', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); mocks.media.transcode.mockRejectedValueOnce(new Error('error')); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledTimes(2); @@ -2291,7 +2238,6 @@ describe(MediaService.name, () => { sut.videoInterfaces = { dri: [], mali: true }; mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await expect(sut.handleVideoConversion({ id: assetStub.video.id })).rejects.toThrowError(); expect(mocks.media.transcode).not.toHaveBeenCalled(); }); @@ -2299,7 +2245,6 @@ describe(MediaService.name, () => { it('should set options for rkmpp', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.RKMPP, accelDecode: true } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2340,7 +2285,6 @@ describe(MediaService.name, () => { targetVideoCodec: VideoCodec.HEVC, }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2358,7 +2302,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.RKMPP, accelDecode: true, crf: 30, maxBitrate: '0' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2376,7 +2319,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.RKMPP, accelDecode: true, crf: 30, maxBitrate: '0' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2399,7 +2341,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.RKMPP, accelDecode: true, crf: 30, maxBitrate: '0' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2419,7 +2360,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.RKMPP, accelDecode: false, crf: 30, maxBitrate: '0' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2442,7 +2382,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.RKMPP, accelDecode: true, crf: 30, maxBitrate: '0' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2462,7 +2401,6 @@ describe(MediaService.name, () => { it('should tonemap when policy is required and video is hdr', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamHDR); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.REQUIRED } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2482,7 +2420,6 @@ describe(MediaService.name, () => { it('should tonemap when policy is optimal and video is hdr', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamHDR); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.OPTIMAL } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2502,7 +2439,6 @@ describe(MediaService.name, () => { it('should transcode when policy is required and video is not yuv420p', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStream10Bit); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.REQUIRED } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2518,7 +2454,6 @@ describe(MediaService.name, () => { it('should convert to yuv420p when scaling without tone-mapping', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStream4K10Bit); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.REQUIRED } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2534,7 +2469,6 @@ describe(MediaService.name, () => { it('should count frames for progress when log level is debug', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.logger.isLevelEnabled.mockReturnValue(true); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); @@ -2557,7 +2491,6 @@ describe(MediaService.name, () => { it('should not count frames for progress when log level is not debug', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStream2160p); mocks.logger.isLevelEnabled.mockReturnValue(false); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.probe).toHaveBeenCalledWith(assetStub.video.originalPath, { countFrames: false }); @@ -2582,48 +2515,39 @@ describe(MediaService.name, () => { describe('isSRGB', () => { it('should return true for srgb colorspace', () => { - const asset = { ...assetStub.image, exifInfo: { colorspace: 'sRGB' } as Exif }; - expect(sut.isSRGB(asset)).toEqual(true); + expect(sut.isSRGB({ colorspace: 'sRGB' } as Exif)).toEqual(true); }); it('should return true for srgb profile description', () => { - const asset = { ...assetStub.image, exifInfo: { profileDescription: 'sRGB v1.31' } as Exif }; - expect(sut.isSRGB(asset)).toEqual(true); + expect(sut.isSRGB({ profileDescription: 'sRGB v1.31' } as Exif)).toEqual(true); }); it('should return true for 8-bit image with no colorspace metadata', () => { - const asset = { ...assetStub.image, exifInfo: { bitsPerSample: 8 } as Exif }; - expect(sut.isSRGB(asset)).toEqual(true); + expect(sut.isSRGB({ bitsPerSample: 8 } as Exif)).toEqual(true); }); it('should return true for image with no colorspace or bit depth metadata', () => { - const asset = { ...assetStub.image, exifInfo: {} as Exif }; - expect(sut.isSRGB(asset)).toEqual(true); + expect(sut.isSRGB({} as Exif)).toEqual(true); }); it('should return false for non-srgb colorspace', () => { - const asset = { ...assetStub.image, exifInfo: { colorspace: 'Adobe RGB' } as Exif }; - expect(sut.isSRGB(asset)).toEqual(false); + expect(sut.isSRGB({ colorspace: 'Adobe RGB' } as Exif)).toEqual(false); }); it('should return false for non-srgb profile description', () => { - const asset = { ...assetStub.image, exifInfo: { profileDescription: 'sP3C' } as Exif }; - expect(sut.isSRGB(asset)).toEqual(false); + expect(sut.isSRGB({ profileDescription: 'sP3C' } as Exif)).toEqual(false); }); it('should return false for 16-bit image with no colorspace metadata', () => { - const asset = { ...assetStub.image, exifInfo: { bitsPerSample: 16 } as Exif }; - expect(sut.isSRGB(asset)).toEqual(false); + expect(sut.isSRGB({ bitsPerSample: 16 } as Exif)).toEqual(false); }); it('should return true for 16-bit image with sRGB colorspace', () => { - const asset = { ...assetStub.image, exifInfo: { colorspace: 'sRGB', bitsPerSample: 16 } as Exif }; - expect(sut.isSRGB(asset)).toEqual(true); + expect(sut.isSRGB({ colorspace: 'sRGB', bitsPerSample: 16 } as Exif)).toEqual(true); }); it('should return true for 16-bit image with sRGB profile', () => { - const asset = { ...assetStub.image, exifInfo: { profileDescription: 'sRGB', bitsPerSample: 16 } as Exif }; - expect(sut.isSRGB(asset)).toEqual(true); + expect(sut.isSRGB({ profileDescription: 'sRGB', bitsPerSample: 16 } as Exif)).toEqual(true); }); }); }); diff --git a/server/src/services/media.service.ts b/server/src/services/media.service.ts index 59d708772b..35d28a175b 100644 --- a/server/src/services/media.service.ts +++ b/server/src/services/media.service.ts @@ -10,11 +10,11 @@ import { AssetType, AudioCodec, Colorspace, - ImageFormat, JobName, JobStatus, LogLevel, QueueName, + RawExtractedFormat, StorageFolder, TranscodeHWAccel, TranscodePolicy, @@ -22,12 +22,11 @@ import { VideoCodec, VideoContainer, } from 'src/enum'; -import { UpsertFileOptions, WithoutProperty } from 'src/repositories/asset.repository'; +import { UpsertFileOptions } from 'src/repositories/asset.repository'; import { BaseService } from 'src/services/base.service'; import { AudioStreamInfo, DecodeToBufferOptions, - GenerateThumbnailOptions, JobItem, JobOf, VideoFormat, @@ -37,7 +36,6 @@ import { import { getAssetFiles } from 'src/utils/asset.util'; import { BaseConfig, ThumbnailConfig } from 'src/utils/media'; import { mimeTypes } from 'src/utils/mime-types'; -import { usePagination } from 'src/utils/pagination'; @Injectable() export class MediaService extends BaseService { @@ -51,18 +49,26 @@ export class MediaService extends BaseService { @OnJob({ name: JobName.QUEUE_GENERATE_THUMBNAILS, queue: QueueName.THUMBNAIL_GENERATION }) async handleQueueGenerateThumbnails({ force }: JobOf<JobName.QUEUE_GENERATE_THUMBNAILS>): Promise<JobStatus> { - const thumbJobs: JobItem[] = []; + let jobs: JobItem[] = []; + + const queueAll = async () => { + await this.jobRepository.queueAll(jobs); + jobs = []; + }; + for await (const asset of this.assetJobRepository.streamForThumbnailJob(!!force)) { const { previewFile, thumbnailFile } = getAssetFiles(asset.files); if (!previewFile || !thumbnailFile || !asset.thumbhash || force) { - thumbJobs.push({ name: JobName.GENERATE_THUMBNAILS, data: { id: asset.id } }); - continue; + jobs.push({ name: JobName.GENERATE_THUMBNAILS, data: { id: asset.id } }); + } + + if (jobs.length >= JOBS_ASSET_PAGINATION_SIZE) { + await queueAll(); } } - await this.jobRepository.queueAll(thumbJobs); - const jobs: JobItem[] = []; + await queueAll(); const people = this.personRepository.getAll(force ? undefined : { thumbnailPath: '' }); @@ -77,32 +83,36 @@ export class MediaService extends BaseService { } jobs.push({ name: JobName.GENERATE_PERSON_THUMBNAIL, data: { id: person.id } }); + if (jobs.length >= JOBS_ASSET_PAGINATION_SIZE) { + await queueAll(); + } } - await this.jobRepository.queueAll(jobs); + await queueAll(); return JobStatus.SUCCESS; } @OnJob({ name: JobName.QUEUE_MIGRATION, queue: QueueName.MIGRATION }) async handleQueueMigration(): Promise<JobStatus> { - const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => - this.assetRepository.getAll(pagination), - ); - const { active, waiting } = await this.jobRepository.getJobCounts(QueueName.MIGRATION); if (active === 1 && waiting === 0) { await this.storageCore.removeEmptyDirs(StorageFolder.THUMBNAILS); await this.storageCore.removeEmptyDirs(StorageFolder.ENCODED_VIDEO); } - for await (const assets of assetPagination) { - await this.jobRepository.queueAll( - assets.map((asset) => ({ name: JobName.MIGRATE_ASSET, data: { id: asset.id } })), - ); + let jobs: JobItem[] = []; + const assets = this.assetJobRepository.streamForMigrationJob(); + for await (const asset of assets) { + jobs.push({ name: JobName.MIGRATE_ASSET, data: { id: asset.id } }); + if (jobs.length >= JOBS_ASSET_PAGINATION_SIZE) { + await this.jobRepository.queueAll(jobs); + jobs = []; + } } - let jobs: { name: JobName.MIGRATE_PERSON; data: { id: string } }[] = []; + await this.jobRepository.queueAll(jobs); + jobs = []; for await (const person of this.personRepository.getAll()) { jobs.push({ name: JobName.MIGRATE_PERSON, data: { id: person.id } }); @@ -213,6 +223,29 @@ export class MediaService extends BaseService { return JobStatus.SUCCESS; } + private async extractImage(originalPath: string, minSize: number) { + let extracted = await this.mediaRepository.extract(originalPath); + if (extracted && !(await this.shouldUseExtractedImage(extracted.buffer, minSize))) { + extracted = null; + } + + return extracted; + } + + private async decodeImage(thumbSource: string | Buffer, exifInfo: Exif, targetSize?: number) { + const { image } = await this.getConfig({ withCache: true }); + const colorspace = this.isSRGB(exifInfo) ? Colorspace.SRGB : image.colorspace; + const decodeOptions: DecodeToBufferOptions = { + colorspace, + processInvalidImages: process.env.IMMICH_PROCESS_INVALID_IMAGES === 'true', + size: targetSize, + orientation: exifInfo.orientation ? Number(exifInfo.orientation) : undefined, + }; + + const { info, data } = await this.mediaRepository.decodeImage(thumbSource, decodeOptions); + return { info, data, colorspace }; + } + private async generateImageThumbnails(asset: { id: string; ownerId: string; @@ -225,68 +258,48 @@ export class MediaService extends BaseService { const thumbnailPath = StorageCore.getImagePath(asset, AssetPathType.THUMBNAIL, image.thumbnail.format); this.storageCore.ensureFolders(previewPath); - const processInvalidImages = process.env.IMMICH_PROCESS_INVALID_IMAGES === 'true'; - const colorspace = this.isSRGB(asset) ? Colorspace.SRGB : image.colorspace; + // 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 convertFullsize = generateFullsize && (!extracted || !mimeTypes.isWebSupportedImage(` .${extracted.format}`)); - // prevents this extra "enabled" from leaking into fullsizeOptions later - const { enabled: imageFullsizeEnabled, ...imageFullsizeConfig } = image.fullsize; + const { info, data, colorspace } = await this.decodeImage( + extracted ? extracted.buffer : asset.originalPath, + asset.exifInfo, + convertFullsize ? undefined : image.preview.size, + ); - const shouldConvertFullsize = imageFullsizeEnabled && !mimeTypes.isWebSupportedImage(asset.originalFileName); - const shouldExtractEmbedded = image.extractEmbedded && mimeTypes.isRaw(asset.originalFileName); - const decodeOptions: DecodeToBufferOptions = { colorspace, processInvalidImages, size: image.preview.size }; - - let useExtracted = false; - let decodeInputPath: string = asset.originalPath; - // Converted or extracted image from non-web-supported formats (e.g. RAW) - let fullsizePath: string | undefined; - - if (shouldConvertFullsize) { - // unset size to decode fullsize image - decodeOptions.size = undefined; - fullsizePath = StorageCore.getImagePath(asset, AssetPathType.FULLSIZE, image.fullsize.format); - } - - if (shouldExtractEmbedded) { - // For RAW files, try extracting embedded preview first - // Assume extracted image from RAW always in JPEG format, as implied from the `jpgFromRaw` tag name - const extractedPath = StorageCore.getImagePath(asset, AssetPathType.FULLSIZE, ImageFormat.JPEG); - const didExtract = await this.mediaRepository.extract(asset.originalPath, extractedPath); - useExtracted = didExtract && (await this.shouldUseExtractedImage(extractedPath, image.preview.size)); - - if (useExtracted) { - if (shouldConvertFullsize) { - // skip re-encoding and directly use extracted as fullsize preview - // as usually the extracted image is already heavily compressed, no point doing lossy conversion again - fullsizePath = extractedPath; - } - // use this as origin of preview and thumbnail - decodeInputPath = extractedPath; - if (asset.exifInfo) { - // write essential orientation and colorspace EXIF for correct fullsize preview and subsequent processing - const exif = { orientation: asset.exifInfo.orientation, colorspace: asset.exifInfo.colorspace }; - await this.mediaRepository.writeExif(exif, extractedPath); - } - } - } - - const { info, data } = await this.mediaRepository.decodeImage(decodeInputPath, decodeOptions); - - const thumbnailOptions = { colorspace, processInvalidImages, raw: info }; + // generate final images + const thumbnailOptions = { colorspace, processInvalidImages: false, raw: info }; const promises = [ this.mediaRepository.generateThumbhash(data, thumbnailOptions), this.mediaRepository.generateThumbnail(data, { ...image.thumbnail, ...thumbnailOptions }, thumbnailPath), this.mediaRepository.generateThumbnail(data, { ...image.preview, ...thumbnailOptions }, previewPath), ]; - // did not extract a usable image from RAW - if (fullsizePath && !useExtracted) { - const fullsizeOptions: GenerateThumbnailOptions = { - ...imageFullsizeConfig, - ...thumbnailOptions, - size: undefined, - }; + let fullsizePath: string | undefined; + + if (convertFullsize) { + // convert a new fullsize image from the same source as the thumbnail + fullsizePath = StorageCore.getImagePath(asset, AssetPathType.FULLSIZE, image.fullsize.format); + const fullsizeOptions = { format: image.fullsize.format, quality: image.fullsize.quality, ...thumbnailOptions }; promises.push(this.mediaRepository.generateThumbnail(data, fullsizeOptions, fullsizePath)); + } else if (generateFullsize && extracted && extracted.format === RawExtractedFormat.JPEG) { + fullsizePath = StorageCore.getImagePath(asset, AssetPathType.FULLSIZE, extracted.format); + this.storageCore.ensureFolders(fullsizePath); + + // Write the buffer to disk with essential EXIF data + await this.storageRepository.createOrOverwriteFile(fullsizePath, extracted.buffer); + await this.mediaRepository.writeExif( + { + orientation: asset.exifInfo.orientation, + colorspace: asset.exifInfo.colorspace, + }, + fullsizePath, + ); } + const outputs = await Promise.all(promises); return { previewPath, thumbnailPath, fullsizePath, thumbhash: outputs[0] as Buffer }; @@ -330,25 +343,25 @@ export class MediaService extends BaseService { async handleQueueVideoConversion(job: JobOf<JobName.QUEUE_VIDEO_CONVERSION>): Promise<JobStatus> { const { force } = job; - const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => { - return force - ? this.assetRepository.getAll(pagination, { type: AssetType.VIDEO }) - : this.assetRepository.getWithout(pagination, WithoutProperty.ENCODED_VIDEO); - }); + let queue: { name: JobName.VIDEO_CONVERSION; data: { id: string } }[] = []; + for await (const asset of this.assetJobRepository.streamForVideoConversion(force)) { + queue.push({ name: JobName.VIDEO_CONVERSION, data: { id: asset.id } }); - for await (const assets of assetPagination) { - await this.jobRepository.queueAll( - assets.map((asset) => ({ name: JobName.VIDEO_CONVERSION, data: { id: asset.id } })), - ); + if (queue.length >= JOBS_ASSET_PAGINATION_SIZE) { + await this.jobRepository.queueAll(queue); + queue = []; + } } + await this.jobRepository.queueAll(queue); + return JobStatus.SUCCESS; } @OnJob({ name: JobName.VIDEO_CONVERSION, queue: QueueName.VIDEO_CONVERSION }) async handleVideoConversion({ id }: JobOf<JobName.VIDEO_CONVERSION>): Promise<JobStatus> { - const [asset] = await this.assetRepository.getByIds([id]); - if (!asset || asset.type !== AssetType.VIDEO) { + const asset = await this.assetJobRepository.getForVideoConversion(id); + if (!asset) { return JobStatus.FAILED; } @@ -521,8 +534,7 @@ export class MediaService extends BaseService { return name !== VideoContainer.MP4 && !ffmpegConfig.acceptedContainers.includes(name); } - isSRGB(asset: { exifInfo: Exif }): boolean { - const { colorspace, profileDescription, bitsPerSample } = asset.exifInfo; + isSRGB({ colorspace, profileDescription, bitsPerSample }: Exif): boolean { if (colorspace || profileDescription) { return [colorspace, profileDescription].some((s) => s?.toLowerCase().includes('srgb')); } else if (bitsPerSample) { @@ -550,10 +562,9 @@ export class MediaService extends BaseService { } } - private async shouldUseExtractedImage(extractedPath: string, targetSize: number) { - const { width, height } = await this.mediaRepository.getImageDimensions(extractedPath); + private async shouldUseExtractedImage(extractedPathOrBuffer: string | Buffer, targetSize: number) { + const { width, height } = await this.mediaRepository.getImageDimensions(extractedPathOrBuffer); const extractedSize = Math.min(width, height); - return extractedSize >= targetSize; } diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index e412b1c31f..e0a283b02a 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -5,7 +5,6 @@ import { constants } from 'node:fs/promises'; import { defaults } from 'src/config'; import { MapAsset } from 'src/dtos/asset-response.dto'; import { AssetType, ExifOrientation, ImmichWorker, JobName, JobStatus, SourceType } from 'src/enum'; -import { WithoutProperty } from 'src/repositories/asset.repository'; import { ImmichTags } from 'src/repositories/metadata.repository'; import { MetadataService } from 'src/services/metadata.service'; import { assetStub } from 'test/fixtures/asset.stub'; @@ -14,7 +13,7 @@ import { probeStub } from 'test/fixtures/media.stub'; import { personStub } from 'test/fixtures/person.stub'; import { tagStub } from 'test/fixtures/tag.stub'; import { factory } from 'test/small.factory'; -import { newTestService, ServiceMocks } from 'test/utils'; +import { makeStream, newTestService, ServiceMocks } from 'test/utils'; const makeFaceTags = (face: Partial<{ Name: string }> = {}) => ({ RegionInfo: { @@ -104,10 +103,10 @@ describe(MetadataService.name, () => { describe('handleQueueMetadataExtraction', () => { it('should queue metadata extraction for all assets without exif values', async () => { - mocks.asset.getWithout.mockResolvedValue({ items: [assetStub.image], hasNextPage: false }); + mocks.assetJob.streamForMetadataExtraction.mockReturnValue(makeStream([assetStub.image])); await expect(sut.handleQueueMetadataExtraction({ force: false })).resolves.toBe(JobStatus.SUCCESS); - expect(mocks.asset.getWithout).toHaveBeenCalled(); + expect(mocks.assetJob.streamForMetadataExtraction).toHaveBeenCalledWith(false); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.METADATA_EXTRACTION, @@ -117,10 +116,10 @@ describe(MetadataService.name, () => { }); it('should queue metadata extraction for all assets', async () => { - mocks.asset.getAll.mockResolvedValue({ items: [assetStub.image], hasNextPage: false }); + mocks.assetJob.streamForMetadataExtraction.mockReturnValue(makeStream([assetStub.image])); await expect(sut.handleQueueMetadataExtraction({ force: true })).resolves.toBe(JobStatus.SUCCESS); - expect(mocks.asset.getAll).toHaveBeenCalled(); + expect(mocks.assetJob.streamForMetadataExtraction).toHaveBeenCalledWith(true); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.METADATA_EXTRACTION, @@ -598,6 +597,10 @@ describe(MetadataService.name, () => { livePhotoVideoId: fileStub.livePhotoMotion.uuid, }); expect(mocks.asset.update).toHaveBeenCalledTimes(3); + expect(mocks.job.queue).toHaveBeenCalledExactlyOnceWith({ + name: JobName.VIDEO_CONVERSION, + data: { id: assetStub.livePhotoMotionAsset.id }, + }); }); it('should extract the EmbeddedVideo tag from Samsung JPEG motion photos', async () => { @@ -652,6 +655,10 @@ describe(MetadataService.name, () => { livePhotoVideoId: fileStub.livePhotoMotion.uuid, }); expect(mocks.asset.update).toHaveBeenCalledTimes(3); + expect(mocks.job.queue).toHaveBeenCalledExactlyOnceWith({ + name: JobName.VIDEO_CONVERSION, + data: { id: assetStub.livePhotoMotionAsset.id }, + }); }); it('should extract the motion photo video from the XMP directory entry ', async () => { @@ -706,6 +713,10 @@ describe(MetadataService.name, () => { livePhotoVideoId: fileStub.livePhotoMotion.uuid, }); expect(mocks.asset.update).toHaveBeenCalledTimes(3); + expect(mocks.job.queue).toHaveBeenCalledExactlyOnceWith({ + name: JobName.VIDEO_CONVERSION, + data: { id: assetStub.livePhotoMotionAsset.id }, + }); }); it('should delete old motion photo video assets if they do not match what is extracted', async () => { @@ -1334,12 +1345,11 @@ describe(MetadataService.name, () => { describe('handleQueueSidecar', () => { it('should queue assets with sidecar files', async () => { - mocks.asset.getAll.mockResolvedValue({ items: [assetStub.sidecar], hasNextPage: false }); + mocks.assetJob.streamForSidecar.mockReturnValue(makeStream([assetStub.image])); await sut.handleQueueSidecar({ force: true }); + expect(mocks.assetJob.streamForSidecar).toHaveBeenCalledWith(true); - expect(mocks.asset.getAll).toHaveBeenCalledWith({ take: 1000, skip: 0 }); - expect(mocks.asset.getWithout).not.toHaveBeenCalled(); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.SIDECAR_SYNC, @@ -1349,12 +1359,11 @@ describe(MetadataService.name, () => { }); it('should queue assets without sidecar files', async () => { - mocks.asset.getWithout.mockResolvedValue({ items: [assetStub.image], hasNextPage: false }); + mocks.assetJob.streamForSidecar.mockReturnValue(makeStream([assetStub.image])); await sut.handleQueueSidecar({ force: false }); - expect(mocks.asset.getWithout).toHaveBeenCalledWith({ take: 1000, skip: 0 }, WithoutProperty.SIDECAR); - expect(mocks.asset.getAll).not.toHaveBeenCalled(); + expect(mocks.assetJob.streamForSidecar).toHaveBeenCalledWith(false); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.SIDECAR_DISCOVERY, diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index faf146a2be..fd7382e163 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -22,14 +22,12 @@ import { QueueName, SourceType, } from 'src/enum'; -import { WithoutProperty } from 'src/repositories/asset.repository'; import { ArgOf } from 'src/repositories/event.repository'; import { ReverseGeocodeResult } from 'src/repositories/map.repository'; import { ImmichTags } from 'src/repositories/metadata.repository'; import { BaseService } from 'src/services/base.service'; -import { JobOf } from 'src/types'; +import { JobItem, JobOf } from 'src/types'; import { isFaceImportEnabled } from 'src/utils/misc'; -import { usePagination } from 'src/utils/pagination'; import { upsertTags } from 'src/utils/tag'; /** look for a date from these tags (in order) */ @@ -168,18 +166,18 @@ export class MetadataService extends BaseService { @OnJob({ name: JobName.QUEUE_METADATA_EXTRACTION, queue: QueueName.METADATA_EXTRACTION }) async handleQueueMetadataExtraction(job: JobOf<JobName.QUEUE_METADATA_EXTRACTION>): Promise<JobStatus> { const { force } = job; - const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => { - return force - ? this.assetRepository.getAll(pagination) - : this.assetRepository.getWithout(pagination, WithoutProperty.EXIF); - }); - for await (const assets of assetPagination) { - await this.jobRepository.queueAll( - assets.map((asset) => ({ name: JobName.METADATA_EXTRACTION, data: { id: asset.id } })), - ); + let queue: { name: JobName.METADATA_EXTRACTION; data: { id: string } }[] = []; + for await (const asset of this.assetJobRepository.streamForMetadataExtraction(force)) { + queue.push({ name: JobName.METADATA_EXTRACTION, data: { id: asset.id } }); + + if (queue.length >= JOBS_ASSET_PAGINATION_SIZE) { + await this.jobRepository.queueAll(queue); + queue = []; + } } + await this.jobRepository.queueAll(queue); return JobStatus.SUCCESS; } @@ -289,23 +287,23 @@ export class MetadataService extends BaseService { } @OnJob({ name: JobName.QUEUE_SIDECAR, queue: QueueName.SIDECAR }) - async handleQueueSidecar(job: JobOf<JobName.QUEUE_SIDECAR>): Promise<JobStatus> { - const { force } = job; - const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => { - return force - ? this.assetRepository.getAll(pagination) - : this.assetRepository.getWithout(pagination, WithoutProperty.SIDECAR); - }); + async handleQueueSidecar({ force }: JobOf<JobName.QUEUE_SIDECAR>): Promise<JobStatus> { + let jobs: JobItem[] = []; + const queueAll = async () => { + await this.jobRepository.queueAll(jobs); + jobs = []; + }; - for await (const assets of assetPagination) { - await this.jobRepository.queueAll( - assets.map((asset) => ({ - name: force ? JobName.SIDECAR_SYNC : JobName.SIDECAR_DISCOVERY, - data: { id: asset.id }, - })), - ); + const assets = this.assetJobRepository.streamForSidecar(force); + for await (const asset of assets) { + jobs.push({ name: force ? JobName.SIDECAR_SYNC : JobName.SIDECAR_DISCOVERY, data: { id: asset.id } }); + if (jobs.length >= JOBS_ASSET_PAGINATION_SIZE) { + await queueAll(); + } } + await queueAll(); + return JobStatus.SUCCESS; } @@ -576,6 +574,7 @@ export class MetadataService extends BaseService { this.logger.log(`Wrote motion photo video to ${motionAsset.originalPath}`); await this.handleMetadataExtraction({ id: motionAsset.id }); + await this.jobRepository.queue({ name: JobName.VIDEO_CONVERSION, data: { id: motionAsset.id } }); } this.logger.debug(`Finished motion photo video extraction for asset ${asset.id}: ${asset.originalPath}`); diff --git a/server/src/services/notification-admin.service.spec.ts b/server/src/services/notification-admin.service.spec.ts new file mode 100644 index 0000000000..4a747d41a3 --- /dev/null +++ b/server/src/services/notification-admin.service.spec.ts @@ -0,0 +1,111 @@ +import { defaults, SystemConfig } from 'src/config'; +import { EmailTemplate } from 'src/repositories/email.repository'; +import { NotificationService } from 'src/services/notification.service'; +import { userStub } from 'test/fixtures/user.stub'; +import { newTestService, ServiceMocks } from 'test/utils'; + +const smtpTransport = Object.freeze<SystemConfig>({ + ...defaults, + notifications: { + smtp: { + ...defaults.notifications.smtp, + enabled: true, + transport: { + ignoreCert: false, + host: 'localhost', + port: 587, + username: 'test', + password: 'test', + }, + }, + }, +}); + +describe(NotificationService.name, () => { + let sut: NotificationService; + let mocks: ServiceMocks; + + beforeEach(() => { + ({ sut, mocks } = newTestService(NotificationService)); + }); + + it('should work', () => { + expect(sut).toBeDefined(); + }); + + describe('sendTestEmail', () => { + it('should throw error if user could not be found', async () => { + await expect(sut.sendTestEmail('', smtpTransport.notifications.smtp)).rejects.toThrow('User not found'); + }); + + it('should throw error if smtp validation fails', async () => { + mocks.user.get.mockResolvedValue(userStub.admin); + mocks.email.verifySmtp.mockRejectedValue(''); + + await expect(sut.sendTestEmail('', smtpTransport.notifications.smtp)).rejects.toThrow( + 'Failed to verify SMTP configuration', + ); + }); + + it('should send email to default domain', async () => { + mocks.user.get.mockResolvedValue(userStub.admin); + mocks.email.verifySmtp.mockResolvedValue(true); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.sendEmail.mockResolvedValue({ messageId: 'message-1', response: '' }); + + await expect(sut.sendTestEmail('', smtpTransport.notifications.smtp)).resolves.not.toThrow(); + expect(mocks.email.renderEmail).toHaveBeenCalledWith({ + template: EmailTemplate.TEST_EMAIL, + data: { baseUrl: 'https://my.immich.app', displayName: userStub.admin.name }, + }); + expect(mocks.email.sendEmail).toHaveBeenCalledWith( + expect.objectContaining({ + subject: 'Test email from Immich', + smtp: smtpTransport.notifications.smtp.transport, + }), + ); + }); + + it('should send email to external domain', async () => { + mocks.user.get.mockResolvedValue(userStub.admin); + mocks.email.verifySmtp.mockResolvedValue(true); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.systemMetadata.get.mockResolvedValue({ server: { externalDomain: 'https://demo.immich.app' } }); + mocks.email.sendEmail.mockResolvedValue({ messageId: 'message-1', response: '' }); + + await expect(sut.sendTestEmail('', smtpTransport.notifications.smtp)).resolves.not.toThrow(); + expect(mocks.email.renderEmail).toHaveBeenCalledWith({ + template: EmailTemplate.TEST_EMAIL, + data: { baseUrl: 'https://demo.immich.app', displayName: userStub.admin.name }, + }); + expect(mocks.email.sendEmail).toHaveBeenCalledWith( + expect.objectContaining({ + subject: 'Test email from Immich', + smtp: smtpTransport.notifications.smtp.transport, + }), + ); + }); + + it('should send email with replyTo', async () => { + mocks.user.get.mockResolvedValue(userStub.admin); + mocks.email.verifySmtp.mockResolvedValue(true); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.sendEmail.mockResolvedValue({ messageId: 'message-1', response: '' }); + + await expect( + sut.sendTestEmail('', { ...smtpTransport.notifications.smtp, replyTo: 'demo@immich.app' }), + ).resolves.not.toThrow(); + expect(mocks.email.renderEmail).toHaveBeenCalledWith({ + template: EmailTemplate.TEST_EMAIL, + data: { baseUrl: 'https://my.immich.app', displayName: userStub.admin.name }, + }); + expect(mocks.email.sendEmail).toHaveBeenCalledWith( + expect.objectContaining({ + subject: 'Test email from Immich', + smtp: smtpTransport.notifications.smtp.transport, + replyTo: 'demo@immich.app', + }), + ); + }); + }); +}); diff --git a/server/src/services/notification-admin.service.ts b/server/src/services/notification-admin.service.ts new file mode 100644 index 0000000000..bf0d2bba41 --- /dev/null +++ b/server/src/services/notification-admin.service.ts @@ -0,0 +1,120 @@ +import { BadRequestException, Injectable } from '@nestjs/common'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { mapNotification, NotificationCreateDto } from 'src/dtos/notification.dto'; +import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto'; +import { NotificationLevel, NotificationType } from 'src/enum'; +import { EmailTemplate } from 'src/repositories/email.repository'; +import { BaseService } from 'src/services/base.service'; +import { getExternalDomain } from 'src/utils/misc'; + +@Injectable() +export class NotificationAdminService extends BaseService { + async create(auth: AuthDto, dto: NotificationCreateDto) { + const item = await this.notificationRepository.create({ + userId: dto.userId, + type: dto.type ?? NotificationType.Custom, + level: dto.level ?? NotificationLevel.Info, + title: dto.title, + description: dto.description, + data: dto.data, + }); + + return mapNotification(item); + } + + async sendTestEmail(id: string, dto: SystemConfigSmtpDto, tempTemplate?: string) { + const user = await this.userRepository.get(id, { withDeleted: false }); + if (!user) { + throw new Error('User not found'); + } + + try { + await this.emailRepository.verifySmtp(dto.transport); + } catch (error) { + throw new BadRequestException('Failed to verify SMTP configuration', { cause: error }); + } + + const { server } = await this.getConfig({ withCache: false }); + const { html, text } = await this.emailRepository.renderEmail({ + template: EmailTemplate.TEST_EMAIL, + data: { + baseUrl: getExternalDomain(server), + displayName: user.name, + }, + customTemplate: tempTemplate!, + }); + const { messageId } = await this.emailRepository.sendEmail({ + to: user.email, + subject: 'Test email from Immich', + html, + text, + from: dto.from, + replyTo: dto.replyTo || dto.from, + smtp: dto.transport, + }); + + return { messageId }; + } + + async getTemplate(name: EmailTemplate, customTemplate: string) { + const { server, templates } = await this.getConfig({ withCache: false }); + + let templateResponse = ''; + + switch (name) { + case EmailTemplate.WELCOME: { + const { html: _welcomeHtml } = await this.emailRepository.renderEmail({ + template: EmailTemplate.WELCOME, + data: { + baseUrl: getExternalDomain(server), + displayName: 'John Doe', + username: 'john@doe.com', + password: 'thisIsAPassword123', + }, + customTemplate: customTemplate || templates.email.welcomeTemplate, + }); + + templateResponse = _welcomeHtml; + break; + } + case EmailTemplate.ALBUM_UPDATE: { + const { html: _updateAlbumHtml } = await this.emailRepository.renderEmail({ + template: EmailTemplate.ALBUM_UPDATE, + data: { + baseUrl: getExternalDomain(server), + albumId: '1', + albumName: 'Favorite Photos', + recipientName: 'Jane Doe', + cid: undefined, + }, + customTemplate: customTemplate || templates.email.albumInviteTemplate, + }); + templateResponse = _updateAlbumHtml; + break; + } + + case EmailTemplate.ALBUM_INVITE: { + const { html } = await this.emailRepository.renderEmail({ + template: EmailTemplate.ALBUM_INVITE, + data: { + baseUrl: getExternalDomain(server), + albumId: '1', + albumName: "John Doe's Favorites", + senderName: 'John Doe', + recipientName: 'Jane Doe', + cid: undefined, + }, + customTemplate: customTemplate || templates.email.albumInviteTemplate, + }); + templateResponse = html; + break; + } + default: { + templateResponse = ''; + break; + } + } + + return { name, html: templateResponse }; + } +} diff --git a/server/src/services/notification.service.spec.ts b/server/src/services/notification.service.spec.ts index 85e425b11f..133eb9e7f6 100644 --- a/server/src/services/notification.service.spec.ts +++ b/server/src/services/notification.service.spec.ts @@ -3,7 +3,6 @@ import { defaults, SystemConfig } from 'src/config'; import { AlbumUser } from 'src/database'; import { SystemConfigDto } from 'src/dtos/system-config.dto'; import { AssetFileType, JobName, JobStatus, UserMetadataKey } from 'src/enum'; -import { EmailTemplate } from 'src/repositories/notification.repository'; import { NotificationService } from 'src/services/notification.service'; import { INotifyAlbumUpdateJob } from 'src/types'; import { albumStub } from 'test/fixtures/album.stub'; @@ -74,18 +73,18 @@ describe(NotificationService.name, () => { const oldConfig = configs.smtpDisabled; const newConfig = configs.smtpEnabled; - mocks.notification.verifySmtp.mockResolvedValue(true); + mocks.email.verifySmtp.mockResolvedValue(true); await expect(sut.onConfigValidate({ oldConfig, newConfig })).resolves.not.toThrow(); - expect(mocks.notification.verifySmtp).toHaveBeenCalledWith(newConfig.notifications.smtp.transport); + expect(mocks.email.verifySmtp).toHaveBeenCalledWith(newConfig.notifications.smtp.transport); }); it('validates smtp config when transport changes', async () => { const oldConfig = configs.smtpEnabled; const newConfig = configs.smtpTransport; - mocks.notification.verifySmtp.mockResolvedValue(true); + mocks.email.verifySmtp.mockResolvedValue(true); await expect(sut.onConfigValidate({ oldConfig, newConfig })).resolves.not.toThrow(); - expect(mocks.notification.verifySmtp).toHaveBeenCalledWith(newConfig.notifications.smtp.transport); + expect(mocks.email.verifySmtp).toHaveBeenCalledWith(newConfig.notifications.smtp.transport); }); it('skips smtp validation when there are no changes', async () => { @@ -93,7 +92,7 @@ describe(NotificationService.name, () => { const newConfig = { ...configs.smtpEnabled }; await expect(sut.onConfigValidate({ oldConfig, newConfig })).resolves.not.toThrow(); - expect(mocks.notification.verifySmtp).not.toHaveBeenCalled(); + expect(mocks.email.verifySmtp).not.toHaveBeenCalled(); }); it('skips smtp validation with DTO when there are no changes', async () => { @@ -101,7 +100,7 @@ describe(NotificationService.name, () => { const newConfig = plainToInstance(SystemConfigDto, configs.smtpEnabled); await expect(sut.onConfigValidate({ oldConfig, newConfig })).resolves.not.toThrow(); - expect(mocks.notification.verifySmtp).not.toHaveBeenCalled(); + expect(mocks.email.verifySmtp).not.toHaveBeenCalled(); }); it('skips smtp validation when smtp is disabled', async () => { @@ -109,14 +108,14 @@ describe(NotificationService.name, () => { const newConfig = { ...configs.smtpDisabled }; await expect(sut.onConfigValidate({ oldConfig, newConfig })).resolves.not.toThrow(); - expect(mocks.notification.verifySmtp).not.toHaveBeenCalled(); + expect(mocks.email.verifySmtp).not.toHaveBeenCalled(); }); it('should fail if smtp configuration is invalid', async () => { const oldConfig = configs.smtpDisabled; const newConfig = configs.smtpEnabled; - mocks.notification.verifySmtp.mockRejectedValue(new Error('Failed validating smtp')); + mocks.email.verifySmtp.mockRejectedValue(new Error('Failed validating smtp')); await expect(sut.onConfigValidate({ oldConfig, newConfig })).rejects.toBeInstanceOf(Error); }); }); @@ -241,82 +240,6 @@ describe(NotificationService.name, () => { }); }); - describe('sendTestEmail', () => { - it('should throw error if user could not be found', async () => { - await expect(sut.sendTestEmail('', configs.smtpTransport.notifications.smtp)).rejects.toThrow('User not found'); - }); - - it('should throw error if smtp validation fails', async () => { - mocks.user.get.mockResolvedValue(userStub.admin); - mocks.notification.verifySmtp.mockRejectedValue(''); - - await expect(sut.sendTestEmail('', configs.smtpTransport.notifications.smtp)).rejects.toThrow( - 'Failed to verify SMTP configuration', - ); - }); - - it('should send email to default domain', async () => { - mocks.user.get.mockResolvedValue(userStub.admin); - mocks.notification.verifySmtp.mockResolvedValue(true); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); - mocks.notification.sendEmail.mockResolvedValue({ messageId: 'message-1', response: '' }); - - await expect(sut.sendTestEmail('', configs.smtpTransport.notifications.smtp)).resolves.not.toThrow(); - expect(mocks.notification.renderEmail).toHaveBeenCalledWith({ - template: EmailTemplate.TEST_EMAIL, - data: { baseUrl: 'https://my.immich.app', displayName: userStub.admin.name }, - }); - expect(mocks.notification.sendEmail).toHaveBeenCalledWith( - expect.objectContaining({ - subject: 'Test email from Immich', - smtp: configs.smtpTransport.notifications.smtp.transport, - }), - ); - }); - - it('should send email to external domain', async () => { - mocks.user.get.mockResolvedValue(userStub.admin); - mocks.notification.verifySmtp.mockResolvedValue(true); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); - mocks.systemMetadata.get.mockResolvedValue({ server: { externalDomain: 'https://demo.immich.app' } }); - mocks.notification.sendEmail.mockResolvedValue({ messageId: 'message-1', response: '' }); - - await expect(sut.sendTestEmail('', configs.smtpTransport.notifications.smtp)).resolves.not.toThrow(); - expect(mocks.notification.renderEmail).toHaveBeenCalledWith({ - template: EmailTemplate.TEST_EMAIL, - data: { baseUrl: 'https://demo.immich.app', displayName: userStub.admin.name }, - }); - expect(mocks.notification.sendEmail).toHaveBeenCalledWith( - expect.objectContaining({ - subject: 'Test email from Immich', - smtp: configs.smtpTransport.notifications.smtp.transport, - }), - ); - }); - - it('should send email with replyTo', async () => { - mocks.user.get.mockResolvedValue(userStub.admin); - mocks.notification.verifySmtp.mockResolvedValue(true); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); - mocks.notification.sendEmail.mockResolvedValue({ messageId: 'message-1', response: '' }); - - await expect( - sut.sendTestEmail('', { ...configs.smtpTransport.notifications.smtp, replyTo: 'demo@immich.app' }), - ).resolves.not.toThrow(); - expect(mocks.notification.renderEmail).toHaveBeenCalledWith({ - template: EmailTemplate.TEST_EMAIL, - data: { baseUrl: 'https://my.immich.app', displayName: userStub.admin.name }, - }); - expect(mocks.notification.sendEmail).toHaveBeenCalledWith( - expect.objectContaining({ - subject: 'Test email from Immich', - smtp: configs.smtpTransport.notifications.smtp.transport, - replyTo: 'demo@immich.app', - }), - ); - }); - }); - describe('handleUserSignup', () => { it('should skip if user could not be found', async () => { await expect(sut.handleUserSignup({ id: '' })).resolves.toBe(JobStatus.SKIPPED); @@ -325,7 +248,7 @@ describe(NotificationService.name, () => { it('should be successful', async () => { mocks.user.get.mockResolvedValue(userStub.admin); mocks.systemMetadata.get.mockResolvedValue({ server: {} }); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); await expect(sut.handleUserSignup({ id: '' })).resolves.toBe(JobStatus.SUCCESS); expect(mocks.job.queue).toHaveBeenCalledWith({ @@ -390,7 +313,7 @@ describe(NotificationService.name, () => { ], }); mocks.systemMetadata.get.mockResolvedValue({ server: {} }); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.SUCCESS); expect(mocks.job.queue).toHaveBeenCalledWith({ @@ -411,7 +334,7 @@ describe(NotificationService.name, () => { ], }); mocks.systemMetadata.get.mockResolvedValue({ server: {} }); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([]); await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.SUCCESS); @@ -440,7 +363,7 @@ describe(NotificationService.name, () => { ], }); mocks.systemMetadata.get.mockResolvedValue({ server: {} }); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([ { id: '1', type: AssetFileType.THUMBNAIL, path: 'path-to-thumb.jpg' }, ]); @@ -471,7 +394,7 @@ describe(NotificationService.name, () => { ], }); mocks.systemMetadata.get.mockResolvedValue({ server: {} }); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([assetStub.image.files[2]]); await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.SUCCESS); @@ -508,12 +431,12 @@ describe(NotificationService.name, () => { albumUsers: [{ user: { id: userStub.user1.id } } as AlbumUser], }); mocks.user.get.mockResolvedValueOnce(userStub.user1); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([]); await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] }); expect(mocks.user.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false }); - expect(mocks.notification.renderEmail).not.toHaveBeenCalled(); + expect(mocks.email.renderEmail).not.toHaveBeenCalled(); }); it('should skip recipient with disabled email notifications', async () => { @@ -530,12 +453,12 @@ describe(NotificationService.name, () => { }, ], }); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([]); await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] }); expect(mocks.user.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false }); - expect(mocks.notification.renderEmail).not.toHaveBeenCalled(); + expect(mocks.email.renderEmail).not.toHaveBeenCalled(); }); it('should skip recipient with disabled email notifications for the album update event', async () => { @@ -552,12 +475,12 @@ describe(NotificationService.name, () => { }, ], }); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([]); await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] }); expect(mocks.user.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false }); - expect(mocks.notification.renderEmail).not.toHaveBeenCalled(); + expect(mocks.email.renderEmail).not.toHaveBeenCalled(); }); it('should send email', async () => { @@ -566,12 +489,12 @@ describe(NotificationService.name, () => { albumUsers: [{ user: { id: userStub.user1.id } } as AlbumUser], }); mocks.user.get.mockResolvedValue(userStub.user1); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([]); await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] }); expect(mocks.user.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false }); - expect(mocks.notification.renderEmail).toHaveBeenCalled(); + expect(mocks.email.renderEmail).toHaveBeenCalled(); expect(mocks.job.queue).toHaveBeenCalled(); }); @@ -599,24 +522,20 @@ describe(NotificationService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ notifications: { smtp: { enabled: true, from: 'test@immich.app' } }, }); - mocks.notification.sendEmail.mockResolvedValue({ messageId: '', response: '' }); + mocks.email.sendEmail.mockResolvedValue({ messageId: '', response: '' }); await expect(sut.handleSendEmail({ html: '', subject: '', text: '', to: '' })).resolves.toBe(JobStatus.SUCCESS); - expect(mocks.notification.sendEmail).toHaveBeenCalledWith( - expect.objectContaining({ replyTo: 'test@immich.app' }), - ); + expect(mocks.email.sendEmail).toHaveBeenCalledWith(expect.objectContaining({ replyTo: 'test@immich.app' })); }); it('should send mail with replyTo successfully', async () => { mocks.systemMetadata.get.mockResolvedValue({ notifications: { smtp: { enabled: true, from: 'test@immich.app', replyTo: 'demo@immich.app' } }, }); - mocks.notification.sendEmail.mockResolvedValue({ messageId: '', response: '' }); + mocks.email.sendEmail.mockResolvedValue({ messageId: '', response: '' }); await expect(sut.handleSendEmail({ html: '', subject: '', text: '', to: '' })).resolves.toBe(JobStatus.SUCCESS); - expect(mocks.notification.sendEmail).toHaveBeenCalledWith( - expect.objectContaining({ replyTo: 'demo@immich.app' }), - ); + expect(mocks.email.sendEmail).toHaveBeenCalledWith(expect.objectContaining({ replyTo: 'demo@immich.app' })); }); }); }); diff --git a/server/src/services/notification.service.ts b/server/src/services/notification.service.ts index 2c4cc76756..be475d1dca 100644 --- a/server/src/services/notification.service.ts +++ b/server/src/services/notification.service.ts @@ -1,9 +1,26 @@ import { BadRequestException, Injectable } from '@nestjs/common'; import { OnEvent, OnJob } from 'src/decorators'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { + mapNotification, + NotificationDeleteAllDto, + NotificationDto, + NotificationSearchDto, + NotificationUpdateAllDto, + NotificationUpdateDto, +} from 'src/dtos/notification.dto'; import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto'; -import { AssetFileType, JobName, JobStatus, QueueName } from 'src/enum'; +import { + AssetFileType, + JobName, + JobStatus, + NotificationLevel, + NotificationType, + Permission, + QueueName, +} from 'src/enum'; +import { EmailTemplate } from 'src/repositories/email.repository'; import { ArgOf } from 'src/repositories/event.repository'; -import { EmailTemplate } from 'src/repositories/notification.repository'; import { BaseService } from 'src/services/base.service'; import { EmailImageAttachment, IEntityJob, INotifyAlbumUpdateJob, JobItem, JobOf } from 'src/types'; import { getFilenameExtension } from 'src/utils/file'; @@ -15,6 +32,80 @@ import { getPreferences } from 'src/utils/preferences'; export class NotificationService extends BaseService { private static albumUpdateEmailDelayMs = 300_000; + async search(auth: AuthDto, dto: NotificationSearchDto): Promise<NotificationDto[]> { + const items = await this.notificationRepository.search(auth.user.id, dto); + return items.map((item) => mapNotification(item)); + } + + async updateAll(auth: AuthDto, dto: NotificationUpdateAllDto) { + await this.requireAccess({ auth, ids: dto.ids, permission: Permission.NOTIFICATION_UPDATE }); + await this.notificationRepository.updateAll(dto.ids, { + readAt: dto.readAt, + }); + } + + async deleteAll(auth: AuthDto, dto: NotificationDeleteAllDto) { + await this.requireAccess({ auth, ids: dto.ids, permission: Permission.NOTIFICATION_DELETE }); + await this.notificationRepository.deleteAll(dto.ids); + } + + async get(auth: AuthDto, id: string) { + await this.requireAccess({ auth, ids: [id], permission: Permission.NOTIFICATION_READ }); + const item = await this.notificationRepository.get(id); + if (!item) { + throw new BadRequestException('Notification not found'); + } + return mapNotification(item); + } + + async update(auth: AuthDto, id: string, dto: NotificationUpdateDto) { + await this.requireAccess({ auth, ids: [id], permission: Permission.NOTIFICATION_UPDATE }); + const item = await this.notificationRepository.update(id, { + readAt: dto.readAt, + }); + return mapNotification(item); + } + + async delete(auth: AuthDto, id: string) { + await this.requireAccess({ auth, ids: [id], permission: Permission.NOTIFICATION_DELETE }); + await this.notificationRepository.delete(id); + } + + @OnJob({ name: JobName.NOTIFICATIONS_CLEANUP, queue: QueueName.BACKGROUND_TASK }) + async onNotificationsCleanup() { + await this.notificationRepository.cleanup(); + } + + @OnEvent({ name: 'job.failed' }) + async onJobFailed({ job, error }: ArgOf<'job.failed'>) { + const admin = await this.userRepository.getAdmin(); + if (!admin) { + return; + } + + this.logger.error(`Unable to run job handler (${job.name}): ${error}`, error?.stack, JSON.stringify(job.data)); + + switch (job.name) { + case JobName.BACKUP_DATABASE: { + const errorMessage = error instanceof Error ? error.message : error; + const item = await this.notificationRepository.create({ + userId: admin.id, + type: NotificationType.JobFailed, + level: NotificationLevel.Error, + title: 'Job Failed', + description: `Job ${[job.name]} failed with error: ${errorMessage}`, + }); + + this.eventRepository.clientSend('on_notification', admin.id, mapNotification(item)); + break; + } + + default: { + return; + } + } + } + @OnEvent({ name: 'config.update' }) onConfigUpdate({ oldConfig, newConfig }: ArgOf<'config.update'>) { this.eventRepository.clientBroadcast('on_config_update'); @@ -28,7 +119,7 @@ export class NotificationService extends BaseService { newConfig.notifications.smtp.enabled && !isEqualObject(oldConfig.notifications.smtp, newConfig.notifications.smtp) ) { - await this.notificationRepository.verifySmtp(newConfig.notifications.smtp.transport); + await this.emailRepository.verifySmtp(newConfig.notifications.smtp.transport); } } catch (error: Error | any) { this.logger.error(`Failed to validate SMTP configuration: ${error}`, error?.stack); @@ -138,13 +229,13 @@ export class NotificationService extends BaseService { } try { - await this.notificationRepository.verifySmtp(dto.transport); + await this.emailRepository.verifySmtp(dto.transport); } catch (error) { throw new BadRequestException('Failed to verify SMTP configuration', { cause: error }); } const { server } = await this.getConfig({ withCache: false }); - const { html, text } = await this.notificationRepository.renderEmail({ + const { html, text } = await this.emailRepository.renderEmail({ template: EmailTemplate.TEST_EMAIL, data: { baseUrl: getExternalDomain(server), @@ -152,7 +243,7 @@ export class NotificationService extends BaseService { }, customTemplate: tempTemplate!, }); - const { messageId } = await this.notificationRepository.sendEmail({ + const { messageId } = await this.emailRepository.sendEmail({ to: user.email, subject: 'Test email from Immich', html, @@ -172,7 +263,7 @@ export class NotificationService extends BaseService { switch (name) { case EmailTemplate.WELCOME: { - const { html: _welcomeHtml } = await this.notificationRepository.renderEmail({ + const { html: _welcomeHtml } = await this.emailRepository.renderEmail({ template: EmailTemplate.WELCOME, data: { baseUrl: getExternalDomain(server), @@ -187,7 +278,7 @@ export class NotificationService extends BaseService { break; } case EmailTemplate.ALBUM_UPDATE: { - const { html: _updateAlbumHtml } = await this.notificationRepository.renderEmail({ + const { html: _updateAlbumHtml } = await this.emailRepository.renderEmail({ template: EmailTemplate.ALBUM_UPDATE, data: { baseUrl: getExternalDomain(server), @@ -203,7 +294,7 @@ export class NotificationService extends BaseService { } case EmailTemplate.ALBUM_INVITE: { - const { html } = await this.notificationRepository.renderEmail({ + const { html } = await this.emailRepository.renderEmail({ template: EmailTemplate.ALBUM_INVITE, data: { baseUrl: getExternalDomain(server), @@ -235,7 +326,7 @@ export class NotificationService extends BaseService { } const { server, templates } = await this.getConfig({ withCache: true }); - const { html, text } = await this.notificationRepository.renderEmail({ + const { html, text } = await this.emailRepository.renderEmail({ template: EmailTemplate.WELCOME, data: { baseUrl: getExternalDomain(server), @@ -271,7 +362,7 @@ export class NotificationService extends BaseService { return JobStatus.SKIPPED; } - const { emailNotifications } = getPreferences(recipient.email, recipient.metadata); + const { emailNotifications } = getPreferences(recipient.metadata); if (!emailNotifications.enabled || !emailNotifications.albumInvite) { return JobStatus.SKIPPED; @@ -280,7 +371,7 @@ export class NotificationService extends BaseService { const attachment = await this.getAlbumThumbnailAttachment(album); const { server, templates } = await this.getConfig({ withCache: false }); - const { html, text } = await this.notificationRepository.renderEmail({ + const { html, text } = await this.emailRepository.renderEmail({ template: EmailTemplate.ALBUM_INVITE, data: { baseUrl: getExternalDomain(server), @@ -333,13 +424,13 @@ export class NotificationService extends BaseService { continue; } - const { emailNotifications } = getPreferences(user.email, user.metadata); + const { emailNotifications } = getPreferences(user.metadata); if (!emailNotifications.enabled || !emailNotifications.albumUpdate) { continue; } - const { html, text } = await this.notificationRepository.renderEmail({ + const { html, text } = await this.emailRepository.renderEmail({ template: EmailTemplate.ALBUM_UPDATE, data: { baseUrl: getExternalDomain(server), @@ -374,7 +465,7 @@ export class NotificationService extends BaseService { } const { to, subject, html, text: plain } = data; - const response = await this.notificationRepository.sendEmail({ + const response = await this.emailRepository.sendEmail({ to, subject, html, diff --git a/server/src/services/person.service.spec.ts b/server/src/services/person.service.spec.ts index 9808522434..5b88883472 100644 --- a/server/src/services/person.service.spec.ts +++ b/server/src/services/person.service.spec.ts @@ -2,7 +2,6 @@ import { BadRequestException, NotFoundException } from '@nestjs/common'; import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto'; import { mapFaces, mapPerson, PersonResponseDto } from 'src/dtos/person.dto'; import { CacheControl, Colorspace, ImageFormat, JobName, JobStatus, SourceType, SystemMetadataKey } from 'src/enum'; -import { WithoutProperty } from 'src/repositories/asset.repository'; import { DetectedFaces } from 'src/repositories/machine-learning.repository'; import { FaceSearchResult } from 'src/repositories/search.repository'; import { PersonService } from 'src/services/person.service'; @@ -455,14 +454,11 @@ describe(PersonService.name, () => { }); it('should queue missing assets', async () => { - mocks.asset.getWithout.mockResolvedValue({ - items: [assetStub.image], - hasNextPage: false, - }); + mocks.assetJob.streamForDetectFacesJob.mockReturnValue(makeStream([assetStub.image])); await sut.handleQueueDetectFaces({ force: false }); - expect(mocks.asset.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.FACES); + expect(mocks.assetJob.streamForDetectFacesJob).toHaveBeenCalledWith(false); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.FACE_DETECTION, @@ -472,10 +468,7 @@ describe(PersonService.name, () => { }); it('should queue all assets', async () => { - mocks.asset.getAll.mockResolvedValue({ - items: [assetStub.image], - hasNextPage: false, - }); + mocks.assetJob.streamForDetectFacesJob.mockReturnValue(makeStream([assetStub.image])); mocks.person.getAllWithoutFaces.mockResolvedValue([personStub.withName]); await sut.handleQueueDetectFaces({ force: true }); @@ -483,7 +476,7 @@ describe(PersonService.name, () => { expect(mocks.person.deleteFaces).toHaveBeenCalledWith({ sourceType: SourceType.MACHINE_LEARNING }); expect(mocks.person.delete).toHaveBeenCalledWith([personStub.withName.id]); expect(mocks.storage.unlink).toHaveBeenCalledWith(personStub.withName.thumbnailPath); - expect(mocks.asset.getAll).toHaveBeenCalled(); + expect(mocks.assetJob.streamForDetectFacesJob).toHaveBeenCalledWith(true); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.FACE_DETECTION, @@ -493,17 +486,14 @@ describe(PersonService.name, () => { }); it('should refresh all assets', async () => { - mocks.asset.getAll.mockResolvedValue({ - items: [assetStub.image], - hasNextPage: false, - }); + mocks.assetJob.streamForDetectFacesJob.mockReturnValue(makeStream([assetStub.image])); await sut.handleQueueDetectFaces({ force: undefined }); expect(mocks.person.delete).not.toHaveBeenCalled(); expect(mocks.person.deleteFaces).not.toHaveBeenCalled(); expect(mocks.storage.unlink).not.toHaveBeenCalled(); - expect(mocks.asset.getAll).toHaveBeenCalled(); + expect(mocks.assetJob.streamForDetectFacesJob).toHaveBeenCalledWith(undefined); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.FACE_DETECTION, @@ -516,16 +506,13 @@ describe(PersonService.name, () => { it('should delete existing people and faces if forced', async () => { mocks.person.getAll.mockReturnValue(makeStream([faceStub.face1.person, personStub.randomPerson])); mocks.person.getAllFaces.mockReturnValue(makeStream([faceStub.face1])); - mocks.asset.getAll.mockResolvedValue({ - items: [assetStub.image], - hasNextPage: false, - }); + mocks.assetJob.streamForDetectFacesJob.mockReturnValue(makeStream([assetStub.image])); mocks.person.getAllWithoutFaces.mockResolvedValue([personStub.randomPerson]); mocks.person.deleteFaces.mockResolvedValue(); await sut.handleQueueDetectFaces({ force: true }); - expect(mocks.asset.getAll).toHaveBeenCalled(); + expect(mocks.assetJob.streamForDetectFacesJob).toHaveBeenCalledWith(true); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.FACE_DETECTION, diff --git a/server/src/services/person.service.ts b/server/src/services/person.service.ts index 66d68857a0..227ea3c1c2 100644 --- a/server/src/services/person.service.ts +++ b/server/src/services/person.service.ts @@ -36,7 +36,6 @@ import { SourceType, SystemMetadataKey, } from 'src/enum'; -import { WithoutProperty } from 'src/repositories/asset.repository'; import { BoundingBox } from 'src/repositories/machine-learning.repository'; import { UpdateFacesData } from 'src/repositories/person.repository'; import { BaseService } from 'src/services/base.service'; @@ -44,7 +43,6 @@ import { CropOptions, ImageDimensions, InputDimensions, JobItem, JobOf } from 's import { ImmichFileResponse } from 'src/utils/file'; import { mimeTypes } from 'src/utils/mime-types'; import { isFaceImportEnabled, isFacialRecognitionEnabled } from 'src/utils/misc'; -import { usePagination } from 'src/utils/pagination'; @Injectable() export class PersonService extends BaseService { @@ -265,23 +263,19 @@ export class PersonService extends BaseService { await this.handlePersonCleanup(); } - const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => { - return force === false - ? this.assetRepository.getWithout(pagination, WithoutProperty.FACES) - : this.assetRepository.getAll(pagination, { - orderDirection: 'desc', - withFaces: true, - withArchived: true, - isVisible: true, - }); - }); + let jobs: JobItem[] = []; + const assets = this.assetJobRepository.streamForDetectFacesJob(force); + for await (const asset of assets) { + jobs.push({ name: JobName.FACE_DETECTION, data: { id: asset.id } }); - for await (const assets of assetPagination) { - await this.jobRepository.queueAll( - assets.map((asset) => ({ name: JobName.FACE_DETECTION, data: { id: asset.id } })), - ); + if (jobs.length >= JOBS_ASSET_PAGINATION_SIZE) { + await this.jobRepository.queueAll(jobs); + jobs = []; + } } + await this.jobRepository.queueAll(jobs); + if (force === undefined) { await this.jobRepository.queue({ name: JobName.PERSON_CLEANUP }); } diff --git a/server/src/services/smart-info.service.spec.ts b/server/src/services/smart-info.service.spec.ts index df26e69108..9cc97a8f0d 100644 --- a/server/src/services/smart-info.service.spec.ts +++ b/server/src/services/smart-info.service.spec.ts @@ -1,11 +1,10 @@ import { SystemConfig } from 'src/config'; import { ImmichWorker, JobName, JobStatus } from 'src/enum'; -import { WithoutProperty } from 'src/repositories/asset.repository'; import { SmartInfoService } from 'src/services/smart-info.service'; import { getCLIPModelInfo } from 'src/utils/misc'; import { assetStub } from 'test/fixtures/asset.stub'; import { systemConfigStub } from 'test/fixtures/system-config.stub'; -import { newTestService, ServiceMocks } from 'test/utils'; +import { makeStream, newTestService, ServiceMocks } from 'test/utils'; describe(SmartInfoService.name, () => { let sut: SmartInfoService; @@ -58,10 +57,6 @@ describe(SmartInfoService.name, () => { expect(mocks.search.getDimensionSize).not.toHaveBeenCalled(); expect(mocks.search.setDimensionSize).not.toHaveBeenCalled(); expect(mocks.search.deleteAllSearchEmbeddings).not.toHaveBeenCalled(); - expect(mocks.job.getQueueStatus).not.toHaveBeenCalled(); - expect(mocks.job.pause).not.toHaveBeenCalled(); - expect(mocks.job.waitForQueueCompletion).not.toHaveBeenCalled(); - expect(mocks.job.resume).not.toHaveBeenCalled(); }); it('should return if model and DB dimension size are equal', async () => { @@ -72,38 +67,15 @@ describe(SmartInfoService.name, () => { expect(mocks.search.getDimensionSize).toHaveBeenCalledTimes(1); expect(mocks.search.setDimensionSize).not.toHaveBeenCalled(); expect(mocks.search.deleteAllSearchEmbeddings).not.toHaveBeenCalled(); - expect(mocks.job.getQueueStatus).not.toHaveBeenCalled(); - expect(mocks.job.pause).not.toHaveBeenCalled(); - expect(mocks.job.waitForQueueCompletion).not.toHaveBeenCalled(); - expect(mocks.job.resume).not.toHaveBeenCalled(); }); it('should update DB dimension size if model and DB have different values', async () => { mocks.search.getDimensionSize.mockResolvedValue(768); - mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); await sut.onConfigInit({ newConfig: systemConfigStub.machineLearningEnabled as SystemConfig }); expect(mocks.search.getDimensionSize).toHaveBeenCalledTimes(1); expect(mocks.search.setDimensionSize).toHaveBeenCalledWith(512); - expect(mocks.job.getQueueStatus).toHaveBeenCalledTimes(1); - expect(mocks.job.pause).toHaveBeenCalledTimes(1); - expect(mocks.job.waitForQueueCompletion).toHaveBeenCalledTimes(1); - expect(mocks.job.resume).toHaveBeenCalledTimes(1); - }); - - it('should skip pausing and resuming queue if already paused', async () => { - mocks.search.getDimensionSize.mockResolvedValue(768); - mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: true }); - - await sut.onConfigInit({ newConfig: systemConfigStub.machineLearningEnabled as SystemConfig }); - - expect(mocks.search.getDimensionSize).toHaveBeenCalledTimes(1); - expect(mocks.search.setDimensionSize).toHaveBeenCalledWith(512); - expect(mocks.job.getQueueStatus).toHaveBeenCalledTimes(1); - expect(mocks.job.pause).not.toHaveBeenCalled(); - expect(mocks.job.waitForQueueCompletion).toHaveBeenCalledTimes(1); - expect(mocks.job.resume).not.toHaveBeenCalled(); }); }); @@ -120,10 +92,6 @@ describe(SmartInfoService.name, () => { expect(mocks.search.getDimensionSize).not.toHaveBeenCalled(); expect(mocks.search.setDimensionSize).not.toHaveBeenCalled(); expect(mocks.search.deleteAllSearchEmbeddings).not.toHaveBeenCalled(); - expect(mocks.job.getQueueStatus).not.toHaveBeenCalled(); - expect(mocks.job.pause).not.toHaveBeenCalled(); - expect(mocks.job.waitForQueueCompletion).not.toHaveBeenCalled(); - expect(mocks.job.resume).not.toHaveBeenCalled(); }); it('should return if model and DB dimension size are equal', async () => { @@ -141,15 +109,10 @@ describe(SmartInfoService.name, () => { expect(mocks.search.getDimensionSize).toHaveBeenCalledTimes(1); expect(mocks.search.setDimensionSize).not.toHaveBeenCalled(); expect(mocks.search.deleteAllSearchEmbeddings).not.toHaveBeenCalled(); - expect(mocks.job.getQueueStatus).not.toHaveBeenCalled(); - expect(mocks.job.pause).not.toHaveBeenCalled(); - expect(mocks.job.waitForQueueCompletion).not.toHaveBeenCalled(); - expect(mocks.job.resume).not.toHaveBeenCalled(); }); it('should update DB dimension size if model and DB have different values', async () => { mocks.search.getDimensionSize.mockResolvedValue(512); - mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); await sut.onConfigUpdate({ newConfig: { @@ -162,15 +125,10 @@ describe(SmartInfoService.name, () => { expect(mocks.search.getDimensionSize).toHaveBeenCalledTimes(1); expect(mocks.search.setDimensionSize).toHaveBeenCalledWith(768); - expect(mocks.job.getQueueStatus).toHaveBeenCalledTimes(1); - expect(mocks.job.pause).toHaveBeenCalledTimes(1); - expect(mocks.job.waitForQueueCompletion).toHaveBeenCalledTimes(1); - expect(mocks.job.resume).toHaveBeenCalledTimes(1); }); it('should clear embeddings if old and new models are different', async () => { mocks.search.getDimensionSize.mockResolvedValue(512); - mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); await sut.onConfigUpdate({ newConfig: { @@ -184,31 +142,6 @@ describe(SmartInfoService.name, () => { expect(mocks.search.deleteAllSearchEmbeddings).toHaveBeenCalled(); expect(mocks.search.getDimensionSize).toHaveBeenCalledTimes(1); expect(mocks.search.setDimensionSize).not.toHaveBeenCalled(); - expect(mocks.job.getQueueStatus).toHaveBeenCalledTimes(1); - expect(mocks.job.pause).toHaveBeenCalledTimes(1); - expect(mocks.job.waitForQueueCompletion).toHaveBeenCalledTimes(1); - expect(mocks.job.resume).toHaveBeenCalledTimes(1); - }); - - it('should skip pausing and resuming queue if already paused', async () => { - mocks.search.getDimensionSize.mockResolvedValue(512); - mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: true }); - - await sut.onConfigUpdate({ - newConfig: { - machineLearning: { clip: { modelName: 'ViT-B-32__openai', enabled: true }, enabled: true }, - } as SystemConfig, - oldConfig: { - machineLearning: { clip: { modelName: 'ViT-B-16__openai', enabled: true }, enabled: true }, - } as SystemConfig, - }); - - expect(mocks.search.getDimensionSize).toHaveBeenCalledTimes(1); - expect(mocks.search.setDimensionSize).not.toHaveBeenCalled(); - expect(mocks.job.getQueueStatus).toHaveBeenCalledTimes(1); - expect(mocks.job.pause).not.toHaveBeenCalled(); - expect(mocks.job.waitForQueueCompletion).toHaveBeenCalledTimes(1); - expect(mocks.job.resume).not.toHaveBeenCalled(); }); }); @@ -218,38 +151,31 @@ describe(SmartInfoService.name, () => { await sut.handleQueueEncodeClip({}); - expect(mocks.asset.getAll).not.toHaveBeenCalled(); - expect(mocks.asset.getWithout).not.toHaveBeenCalled(); + expect(mocks.search.setDimensionSize).not.toHaveBeenCalled(); }); it('should queue the assets without clip embeddings', async () => { - mocks.asset.getWithout.mockResolvedValue({ - items: [assetStub.image], - hasNextPage: false, - }); + mocks.assetJob.streamForEncodeClip.mockReturnValue(makeStream([assetStub.image])); await sut.handleQueueEncodeClip({ force: false }); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.SMART_SEARCH, data: { id: assetStub.image.id } }, ]); - expect(mocks.asset.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.SMART_SEARCH); - expect(mocks.search.deleteAllSearchEmbeddings).not.toHaveBeenCalled(); + expect(mocks.assetJob.streamForEncodeClip).toHaveBeenCalledWith(false); + expect(mocks.search.setDimensionSize).not.toHaveBeenCalled(); }); it('should queue all the assets', async () => { - mocks.asset.getAll.mockResolvedValue({ - items: [assetStub.image], - hasNextPage: false, - }); + mocks.assetJob.streamForEncodeClip.mockReturnValue(makeStream([assetStub.image])); await sut.handleQueueEncodeClip({ force: true }); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.SMART_SEARCH, data: { id: assetStub.image.id } }, ]); - expect(mocks.asset.getAll).toHaveBeenCalled(); - expect(mocks.search.deleteAllSearchEmbeddings).toHaveBeenCalled(); + expect(mocks.assetJob.streamForEncodeClip).toHaveBeenCalledWith(true); + expect(mocks.search.setDimensionSize).toHaveBeenCalledExactlyOnceWith(512); }); }); diff --git a/server/src/services/smart-info.service.ts b/server/src/services/smart-info.service.ts index 411114eb17..5ee5dac57e 100644 --- a/server/src/services/smart-info.service.ts +++ b/server/src/services/smart-info.service.ts @@ -3,12 +3,10 @@ import { SystemConfig } from 'src/config'; import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants'; import { OnEvent, OnJob } from 'src/decorators'; import { DatabaseLock, ImmichWorker, JobName, JobStatus, QueueName } from 'src/enum'; -import { WithoutProperty } from 'src/repositories/asset.repository'; import { ArgOf } from 'src/repositories/event.repository'; import { BaseService } from 'src/services/base.service'; -import { JobOf } from 'src/types'; +import { JobItem, JobOf } from 'src/types'; import { getCLIPModelInfo, isSmartSearchEnabled } from 'src/utils/misc'; -import { usePagination } from 'src/utils/pagination'; @Injectable() export class SmartInfoService extends BaseService { @@ -50,12 +48,6 @@ export class SmartInfoService extends BaseService { return; } - const { isPaused } = await this.jobRepository.getQueueStatus(QueueName.SMART_SEARCH); - if (!isPaused) { - await this.jobRepository.pause(QueueName.SMART_SEARCH); - } - await this.jobRepository.waitForQueueCompletion(QueueName.SMART_SEARCH); - if (dimSizeChange) { this.logger.log( `Dimension size of model ${newConfig.machineLearning.clip.modelName} is ${dimSize}, but database expects ${dbDimSize}.`, @@ -67,9 +59,8 @@ export class SmartInfoService extends BaseService { await this.searchRepository.deleteAllSearchEmbeddings(); } - if (!isPaused) { - await this.jobRepository.resume(QueueName.SMART_SEARCH); - } + // TODO: A job to reindex all assets should be scheduled, though user + // confirmation should probably be requested before doing that. }); } @@ -81,21 +72,23 @@ export class SmartInfoService extends BaseService { } if (force) { - await this.searchRepository.deleteAllSearchEmbeddings(); + const { dimSize } = getCLIPModelInfo(machineLearning.clip.modelName); + // in addition to deleting embeddings, update the dimension size in case it failed earlier + await this.searchRepository.setDimensionSize(dimSize); } - const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => { - return force - ? this.assetRepository.getAll(pagination, { isVisible: true }) - : this.assetRepository.getWithout(pagination, WithoutProperty.SMART_SEARCH); - }); - - for await (const assets of assetPagination) { - await this.jobRepository.queueAll( - assets.map((asset) => ({ name: JobName.SMART_SEARCH, data: { id: asset.id } })), - ); + let queue: JobItem[] = []; + const assets = this.assetJobRepository.streamForEncodeClip(force); + for await (const asset of assets) { + queue.push({ name: JobName.SMART_SEARCH, data: { id: asset.id } }); + if (queue.length >= JOBS_ASSET_PAGINATION_SIZE) { + await this.jobRepository.queueAll(queue); + queue = []; + } } + await this.jobRepository.queueAll(queue); + return JobStatus.SUCCESS; } @@ -126,6 +119,12 @@ export class SmartInfoService extends BaseService { await this.databaseRepository.wait(DatabaseLock.CLIPDimSize); } + const newConfig = await this.getConfig({ withCache: true }); + if (machineLearning.clip.modelName !== newConfig.machineLearning.clip.modelName) { + // Skip the job if the the model has changed since the embedding was generated. + return JobStatus.SKIPPED; + } + await this.searchRepository.upsert(asset.id, embedding); return JobStatus.SUCCESS; diff --git a/server/src/services/storage-template.service.spec.ts b/server/src/services/storage-template.service.spec.ts index 971a9e8302..9c4fe02f3e 100644 --- a/server/src/services/storage-template.service.spec.ts +++ b/server/src/services/storage-template.service.spec.ts @@ -69,6 +69,7 @@ describe(StorageTemplateService.name, () => { '{{y}}/{{MMMM}}-{{dd}}/{{filename}}', '{{y}}/{{MM}}/{{filename}}', '{{y}}/{{#if album}}{{album}}{{else}}Other/{{MM}}{{/if}}/{{filename}}', + '{{#if album}}{{album-startDate-y}}/{{album}}{{else}}{{y}}/Other/{{MM}}{{/if}}/{{filename}}', '{{y}}/{{MMM}}/{{filename}}', '{{y}}/{{MMMM}}/{{filename}}', '{{y}}/{{MM}}/{{dd}}/{{filename}}', @@ -182,6 +183,63 @@ describe(StorageTemplateService.name, () => { }); }); + it('should handle album startDate', async () => { + const asset = assetStub.storageAsset(); + const user = userStub.user1; + const album = albumStub.oneAsset; + const config = structuredClone(defaults); + config.storageTemplate.template = + '{{#if album}}{{album-startDate-y}}/{{album-startDate-MM}} - {{album}}{{else}}{{y}}/{{MM}}/{{/if}}/{{filename}}'; + + sut.onConfigInit({ newConfig: config }); + + mocks.user.get.mockResolvedValue(user); + mocks.assetJob.getForStorageTemplateJob.mockResolvedValueOnce(asset); + mocks.album.getByAssetId.mockResolvedValueOnce([album]); + mocks.album.getMetadataForIds.mockResolvedValueOnce([ + { + startDate: asset.fileCreatedAt, + endDate: asset.fileCreatedAt, + albumId: album.id, + assetCount: 1, + lastModifiedAssetTimestamp: null, + }, + ]); + + expect(await sut.handleMigrationSingle({ id: asset.id })).toBe(JobStatus.SUCCESS); + + const month = (asset.fileCreatedAt.getMonth() + 1).toString().padStart(2, '0'); + expect(mocks.move.create).toHaveBeenCalledWith({ + entityId: asset.id, + newPath: `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month} - ${album.albumName}/${asset.originalFileName}`, + oldPath: asset.originalPath, + pathType: AssetPathType.ORIGINAL, + }); + }); + + it('should handle else condition from album startDate', async () => { + const asset = assetStub.storageAsset(); + const user = userStub.user1; + const config = structuredClone(defaults); + config.storageTemplate.template = + '{{#if album}}{{album-startDate-y}}/{{album-startDate-MM}} - {{album}}{{else}}{{y}}/{{MM}}/{{/if}}/{{filename}}'; + + sut.onConfigInit({ newConfig: config }); + + mocks.user.get.mockResolvedValue(user); + mocks.assetJob.getForStorageTemplateJob.mockResolvedValueOnce(asset); + + expect(await sut.handleMigrationSingle({ id: asset.id })).toBe(JobStatus.SUCCESS); + + const month = (asset.fileCreatedAt.getMonth() + 1).toString().padStart(2, '0'); + expect(mocks.move.create).toHaveBeenCalledWith({ + entityId: asset.id, + newPath: `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month}/${asset.originalFileName}`, + oldPath: asset.originalPath, + pathType: AssetPathType.ORIGINAL, + }); + }); + it('should migrate previously failed move from original path when it still exists', async () => { mocks.user.get.mockResolvedValue(userStub.user1); diff --git a/server/src/services/storage-template.service.ts b/server/src/services/storage-template.service.ts index 71a0160ee2..542633a03f 100644 --- a/server/src/services/storage-template.service.ts +++ b/server/src/services/storage-template.service.ts @@ -28,6 +28,7 @@ const storagePresets = [ '{{y}}/{{MMMM}}-{{dd}}/{{filename}}', '{{y}}/{{MM}}/{{filename}}', '{{y}}/{{#if album}}{{album}}{{else}}Other/{{MM}}{{/if}}/{{filename}}', + '{{#if album}}{{album-startDate-y}}/{{album}}{{else}}{{y}}/Other/{{MM}}{{/if}}/{{filename}}', '{{y}}/{{MMM}}/{{filename}}', '{{y}}/{{MMMM}}/{{filename}}', '{{y}}/{{MM}}/{{dd}}/{{filename}}', @@ -54,6 +55,8 @@ interface RenderMetadata { filename: string; extension: string; albumName: string | null; + albumStartDate: Date | null; + albumEndDate: Date | null; } @Injectable() @@ -62,6 +65,7 @@ export class StorageTemplateService extends BaseService { compiled: HandlebarsTemplateDelegate<any>; raw: string; needsAlbum: boolean; + needsAlbumMetadata: boolean; } | null = null; private get template() { @@ -99,6 +103,8 @@ export class StorageTemplateService extends BaseService { filename: 'IMG_123', extension: 'jpg', albumName: 'album', + albumStartDate: new Date(), + albumEndDate: new Date(), }); } catch (error) { this.logger.warn(`Storage template validation failed: ${JSON.stringify(error)}`); @@ -255,9 +261,20 @@ export class StorageTemplateService extends BaseService { } let albumName = null; + let albumStartDate = null; + let albumEndDate = null; if (this.template.needsAlbum) { const albums = await this.albumRepository.getByAssetId(asset.ownerId, asset.id); - albumName = albums?.[0]?.albumName || null; + const album = albums?.[0]; + if (album) { + albumName = album.albumName || null; + + if (this.template.needsAlbumMetadata) { + const [metadata] = await this.albumRepository.getMetadataForIds([album.id]); + albumStartDate = metadata?.startDate || null; + albumEndDate = metadata?.endDate || null; + } + } } const storagePath = this.render(this.template.compiled, { @@ -265,6 +282,8 @@ export class StorageTemplateService extends BaseService { filename: sanitized, extension, albumName, + albumStartDate, + albumEndDate, }); const fullPath = path.normalize(path.join(rootPath, storagePath)); let destination = `${fullPath}.${extension}`; @@ -323,12 +342,13 @@ export class StorageTemplateService extends BaseService { return { raw: template, compiled: handlebar.compile(template, { knownHelpers: undefined, strict: true }), - needsAlbum: template.includes('{{album}}'), + needsAlbum: template.includes('album'), + needsAlbumMetadata: template.includes('album-startDate') || template.includes('album-endDate'), }; } private render(template: HandlebarsTemplateDelegate<any>, options: RenderMetadata) { - const { filename, extension, asset, albumName } = options; + const { filename, extension, asset, albumName, albumStartDate, albumEndDate } = options; const substitutions: Record<string, string> = { filename, ext: extension, @@ -346,6 +366,15 @@ export class StorageTemplateService extends BaseService { for (const token of Object.values(storageTokens).flat()) { substitutions[token] = dt.toFormat(token); + if (albumName) { + // Use system time zone for album dates to ensure all assets get the exact same date. + substitutions['album-startDate-' + token] = albumStartDate + ? DateTime.fromJSDate(albumStartDate, { zone: systemTimeZone }).toFormat(token) + : ''; + substitutions['album-endDate-' + token] = albumEndDate + ? DateTime.fromJSDate(albumEndDate, { zone: systemTimeZone }).toFormat(token) + : ''; + } } return template(substitutions).replaceAll(/\/{2,}/gm, '/'); diff --git a/server/src/services/system-config.service.spec.ts b/server/src/services/system-config.service.spec.ts index 936acf27ad..176e6d6f04 100644 --- a/server/src/services/system-config.service.spec.ts +++ b/server/src/services/system-config.service.spec.ts @@ -6,6 +6,7 @@ import { CQMode, ImageFormat, LogLevel, + OAuthTokenEndpointAuthMethod, QueueName, ToneMapping, TranscodeHWAccel, @@ -119,6 +120,8 @@ const updatedConfig = Object.freeze<SystemConfig>({ scope: 'openid email profile', signingAlgorithm: 'RS256', profileSigningAlgorithm: 'none', + tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod.CLIENT_SECRET_POST, + timeout: 30_000, storageLabelClaim: 'preferred_username', storageQuotaClaim: 'immich_quota', }, diff --git a/server/src/services/user-admin.service.ts b/server/src/services/user-admin.service.ts index 0cba749d36..c1c6cc49ec 100644 --- a/server/src/services/user-admin.service.ts +++ b/server/src/services/user-admin.service.ts @@ -106,21 +106,19 @@ export class UserAdminService extends BaseService { } async getPreferences(auth: AuthDto, id: string): Promise<UserPreferencesResponseDto> { - const { email } = await this.findOrFail(id, { withDeleted: true }); + await this.findOrFail(id, { withDeleted: true }); const metadata = await this.userRepository.getMetadata(id); - const preferences = getPreferences(email, metadata); - return mapPreferences(preferences); + return mapPreferences(getPreferences(metadata)); } async updatePreferences(auth: AuthDto, id: string, dto: UserPreferencesUpdateDto) { - const { email } = await this.findOrFail(id, { withDeleted: false }); + await this.findOrFail(id, { withDeleted: false }); const metadata = await this.userRepository.getMetadata(id); - const preferences = getPreferences(email, metadata); - const newPreferences = mergePreferences(preferences, dto); + const newPreferences = mergePreferences(getPreferences(metadata), dto); await this.userRepository.upsertMetadata(id, { key: UserMetadataKey.PREFERENCES, - value: getPreferencesPartial({ email }, newPreferences), + value: getPreferencesPartial(newPreferences), }); return mapPreferences(newPreferences); diff --git a/server/src/services/user.service.ts b/server/src/services/user.service.ts index 327328eb1c..a0304d51ad 100644 --- a/server/src/services/user.service.ts +++ b/server/src/services/user.service.ts @@ -53,6 +53,7 @@ export class UserService extends BaseService { const update: Updateable<UserTable> = { email: dto.email, name: dto.name, + avatarColor: dto.avatarColor, }; if (dto.password) { @@ -68,18 +69,16 @@ export class UserService extends BaseService { async getMyPreferences(auth: AuthDto): Promise<UserPreferencesResponseDto> { const metadata = await this.userRepository.getMetadata(auth.user.id); - const preferences = getPreferences(auth.user.email, metadata); - return mapPreferences(preferences); + return mapPreferences(getPreferences(metadata)); } async updateMyPreferences(auth: AuthDto, dto: UserPreferencesUpdateDto) { const metadata = await this.userRepository.getMetadata(auth.user.id); - const current = getPreferences(auth.user.email, metadata); - const updated = mergePreferences(current, dto); + const updated = mergePreferences(getPreferences(metadata), dto); await this.userRepository.upsertMetadata(auth.user.id, { key: UserMetadataKey.PREFERENCES, - value: getPreferencesPartial(auth.user, updated), + value: getPreferencesPartial(updated), }); return mapPreferences(updated); diff --git a/server/src/types.ts b/server/src/types.ts index 88ba644739..ba33e97aad 100644 --- a/server/src/types.ts +++ b/server/src/types.ts @@ -11,7 +11,6 @@ import { SyncEntityType, SystemMetadataKey, TranscodeTarget, - UserAvatarColor, UserMetadataKey, VideoCodec, } from 'src/enum'; @@ -298,6 +297,10 @@ export type JobItem = // Metadata Extraction | { name: JobName.QUEUE_METADATA_EXTRACTION; data: IBaseJob } | { name: JobName.METADATA_EXTRACTION; data: IEntityJob } + + // Notifications + | { name: JobName.NOTIFICATIONS_CLEANUP; data?: IBaseJob } + // Sidecar Scanning | { name: JobName.QUEUE_SIDECAR; data: IBaseJob } | { name: JobName.SIDECAR_DISCOVERY; data: IEntityJob } @@ -486,9 +489,6 @@ export interface UserPreferences { enabled: boolean; sidebarWeb: boolean; }; - avatar: { - color: UserAvatarColor; - }; emailNotifications: { enabled: boolean; albumInvite: boolean; diff --git a/server/src/utils/access.ts b/server/src/utils/access.ts index 4e21a9226e..b04d23f114 100644 --- a/server/src/utils/access.ts +++ b/server/src/utils/access.ts @@ -221,6 +221,12 @@ const checkOtherAccess = async (access: AccessRepository, request: OtherAccessRe return access.person.checkFaceOwnerAccess(auth.user.id, ids); } + case Permission.NOTIFICATION_READ: + case Permission.NOTIFICATION_UPDATE: + case Permission.NOTIFICATION_DELETE: { + return access.notification.checkOwnerAccess(auth.user.id, ids); + } + case Permission.TAG_ASSET: case Permission.TAG_READ: case Permission.TAG_UPDATE: diff --git a/server/src/utils/database.spec.ts b/server/src/utils/database.spec.ts new file mode 100644 index 0000000000..4c6a82ad8f --- /dev/null +++ b/server/src/utils/database.spec.ts @@ -0,0 +1,83 @@ +import { asPostgresConnectionConfig } from 'src/utils/database'; + +describe('database utils', () => { + describe('asPostgresConnectionConfig', () => { + it('should handle sslmode=require', () => { + expect( + asPostgresConnectionConfig({ + connectionType: 'url', + url: 'postgres://postgres1:postgres2@database1:54320/immich?sslmode=require', + }), + ).toMatchObject({ ssl: {} }); + }); + + it('should handle sslmode=prefer', () => { + expect( + asPostgresConnectionConfig({ + connectionType: 'url', + url: 'postgres://postgres1:postgres2@database1:54320/immich?sslmode=prefer', + }), + ).toMatchObject({ ssl: {} }); + }); + + it('should handle sslmode=verify-ca', () => { + expect( + asPostgresConnectionConfig({ + connectionType: 'url', + url: 'postgres://postgres1:postgres2@database1:54320/immich?sslmode=verify-ca', + }), + ).toMatchObject({ ssl: {} }); + }); + + it('should handle sslmode=verify-full', () => { + expect( + asPostgresConnectionConfig({ + connectionType: 'url', + url: 'postgres://postgres1:postgres2@database1:54320/immich?sslmode=verify-full', + }), + ).toMatchObject({ ssl: {} }); + }); + + it('should handle sslmode=no-verify', () => { + expect( + asPostgresConnectionConfig({ + connectionType: 'url', + url: 'postgres://postgres1:postgres2@database1:54320/immich?sslmode=no-verify', + }), + ).toMatchObject({ ssl: { rejectUnauthorized: false } }); + }); + + it('should handle ssl=true', () => { + expect( + asPostgresConnectionConfig({ + connectionType: 'url', + url: 'postgres://postgres1:postgres2@database1:54320/immich?ssl=true', + }), + ).toMatchObject({ ssl: true }); + }); + + it('should reject invalid ssl', () => { + expect(() => + asPostgresConnectionConfig({ + connectionType: 'url', + url: 'postgres://postgres1:postgres2@database1:54320/immich?ssl=invalid', + }), + ).toThrowError('Invalid ssl option'); + }); + + it('should handle socket: URLs', () => { + expect( + asPostgresConnectionConfig({ connectionType: 'url', url: 'socket:/run/postgresql?db=database1' }), + ).toMatchObject({ host: '/run/postgresql', database: 'database1' }); + }); + + it('should handle sockets in postgres: URLs', () => { + expect( + asPostgresConnectionConfig({ connectionType: 'url', url: 'postgres:///database2?host=/path/to/socket' }), + ).toMatchObject({ + host: '/path/to/socket', + database: 'database2', + }); + }); + }); +}); diff --git a/server/src/utils/database.ts b/server/src/utils/database.ts index 1af0aa4b4e..b44ea5da46 100644 --- a/server/src/utils/database.ts +++ b/server/src/utils/database.ts @@ -13,33 +13,57 @@ import { } from 'kysely'; import { PostgresJSDialect } from 'kysely-postgres-js'; import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres'; +import { parse } from 'pg-connection-string'; import postgres, { Notice } from 'postgres'; import { columns, Exif, Person } from 'src/database'; import { DB } from 'src/db'; -import { AssetFileType } from 'src/enum'; +import { AssetFileType, DatabaseExtension } from 'src/enum'; import { TimeBucketSize } from 'src/repositories/asset.repository'; import { AssetSearchBuilderOptions } from 'src/repositories/search.repository'; +import { DatabaseConnectionParams, VectorExtension } from 'src/types'; type Ssl = 'require' | 'allow' | 'prefer' | 'verify-full' | boolean | object; -export type PostgresConnectionConfig = { - host?: string; - password?: string; - user?: string; - port?: number; - database?: string; - max?: number; - client_encoding?: string; - ssl?: Ssl; - application_name?: string; - fallback_application_name?: string; - options?: string; -}; - -export const isValidSsl = (ssl?: string | boolean | object): ssl is Ssl => +const isValidSsl = (ssl?: string | boolean | object): ssl is Ssl => typeof ssl !== 'string' || ssl === 'require' || ssl === 'allow' || ssl === 'prefer' || ssl === 'verify-full'; -export const getKyselyConfig = (options: PostgresConnectionConfig): KyselyConfig => { +export const asPostgresConnectionConfig = (params: DatabaseConnectionParams) => { + if (params.connectionType === 'parts') { + return { + host: params.host, + port: params.port, + username: params.username, + password: params.password, + database: params.database, + ssl: undefined, + }; + } + + const { host, port, user, password, database, ...rest } = parse(params.url); + let ssl: Ssl | undefined; + if (rest.ssl) { + if (!isValidSsl(rest.ssl)) { + throw new Error(`Invalid ssl option: ${rest.ssl}`); + } + ssl = rest.ssl; + } + + return { + host: host ?? undefined, + port: port ? Number(port) : undefined, + username: user, + password, + database: database ?? undefined, + ssl, + }; +}; + +export const getKyselyConfig = ( + params: DatabaseConnectionParams, + options: Partial<postgres.Options<Record<string, postgres.PostgresType>>> = {}, +): KyselyConfig => { + const config = asPostgresConnectionConfig(params); + return { dialect: new PostgresJSDialect({ postgres: postgres({ @@ -66,6 +90,12 @@ export const getKyselyConfig = (options: PostgresConnectionConfig): KyselyConfig connection: { TimeZone: 'UTC', }, + host: config.host, + port: config.port, + username: config.username, + password: config.password, + database: config.database, + ssl: config.ssl, ...options, }), }), @@ -343,3 +373,28 @@ export function searchAssetBuilder(kysely: Kysely<DB>, options: AssetSearchBuild .$if(!!(options.withFaces || options.withPeople || options.personIds), (qb) => qb.select(withFacesAndPeople)) .$if(!options.withDeleted, (qb) => qb.where('assets.deletedAt', 'is', null)); } + +type VectorIndexOptions = { vectorExtension: VectorExtension; table: string; indexName: string }; + +export function vectorIndexQuery({ vectorExtension, table, indexName }: VectorIndexOptions): string { + switch (vectorExtension) { + case DatabaseExtension.VECTORS: { + return ` + CREATE INDEX IF NOT EXISTS ${indexName} ON ${table} + USING vectors (embedding vector_cos_ops) WITH (options = $$ + [indexing.hnsw] + m = 16 + ef_construction = 300 + $$)`; + } + case DatabaseExtension.VECTOR: { + return ` + CREATE INDEX IF NOT EXISTS ${indexName} ON ${table} + USING hnsw (embedding vector_cosine_ops) + WITH (ef_construction = 300, m = 16)`; + } + default: { + throw new Error(`Unsupported vector extension: '${vectorExtension}'`); + } + } +} diff --git a/server/src/utils/mime-types.ts b/server/src/utils/mime-types.ts index b1a9c77588..6aad418d9f 100644 --- a/server/src/utils/mime-types.ts +++ b/server/src/utils/mime-types.ts @@ -34,45 +34,40 @@ const raw: Record<string, string[]> = { '.x3f': ['image/x3f', 'image/x-sigma-x3f'], }; +/** + * list of supported image extensions from https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types excluding svg + * @TODO share with the client + * @see {@link web/src/lib/utils/asset-utils.ts#L329} + **/ +const webSupportedImage = { + '.avif': ['image/avif'], + '.gif': ['image/gif'], + '.jpeg': ['image/jpeg'], + '.jpg': ['image/jpeg'], + '.png': ['image/png', 'image/apng'], + '.webp': ['image/webp'], +}; + const image: Record<string, string[]> = { ...raw, - '.avif': ['image/avif'], + ...webSupportedImage, '.bmp': ['image/bmp'], - '.gif': ['image/gif'], '.heic': ['image/heic'], '.heif': ['image/heif'], '.hif': ['image/hif'], '.insp': ['image/jpeg'], '.jp2': ['image/jp2'], '.jpe': ['image/jpeg'], - '.jpeg': ['image/jpeg'], - '.jpg': ['image/jpeg'], '.jxl': ['image/jxl'], - '.png': ['image/png'], '.svg': ['image/svg'], '.tif': ['image/tiff'], '.tiff': ['image/tiff'], - '.webp': ['image/webp'], }; const extensionOverrides: Record<string, string> = { 'image/jpeg': '.jpg', }; -/** - * list of supported image extensions from https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types excluding svg - * @TODO share with the client - * @see {@link web/src/lib/utils/asset-utils.ts#L329} - **/ -const webSupportedImageMimeTypes = new Set([ - 'image/apng', - 'image/avif', - 'image/gif', - 'image/jpeg', - 'image/png', - 'image/webp', -]); - const profileExtensions = new Set(['.avif', '.dng', '.heic', '.heif', '.jpeg', '.jpg', '.png', '.webp', '.svg']); const profile: Record<string, string[]> = Object.fromEntries( Object.entries(image).filter(([key]) => profileExtensions.has(key)), @@ -123,7 +118,7 @@ export const mimeTypes = { isAsset: (filename: string) => isType(filename, image) || isType(filename, video), isImage: (filename: string) => isType(filename, image), - isWebSupportedImage: (filename: string) => webSupportedImageMimeTypes.has(lookup(filename)), + isWebSupportedImage: (filename: string) => isType(filename, webSupportedImage), isProfile: (filename: string) => isType(filename, profile), isSidecar: (filename: string) => isType(filename, sidecar), isVideo: (filename: string) => isType(filename, video), diff --git a/server/src/utils/preferences.ts b/server/src/utils/preferences.ts index 584c5300cd..a013c0b74e 100644 --- a/server/src/utils/preferences.ts +++ b/server/src/utils/preferences.ts @@ -1,16 +1,11 @@ import _ from 'lodash'; import { UserPreferencesUpdateDto } from 'src/dtos/user-preferences.dto'; -import { UserAvatarColor, UserMetadataKey } from 'src/enum'; +import { UserMetadataKey } from 'src/enum'; import { DeepPartial, UserMetadataItem, UserPreferences } from 'src/types'; import { HumanReadableSize } from 'src/utils/bytes'; import { getKeysDeep } from 'src/utils/misc'; -const getDefaultPreferences = (user: { email: string }): UserPreferences => { - const values = Object.values(UserAvatarColor); - const randomIndex = Math.floor( - [...user.email].map((letter) => letter.codePointAt(0) ?? 0).reduce((a, b) => a + b, 0) % values.length, - ); - +const getDefaultPreferences = (): UserPreferences => { return { folders: { enabled: false, @@ -34,9 +29,6 @@ const getDefaultPreferences = (user: { email: string }): UserPreferences => { enabled: false, sidebarWeb: false, }, - avatar: { - color: values[randomIndex], - }, emailNotifications: { enabled: true, albumInvite: true, @@ -53,8 +45,8 @@ const getDefaultPreferences = (user: { email: string }): UserPreferences => { }; }; -export const getPreferences = (email: string, metadata: UserMetadataItem[]): UserPreferences => { - const preferences = getDefaultPreferences({ email }); +export const getPreferences = (metadata: UserMetadataItem[]): UserPreferences => { + const preferences = getDefaultPreferences(); const item = metadata.find(({ key }) => key === UserMetadataKey.PREFERENCES); const partial = item?.value || {}; for (const property of getKeysDeep(partial)) { @@ -64,8 +56,8 @@ export const getPreferences = (email: string, metadata: UserMetadataItem[]): Use return preferences; }; -export const getPreferencesPartial = (user: { email: string }, newPreferences: UserPreferences) => { - const defaultPreferences = getDefaultPreferences(user); +export const getPreferencesPartial = (newPreferences: UserPreferences) => { + const defaultPreferences = getDefaultPreferences(); const partial: DeepPartial<UserPreferences> = {}; for (const property of getKeysDeep(defaultPreferences)) { const newValue = _.get(newPreferences, property); diff --git a/server/src/utils/response.ts b/server/src/utils/response.ts index 679d947afb..a50e86a4ff 100644 --- a/server/src/utils/response.ts +++ b/server/src/utils/response.ts @@ -15,6 +15,8 @@ export const respondWithCookie = <T>(res: Response, body: T, { isSecure, values const cookieOptions: Record<ImmichCookie, CookieOptions> = { [ImmichCookie.AUTH_TYPE]: defaults, [ImmichCookie.ACCESS_TOKEN]: defaults, + [ImmichCookie.OAUTH_STATE]: defaults, + [ImmichCookie.OAUTH_CODE_VERIFIER]: defaults, // no httpOnly so that the client can know the auth state [ImmichCookie.IS_AUTHENTICATED]: { ...defaults, httpOnly: false }, [ImmichCookie.SHARED_LINK_TOKEN]: { ...defaults, maxAge: Duration.fromObject({ days: 1 }).toMillis() }, diff --git a/server/src/workers/api.ts b/server/src/workers/api.ts index ddf6e50aa2..4248b23d30 100644 --- a/server/src/workers/api.ts +++ b/server/src/workers/api.ts @@ -1,6 +1,7 @@ import { NestFactory } from '@nestjs/core'; import { NestExpressApplication } from '@nestjs/platform-express'; import { json } from 'body-parser'; +import compression from 'compression'; import cookieParser from 'cookie-parser'; import { existsSync } from 'node:fs'; import sirv from 'sirv'; @@ -60,6 +61,7 @@ async function bootstrap() { ); } app.use(app.get(ApiService).ssr(excludePaths)); + app.use(compression()); const server = await (host ? app.listen(port, host) : app.listen(port)); server.requestTimeout = 24 * 60 * 60 * 1000; diff --git a/server/test/fixtures/user.stub.ts b/server/test/fixtures/user.stub.ts index f0043d174a..0db58e2eed 100644 --- a/server/test/fixtures/user.stub.ts +++ b/server/test/fixtures/user.stub.ts @@ -1,5 +1,5 @@ import { UserAdmin } from 'src/database'; -import { UserAvatarColor, UserMetadataKey, UserStatus } from 'src/enum'; +import { UserStatus } from 'src/enum'; import { authStub } from 'test/fixtures/auth.stub'; export const userStub = { @@ -12,6 +12,7 @@ export const userStub = { storageLabel: 'admin', oauthId: '', shouldChangePassword: false, + avatarColor: null, profileImagePath: '', createdAt: new Date('2021-01-01'), deletedAt: null, @@ -28,16 +29,12 @@ export const userStub = { storageLabel: null, oauthId: '', shouldChangePassword: false, + avatarColor: null, profileImagePath: '', createdAt: new Date('2021-01-01'), deletedAt: null, updatedAt: new Date('2021-01-01'), - metadata: [ - { - key: UserMetadataKey.PREFERENCES, - value: { avatar: { color: UserAvatarColor.PRIMARY } }, - }, - ], + metadata: [], quotaSizeInBytes: null, quotaUsageInBytes: 0, }, @@ -50,6 +47,7 @@ export const userStub = { storageLabel: null, oauthId: '', shouldChangePassword: false, + avatarColor: null, profileImagePath: '', createdAt: new Date('2021-01-01'), deletedAt: null, diff --git a/server/test/medium.factory.ts b/server/test/medium.factory.ts index d3ab876e07..388c4df96b 100644 --- a/server/test/medium.factory.ts +++ b/server/test/medium.factory.ts @@ -13,9 +13,11 @@ import { AssetRepository } from 'src/repositories/asset.repository'; import { ConfigRepository } from 'src/repositories/config.repository'; import { CryptoRepository } from 'src/repositories/crypto.repository'; import { DatabaseRepository } from 'src/repositories/database.repository'; +import { EmailRepository } from 'src/repositories/email.repository'; import { JobRepository } from 'src/repositories/job.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { MemoryRepository } from 'src/repositories/memory.repository'; +import { NotificationRepository } from 'src/repositories/notification.repository'; import { PartnerRepository } from 'src/repositories/partner.repository'; import { PersonRepository } from 'src/repositories/person.repository'; import { SearchRepository } from 'src/repositories/search.repository'; @@ -42,10 +44,12 @@ type RepositoriesTypes = { config: ConfigRepository; crypto: CryptoRepository; database: DatabaseRepository; + email: EmailRepository; job: JobRepository; user: UserRepository; logger: LoggingRepository; memory: MemoryRepository; + notification: NotificationRepository; partner: PartnerRepository; person: PersonRepository; search: SearchRepository; @@ -142,6 +146,11 @@ export const getRepository = <K extends keyof RepositoriesTypes>(key: K, db: Kys return new DatabaseRepository(db, new LoggingRepository(undefined, configRepo), configRepo); } + case 'email': { + const logger = new LoggingRepository(undefined, new ConfigRepository()); + return new EmailRepository(logger); + } + case 'logger': { const configMock = { getEnv: () => ({ noColor: false }) }; return new LoggingRepository(undefined, configMock as ConfigRepository); @@ -151,6 +160,10 @@ export const getRepository = <K extends keyof RepositoriesTypes>(key: K, db: Kys return new MemoryRepository(db); } + case 'notification': { + return new NotificationRepository(db); + } + case 'partner': { return new PartnerRepository(db); } @@ -160,7 +173,7 @@ export const getRepository = <K extends keyof RepositoriesTypes>(key: K, db: Kys } case 'search': { - return new SearchRepository(db); + return new SearchRepository(db, new ConfigRepository()); } case 'session': { @@ -221,6 +234,10 @@ const getRepositoryMock = <K extends keyof RepositoryMocks>(key: K) => { }); } + case 'email': { + return automock(EmailRepository, { args: [{ setContext: () => {} }] }); + } + case 'job': { return automock(JobRepository, { args: [undefined, undefined, undefined, { setContext: () => {} }] }); } @@ -234,6 +251,10 @@ const getRepositoryMock = <K extends keyof RepositoryMocks>(key: K) => { return automock(MemoryRepository); } + case 'notification': { + return automock(NotificationRepository); + } + case 'partner': { return automock(PartnerRepository); } @@ -284,6 +305,7 @@ export const asDeps = (repositories: ServiceOverrides) => { repositories.crypto || getRepositoryMock('crypto'), repositories.database || getRepositoryMock('database'), repositories.downloadRepository, + repositories.email || getRepositoryMock('email'), repositories.event, repositories.job || getRepositoryMock('job'), repositories.library, @@ -293,7 +315,7 @@ export const asDeps = (repositories: ServiceOverrides) => { repositories.memory || getRepositoryMock('memory'), repositories.metadata, repositories.move, - repositories.notification, + repositories.notification || getRepositoryMock('notification'), repositories.oauth, repositories.partner || getRepositoryMock('partner'), repositories.person || getRepositoryMock('person'), diff --git a/server/test/medium/globalSetup.ts b/server/test/medium/globalSetup.ts index 46eb1a733f..e63c9f5224 100644 --- a/server/test/medium/globalSetup.ts +++ b/server/test/medium/globalSetup.ts @@ -1,5 +1,4 @@ import { Kysely } from 'kysely'; -import { parse } from 'pg-connection-string'; import { DB } from 'src/db'; import { ConfigRepository } from 'src/repositories/config.repository'; import { DatabaseRepository } from 'src/repositories/database.repository'; @@ -37,19 +36,10 @@ const globalSetup = async () => { const postgresPort = postgresContainer.getMappedPort(5432); const postgresUrl = `postgres://postgres:postgres@localhost:${postgresPort}/immich`; - const parsed = parse(postgresUrl); process.env.IMMICH_TEST_POSTGRES_URL = postgresUrl; - const db = new Kysely<DB>( - getKyselyConfig({ - ...parsed, - ssl: false, - host: parsed.host ?? undefined, - port: parsed.port ? Number(parsed.port) : undefined, - database: parsed.database ?? undefined, - }), - ); + const db = new Kysely<DB>(getKyselyConfig({ connectionType: 'url', url: postgresUrl })); const configRepository = new ConfigRepository(); const logger = new LoggingRepository(undefined, configRepository); diff --git a/server/test/medium/specs/controllers/notification.controller.spec.ts b/server/test/medium/specs/controllers/notification.controller.spec.ts new file mode 100644 index 0000000000..f4a0ec82d5 --- /dev/null +++ b/server/test/medium/specs/controllers/notification.controller.spec.ts @@ -0,0 +1,86 @@ +import { NotificationController } from 'src/controllers/notification.controller'; +import { AuthService } from 'src/services/auth.service'; +import { NotificationService } from 'src/services/notification.service'; +import request from 'supertest'; +import { errorDto } from 'test/medium/responses'; +import { createControllerTestApp, TestControllerApp } from 'test/medium/utils'; +import { factory } from 'test/small.factory'; + +describe(NotificationController.name, () => { + let realApp: TestControllerApp; + let mockApp: TestControllerApp; + + beforeEach(async () => { + realApp = await createControllerTestApp({ authType: 'real' }); + mockApp = await createControllerTestApp({ authType: 'mock' }); + }); + + describe('GET /notifications', () => { + it('should require authentication', async () => { + const { status, body } = await request(realApp.getHttpServer()).get('/notifications'); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should call the service with an auth dto', async () => { + const auth = factory.auth({ user: factory.user() }); + mockApp.getMockedService(AuthService).authenticate.mockResolvedValue(auth); + const service = mockApp.getMockedService(NotificationService); + + const { status } = await request(mockApp.getHttpServer()) + .get('/notifications') + .set('Authorization', `Bearer token`); + + expect(status).toBe(200); + expect(service.search).toHaveBeenCalledWith(auth, {}); + }); + + it(`should reject an invalid notification level`, async () => { + const auth = factory.auth({ user: factory.user() }); + mockApp.getMockedService(AuthService).authenticate.mockResolvedValue(auth); + const service = mockApp.getMockedService(NotificationService); + + const { status, body } = await request(mockApp.getHttpServer()) + .get(`/notifications`) + .query({ level: 'invalid' }) + .set('Authorization', `Bearer token`); + + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest([expect.stringContaining('level must be one of the following values')])); + expect(service.search).not.toHaveBeenCalled(); + }); + }); + + describe('PUT /notifications', () => { + it('should require authentication', async () => { + const { status, body } = await request(realApp.getHttpServer()) + .put(`/notifications`) + .send({ ids: [], readAt: new Date().toISOString() }); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + }); + + describe('GET /notifications/:id', () => { + it('should require authentication', async () => { + const { status, body } = await request(realApp.getHttpServer()).get(`/notifications/${factory.uuid()}`); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + }); + + describe('PUT /notifications/:id', () => { + it('should require authentication', async () => { + const { status, body } = await request(realApp.getHttpServer()) + .put(`/notifications/${factory.uuid()}`) + .send({ readAt: factory.date() }); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + }); + + afterAll(async () => { + await realApp.close(); + await mockApp.close(); + }); +}); diff --git a/server/test/repositories/access.repository.mock.ts b/server/test/repositories/access.repository.mock.ts index ec5115b839..5b98b95e27 100644 --- a/server/test/repositories/access.repository.mock.ts +++ b/server/test/repositories/access.repository.mock.ts @@ -37,6 +37,10 @@ export const newAccessRepositoryMock = (): IAccessRepositoryMock => { checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()), }, + notification: { + checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()), + }, + person: { checkFaceOwnerAccess: vitest.fn().mockResolvedValue(new Set()), checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()), diff --git a/server/test/repositories/asset.repository.mock.ts b/server/test/repositories/asset.repository.mock.ts index d540e55b2a..d8230a23f3 100644 --- a/server/test/repositories/asset.repository.mock.ts +++ b/server/test/repositories/asset.repository.mock.ts @@ -13,14 +13,11 @@ export const newAssetRepositoryMock = (): Mocked<RepositoryInterface<AssetReposi getByIds: vitest.fn().mockResolvedValue([]), getByIdsWithAllRelationsButStacks: vitest.fn().mockResolvedValue([]), getByDeviceIds: vitest.fn(), - getByUserId: vitest.fn(), getById: vitest.fn(), - getWithout: vitest.fn(), getByChecksum: vitest.fn(), getByChecksums: vitest.fn(), getUploadAssetIdByChecksum: vitest.fn(), getRandom: vitest.fn(), - getAll: vitest.fn().mockResolvedValue({ items: [], hasNextPage: false }), getAllByDeviceId: vitest.fn(), getLivePhotoCount: vitest.fn(), getLibraryAssetCount: vitest.fn(), diff --git a/server/test/repositories/config.repository.mock.ts b/server/test/repositories/config.repository.mock.ts index 7c5450c36e..4943a56a33 100644 --- a/server/test/repositories/config.repository.mock.ts +++ b/server/test/repositories/config.repository.mock.ts @@ -21,19 +21,12 @@ const envData: EnvData = { database: { config: { - kysely: { database: 'immich', host: 'database', port: 5432 }, - typeorm: { - connectionType: 'parts', - database: 'immich', - type: 'postgres', - host: 'database', - port: 5432, - username: 'postgres', - password: 'postgres', - name: 'immich', - synchronize: false, - migrationsRun: true, - }, + connectionType: 'parts', + database: 'immich', + host: 'database', + port: 5432, + username: 'postgres', + password: 'postgres', }, skipMigrations: false, diff --git a/server/test/repositories/media.repository.mock.ts b/server/test/repositories/media.repository.mock.ts index e9f624d6bf..c6ab11aaa1 100644 --- a/server/test/repositories/media.repository.mock.ts +++ b/server/test/repositories/media.repository.mock.ts @@ -8,7 +8,7 @@ export const newMediaRepositoryMock = (): Mocked<RepositoryInterface<MediaReposi writeExif: vitest.fn().mockImplementation(() => Promise.resolve()), generateThumbhash: vitest.fn().mockResolvedValue(Buffer.from('')), decodeImage: vitest.fn().mockResolvedValue({ data: Buffer.from(''), info: {} }), - extract: vitest.fn().mockResolvedValue(false), + extract: vitest.fn().mockResolvedValue(null), probe: vitest.fn(), transcode: vitest.fn(), getImageDimensions: vitest.fn(), diff --git a/server/test/small.factory.ts b/server/test/small.factory.ts index 29eef7002e..d2742f7f80 100644 --- a/server/test/small.factory.ts +++ b/server/test/small.factory.ts @@ -140,6 +140,7 @@ const userFactory = (user: Partial<User> = {}) => ({ id: newUuid(), name: 'Test User', email: 'test@immich.cloud', + avatarColor: null, profileImagePath: '', profileChangedAt: newDate(), ...user, @@ -155,6 +156,7 @@ const userAdminFactory = (user: Partial<UserAdmin> = {}) => { storageLabel = null, shouldChangePassword = false, isAdmin = false, + avatarColor = null, createdAt = newDate(), updatedAt = newDate(), deletedAt = null, @@ -173,6 +175,7 @@ const userAdminFactory = (user: Partial<UserAdmin> = {}) => { storageLabel, shouldChangePassword, isAdmin, + avatarColor, createdAt, updatedAt, deletedAt, @@ -311,4 +314,5 @@ export const factory = { sidecarWrite: assetSidecarWriteFactory, }, uuid: newUuid, + date: newDate, }; diff --git a/server/test/utils.ts b/server/test/utils.ts index 52984d97a2..2c444f491e 100644 --- a/server/test/utils.ts +++ b/server/test/utils.ts @@ -1,9 +1,9 @@ import { ClassConstructor } from 'class-transformer'; -import { Kysely, sql } from 'kysely'; +import { Kysely } from 'kysely'; import { ChildProcessWithoutNullStreams } from 'node:child_process'; import { Writable } from 'node:stream'; -import { parse } from 'pg-connection-string'; import { PNG } from 'pngjs'; +import postgres from 'postgres'; import { DB } from 'src/db'; import { AccessRepository } from 'src/repositories/access.repository'; import { ActivityRepository } from 'src/repositories/activity.repository'; @@ -18,6 +18,7 @@ import { CronRepository } from 'src/repositories/cron.repository'; import { CryptoRepository } from 'src/repositories/crypto.repository'; import { DatabaseRepository } from 'src/repositories/database.repository'; import { DownloadRepository } from 'src/repositories/download.repository'; +import { EmailRepository } from 'src/repositories/email.repository'; import { EventRepository } from 'src/repositories/event.repository'; import { JobRepository } from 'src/repositories/job.repository'; import { LibraryRepository } from 'src/repositories/library.repository'; @@ -49,7 +50,7 @@ import { VersionHistoryRepository } from 'src/repositories/version-history.repos import { ViewRepository } from 'src/repositories/view-repository'; import { BaseService } from 'src/services/base.service'; import { RepositoryInterface } from 'src/types'; -import { getKyselyConfig } from 'src/utils/database'; +import { asPostgresConnectionConfig, getKyselyConfig } from 'src/utils/database'; import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; import { newConfigRepositoryMock } from 'test/repositories/config.repository.mock'; @@ -124,6 +125,7 @@ export type ServiceOverrides = { crypto: CryptoRepository; database: DatabaseRepository; downloadRepository: DownloadRepository; + email: EmailRepository; event: EventRepository; job: JobRepository; library: LibraryRepository; @@ -190,6 +192,7 @@ export const newTestService = <T extends BaseService>( config: newConfigRepositoryMock(), database: newDatabaseRepositoryMock(), downloadRepository: automock(DownloadRepository, { strict: false }), + email: automock(EmailRepository, { args: [loggerMock] }), // eslint-disable-next-line no-sparse-arrays event: automock(EventRepository, { args: [, , loggerMock], strict: false }), job: newJobRepositoryMock(), @@ -201,7 +204,7 @@ export const newTestService = <T extends BaseService>( memory: automock(MemoryRepository), metadata: newMetadataRepositoryMock(), move: automock(MoveRepository, { strict: false }), - notification: automock(NotificationRepository, { args: [loggerMock] }), + notification: automock(NotificationRepository), oauth: automock(OAuthRepository, { args: [loggerMock] }), partner: automock(PartnerRepository, { strict: false }), person: newPersonRepositoryMock(), @@ -240,6 +243,7 @@ export const newTestService = <T extends BaseService>( overrides.crypto || (mocks.crypto as As<CryptoRepository>), overrides.database || (mocks.database as As<DatabaseRepository>), overrides.downloadRepository || (mocks.downloadRepository as As<DownloadRepository>), + overrides.email || (mocks.email as As<EmailRepository>), overrides.event || (mocks.event as As<EventRepository>), overrides.job || (mocks.job as As<JobRepository>), overrides.library || (mocks.library as As<LibraryRepository>), @@ -297,24 +301,20 @@ function* newPngFactory() { const pngFactory = newPngFactory(); +const withDatabase = (url: string, name: string) => url.replace('/immich', `/${name}`); + export const getKyselyDB = async (suffix?: string): Promise<Kysely<DB>> => { - const parsed = parse(process.env.IMMICH_TEST_POSTGRES_URL!); + const testUrl = process.env.IMMICH_TEST_POSTGRES_URL!; + const sql = postgres({ + ...asPostgresConnectionConfig({ connectionType: 'url', url: withDatabase(testUrl, 'postgres') }), + max: 1, + }); - const parsedOptions = { - ...parsed, - ssl: false, - host: parsed.host ?? undefined, - port: parsed.port ? Number(parsed.port) : undefined, - database: parsed.database ?? undefined, - }; - - const kysely = new Kysely<DB>(getKyselyConfig({ ...parsedOptions, max: 1, database: 'postgres' })); const randomSuffix = Math.random().toString(36).slice(2, 7); const dbName = `immich_${suffix ?? randomSuffix}`; + await sql.unsafe(`CREATE DATABASE ${dbName} WITH TEMPLATE immich OWNER postgres;`); - await sql.raw(`CREATE DATABASE ${dbName} WITH TEMPLATE immich OWNER postgres;`).execute(kysely); - - return new Kysely<DB>(getKyselyConfig({ ...parsedOptions, database: dbName })); + return new Kysely<DB>(getKyselyConfig({ connectionType: 'url', url: withDatabase(testUrl, dbName) })); }; export const newRandomImage = () => { diff --git a/server/test/vitest.config.mjs b/server/test/vitest.config.mjs index a6929bf806..a22a6751c3 100644 --- a/server/test/vitest.config.mjs +++ b/server/test/vitest.config.mjs @@ -20,12 +20,6 @@ export default defineConfig({ 'src/services/index.ts', 'src/sql-tools/from-database/index.ts', ], - thresholds: { - lines: 85, - statements: 85, - branches: 90, - functions: 85, - }, }, server: { deps: { diff --git a/web/Dockerfile b/web/Dockerfile index 8c2e67e62e..c4244a1aa0 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -1,4 +1,4 @@ -FROM node:22.14.0-alpine3.20@sha256:40be979442621049f40b1d51a26b55e281246b5de4e5f51a18da7beb6e17e3f9 +FROM node:22.15.0-alpine3.20@sha256:686b8892b69879ef5bfd6047589666933508f9a5451c67320df3070ba0e9807b RUN apk add --no-cache tini USER node diff --git a/web/bin/immich-web b/web/bin/immich-web index 6b2880d6d2..ea748863db 100755 --- a/web/bin/immich-web +++ b/web/bin/immich-web @@ -5,10 +5,17 @@ TYPESCRIPT_SDK=/usr/src/open-api/typescript-sdk npm --prefix "$TYPESCRIPT_SDK" install npm --prefix "$TYPESCRIPT_SDK" run build + +COUNT=0 UPSTREAM="${IMMICH_SERVER_URL:-http://immich-server:2283/}" -until wget --spider --quiet "${UPSTREAM}/api/server/config"; do - echo 'waiting for api server...' +until wget --spider --quiet "${UPSTREAM}/api/server/config" > /dev/null 2>&1; do + if [ $((COUNT % 10)) -eq 0 ]; then + echo "Waiting for $UPSTREAM to start..." + fi + COUNT=$((COUNT + 1)) sleep 1 done +echo "Connected to $UPSTREAM" + node ./node_modules/.bin/vite dev --host 0.0.0.0 --port 3000 diff --git a/web/eslint.config.js b/web/eslint.config.js index 5c24cd1aeb..9ced619504 100644 --- a/web/eslint.config.js +++ b/web/eslint.config.js @@ -58,6 +58,8 @@ export default typescriptEslint.config( }, }, + ignores: ['**/service-worker/**'], + rules: { '@typescript-eslint/no-unused-vars': [ 'warn', diff --git a/web/package-lock.json b/web/package-lock.json index 57af7fa56d..c76dd64840 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,17 +1,17 @@ { "name": "immich-web", - "version": "1.131.3", + "version": "1.132.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-web", - "version": "1.131.3", + "version": "1.132.3", "license": "GNU Affero General Public License version 3", "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.17.3", + "@immich/ui": "^0.18.1", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", @@ -59,6 +59,7 @@ "dotenv": "^16.4.7", "eslint": "^9.18.0", "eslint-config-prettier": "^10.0.0", + "eslint-p": "^0.21.0", "eslint-plugin-svelte": "^3.0.0", "eslint-plugin-unicorn": "^57.0.0", "factory.ts": "^1.4.1", @@ -81,13 +82,13 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.131.3", + "version": "1.132.3", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.14.0", + "@types/node": "^22.14.1", "typescript": "^5.3.3" } }, @@ -495,9 +496,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", - "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz", + "integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==", "cpu": [ "arm64" ], @@ -528,9 +529,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", - "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz", + "integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==", "cpu": [ "arm64" ], @@ -692,9 +693,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", + "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -742,9 +743,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", - "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", + "version": "9.25.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.1.tgz", + "integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==", "dev": true, "license": "MIT", "engines": { @@ -775,19 +776,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@faker-js/faker": { "version": "9.7.0", "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.7.0.tgz", @@ -1332,9 +1320,9 @@ "link": true }, "node_modules/@immich/ui": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.17.4.tgz", - "integrity": "sha512-a6M7Fxno5fwY5A0kxdluS8r+A4L6xZhSTKMW8c8hoFhQHvbBTHAsGFKQF3GOEQLOlUuvsS2Lt7dMevBlAPgo/A==", + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.18.1.tgz", + "integrity": "sha512-XWWO6OTfH3MektyxCn0hWefZyOGyWwwx/2zHinuShpxTHSyfveJ4mOkFP8DkyMz0dnvJ1EfdkPBMkld3y5R/Hw==", "license": "GNU Affero General Public License version 3", "dependencies": { "@mdi/js": "^7.4.47", @@ -1705,50 +1693,50 @@ } }, "node_modules/@photo-sphere-viewer/core": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.13.1.tgz", - "integrity": "sha512-f5fkoGPCBUt5BD9S9U37h+UDmo2slMZThesoH82iyjKR6uRyYnJvJXwopwo+iMfc6x1ZAWmustBBuES4qKzx+g==", + "version": "5.13.2", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.13.2.tgz", + "integrity": "sha512-rL4Ey39Prx4Iyxt1f2tAqlXvqu4/ovXfUvIpLt540OpZJiFjWccs6qLywof9vuhBJ7PXHudHWCjRPce0W8kx8w==", "license": "MIT", "dependencies": { "three": "^0.175.0" } }, "node_modules/@photo-sphere-viewer/equirectangular-video-adapter": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-video-adapter/-/equirectangular-video-adapter-5.13.1.tgz", - "integrity": "sha512-5LCMMc1bnKMFvR//TKguSwyBEBF+fTYLOGnnCzS7HHHNr8jc+bmaxBsPhOENZf8VcoupXYxo4KRNfYwIB0nTEA==", + "version": "5.13.2", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-video-adapter/-/equirectangular-video-adapter-5.13.2.tgz", + "integrity": "sha512-Ln9VyZSGAEjqtJ5dYluiSYkUF87FsOwzZvQoEgAt4odQR/q7ktSaVDdRfuuTMcbBKq6kTdsavNzdg+g877WyhA==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.1", - "@photo-sphere-viewer/video-plugin": "5.13.1" + "@photo-sphere-viewer/core": "5.13.2", + "@photo-sphere-viewer/video-plugin": "5.13.2" } }, "node_modules/@photo-sphere-viewer/resolution-plugin": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/resolution-plugin/-/resolution-plugin-5.13.1.tgz", - "integrity": "sha512-XVxR5rAtGYbcy0PQfgGgMAuRAg5gb/tRjgMiB9zzQ6sESLviWCqvk247z4Q6J4TxNYeGSQzKbyous1eS+nUqTg==", + "version": "5.13.2", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/resolution-plugin/-/resolution-plugin-5.13.2.tgz", + "integrity": "sha512-T2bUvtKqhPk7FVqRJfynWhnglMpar5FNxCgf3EsnFjV9g+Xnc0LmOLlCeNmsCWXv0lRmNbohDMRN1WpY1O3ojA==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.1", - "@photo-sphere-viewer/settings-plugin": "5.13.1" + "@photo-sphere-viewer/core": "5.13.2", + "@photo-sphere-viewer/settings-plugin": "5.13.2" } }, "node_modules/@photo-sphere-viewer/settings-plugin": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/settings-plugin/-/settings-plugin-5.13.1.tgz", - "integrity": "sha512-W2naZCP9huhN6cmFcGfgJEvxqrBB481/an8o/qice5iIH9xw50qXiDq6czLCtUo8GD4P9ULtsoWo9DUT2EsWzw==", + "version": "5.13.2", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/settings-plugin/-/settings-plugin-5.13.2.tgz", + "integrity": "sha512-z1539qy4XC9UextvgxFBBZqiNKQ1DzaI4EZRbrRbfG6LnSsjKwGgX8gIZ8ZpBHoZdU+b2d8PRPmNKiSjhYOvGA==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.1" + "@photo-sphere-viewer/core": "5.13.2" } }, "node_modules/@photo-sphere-viewer/video-plugin": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/video-plugin/-/video-plugin-5.13.1.tgz", - "integrity": "sha512-GmjI4weoKRCOACNEIloL3XSAbYVyrO6s8esJfmpjdGKdDr3kQjKK+oiplRSD28ALIhSxyqemNOGodfgzWH7xLA==", + "version": "5.13.2", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/video-plugin/-/video-plugin-5.13.2.tgz", + "integrity": "sha512-6/tajOJaPUDP7mwtdQZul+KNfjL2sUPUt7EcAHZ9KcSq1WcwqZfaUYSCdKaW2uxdpn4BLESSD1h0mJdWNw7vJA==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.1" + "@photo-sphere-viewer/core": "5.13.2" } }, "node_modules/@pkgjs/parseargs": { @@ -2455,17 +2443,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", - "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.0.tgz", + "integrity": "sha512-evaQJZ/J/S4wisevDvC1KFZkPzRetH8kYZbkgcTRyql3mcKsf+ZFDV1BVWUGTCAW5pQHoqn5gK5b8kn7ou9aFQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/type-utils": "8.30.1", - "@typescript-eslint/utils": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/scope-manager": "8.31.0", + "@typescript-eslint/type-utils": "8.31.0", + "@typescript-eslint/utils": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -2485,16 +2473,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", - "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.31.0.tgz", + "integrity": "sha512-67kYYShjBR0jNI5vsf/c3WG4u+zDnCTHTPqVMQguffaWWFs7artgwKmfwdifl+r6XyM5LYLas/dInj2T0SgJyw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/typescript-estree": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/scope-manager": "8.31.0", + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/typescript-estree": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0", "debug": "^4.3.4" }, "engines": { @@ -2510,14 +2498,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", - "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.31.0.tgz", + "integrity": "sha512-knO8UyF78Nt8O/B64i7TlGXod69ko7z6vJD9uhSlm0qkAbGeRUSudcm0+K/4CrRjrpiHfBCjMWlc08Vav1xwcw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1" + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2528,14 +2516,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", - "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.31.0.tgz", + "integrity": "sha512-DJ1N1GdjI7IS7uRlzJuEDCgDQix3ZVYVtgeWEyhyn4iaoitpMBX6Ndd488mXSx0xah/cONAkEaYyylDyAeHMHg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.30.1", - "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/typescript-estree": "8.31.0", + "@typescript-eslint/utils": "8.31.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, @@ -2552,9 +2540,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", - "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.31.0.tgz", + "integrity": "sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ==", "dev": true, "license": "MIT", "engines": { @@ -2566,14 +2554,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", - "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.0.tgz", + "integrity": "sha512-xLmgn4Yl46xi6aDSZ9KkyfhhtnYI15/CvHbpOy/eR5NWhK/BK8wc709KKwhAR0m4ZKRP7h07bm4BWUYOCuRpQQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2619,16 +2607,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", - "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.31.0.tgz", + "integrity": "sha512-qi6uPLt9cjTFxAb1zGNgTob4x9ur7xC6mHQJ8GwEzGMGE9tYniublmJaowOJ9V2jUzxrltTPfdG2nKlWsq0+Ww==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/typescript-estree": "8.30.1" + "@typescript-eslint/scope-manager": "8.31.0", + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/typescript-estree": "8.31.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2643,13 +2631,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", - "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.0.tgz", + "integrity": "sha512-QcGHmlRHWOl93o64ZUMNewCdwKGU6WItOU52H0djgNmn1EOrhVudrDzXz4OycCRSCPwFCDrE2iIt5vmuUdHxuQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/types": "8.31.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -2661,9 +2649,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.1.tgz", - "integrity": "sha512-MgV6D2dhpD6Hp/uroUoAIvFqA8AuvXEFBC2eepG3WFc1pxTfdk1LEqqkWoWhjz+rytoqrnUUCdf6Lzco3iHkLQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.2.tgz", + "integrity": "sha512-XDdaDOeaTMAMYW7N63AqoK32sYUWbXnTkC6tEbVcu3RlU1bB9of32T+PGf8KZvxqLNqeXhafDFqCkwpf2+dyaQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2676,7 +2664,7 @@ "istanbul-reports": "^3.1.7", "magic-string": "^0.30.17", "magicast": "^0.3.5", - "std-env": "^3.8.1", + "std-env": "^3.9.0", "test-exclude": "^7.0.1", "tinyrainbow": "^2.0.0" }, @@ -2684,8 +2672,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "3.1.1", - "vitest": "3.1.1" + "@vitest/browser": "3.1.2", + "vitest": "3.1.2" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -2694,14 +2682,14 @@ } }, "node_modules/@vitest/expect": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.1.tgz", - "integrity": "sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.2.tgz", + "integrity": "sha512-O8hJgr+zREopCAqWl3uCVaOdqJwZ9qaDwUP7vy3Xigad0phZe9APxKhPcDNqYYi0rX5oMvwJMSCAXY2afqeTSA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", + "@vitest/spy": "3.1.2", + "@vitest/utils": "3.1.2", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, @@ -2710,13 +2698,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.1.tgz", - "integrity": "sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.2.tgz", + "integrity": "sha512-kOtd6K2lc7SQ0mBqYv/wdGedlqPdM/B38paPY+OwJ1XiNi44w3Fpog82UfOibmHaV9Wod18A09I9SCKLyDMqgw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.1", + "@vitest/spy": "3.1.2", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -2747,9 +2735,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.1.tgz", - "integrity": "sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.2.tgz", + "integrity": "sha512-R0xAiHuWeDjTSB3kQ3OQpT8Rx3yhdOAIm/JM4axXxnG7Q/fS8XUwggv/A4xzbQA+drYRjzkMnpYnOGAc4oeq8w==", "dev": true, "license": "MIT", "dependencies": { @@ -2760,13 +2748,13 @@ } }, "node_modules/@vitest/runner": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.1.tgz", - "integrity": "sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.2.tgz", + "integrity": "sha512-bhLib9l4xb4sUMPXnThbnhX2Yi8OutBMA8Yahxa7yavQsFDtwY/jrUZwpKp2XH9DhRFJIeytlyGpXCqZ65nR+g==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.1.1", + "@vitest/utils": "3.1.2", "pathe": "^2.0.3" }, "funding": { @@ -2774,13 +2762,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.1.tgz", - "integrity": "sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.2.tgz", + "integrity": "sha512-Q1qkpazSF/p4ApZg1vfZSQ5Yw6OCQxVMVrLjslbLFA1hMDrT2uxtqMaw8Tc/jy5DLka1sNs1Y7rBcftMiaSH/Q==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.1", + "@vitest/pretty-format": "3.1.2", "magic-string": "^0.30.17", "pathe": "^2.0.3" }, @@ -2789,9 +2777,9 @@ } }, "node_modules/@vitest/spy": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.1.tgz", - "integrity": "sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.2.tgz", + "integrity": "sha512-OEc5fSXMws6sHVe4kOFyDSj/+4MSwst0ib4un0DlcYgQvRuYQ0+M2HyqGaauUMnjq87tmUaMNDxKQx7wNfVqPA==", "dev": true, "license": "MIT", "dependencies": { @@ -2802,13 +2790,13 @@ } }, "node_modules/@vitest/utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.1.tgz", - "integrity": "sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.2.tgz", + "integrity": "sha512-5GGd0ytZ7BH3H6JTj9Kw7Prn1Nbg0wZVrIvou+UWxm54d+WoXXgAgjFJ8wn3LdagWLFSEfpPeyYrByZaGEZHLg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.1", + "@vitest/pretty-format": "3.1.2", "loupe": "^3.1.3", "tinyrainbow": "^2.0.0" }, @@ -4018,9 +4006,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", "dev": true, "license": "MIT" }, @@ -4189,20 +4177,20 @@ } }, "node_modules/eslint": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", - "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", + "version": "9.25.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.1.tgz", + "integrity": "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.20.0", - "@eslint/config-helpers": "^0.2.0", - "@eslint/core": "^0.12.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.13.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.24.0", - "@eslint/plugin-kit": "^0.2.7", + "@eslint/js": "9.25.1", + "@eslint/plugin-kit": "^0.2.8", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -4262,6 +4250,22 @@ "eslint": ">=7.0.0" } }, + "node_modules/eslint-p": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/eslint-p/-/eslint-p-0.21.0.tgz", + "integrity": "sha512-w6krTooDpMF5Rezfc14FOQwX87QyH2ouZoTDySixbL8auiBRsxA4JPBrzOS5IfuLxcJHJPHjf/FnGfjlvI/kDw==", + "dev": true, + "license": "ISC", + "dependencies": { + "eslint": "9.25.1" + }, + "bin": { + "eslint-p": "lib/eslint-p.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/eslint-plugin-svelte": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.5.1.tgz", @@ -4601,9 +4605,9 @@ } }, "node_modules/fdir": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", - "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -5961,9 +5965,9 @@ } }, "node_modules/maplibre-gl": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.3.1.tgz", - "integrity": "sha512-Ihx+oUUSsZkjMou1Cw5J6silE+5OtFFQSPslWF9+7v4yFC/XDHrpsORYO9lWE4KZI0djCEUpZQJpkpnMArAbeA==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.4.0.tgz", + "integrity": "sha512-ZVrtdFIhFAqt53H2k5Ssqn7QIKNI19fW+He5tr4loxZxWZffp1aZYY9ImNncAJaALU/NYlV6Eul7UVB56/N7WQ==", "license": "BSD-3-Clause", "dependencies": { "@mapbox/geojson-rewind": "^0.5.2", @@ -8174,9 +8178,9 @@ } }, "node_modules/svelte": { - "version": "5.27.0", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.27.0.tgz", - "integrity": "sha512-Uai13Ydt1ZE+bUHme6b9U38PCYVNCqBRoBMkUKbFbKiD7kHWjdUUrklYAQZJxyKK81qII4mrBwe/YmvEMSlC9w==", + "version": "5.28.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.28.2.tgz", + "integrity": "sha512-FbWBxgWOpQfhKvoGJv/TFwzqb4EhJbwCD17dB0tEpQiw1XyUEKZJtgm4nA4xq3LLsMo7hu5UY/BOFmroAxKTMg==", "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", @@ -8252,9 +8256,9 @@ } }, "node_modules/svelte-gestures": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/svelte-gestures/-/svelte-gestures-5.1.3.tgz", - "integrity": "sha512-ELOlzuH9E4+S1biCCTfusRlvzFpnqRPlljEqayoBTu5STH42u0kTT45D1m3Py3E9UmIyZTgrSLw6Fus/fh75Dw==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/svelte-gestures/-/svelte-gestures-5.1.4.tgz", + "integrity": "sha512-gfSO/GqWLu9nRMCz12jqdyA0+NTsojYcIBcRqZjwWrpQbqMXr0zWPFpZBtzfYbRHtuFxZImMZp9MrVaFCYbhDg==", "license": "MIT" }, "node_modules/svelte-i18n": { @@ -8741,6 +8745,23 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/tinypool": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", @@ -8901,15 +8922,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.30.1.tgz", - "integrity": "sha512-D7lC0kcehVH7Mb26MRQi64LMyRJsj3dToJxM1+JVTl53DQSV5/7oUGWQLcKl1C1KnoVHxMMU2FNQMffr7F3Row==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.31.0.tgz", + "integrity": "sha512-u+93F0sB0An8WEAPtwxVhFby573E8ckdjwUUQUj9QA4v8JAvgtoDdIyYR3XFwFHq2W1KJ1AurwJCO+w+Y1ixyQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.30.1", - "@typescript-eslint/parser": "8.30.1", - "@typescript-eslint/utils": "8.30.1" + "@typescript-eslint/eslint-plugin": "8.31.0", + "@typescript-eslint/parser": "8.31.0", + "@typescript-eslint/utils": "8.31.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -9036,15 +9057,18 @@ } }, "node_modules/vite": { - "version": "6.2.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz", - "integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==", + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.3.tgz", + "integrity": "sha512-5nXH+QsELbFKhsEfWLkHrvgRpTdGJzqOZ+utSdmPTvwHmvU6ITTm3xx+mRusihkcI8GeC7lCDyn3kDtiki9scw==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", "postcss": "^8.5.3", - "rollup": "^4.30.1" + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" @@ -9123,9 +9147,9 @@ } }, "node_modules/vite-node": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.1.tgz", - "integrity": "sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.2.tgz", + "integrity": "sha512-/8iMryv46J3aK13iUXsei5G/A3CUlW4665THCPS+K8xAaqrVWiGB4RfXMQXCLjpK9P2eK//BczrVkn5JLAk6DA==", "dev": true, "license": "MIT", "dependencies": { @@ -9146,9 +9170,9 @@ } }, "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", - "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz", + "integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==", "cpu": [ "ppc64" ], @@ -9163,9 +9187,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", - "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz", + "integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==", "cpu": [ "arm" ], @@ -9180,9 +9204,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", - "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz", + "integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==", "cpu": [ "arm64" ], @@ -9197,9 +9221,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", - "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz", + "integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==", "cpu": [ "x64" ], @@ -9214,9 +9238,9 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", - "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz", + "integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==", "cpu": [ "arm64" ], @@ -9231,9 +9255,9 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", - "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz", + "integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==", "cpu": [ "x64" ], @@ -9248,9 +9272,9 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", - "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz", + "integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==", "cpu": [ "arm64" ], @@ -9265,9 +9289,9 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", - "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz", + "integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==", "cpu": [ "x64" ], @@ -9282,9 +9306,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", - "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz", + "integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==", "cpu": [ "arm" ], @@ -9299,9 +9323,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", - "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz", + "integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==", "cpu": [ "arm64" ], @@ -9316,9 +9340,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", - "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz", + "integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==", "cpu": [ "ia32" ], @@ -9333,9 +9357,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", - "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz", + "integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==", "cpu": [ "loong64" ], @@ -9350,9 +9374,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", - "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz", + "integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==", "cpu": [ "mips64el" ], @@ -9367,9 +9391,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", - "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz", + "integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==", "cpu": [ "ppc64" ], @@ -9384,9 +9408,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", - "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz", + "integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==", "cpu": [ "riscv64" ], @@ -9401,9 +9425,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", - "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz", + "integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==", "cpu": [ "s390x" ], @@ -9418,9 +9442,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", - "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz", + "integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==", "cpu": [ "x64" ], @@ -9435,9 +9459,9 @@ } }, "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", - "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz", + "integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==", "cpu": [ "x64" ], @@ -9452,9 +9476,9 @@ } }, "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", - "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz", + "integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==", "cpu": [ "x64" ], @@ -9469,9 +9493,9 @@ } }, "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", - "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz", + "integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==", "cpu": [ "x64" ], @@ -9486,9 +9510,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", - "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz", + "integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==", "cpu": [ "arm64" ], @@ -9503,9 +9527,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", - "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz", + "integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==", "cpu": [ "ia32" ], @@ -9520,9 +9544,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", - "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz", + "integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==", "cpu": [ "x64" ], @@ -9537,9 +9561,9 @@ } }, "node_modules/vite/node_modules/esbuild": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", - "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz", + "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -9550,31 +9574,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.2", - "@esbuild/android-arm": "0.25.2", - "@esbuild/android-arm64": "0.25.2", - "@esbuild/android-x64": "0.25.2", - "@esbuild/darwin-arm64": "0.25.2", - "@esbuild/darwin-x64": "0.25.2", - "@esbuild/freebsd-arm64": "0.25.2", - "@esbuild/freebsd-x64": "0.25.2", - "@esbuild/linux-arm": "0.25.2", - "@esbuild/linux-arm64": "0.25.2", - "@esbuild/linux-ia32": "0.25.2", - "@esbuild/linux-loong64": "0.25.2", - "@esbuild/linux-mips64el": "0.25.2", - "@esbuild/linux-ppc64": "0.25.2", - "@esbuild/linux-riscv64": "0.25.2", - "@esbuild/linux-s390x": "0.25.2", - "@esbuild/linux-x64": "0.25.2", - "@esbuild/netbsd-arm64": "0.25.2", - "@esbuild/netbsd-x64": "0.25.2", - "@esbuild/openbsd-arm64": "0.25.2", - "@esbuild/openbsd-x64": "0.25.2", - "@esbuild/sunos-x64": "0.25.2", - "@esbuild/win32-arm64": "0.25.2", - "@esbuild/win32-ia32": "0.25.2", - "@esbuild/win32-x64": "0.25.2" + "@esbuild/aix-ppc64": "0.25.3", + "@esbuild/android-arm": "0.25.3", + "@esbuild/android-arm64": "0.25.3", + "@esbuild/android-x64": "0.25.3", + "@esbuild/darwin-arm64": "0.25.3", + "@esbuild/darwin-x64": "0.25.3", + "@esbuild/freebsd-arm64": "0.25.3", + "@esbuild/freebsd-x64": "0.25.3", + "@esbuild/linux-arm": "0.25.3", + "@esbuild/linux-arm64": "0.25.3", + "@esbuild/linux-ia32": "0.25.3", + "@esbuild/linux-loong64": "0.25.3", + "@esbuild/linux-mips64el": "0.25.3", + "@esbuild/linux-ppc64": "0.25.3", + "@esbuild/linux-riscv64": "0.25.3", + "@esbuild/linux-s390x": "0.25.3", + "@esbuild/linux-x64": "0.25.3", + "@esbuild/netbsd-arm64": "0.25.3", + "@esbuild/netbsd-x64": "0.25.3", + "@esbuild/openbsd-arm64": "0.25.3", + "@esbuild/openbsd-x64": "0.25.3", + "@esbuild/sunos-x64": "0.25.3", + "@esbuild/win32-arm64": "0.25.3", + "@esbuild/win32-ia32": "0.25.3", + "@esbuild/win32-x64": "0.25.3" } }, "node_modules/vitefu": { @@ -9597,31 +9621,32 @@ } }, "node_modules/vitest": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.1.tgz", - "integrity": "sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.2.tgz", + "integrity": "sha512-WaxpJe092ID1C0mr+LH9MmNrhfzi8I65EX/NRU/Ld016KqQNRgxSOlGNP1hHN+a/F8L15Mh8klwaF77zR3GeDQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "3.1.1", - "@vitest/mocker": "3.1.1", - "@vitest/pretty-format": "^3.1.1", - "@vitest/runner": "3.1.1", - "@vitest/snapshot": "3.1.1", - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", + "@vitest/expect": "3.1.2", + "@vitest/mocker": "3.1.2", + "@vitest/pretty-format": "^3.1.2", + "@vitest/runner": "3.1.2", + "@vitest/snapshot": "3.1.2", + "@vitest/spy": "3.1.2", + "@vitest/utils": "3.1.2", "chai": "^5.2.0", "debug": "^4.4.0", - "expect-type": "^1.2.0", + "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", - "std-env": "^3.8.1", + "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.13", "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.1.1", + "vite-node": "3.1.2", "why-is-node-running": "^2.3.0" }, "bin": { @@ -9637,8 +9662,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.1.1", - "@vitest/ui": "3.1.1", + "@vitest/browser": "3.1.2", + "@vitest/ui": "3.1.2", "happy-dom": "*", "jsdom": "*" }, diff --git a/web/package.json b/web/package.json index 4ee7c015c8..9aa9bee6bc 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "immich-web", - "version": "1.131.3", + "version": "1.132.3", "license": "GNU Affero General Public License version 3", "type": "module", "scripts": { @@ -12,9 +12,10 @@ "check:svelte": "svelte-check --no-tsconfig --fail-on-warnings --compiler-warnings 'reactive_declaration_non_reactive_property:ignore' --ignore src/lib/components/photos-page/asset-grid.svelte", "check:typescript": "tsc --noEmit", "check:watch": "npm run check:svelte -- --watch", - "check:code": "npm run format && npm run lint && npm run check:svelte && npm run check:typescript", + "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:fix": "npm run lint -- --fix", "format": "prettier --check .", "format:fix": "prettier --write .", @@ -26,7 +27,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.17.3", + "@immich/ui": "^0.18.1", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", @@ -74,6 +75,7 @@ "dotenv": "^16.4.7", "eslint": "^9.18.0", "eslint-config-prettier": "^10.0.0", + "eslint-p": "^0.21.0", "eslint-plugin-svelte": "^3.0.0", "eslint-plugin-unicorn": "^57.0.0", "factory.ts": "^1.4.1", diff --git a/web/src/lib/actions/__test__/focus-trap.spec.ts b/web/src/lib/actions/__test__/focus-trap.spec.ts index d92d8e037d..b03064a91d 100644 --- a/web/src/lib/actions/__test__/focus-trap.spec.ts +++ b/web/src/lib/actions/__test__/focus-trap.spec.ts @@ -1,8 +1,11 @@ import FocusTrapTest from '$lib/actions/__test__/focus-trap-test.svelte'; +import { setDefaultTabbleOptions } from '$lib/utils/focus-util'; import { render, screen } from '@testing-library/svelte'; import userEvent from '@testing-library/user-event'; import { tick } from 'svelte'; +setDefaultTabbleOptions({ displayCheck: 'none' }); + describe('focusTrap action', () => { const user = userEvent.setup(); @@ -38,6 +41,7 @@ describe('focusTrap action', () => { const openButton = screen.getByText('Open'); await user.click(openButton); + await tick(); expect(document.activeElement).toEqual(screen.getByTestId('one')); screen.getByText('Close').click(); diff --git a/web/src/lib/actions/focus-trap.ts b/web/src/lib/actions/focus-trap.ts index 1564dd90d0..2b03282c2d 100644 --- a/web/src/lib/actions/focus-trap.ts +++ b/web/src/lib/actions/focus-trap.ts @@ -1,5 +1,5 @@ import { shortcuts } from '$lib/actions/shortcut'; -import { getFocusable } from '$lib/utils/focus-util'; +import { getTabbable } from '$lib/utils/focus-util'; import { tick } from 'svelte'; interface Options { @@ -18,18 +18,21 @@ export function focusTrap(container: HTMLElement, options?: Options) { }; }; - const setInitialFocus = () => { - const focusableElement = getFocusable(container)[0]; - // Use tick() to ensure focus trap works correctly inside <Portal /> - void tick().then(() => focusableElement?.focus()); + const setInitialFocus = async () => { + const focusableElement = getTabbable(container, false)[0]; + if (focusableElement) { + // Use tick() to ensure focus trap works correctly inside <Portal /> + await tick(); + focusableElement?.focus(); + } }; if (withDefaults(options).active) { - setInitialFocus(); + void setInitialFocus(); } const getFocusableElements = () => { - const focusableElements = getFocusable(container); + const focusableElements = getTabbable(container); return [ focusableElements.at(0), // focusableElements.at(-1), @@ -67,7 +70,7 @@ export function focusTrap(container: HTMLElement, options?: Options) { update(newOptions?: Options) { options = newOptions; if (withDefaults(options).active) { - setInitialFocus(); + void setInitialFocus(); } }, destroy() { diff --git a/web/src/lib/components/admin-page/jobs/job-tile.svelte b/web/src/lib/components/admin-page/jobs/job-tile.svelte index 80dd29e0be..c77ff60f22 100644 --- a/web/src/lib/components/admin-page/jobs/job-tile.svelte +++ b/web/src/lib/components/admin-page/jobs/job-tile.svelte @@ -51,7 +51,7 @@ let isIdle = $derived(!queueStatus.isActive && !queueStatus.isPaused); let multipleButtons = $derived(allText || refreshText); - const commonClasses = 'flex place-items-center justify-between w-full py-2 sm:py-4 pr-4 pl-6'; + const commonClasses = 'flex place-items-center justify-between w-full py-2 sm:py-4 pe-4 ps-6'; </script> <div @@ -110,7 +110,7 @@ <div class="mt-2 flex w-full max-w-md flex-col sm:flex-row"> <div - class="{commonClasses} rounded-t-lg bg-immich-primary text-white dark:bg-immich-dark-primary dark:text-immich-dark-gray sm:rounded-l-lg sm:rounded-r-none" + class="{commonClasses} rounded-t-lg bg-immich-primary text-white dark:bg-immich-dark-primary dark:text-immich-dark-gray sm:rounded-s-lg sm:rounded-e-none" > <p>{$t('active')}</p> <p class="text-2xl"> @@ -119,7 +119,7 @@ </div> <div - class="{commonClasses} flex-row-reverse rounded-b-lg bg-gray-200 text-immich-dark-bg dark:bg-gray-700 dark:text-immich-gray sm:rounded-l-none sm:rounded-r-lg" + class="{commonClasses} flex-row-reverse rounded-b-lg bg-gray-200 text-immich-dark-bg dark:bg-gray-700 dark:text-immich-gray sm:rounded-s-none sm:rounded-e-lg" > <p class="text-2xl"> {waitingCount.toLocaleString($locale)} diff --git a/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte b/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte index bb288511ac..8bae8fee4b 100644 --- a/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte +++ b/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte @@ -79,7 +79,7 @@ <span class="text-[#DCDADA] dark:text-[#525252]">{zeros(statsUsage)}</span><span class="text-immich-primary dark:text-immich-dark-primary">{statsUsage}</span > - <span class="my-auto ml-2 text-center text-base font-light text-gray-400">{statsUsageUnit}</span> + <span class="my-auto ms-2 text-center text-base font-light text-gray-400">{statsUsageUnit}</span> </div> </div> </div> @@ -88,7 +88,7 @@ <div> <p class="text-sm dark:text-immich-dark-fg">{$t('user_usage_detail').toUpperCase()}</p> - <table class="mt-5 w-full text-left"> + <table class="mt-5 w-full text-start"> <thead class="mb-4 flex h-12 w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary" > diff --git a/web/src/lib/components/admin-page/server-stats/stats-card.svelte b/web/src/lib/components/admin-page/server-stats/stats-card.svelte index 14d32c055f..b1804427e9 100644 --- a/web/src/lib/components/admin-page/server-stats/stats-card.svelte +++ b/web/src/lib/components/admin-page/server-stats/stats-card.svelte @@ -31,7 +31,7 @@ class="text-immich-primary dark:text-immich-dark-primary">{value}</span > {#if unit} - <span class="absolute -top-5 right-2 text-base font-light text-gray-400">{unit}</span> + <span class="absolute -top-5 end-2 text-base font-light text-gray-400">{unit}</span> {/if} </div> </div> diff --git a/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte b/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte index 5380a76286..b2454b06c3 100644 --- a/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte +++ b/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte @@ -1,16 +1,17 @@ <script lang="ts"> + import FormatMessage from '$lib/components/i18n/format-message.svelte'; import ConfirmDialog from '$lib/components/shared-components/dialog/confirm-dialog.svelte'; import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte'; import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte'; import SettingInputField from '$lib/components/shared-components/settings/setting-input-field.svelte'; + import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte'; import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; - import { type SystemConfigDto } from '@immich/sdk'; + import { SettingInputFieldType } from '$lib/constants'; + import { OAuthTokenEndpointAuthMethod, type SystemConfigDto } from '@immich/sdk'; import { isEqual } from 'lodash-es'; + import { t } from 'svelte-i18n'; import { fade } from 'svelte/transition'; import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings'; - import { t } from 'svelte-i18n'; - import FormatMessage from '$lib/components/i18n/format-message.svelte'; - import { SettingInputFieldType } from '$lib/constants'; interface Props { savedConfig: SystemConfigDto; @@ -76,13 +77,13 @@ <div> <div in:fade={{ duration: 500 }}> <form autocomplete="off" onsubmit={(e) => e.preventDefault()}> - <div class="ml-4 mt-4 flex flex-col"> + <div class="ms-4 mt-4 flex flex-col"> <SettingAccordion key="oauth" title={$t('admin.oauth_settings')} subtitle={$t('admin.oauth_settings_description')} > - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <p class="text-sm dark:text-immich-dark-fg"> <FormatMessage key="admin.oauth_settings_more_details"> {#snippet children({ message })} @@ -108,7 +109,7 @@ <hr /> <SettingInputField inputType={SettingInputFieldType.TEXT} - label={$t('admin.oauth_issuer_url').toUpperCase()} + label="ISSUER_URL" bind:value={config.oauth.issuerUrl} required={true} disabled={disabled || !config.oauth.enabled} @@ -117,7 +118,7 @@ <SettingInputField inputType={SettingInputFieldType.TEXT} - label={$t('admin.oauth_client_id').toUpperCase()} + label="CLIENT_ID" bind:value={config.oauth.clientId} required={true} disabled={disabled || !config.oauth.enabled} @@ -126,16 +127,30 @@ <SettingInputField inputType={SettingInputFieldType.TEXT} - label={$t('admin.oauth_client_secret').toUpperCase()} + label="CLIENT_SECRET" + description={$t('admin.oauth_client_secret_description')} bind:value={config.oauth.clientSecret} - required={true} disabled={disabled || !config.oauth.enabled} isEdited={!(config.oauth.clientSecret == savedConfig.oauth.clientSecret)} /> + {#if config.oauth.clientSecret} + <SettingSelect + label="TOKEN_ENDPOINT_AUTH_METHOD" + bind:value={config.oauth.tokenEndpointAuthMethod} + disabled={disabled || !config.oauth.enabled || !config.oauth.clientSecret} + isEdited={!(config.oauth.tokenEndpointAuthMethod == savedConfig.oauth.tokenEndpointAuthMethod)} + options={[ + { value: OAuthTokenEndpointAuthMethod.ClientSecretPost, text: 'client_secret_post' }, + { value: OAuthTokenEndpointAuthMethod.ClientSecretBasic, text: 'client_secret_basic' }, + ]} + name="tokenEndpointAuthMethod" + /> + {/if} + <SettingInputField inputType={SettingInputFieldType.TEXT} - label={$t('admin.oauth_scope').toUpperCase()} + label="SCOPE" bind:value={config.oauth.scope} required={true} disabled={disabled || !config.oauth.enabled} @@ -144,7 +159,7 @@ <SettingInputField inputType={SettingInputFieldType.TEXT} - label={$t('admin.oauth_signing_algorithm').toUpperCase()} + label="ID_TOKEN_SIGNED_RESPONSE_ALG" bind:value={config.oauth.signingAlgorithm} required={true} disabled={disabled || !config.oauth.enabled} @@ -153,14 +168,23 @@ <SettingInputField inputType={SettingInputFieldType.TEXT} - label={$t('admin.oauth_profile_signing_algorithm').toUpperCase()} - description={$t('admin.oauth_profile_signing_algorithm_description')} + label="USERINFO_SIGNED_RESPONSE_ALG" bind:value={config.oauth.profileSigningAlgorithm} required={true} disabled={disabled || !config.oauth.enabled} isEdited={!(config.oauth.profileSigningAlgorithm == savedConfig.oauth.profileSigningAlgorithm)} /> + <SettingInputField + inputType={SettingInputFieldType.TEXT} + label={$t('admin.oauth_timeout').toUpperCase()} + description={$t('admin.oauth_timeout_description')} + required={true} + bind:value={config.oauth.timeout} + disabled={disabled || !config.oauth.enabled} + isEdited={!(config.oauth.timeout == savedConfig.oauth.timeout)} + /> + <SettingInputField inputType={SettingInputFieldType.TEXT} label={$t('admin.oauth_storage_label_claim').toUpperCase()} @@ -243,8 +267,8 @@ title={$t('admin.password_settings')} subtitle={$t('admin.password_settings_description')} > - <div class="ml-4 mt-4 flex flex-col gap-4"> - <div class="ml-4 mt-4 flex flex-col"> + <div class="ms-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col"> <SettingSwitch title={$t('admin.password_enable_description')} {disabled} diff --git a/web/src/lib/components/admin-page/settings/backup-settings/backup-settings.svelte b/web/src/lib/components/admin-page/settings/backup-settings/backup-settings.svelte index 3ec477e29c..61f4cafe87 100644 --- a/web/src/lib/components/admin-page/settings/backup-settings/backup-settings.svelte +++ b/web/src/lib/components/admin-page/settings/backup-settings/backup-settings.svelte @@ -37,7 +37,7 @@ <div> <div in:fade={{ duration: 500 }}> <form autocomplete="off" {onsubmit}> - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingSwitch title={$t('admin.backup_database_enable_description')} {disabled} diff --git a/web/src/lib/components/admin-page/settings/ffmpeg/ffmpeg-settings.svelte b/web/src/lib/components/admin-page/settings/ffmpeg/ffmpeg-settings.svelte index 58c4be0ca0..bb9b41f8de 100644 --- a/web/src/lib/components/admin-page/settings/ffmpeg/ffmpeg-settings.svelte +++ b/web/src/lib/components/admin-page/settings/ffmpeg/ffmpeg-settings.svelte @@ -43,7 +43,7 @@ <div> <div in:fade={{ duration: 500 }}> <form autocomplete="off" {onsubmit}> - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <p class="text-sm dark:text-immich-dark-fg"> <Icon path={mdiHelpCircleOutline} class="inline" size="15" /> <FormatMessage key="admin.transcoding_codecs_learn_more"> @@ -70,7 +70,7 @@ title={$t('admin.transcoding_policy')} subtitle={$t('admin.transcoding_policy_description')} > - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingSelect label={$t('admin.transcoding_transcode_policy')} {disabled} @@ -159,7 +159,7 @@ title={$t('admin.transcoding_encoding_options')} subtitle={$t('admin.transcoding_encoding_options_description')} > - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingSelect label={$t('admin.transcoding_video_codec')} {disabled} @@ -302,7 +302,7 @@ title={$t('admin.transcoding_hardware_acceleration')} subtitle={$t('admin.transcoding_hardware_acceleration_description')} > - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingSelect label={$t('admin.transcoding_acceleration_api')} {disabled} @@ -376,7 +376,7 @@ title={$t('advanced')} subtitle={$t('admin.transcoding_advanced_options_description')} > - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingInputField inputType={SettingInputFieldType.NUMBER} label={$t('admin.transcoding_max_b_frames')} @@ -407,7 +407,7 @@ </SettingAccordion> </div> - <div class="ml-4"> + <div class="ms-4"> <SettingButtonsRow onReset={(options) => onReset({ ...options, configKeys: ['ffmpeg'] })} onSave={() => onSave({ ffmpeg: config.ffmpeg })} diff --git a/web/src/lib/components/admin-page/settings/image/image-settings.svelte b/web/src/lib/components/admin-page/settings/image/image-settings.svelte index 9a66ad9c97..9a32e8e4e0 100644 --- a/web/src/lib/components/admin-page/settings/image/image-settings.svelte +++ b/web/src/lib/components/admin-page/settings/image/image-settings.svelte @@ -40,7 +40,7 @@ <div> <div in:fade={{ duration: 500 }}> <form autocomplete="off" {onsubmit}> - <div class="ml-4 mt-4"> + <div class="ms-4 mt-4"> <SettingAccordion key="thumbnail-settings" title={$t('admin.image_thumbnail_title')} @@ -195,7 +195,7 @@ </div> </div> - <div class="ml-4 mt-4"> + <div class="ms-4 mt-4"> <SettingButtonsRow onReset={(options) => onReset({ ...options, configKeys: ['image'] })} onSave={() => onSave({ image: config.image })} diff --git a/web/src/lib/components/admin-page/settings/job-settings/job-settings.svelte b/web/src/lib/components/admin-page/settings/job-settings/job-settings.svelte index 5a95dbea30..e9f54e7ee8 100644 --- a/web/src/lib/components/admin-page/settings/job-settings/job-settings.svelte +++ b/web/src/lib/components/admin-page/settings/job-settings/job-settings.svelte @@ -47,7 +47,7 @@ <div in:fade={{ duration: 500 }}> <form autocomplete="off" {onsubmit}> {#each jobNames as jobName (jobName)} - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> {#if isSystemConfigJobDto(jobName)} <SettingInputField inputType={SettingInputFieldType.NUMBER} @@ -71,7 +71,7 @@ </div> {/each} - <div class="ml-4"> + <div class="ms-4"> <SettingButtonsRow onReset={(options) => onReset({ ...options, configKeys: ['job'] })} onSave={() => onSave({ job: config.job })} diff --git a/web/src/lib/components/admin-page/settings/library-settings/library-settings.svelte b/web/src/lib/components/admin-page/settings/library-settings/library-settings.svelte index b1012c0287..390b167a54 100644 --- a/web/src/lib/components/admin-page/settings/library-settings/library-settings.svelte +++ b/web/src/lib/components/admin-page/settings/library-settings/library-settings.svelte @@ -47,14 +47,14 @@ <div> <div in:fade={{ duration: 500 }}> <form autocomplete="off" {onsubmit}> - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingAccordion key="library-watching" title={$t('admin.library_watching_settings')} subtitle={$t('admin.library_watching_settings_description')} isOpen={openByDefault} > - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingSwitch title={$t('admin.library_watching_enable_description')} {disabled} @@ -69,7 +69,7 @@ subtitle={$t('admin.library_scanning_description')} isOpen={openByDefault} > - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingSwitch title={$t('admin.library_scanning_enable_description')} {disabled} diff --git a/web/src/lib/components/admin-page/settings/logging-settings/logging-settings.svelte b/web/src/lib/components/admin-page/settings/logging-settings/logging-settings.svelte index 29a1c65162..5538183442 100644 --- a/web/src/lib/components/admin-page/settings/logging-settings/logging-settings.svelte +++ b/web/src/lib/components/admin-page/settings/logging-settings/logging-settings.svelte @@ -27,7 +27,7 @@ <div> <div in:fade={{ duration: 500 }}> <form autocomplete="off" {onsubmit}> - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingSwitch title={$t('admin.logging_enable_description')} {disabled} diff --git a/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte b/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte index 59bb798a0b..cd2684a3e3 100644 --- a/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte +++ b/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte @@ -51,7 +51,7 @@ {#if config.machineLearning.urls.length > 1} <CircleIconButton size="24" - class="ml-2" + class="ms-2" padding="2" color="red" title="" @@ -88,7 +88,7 @@ title={$t('admin.machine_learning_smart_search')} subtitle={$t('admin.machine_learning_smart_search_description')} > - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingSwitch title={$t('admin.machine_learning_smart_search_enabled')} subtitle={$t('admin.machine_learning_smart_search_enabled_description')} @@ -124,7 +124,7 @@ title={$t('admin.machine_learning_duplicate_detection')} subtitle={$t('admin.machine_learning_duplicate_detection_setting_description')} > - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingSwitch title={$t('admin.machine_learning_duplicate_detection_enabled')} subtitle={$t('admin.machine_learning_duplicate_detection_enabled_description')} @@ -154,7 +154,7 @@ title={$t('admin.machine_learning_facial_recognition')} subtitle={$t('admin.machine_learning_facial_recognition_description')} > - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingSwitch title={$t('admin.machine_learning_facial_recognition_setting')} subtitle={$t('admin.machine_learning_facial_recognition_setting_description')} diff --git a/web/src/lib/components/admin-page/settings/map-settings/map-settings.svelte b/web/src/lib/components/admin-page/settings/map-settings/map-settings.svelte index 4a4b23ded2..7046196c31 100644 --- a/web/src/lib/components/admin-page/settings/map-settings/map-settings.svelte +++ b/web/src/lib/components/admin-page/settings/map-settings/map-settings.svelte @@ -32,7 +32,7 @@ <form autocomplete="off" {onsubmit}> <div class="flex flex-col gap-4"> <SettingAccordion key="map" title={$t('admin.map_settings')} subtitle={$t('admin.map_settings_description')}> - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingSwitch title={$t('admin.map_enable_description')} subtitle={$t('admin.map_implications')} @@ -78,7 +78,7 @@ </FormatMessage> </p> {/snippet} - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingSwitch title={$t('admin.map_reverse_geocoding_enable_description')} {disabled} diff --git a/web/src/lib/components/admin-page/settings/metadata-settings/metadata-settings.svelte b/web/src/lib/components/admin-page/settings/metadata-settings/metadata-settings.svelte index 1ba82b2eb9..c5d3860ecd 100644 --- a/web/src/lib/components/admin-page/settings/metadata-settings/metadata-settings.svelte +++ b/web/src/lib/components/admin-page/settings/metadata-settings/metadata-settings.svelte @@ -26,7 +26,7 @@ <div class="mt-2"> <div in:fade={{ duration: 500 }}> <form autocomplete="off" {onsubmit} class="mx-4 mt-4"> - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingSwitch title={$t('admin.metadata_faces_import_setting')} subtitle={$t('admin.metadata_faces_import_setting_description')} diff --git a/web/src/lib/components/admin-page/settings/new-version-check-settings/new-version-check-settings.svelte b/web/src/lib/components/admin-page/settings/new-version-check-settings/new-version-check-settings.svelte index 1a6f0ab866..eb032a6115 100644 --- a/web/src/lib/components/admin-page/settings/new-version-check-settings/new-version-check-settings.svelte +++ b/web/src/lib/components/admin-page/settings/new-version-check-settings/new-version-check-settings.svelte @@ -26,7 +26,7 @@ <div> <div in:fade={{ duration: 500 }}> <form autocomplete="off" {onsubmit}> - <div class="ml-4 mt-4"> + <div class="ms-4 mt-4"> <SettingSwitch title={$t('admin.version_check_enabled_description')} subtitle={$t('admin.version_check_implications')} diff --git a/web/src/lib/components/admin-page/settings/notification-settings/notification-settings.svelte b/web/src/lib/components/admin-page/settings/notification-settings/notification-settings.svelte index a2b6305f76..e3bd413900 100644 --- a/web/src/lib/components/admin-page/settings/notification-settings/notification-settings.svelte +++ b/web/src/lib/components/admin-page/settings/notification-settings/notification-settings.svelte @@ -12,7 +12,7 @@ import { SettingInputFieldType } from '$lib/constants'; import { user } from '$lib/stores/user.store'; import { handleError } from '$lib/utils/handle-error'; - import { sendTestEmail, type SystemConfigDto } from '@immich/sdk'; + import { sendTestEmailAdmin, type SystemConfigDto } from '@immich/sdk'; import { Button } from '@immich/ui'; import { isEqual } from 'lodash-es'; import { t } from 'svelte-i18n'; @@ -40,7 +40,7 @@ isSending = true; try { - await sendTestEmail({ + await sendTestEmailAdmin({ systemConfigSmtpDto: { enabled: config.notifications.smtp.enabled, transport: { @@ -80,7 +80,7 @@ <form autocomplete="off" {onsubmit} class="mt-4"> <div class="flex flex-col gap-4"> <SettingAccordion key="email" title={$t('email')} subtitle={$t('admin.notification_email_setting_description')}> - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingSwitch title={$t('admin.notification_enable_email_notifications')} {disabled} diff --git a/web/src/lib/components/admin-page/settings/server/server-settings.svelte b/web/src/lib/components/admin-page/settings/server/server-settings.svelte index b9134d1e50..8a1ff807f3 100644 --- a/web/src/lib/components/admin-page/settings/server/server-settings.svelte +++ b/web/src/lib/components/admin-page/settings/server/server-settings.svelte @@ -28,7 +28,7 @@ <div> <div in:fade={{ duration: 500 }}> <form autocomplete="off" {onsubmit}> - <div class="mt-4 ml-4"> + <div class="mt-4 ms-4"> <SettingInputField inputType={SettingInputFieldType.TEXT} label={$t('admin.server_external_domain_settings')} @@ -52,7 +52,7 @@ bind:checked={config.server.publicUsers} /> - <div class="ml-4"> + <div class="ms-4"> <SettingButtonsRow onReset={(options) => onReset({ ...options, configKeys: ['server'] })} onSave={() => onSave({ server: config.server })} diff --git a/web/src/lib/components/admin-page/settings/storage-template/storage-template-settings.svelte b/web/src/lib/components/admin-page/settings/storage-template/storage-template-settings.svelte index 9b4aa5e934..efc42bf8b7 100644 --- a/web/src/lib/components/admin-page/settings/storage-template/storage-template-settings.svelte +++ b/web/src/lib/components/admin-page/settings/storage-template/storage-template-settings.svelte @@ -78,6 +78,8 @@ }; const dt = luxon.DateTime.fromISO(new Date('2022-02-03T04:56:05.250').toISOString()); + const albumStartTime = luxon.DateTime.fromISO(new Date('2021-12-31T05:32:41.750').toISOString()); + const albumEndTime = luxon.DateTime.fromISO(new Date('2023-05-06T09:15:17.100').toISOString()); const dateTokens = [ ...templateOptions.yearOptions, @@ -91,6 +93,8 @@ for (const token of dateTokens) { substitutions[token] = dt.toFormat(token); + substitutions['album-startDate-' + token] = albumStartTime.toFormat(token); + substitutions['album-endDate-' + token] = albumEndTime.toFormat(token); } return template(substitutions); @@ -137,7 +141,7 @@ </p> </div> {#await getTemplateOptions() then} - <div id="directory-path-builder" class="flex flex-col gap-4 {minified ? '' : 'ml-4 mt-4'}"> + <div id="directory-path-builder" class="flex flex-col gap-4 {minified ? '' : 'ms-4 mt-4'}"> <SettingSwitch title={$t('admin.storage_template_enable_description')} {disabled} diff --git a/web/src/lib/components/admin-page/settings/storage-template/supported-variables-panel.svelte b/web/src/lib/components/admin-page/settings/storage-template/supported-variables-panel.svelte index fc8f913281..c1255c252d 100644 --- a/web/src/lib/components/admin-page/settings/storage-template/supported-variables-panel.svelte +++ b/web/src/lib/components/admin-page/settings/storage-template/supported-variables-panel.svelte @@ -29,6 +29,14 @@ <li>{`{{assetId}}`} - Asset ID</li> <li>{`{{assetIdShort}}`} - Asset ID (last 12 characters)</li> <li>{`{{album}}`} - Album Name</li> + <li> + {`{{album-startDate-x}}`} - Album Start Date and Time (e.g. album-startDate-yy). + {$t('admin.storage_template_date_time_sample', { values: { date: '2021-12-31T05:32:41.750' } })} + </li> + <li> + {`{{album-endDate-x}}`} - Album End Date and Time (e.g. album-endDate-MM). + {$t('admin.storage_template_date_time_sample', { values: { date: '2023-05-06T09:15:17.100' } })} + </li> </ul> </div> </div> diff --git a/web/src/lib/components/admin-page/settings/template-settings/template-settings.svelte b/web/src/lib/components/admin-page/settings/template-settings/template-settings.svelte index b418aebb2b..a294eb1768 100644 --- a/web/src/lib/components/admin-page/settings/template-settings/template-settings.svelte +++ b/web/src/lib/components/admin-page/settings/template-settings/template-settings.svelte @@ -6,7 +6,7 @@ import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte'; import SettingTextarea from '$lib/components/shared-components/settings/setting-textarea.svelte'; import { handleError } from '$lib/utils/handle-error'; - import { type SystemConfigDto, type SystemConfigTemplateEmailsDto, getNotificationTemplate } from '@immich/sdk'; + import { type SystemConfigDto, type SystemConfigTemplateEmailsDto, getNotificationTemplateAdmin } from '@immich/sdk'; import { Button } from '@immich/ui'; import { mdiEyeOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; @@ -25,7 +25,7 @@ const getTemplate = async (name: string, template: string) => { try { loadingPreview = true; - const result = await getNotificationTemplate({ name, templateDto: { template } }); + const result = await getNotificationTemplateAdmin({ name, templateDto: { template } }); htmlPreview = result.html; } catch (error) { handleError(error, 'Could not load template.'); @@ -76,7 +76,7 @@ title={$t('admin.template_email_settings')} subtitle={$t('admin.template_settings_description')} > - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <p class="text-sm dark:text-immich-dark-fg"> <FormatMessage key="admin.template_email_if_empty"> {$t('admin.template_email_if_empty')} @@ -102,7 +102,7 @@ onclick={() => getTemplate(templateName, config.templates.email[templateKey])} title={$t('admin.template_email_preview')} > - <Icon path={mdiEyeOutline} class="mr-1" /> + <Icon path={mdiEyeOutline} class="me-1" /> {$t('admin.template_email_preview')} </Button> </div> diff --git a/web/src/lib/components/admin-page/settings/theme/theme-settings.svelte b/web/src/lib/components/admin-page/settings/theme/theme-settings.svelte index 79b5f838e3..64b4b92b5e 100644 --- a/web/src/lib/components/admin-page/settings/theme/theme-settings.svelte +++ b/web/src/lib/components/admin-page/settings/theme/theme-settings.svelte @@ -26,7 +26,7 @@ <div> <div in:fade={{ duration: 500 }}> <form autocomplete="off" {onsubmit}> - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingTextarea {disabled} label={$t('admin.theme_custom_css_settings')} diff --git a/web/src/lib/components/admin-page/settings/trash-settings/trash-settings.svelte b/web/src/lib/components/admin-page/settings/trash-settings/trash-settings.svelte index 05979bf9f0..f0f18f5d95 100644 --- a/web/src/lib/components/admin-page/settings/trash-settings/trash-settings.svelte +++ b/web/src/lib/components/admin-page/settings/trash-settings/trash-settings.svelte @@ -28,7 +28,7 @@ <div> <div in:fade={{ duration: 500 }}> <form autocomplete="off" {onsubmit}> - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingSwitch title={$t('admin.trash_enabled_description')} {disabled} bind:checked={config.trash.enabled} /> <hr /> diff --git a/web/src/lib/components/admin-page/settings/user-settings/user-settings.svelte b/web/src/lib/components/admin-page/settings/user-settings/user-settings.svelte index f96c3808a8..422e1ebe49 100644 --- a/web/src/lib/components/admin-page/settings/user-settings/user-settings.svelte +++ b/web/src/lib/components/admin-page/settings/user-settings/user-settings.svelte @@ -24,7 +24,7 @@ <div> <div in:fade={{ duration: 500 }}> <form autocomplete="off" onsubmit={(e) => e.preventDefault()}> - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingInputField inputType={SettingInputFieldType.NUMBER} min={1} @@ -35,7 +35,7 @@ /> </div> - <div class="ml-4"> + <div class="ms-4"> <SettingButtonsRow onReset={(options) => onReset({ ...options, configKeys: ['user'] })} onSave={() => onSave({ user: config.user })} diff --git a/web/src/lib/components/album-page/album-card-group.svelte b/web/src/lib/components/album-page/album-card-group.svelte index 9b2aa11552..3556d9fea4 100644 --- a/web/src/lib/components/album-page/album-card-group.svelte +++ b/web/src/lib/components/album-page/album-card-group.svelte @@ -48,7 +48,7 @@ <button type="button" onclick={() => toggleAlbumGroupCollapsing(group.id)} - class="w-full text-left mt-2 pt-2 pr-2 pb-2 rounded-md transition-colors cursor-pointer dark:text-immich-dark-fg hover:text-immich-primary dark:hover:text-immich-dark-primary hover:bg-immich-gray dark:hover:bg-immich-dark-gray" + class="w-full text-start mt-2 pt-2 pe-2 pb-2 rounded-md transition-colors cursor-pointer dark:text-immich-dark-fg hover:text-immich-primary dark:hover:text-immich-dark-primary hover:bg-immich-gray dark:hover:bg-immich-dark-gray" aria-expanded={!isCollapsed} > <Icon @@ -57,7 +57,7 @@ class="inline-block -mt-2.5 transition-all duration-[250ms] {iconRotation}" /> <span class="font-bold text-3xl text-black dark:text-white">{group.name}</span> - <span class="ml-1.5">({$t('albums_count', { values: { count: albums.length } })})</span> + <span class="ms-1.5">({$t('albums_count', { values: { count: albums.length } })})</span> </button> <hr class="dark:border-immich-dark-gray" /> </div> diff --git a/web/src/lib/components/album-page/album-card.svelte b/web/src/lib/components/album-page/album-card.svelte index cec4919e4e..06ec030bea 100644 --- a/web/src/lib/components/album-page/album-card.svelte +++ b/web/src/lib/components/album-page/album-card.svelte @@ -40,7 +40,7 @@ {#if onShowContextMenu} <div id="icon-{album.id}" - class="absolute right-6 top-6 z-10 opacity-0 group-hover:opacity-100 focus-within:opacity-100" + class="absolute end-6 top-6 z-10 opacity-0 group-hover:opacity-100 focus-within:opacity-100" data-testid="context-button-parent" > <CircleIconButton diff --git a/web/src/lib/components/album-page/album-viewer.svelte b/web/src/lib/components/album-page/album-viewer.svelte index ea9e7e3dd1..09ec67e92b 100644 --- a/web/src/lib/components/album-page/album-viewer.svelte +++ b/web/src/lib/components/album-page/album-viewer.svelte @@ -117,7 +117,7 @@ <!-- ALBUM DESCRIPTION --> {#if album.description} <p - class="whitespace-pre-line mb-12 mt-6 w-full pb-2 text-left font-medium text-base text-black dark:text-gray-300" + class="whitespace-pre-line mb-12 mt-6 w-full pb-2 text-start font-medium text-base text-black dark:text-gray-300" > {album.description} </p> diff --git a/web/src/lib/components/album-page/albums-table-row.svelte b/web/src/lib/components/album-page/albums-table-row.svelte index c900930f8a..034ed62010 100644 --- a/web/src/lib/components/album-page/albums-table-row.svelte +++ b/web/src/lib/components/album-page/albums-table-row.svelte @@ -35,13 +35,13 @@ onclick={() => goto(`${AppRoute.ALBUMS}/${album.id}`)} {oncontextmenu} > - <td class="text-md text-ellipsis text-left w-8/12 sm:w-4/12 md:w-4/12 xl:w-[30%] 2xl:w-[40%] items-center"> + <td class="text-md text-ellipsis text-start w-8/12 sm:w-4/12 md:w-4/12 xl:w-[30%] 2xl:w-[40%] items-center"> {album.albumName} {#if album.shared} <Icon path={mdiShareVariantOutline} size="16" - class="inline ml-1 opacity-70" + class="inline ms-1 opacity-70" title={album.ownerId === $user.id ? $t('shared_by_you') : $t('shared_by_user', { values: { user: album.owner.name } })} diff --git a/web/src/lib/components/album-page/albums-table.svelte b/web/src/lib/components/album-page/albums-table.svelte index bd7c7fd7f5..9f51f9a19a 100644 --- a/web/src/lib/components/album-page/albums-table.svelte +++ b/web/src/lib/components/album-page/albums-table.svelte @@ -24,7 +24,7 @@ let { groupedAlbums, albumGroupOption = AlbumGroupBy.None, onShowContextMenu }: Props = $props(); </script> -<table class="mt-2 w-full text-left"> +<table class="mt-2 w-full text-start"> <thead class="mb-4 flex h-12 w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary" > @@ -48,18 +48,18 @@ class="block w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray dark:text-immich-dark-fg" > <tr - class="flex w-full place-items-center p-2 md:pl-5 md:pr-5 md:pt-3 md:pb-3" + class="flex w-full place-items-center p-2 md:ps-5 md:pe-5 md:pt-3 md:pb-3" onclick={() => toggleAlbumGroupCollapsing(albumGroup.id)} aria-expanded={!isCollapsed} > - <td class="text-md text-left -mb-1"> + <td class="text-md text-start -mb-1"> <Icon path={mdiChevronRight} size="20" class="inline-block -mt-2 transition-all duration-[250ms] {iconRotation}" /> <span class="font-bold text-2xl">{albumGroup.name}</span> - <span class="ml-1.5"> + <span class="ms-1.5"> ({$t('albums_count', { values: { count: albumGroup.albums.length } })}) </span> </td> diff --git a/web/src/lib/components/album-page/user-selection-modal.svelte b/web/src/lib/components/album-page/user-selection-modal.svelte index 1496c1ce66..9ee7cc550d 100644 --- a/web/src/lib/components/album-page/user-selection-modal.svelte +++ b/web/src/lib/components/album-page/user-selection-modal.svelte @@ -94,7 +94,7 @@ </div> <!-- <UserAvatar {user} size="md" /> --> - <div class="text-left flex-grow"> + <div class="text-start flex-grow"> <p class="text-immich-fg dark:text-immich-dark-fg"> {user.name} </p> @@ -136,7 +136,7 @@ class="flex w-full place-items-center gap-4 p-4" > <UserAvatar {user} size="md" /> - <div class="text-left flex-grow"> + <div class="text-start flex-grow"> <p class="text-immich-fg dark:text-immich-dark-fg"> {user.name} </p> diff --git a/web/src/lib/components/asset-viewer/activity-viewer.svelte b/web/src/lib/components/asset-viewer/activity-viewer.svelte index caa1ced290..e98769d495 100644 --- a/web/src/lib/components/asset-viewer/activity-viewer.svelte +++ b/web/src/lib/components/asset-viewer/activity-viewer.svelte @@ -1,32 +1,24 @@ <script lang="ts"> - import Icon from '$lib/components/elements/icon.svelte'; - import { AppRoute, timeBeforeShowLoadingSpinner } from '$lib/constants'; - import { getAssetThumbnailUrl, handlePromiseError } from '$lib/utils'; - import { getAssetType } from '$lib/utils/asset-utils'; import { autoGrowHeight } from '$lib/actions/autogrow'; + import { shortcut } from '$lib/actions/shortcut'; + import Icon from '$lib/components/elements/icon.svelte'; + import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte'; + import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte'; + import { AppRoute, timeBeforeShowLoadingSpinner } from '$lib/constants'; + import { activityManager } from '$lib/managers/activity-manager.svelte'; + import { locale } from '$lib/stores/preferences.store'; + import { getAssetThumbnailUrl } from '$lib/utils'; + import { getAssetType } from '$lib/utils/asset-utils'; import { handleError } from '$lib/utils/handle-error'; import { isTenMinutesApart } from '$lib/utils/timesince'; - import { - ReactionType, - createActivity, - deleteActivity, - getActivities, - type ActivityResponseDto, - type AssetTypeEnum, - type UserResponseDto, - } from '@immich/sdk'; - import { mdiClose, mdiDotsVertical, mdiHeart, mdiSend, mdiDeleteOutline } from '@mdi/js'; + import { ReactionType, type ActivityResponseDto, type AssetTypeEnum, type UserResponseDto } from '@immich/sdk'; + import { mdiClose, mdiDeleteOutline, mdiDotsVertical, mdiHeart, mdiSend } from '@mdi/js'; import * as luxon from 'luxon'; - import { onMount } from 'svelte'; + import { t } from 'svelte-i18n'; import CircleIconButton from '../elements/buttons/circle-icon-button.svelte'; import LoadingSpinner from '../shared-components/loading-spinner.svelte'; import { NotificationType, notificationController } from '../shared-components/notification/notification'; import UserAvatar from '../shared-components/user-avatar.svelte'; - import { locale } from '$lib/stores/preferences.store'; - import { shortcut } from '$lib/actions/shortcut'; - import { t } from 'svelte-i18n'; - import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte'; - import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte'; const units: Intl.RelativeTimeFormatUnit[] = ['year', 'month', 'week', 'day', 'hour', 'minute', 'second']; @@ -48,34 +40,16 @@ }; interface Props { - reactions: ActivityResponseDto[]; user: UserResponseDto; assetId?: string | undefined; albumId: string; assetType?: AssetTypeEnum | undefined; albumOwnerId: string; disabled: boolean; - isLiked: ActivityResponseDto | null; - onDeleteComment: () => void; - onDeleteLike: () => void; - onAddComment: () => void; onClose: () => void; } - let { - reactions = $bindable(), - user, - assetId = undefined, - albumId, - assetType = undefined, - albumOwnerId, - disabled, - isLiked, - onDeleteComment, - onDeleteLike, - onAddComment, - onClose, - }: Props = $props(); + let { user, assetId = undefined, albumId, assetType = undefined, albumOwnerId, disabled, onClose }: Props = $props(); let innerHeight: number = $state(0); let activityHeight: number = $state(0); @@ -85,36 +59,18 @@ let message = $state(''); let isSendingMessage = $state(false); - onMount(async () => { - await getReactions(); - }); - - const getReactions = async () => { - try { - reactions = await getActivities({ assetId, albumId }); - } catch (error) { - handleError(error, $t('errors.unable_to_load_asset_activity')); - } - }; - - const timeOptions = { + const timeOptions: Intl.DateTimeFormatOptions = { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', hour12: false, - } as Intl.DateTimeFormatOptions; + }; const handleDeleteReaction = async (reaction: ActivityResponseDto, index: number) => { try { - await deleteActivity({ id: reaction.id }); - reactions.splice(index, 1); - if (isLiked && reaction.type === ReactionType.Like && reaction.id == isLiked.id) { - onDeleteLike(); - } else { - onDeleteComment(); - } + await activityManager.deleteActivity(reaction, index); const deleteMessages: Record<ReactionType, string> = { [ReactionType.Comment]: $t('comment_deleted'), @@ -135,13 +91,9 @@ } const timeout = setTimeout(() => (isSendingMessage = true), timeBeforeShowLoadingSpinner); try { - const data = await createActivity({ - activityCreateDto: { albumId, assetId, type: ReactionType.Comment, comment: message }, - }); - reactions.push(data); + await activityManager.addActivity({ albumId, assetId, type: ReactionType.Comment, comment: message }); message = ''; - onAddComment(); } catch (error) { handleError(error, $t('errors.unable_to_add_comment')); } finally { @@ -156,7 +108,6 @@ }); $effect(() => { if (assetId && previousAssetId != assetId) { - handlePromiseError(getReactions()); previousAssetId = assetId; } }); @@ -184,9 +135,9 @@ class="overflow-y-auto immich-scrollbar relative w-full px-2" style="height: {divHeight}px;padding-bottom: {chatHeight}px" > - {#each reactions as reaction, index (reaction.id)} + {#each activityManager.activities as reaction, index (reaction.id)} {#if reaction.type === ReactionType.Comment} - <div class="flex dark:bg-gray-800 bg-gray-200 py-3 pl-3 mt-3 rounded-lg gap-4 justify-start"> + <div class="flex dark:bg-gray-800 bg-gray-200 py-3 ps-3 mt-3 rounded-lg gap-4 justify-start"> <div class="flex items-center"> <UserAvatar user={reaction.user} size="sm" /> </div> @@ -202,7 +153,7 @@ </a> {/if} {#if reaction.user.id === user.id || albumOwnerId === user.id} - <div class="mr-4"> + <div class="me-4"> <ButtonContextMenu icon={mdiDotsVertical} title={$t('comment_options')} @@ -221,7 +172,7 @@ {/if} </div> - {#if (index != reactions.length - 1 && !shouldGroup(reactions[index].createdAt, reactions[index + 1].createdAt)) || index === reactions.length - 1} + {#if (index != activityManager.activities.length - 1 && !shouldGroup(activityManager.activities[index].createdAt, activityManager.activities[index + 1].createdAt)) || index === activityManager.activities.length - 1} <div class="pt-1 px-2 text-right w-full text-sm text-gray-500 dark:text-gray-300" title={new Date(reaction.createdAt).toLocaleDateString(undefined, timeOptions)} @@ -231,7 +182,7 @@ {/if} {:else if reaction.type === ReactionType.Like} <div class="relative"> - <div class="flex py-3 pl-3 mt-3 gap-4 items-center text-sm"> + <div class="flex py-3 ps-3 mt-3 gap-4 items-center text-sm"> <div class="text-red-600"><Icon path={mdiHeart} size={20} /></div> <div class="w-full" title={`${reaction.user.name} (${reaction.user.email})`}> @@ -255,7 +206,7 @@ </a> {/if} {#if reaction.user.id === user.id || albumOwnerId === user.id} - <div class="mr-4"> + <div class="me-4"> <ButtonContextMenu icon={mdiDotsVertical} title={$t('reaction_options')} @@ -273,7 +224,7 @@ </div> {/if} </div> - {#if (index != reactions.length - 1 && isTenMinutesApart(reactions[index].createdAt, reactions[index + 1].createdAt)) || index === reactions.length - 1} + {#if (index != activityManager.activities.length - 1 && isTenMinutesApart(activityManager.activities[index].createdAt, activityManager.activities[index + 1].createdAt)) || index === activityManager.activities.length - 1} <div class="pt-1 px-2 text-right w-full text-sm text-gray-500 dark:text-gray-300" title={new Date(reaction.createdAt).toLocaleDateString(navigator.language, timeOptions)} @@ -307,17 +258,17 @@ }} class="h-[18px] {disabled ? 'cursor-not-allowed' - : ''} w-full max-h-56 pr-2 items-center overflow-y-auto leading-4 outline-none resize-none bg-gray-200" + : ''} w-full max-h-56 pe-2 items-center overflow-y-auto leading-4 outline-none resize-none bg-gray-200" ></textarea> </div> {#if isSendingMessage} - <div class="flex items-end place-items-center pb-2 ml-0"> + <div class="flex items-end place-items-center pb-2 ms-0"> <div class="flex w-full place-items-center"> <LoadingSpinner /> </div> </div> {:else if message} - <div class="flex items-end w-fit ml-0"> + <div class="flex items-end w-fit ms-0"> <CircleIconButton title={$t('send_message')} size="15" diff --git a/web/src/lib/components/asset-viewer/album-list-item.svelte b/web/src/lib/components/asset-viewer/album-list-item.svelte index 1d7ee2971a..6c50ecbc1d 100644 --- a/web/src/lib/components/asset-viewer/album-list-item.svelte +++ b/web/src/lib/components/asset-viewer/album-list-item.svelte @@ -43,7 +43,7 @@ type="button" onclick={onAlbumClick} use:scrollIntoViewIfSelected - class="flex w-full gap-4 px-6 py-2 text-left transition-colors hover:bg-gray-200 dark:hover:bg-gray-700 rounded-xl" + class="flex w-full gap-4 px-6 py-2 text-start transition-colors hover:bg-gray-200 dark:hover:bg-gray-700 rounded-xl" class:bg-gray-200={selected} class:dark:bg-gray-700={selected} > diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte index 91461d574d..08772bcec4 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte @@ -5,28 +5,23 @@ import NextAssetAction from '$lib/components/asset-viewer/actions/next-asset-action.svelte'; import PreviousAssetAction from '$lib/components/asset-viewer/actions/previous-asset-action.svelte'; import { AssetAction, ProjectionType } from '$lib/constants'; - import { updateNumberOfComments } from '$lib/stores/activity.store'; + import { activityManager } from '$lib/managers/activity-manager.svelte'; + import { authManager } from '$lib/managers/auth-manager.svelte'; import { closeEditorCofirm } from '$lib/stores/asset-editor.store'; import { assetViewingStore } from '$lib/stores/asset-viewing.store'; import { isShowDetail } from '$lib/stores/preferences.store'; import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store'; import { user } from '$lib/stores/user.store'; import { websocketEvents } from '$lib/stores/websocket'; - import { getAssetJobMessage, getSharedLink, handlePromiseError, isSharedLink } from '$lib/utils'; + import { getAssetJobMessage, getSharedLink, handlePromiseError } from '$lib/utils'; import { handleError } from '$lib/utils/handle-error'; import { SlideshowHistory } from '$lib/utils/slideshow-history'; import { AssetJobName, AssetTypeEnum, - ReactionType, - createActivity, - deleteActivity, - getActivities, - getActivityStatistics, getAllAlbums, getStack, runAssetJobs, - type ActivityResponseDto, type AlbumResponseDto, type AssetResponseDto, type PersonResponseDto, @@ -60,7 +55,6 @@ person?: PersonResponseDto | null; preAction?: PreAction | undefined; onAction?: OnAction | undefined; - reactions?: ActivityResponseDto[]; showCloseButton?: boolean; onClose: (dto: { asset: AssetResponseDto }) => void; onNext: () => Promise<HasAsset>; @@ -79,7 +73,6 @@ person = null, preAction = undefined, onAction = undefined, - reactions = $bindable([]), showCloseButton, onClose, onNext, @@ -106,8 +99,6 @@ let previewStackedAsset: AssetResponseDto | undefined = $state(); let isShowActivity = $state(false); let isShowEditor = $state(false); - let isLiked: ActivityResponseDto | null = $state(null); - let numberOfComments = $state(0); let fullscreenElement = $state<Element>(); let unsubscribes: (() => void)[] = []; let selectedEditType: string = $state(''); @@ -116,7 +107,7 @@ let zoomToggle = $state(() => void 0); const refreshStack = async () => { - if (isSharedLink()) { + if (authManager.key) { return; } @@ -135,59 +126,20 @@ }); }; - const handleAddComment = () => { - numberOfComments++; - updateNumberOfComments(1); - }; - - const handleRemoveComment = () => { - numberOfComments--; - updateNumberOfComments(-1); - }; - const handleFavorite = async () => { if (album && album.isActivityEnabled) { try { - if (isLiked) { - const activityId = isLiked.id; - await deleteActivity({ id: activityId }); - reactions = reactions.filter((reaction) => reaction.id !== activityId); - isLiked = null; - } else { - const data = await createActivity({ - activityCreateDto: { albumId: album.id, assetId: asset.id, type: ReactionType.Like }, - }); - - isLiked = data; - reactions = [...reactions, isLiked]; - } + await activityManager.toggleLike(); } catch (error) { handleError(error, $t('errors.unable_to_change_favorite')); } } }; - const getFavorite = async () => { - if (album && $user) { - try { - const data = await getActivities({ - userId: $user.id, - assetId: asset.id, - albumId: album.id, - $type: ReactionType.Like, - }); - isLiked = data.length > 0 ? data[0] : null; - } catch (error) { - handleError(error, $t('errors.unable_to_load_liked_status')); - } - } - }; - - const getNumberOfComments = async () => { + const updateComments = async () => { if (album) { try { - const { comments } = await getActivityStatistics({ assetId: asset.id, albumId: album.id }); - numberOfComments = comments; + await activityManager.refreshActivities(album.id, asset.id); } catch (error) { handleError(error, $t('errors.unable_to_get_comments_number')); } @@ -226,6 +178,10 @@ if (!sharedLink) { await handleGetAllAlbums(); } + + if (album) { + activityManager.init(album.id, asset.id); + } }); onDestroy(() => { @@ -240,10 +196,12 @@ for (const unsubscribe of unsubscribes) { unsubscribe(); } + + activityManager.reset(); }); const handleGetAllAlbums = async () => { - if (isSharedLink()) { + if (authManager.key) { return; } @@ -401,18 +359,17 @@ } }); $effect(() => { - if (album && !album.isActivityEnabled && numberOfComments === 0) { + if (album && !album.isActivityEnabled && activityManager.commentCount === 0) { isShowActivity = false; } }); $effect(() => { if (isShared && asset.id) { - handlePromiseError(getFavorite()); - handlePromiseError(getNumberOfComments()); + handlePromiseError(updateComments()); } }); $effect(() => { - if (asset.id && !sharedLink) { + if (asset.id) { handlePromiseError(handleGetAllAlbums()); } }); @@ -422,7 +379,7 @@ <section id="immich-asset-viewer" - class="fixed left-0 top-0 z-[1001] grid size-full grid-cols-4 grid-rows-[64px_1fr] overflow-hidden bg-black" + class="fixed start-0 top-0 z-[1001] grid size-full grid-cols-4 grid-rows-[64px_1fr] overflow-hidden bg-black" use:focusTrap > <!-- Top navigation bar --> @@ -546,12 +503,12 @@ onVideoStarted={handleVideoStarted} /> {/if} - {#if $slideshowState === SlideshowState.None && isShared && ((album && album.isActivityEnabled) || numberOfComments > 0)} - <div class="z-[9999] absolute bottom-0 right-0 mb-20 mr-8"> + {#if $slideshowState === SlideshowState.None && isShared && ((album && album.isActivityEnabled) || activityManager.commentCount > 0)} + <div class="z-[9999] absolute bottom-0 end-0 mb-20 me-8"> <ActivityStatus disabled={!album?.isActivityEnabled} - {isLiked} - {numberOfComments} + isLiked={activityManager.isLiked} + numberOfComments={activityManager.commentCount} onFavorite={handleFavorite} onOpenActivityTab={handleOpenActivity} /> @@ -571,7 +528,7 @@ <div transition:fly={{ duration: 150 }} id="detail-panel" - class="z-[1002] row-start-1 row-span-4 w-[360px] overflow-y-auto bg-immich-bg transition-all dark:border-l dark:border-l-immich-dark-gray dark:bg-immich-dark-bg" + class="z-[1002] row-start-1 row-span-4 w-[360px] overflow-y-auto bg-immich-bg transition-all dark:border-l dark:border-s-immich-dark-gray dark:bg-immich-dark-bg" translate="yes" > <DetailPanel {asset} currentAlbum={album} albums={appearsInAlbums} onClose={() => ($isShowDetail = false)} /> @@ -582,7 +539,7 @@ <div transition:fly={{ duration: 150 }} id="editor-panel" - class="z-[1002] row-start-1 row-span-4 w-[400px] overflow-y-auto bg-immich-bg transition-all dark:border-l dark:border-l-immich-dark-gray dark:bg-immich-dark-bg" + class="z-[1002] row-start-1 row-span-4 w-[400px] overflow-y-auto bg-immich-bg transition-all dark:border-l dark:border-s-immich-dark-gray dark:bg-immich-dark-bg" translate="yes" > <EditorPanel {asset} onUpdateSelectedType={handleUpdateSelectedEditType} onClose={closeEditor} /> @@ -631,7 +588,7 @@ <div transition:fly={{ duration: 150 }} id="activity-panel" - class="z-[1002] row-start-1 row-span-5 w-[360px] md:w-[460px] overflow-y-auto bg-immich-bg transition-all dark:border-l dark:border-l-immich-dark-gray dark:bg-immich-dark-bg" + class="z-[1002] row-start-1 row-span-5 w-[360px] md:w-[460px] overflow-y-auto bg-immich-bg transition-all dark:border-l dark:border-s-immich-dark-gray dark:bg-immich-dark-bg" translate="yes" > <ActivityViewer @@ -641,11 +598,6 @@ albumOwnerId={album.ownerId} albumId={album.id} assetId={asset.id} - {isLiked} - bind:reactions - onAddComment={handleAddComment} - onDeleteComment={handleRemoveComment} - onDeleteLike={() => (isLiked = null)} onClose={() => (isShowActivity = false)} /> </div> diff --git a/web/src/lib/components/asset-viewer/detail-panel-location.svelte b/web/src/lib/components/asset-viewer/detail-panel-location.svelte index 9e59243aa1..42cbefadf1 100644 --- a/web/src/lib/components/asset-viewer/detail-panel-location.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel-location.svelte @@ -33,7 +33,7 @@ {#if asset.exifInfo?.country} <button type="button" - class="flex w-full text-left justify-between place-items-start gap-4 py-4" + class="flex w-full text-start justify-between place-items-start gap-4 py-4" onclick={() => (isOwner ? (isShowChangeLocation = true) : null)} title={isOwner ? $t('edit_location') : ''} class:hover:dark:text-immich-dark-primary={isOwner} @@ -68,7 +68,7 @@ {:else if !asset.exifInfo?.city && isOwner} <button type="button" - class="flex w-full text-left justify-between place-items-start gap-4 py-4 rounded-lg hover:dark:text-immich-dark-primary hover:text-immich-primary" + class="flex w-full text-start justify-between place-items-start gap-4 py-4 rounded-lg hover:dark:text-immich-dark-primary hover:text-immich-primary" onclick={() => (isShowChangeLocation = true)} title={$t('add_location')} > diff --git a/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte b/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte index 4c5bfd71a8..0ec8692180 100644 --- a/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte @@ -1,10 +1,11 @@ <script lang="ts"> + import StarRating from '$lib/components/shared-components/star-rating.svelte'; + import { authManager } from '$lib/managers/auth-manager.svelte'; + import { preferences } from '$lib/stores/user.store'; + import { handlePromiseError } from '$lib/utils'; import { handleError } from '$lib/utils/handle-error'; import { updateAsset, type AssetResponseDto } from '@immich/sdk'; import { t } from 'svelte-i18n'; - import StarRating from '$lib/components/shared-components/star-rating.svelte'; - import { handlePromiseError, isSharedLink } from '$lib/utils'; - import { preferences } from '$lib/stores/user.store'; interface Props { asset: AssetResponseDto; @@ -24,7 +25,7 @@ }; </script> -{#if !isSharedLink() && $preferences?.ratings.enabled} +{#if !authManager.key && $preferences?.ratings.enabled} <section class="px-4 pt-2"> <StarRating {rating} readOnly={!isOwner} onRating={(rating) => handlePromiseError(handleChangeRating(rating))} /> </section> diff --git a/web/src/lib/components/asset-viewer/detail-panel-tags.svelte b/web/src/lib/components/asset-viewer/detail-panel-tags.svelte index 39ca096efd..eee7a7c0b6 100644 --- a/web/src/lib/components/asset-viewer/detail-panel-tags.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel-tags.svelte @@ -3,7 +3,7 @@ import TagAssetForm from '$lib/components/forms/tag-asset-form.svelte'; import Portal from '$lib/components/shared-components/portal/portal.svelte'; import { AppRoute } from '$lib/constants'; - import { isSharedLink } from '$lib/utils'; + import { authManager } from '$lib/managers/auth-manager.svelte'; import { removeTag, tagAssets } from '$lib/utils/asset-utils'; import { getAssetInfo, type AssetResponseDto } from '@immich/sdk'; import { mdiClose, mdiPlus } from '@mdi/js'; @@ -41,7 +41,7 @@ }; </script> -{#if isOwner && !isSharedLink()} +{#if isOwner && !authManager.key} <section class="px-4 mt-4"> <div class="flex h-10 w-full items-center justify-between text-sm"> <h2>{$t('tags').toUpperCase()}</h2> @@ -50,7 +50,7 @@ {#each tags as tag (tag.id)} <div class="flex group transition-all"> <a - class="inline-block h-min whitespace-nowrap pl-3 pr-1 group-hover:pl-3 py-1 text-center align-baseline leading-none text-gray-100 dark:text-immich-dark-gray bg-immich-primary dark:bg-immich-dark-primary rounded-tl-full rounded-bl-full hover:bg-immich-primary/80 dark:hover:bg-immich-dark-primary/80 transition-all" + class="inline-block h-min whitespace-nowrap ps-3 pe-1 group-hover:ps-3 py-1 text-center align-baseline leading-none text-gray-100 dark:text-immich-dark-gray bg-immich-primary dark:bg-immich-dark-primary roudned-s-full hover:bg-immich-primary/80 dark:hover:bg-immich-dark-primary/80 transition-all" href={encodeURI(`${AppRoute.TAGS}/?path=${tag.value}`)} > <p class="text-sm"> @@ -60,7 +60,7 @@ <button type="button" - class="text-gray-100 dark:text-immich-dark-gray bg-immich-primary/95 dark:bg-immich-dark-primary/95 rounded-tr-full rounded-br-full place-items-center place-content-center pr-2 pl-1 py-1 hover:bg-immich-primary/80 dark:hover:bg-immich-dark-primary/80 transition-all" + class="text-gray-100 dark:text-immich-dark-gray bg-immich-primary/95 dark:bg-immich-dark-primary/95 rounded-e-full place-items-center place-content-center pe-2 ps-1 py-1 hover:bg-immich-primary/80 dark:hover:bg-immich-dark-primary/80 transition-all" title="Remove tag" onclick={() => handleRemove(tag.id)} > diff --git a/web/src/lib/components/asset-viewer/detail-panel.svelte b/web/src/lib/components/asset-viewer/detail-panel.svelte index b96a4660ee..15bc42d001 100644 --- a/web/src/lib/components/asset-viewer/detail-panel.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel.svelte @@ -6,15 +6,19 @@ import DetailPanelTags from '$lib/components/asset-viewer/detail-panel-tags.svelte'; import Icon from '$lib/components/elements/icon.svelte'; import ChangeDate from '$lib/components/shared-components/change-date.svelte'; + import Portal from '$lib/components/shared-components/portal/portal.svelte'; import { AppRoute, QueryParameter, timeToLoadTheMap } from '$lib/constants'; + import { authManager } from '$lib/managers/auth-manager.svelte'; + import { isFaceEditMode } from '$lib/stores/face-edit.svelte'; import { boundingBoxesArray } from '$lib/stores/people.store'; import { locale } from '$lib/stores/preferences.store'; import { featureFlags } from '$lib/stores/server-config.store'; import { preferences, user } from '$lib/stores/user.store'; - import { getAssetThumbnailUrl, getPeopleThumbnailUrl, handlePromiseError, isSharedLink } from '$lib/utils'; + import { getAssetThumbnailUrl, getPeopleThumbnailUrl, handlePromiseError } from '$lib/utils'; import { delay, isFlipped } 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 { fromDateTimeOriginal, fromLocalDateTime } from '$lib/utils/timeline-util'; import { AssetMediaSize, @@ -44,9 +48,6 @@ import LoadingSpinner from '../shared-components/loading-spinner.svelte'; import UserAvatar from '../shared-components/user-avatar.svelte'; import AlbumListItemDetails from './album-list-item-details.svelte'; - import Portal from '$lib/components/shared-components/portal/portal.svelte'; - import { getMetadataSearchQuery } from '$lib/utils/metadata-search'; - import { isFaceEditMode } from '$lib/stores/face-edit.svelte'; interface Props { asset: AssetResponseDto; @@ -84,7 +85,7 @@ const handleNewAsset = async (newAsset: AssetResponseDto) => { // TODO: check if reloading asset data is necessary - if (newAsset.id && !isSharedLink()) { + if (newAsset.id && !authManager.key) { const data = await getAssetInfo({ id: asset.id }); people = data?.people || []; unassignedFaces = data?.unassignedFaces || []; @@ -187,7 +188,7 @@ <DetailPanelDescription {asset} {isOwner} /> <DetailPanelRating {asset} {isOwner} /> - {#if !isSharedLink() && isOwner} + {#if !authManager.key && isOwner} <section class="px-4 pt-4 text-sm"> <div class="flex h-10 w-full items-center justify-between"> <h2>{$t('people').toUpperCase()}</h2> @@ -296,7 +297,7 @@ {#if dateTime} <button type="button" - class="flex w-full text-left justify-between place-items-start gap-4 py-4" + class="flex w-full text-start justify-between place-items-start gap-4 py-4" onclick={() => (isOwner ? (isShowChangeDate = true) : null)} title={isOwner ? $t('edit_date') : ''} class:hover:dark:text-immich-dark-primary={isOwner} diff --git a/web/src/lib/components/asset-viewer/download-panel.svelte b/web/src/lib/components/asset-viewer/download-panel.svelte index 80a14a5ac3..95633134a8 100644 --- a/web/src/lib/components/asset-viewer/download-panel.svelte +++ b/web/src/lib/components/asset-viewer/download-panel.svelte @@ -1,11 +1,11 @@ <script lang="ts"> - import { type DownloadProgress, downloadManager, downloadStore } from '$lib/stores/download-store.svelte'; + import { type DownloadProgress, downloadManager } from '$lib/managers/download-manager.svelte'; import { locale } from '$lib/stores/preferences.store'; + import { mdiClose } from '@mdi/js'; + import { t } from 'svelte-i18n'; import { fly, slide } from 'svelte/transition'; import { getByteUnitString } from '../../utils/byte-units'; import CircleIconButton from '../elements/buttons/circle-icon-button.svelte'; - import { mdiClose } from '@mdi/js'; - import { t } from 'svelte-i18n'; const abort = (downloadKey: string, download: DownloadProgress) => { download.abort?.abort(); @@ -13,17 +13,17 @@ }; </script> -{#if downloadStore.isDownloading} +{#if downloadManager.isDownloading} <div transition:fly={{ x: -100, duration: 350 }} - class="fixed bottom-10 left-2 z-[10000] max-h-[270px] w-[315px] rounded-2xl border bg-immich-bg p-4 text-sm shadow-sm" + class="fixed bottom-10 start-2 z-[10000] max-h-[270px] w-[315px] rounded-2xl border bg-immich-bg p-4 text-sm shadow-sm" > <p class="mb-2 text-xs text-gray-500">{$t('downloading').toUpperCase()}</p> <div class="my-2 mb-2 flex max-h-[200px] flex-col overflow-y-auto text-sm"> - {#each Object.keys(downloadStore.assets) as downloadKey (downloadKey)} - {@const download = downloadStore.assets[downloadKey]} + {#each Object.keys(downloadManager.assets) as downloadKey (downloadKey)} + {@const download = downloadManager.assets[downloadKey]} <div class="mb-2 flex place-items-center" transition:slide> - <div class="w-full pr-10"> + <div class="w-full pe-10"> <div class="flex place-items-center justify-between gap-2 text-xs font-medium"> <p class="truncate">â {downloadKey}</p> {#if download.total} @@ -41,7 +41,7 @@ </p> </div> </div> - <div class="absolute right-2"> + <div class="absolute end-2"> <CircleIconButton title={$t('close')} onclick={() => abort(downloadKey, download)} diff --git a/web/src/lib/components/asset-viewer/face-editor/face-editor.svelte b/web/src/lib/components/asset-viewer/face-editor/face-editor.svelte index c50c21bef8..f25276aca9 100644 --- a/web/src/lib/components/asset-viewer/face-editor/face-editor.svelte +++ b/web/src/lib/components/asset-viewer/face-editor/face-editor.svelte @@ -2,14 +2,15 @@ import ImageThumbnail from '$lib/components/assets/thumbnail/image-thumbnail.svelte'; import { dialogController } from '$lib/components/shared-components/dialog/dialog'; import { notificationController } from '$lib/components/shared-components/notification/notification'; + import { assetViewingStore } from '$lib/stores/asset-viewing.store'; import { isFaceEditMode } from '$lib/stores/face-edit.svelte'; import { getPeopleThumbnailUrl } from '$lib/utils'; - import { getAllPeople, createFace, type PersonResponseDto } from '@immich/sdk'; + import { handleError } from '$lib/utils/handle-error'; + import { createFace, getAllPeople, type PersonResponseDto } from '@immich/sdk'; import { Button, Input } from '@immich/ui'; import { Canvas, InteractiveFabricObject, Rect } from 'fabric'; import { onMount } from 'svelte'; - import { assetViewingStore } from '$lib/stores/asset-viewing.store'; - import { handleError } from '$lib/utils/handle-error'; + import { t } from 'svelte-i18n'; interface Props { htmlElement: HTMLImageElement | HTMLVideoElement; @@ -308,15 +309,15 @@ }; </script> -<div class="absolute left-0 top-0"> - <canvas bind:this={canvasEl} id="face-editor" class="absolute top-0 left-0"></canvas> +<div class="absolute start-0 top-0"> + <canvas bind:this={canvasEl} id="face-editor" class="absolute top-0 start-0"></canvas> <div id="face-selector" bind:this={faceSelectorEl} - class="absolute top-[calc(50%-250px)] left-[calc(50%-125px)] max-w-[250px] w-[250px] bg-white dark:bg-immich-dark-gray dark:text-immich-dark-fg backdrop-blur-sm px-2 py-4 rounded-xl border border-gray-200 dark:border-gray-800" + class="absolute top-[calc(50%-250px)] start-[calc(50%-125px)] max-w-[250px] w-[250px] bg-white dark:bg-immich-dark-gray dark:text-immich-dark-fg backdrop-blur-sm px-2 py-4 rounded-xl border border-gray-200 dark:border-gray-800" > - <p class="text-center text-sm">Select a person to tag</p> + <p class="text-center text-sm">{$t('select_person_to_tag')}</p> <div class="my-3 relative"> <Input placeholder="Search person..." bind:value={searchTerm} size="tiny" /> @@ -329,7 +330,7 @@ <button onclick={() => tagFace(person)} type="button" - class="w-full flex place-items-center gap-2 rounded-lg pl-1 pr-4 py-2 hover:bg-immich-primary/25" + class="w-full flex place-items-center gap-2 rounded-lg ps-1 pe-4 py-2 hover:bg-immich-primary/25" > <ImageThumbnail curve @@ -348,11 +349,11 @@ </div> {:else} <div class="flex items-center justify-center py-4"> - <p class="text-sm text-gray-500">No matching people found</p> + <p class="text-sm text-gray-500">{$t('no_people_found')}</p> </div> {/if} </div> - <Button size="small" fullWidth onclick={cancel} color="danger" class="mt-2">Cancel</Button> + <Button size="small" fullWidth onclick={cancel} color="danger" class="mt-2">{$t('cancel')}</Button> </div> </div> diff --git a/web/src/lib/components/asset-viewer/image-panorama-viewer.svelte b/web/src/lib/components/asset-viewer/image-panorama-viewer.svelte index 7b9fd85b4a..d678b00ddb 100644 --- a/web/src/lib/components/asset-viewer/image-panorama-viewer.svelte +++ b/web/src/lib/components/asset-viewer/image-panorama-viewer.svelte @@ -1,10 +1,11 @@ <script lang="ts"> - import { getAssetOriginalUrl, getKey } from '$lib/utils'; + import { authManager } from '$lib/managers/auth-manager.svelte'; + import { getAssetOriginalUrl } from '$lib/utils'; import { isWebCompatibleImage } from '$lib/utils/asset-utils'; import { AssetMediaSize, viewAsset, type AssetResponseDto } from '@immich/sdk'; + import { t } from 'svelte-i18n'; import { fade } from 'svelte/transition'; import LoadingSpinner from '../shared-components/loading-spinner.svelte'; - import { t } from 'svelte-i18n'; interface Props { asset: AssetResponseDto; @@ -13,7 +14,7 @@ const { asset }: Props = $props(); const loadAssetData = async (id: string) => { - const data = await viewAsset({ id, size: AssetMediaSize.Preview, key: getKey() }); + const data = await viewAsset({ id, size: AssetMediaSize.Preview, key: authManager.key }); return URL.createObjectURL(data); }; </script> diff --git a/web/src/lib/components/asset-viewer/photo-viewer.svelte b/web/src/lib/components/asset-viewer/photo-viewer.svelte index d3a9da3633..531f075b86 100644 --- a/web/src/lib/components/asset-viewer/photo-viewer.svelte +++ b/web/src/lib/components/asset-viewer/photo-viewer.svelte @@ -21,6 +21,7 @@ import FaceEditor from '$lib/components/asset-viewer/face-editor/face-editor.svelte'; import { photoViewerImgElement } from '$lib/stores/assets-store.svelte'; import { isFaceEditMode } from '$lib/stores/face-edit.svelte'; + import { cancelImageUrl, preloadImageUrl } from '$lib/utils/sw-messaging'; interface Props { asset: AssetResponseDto; @@ -71,8 +72,7 @@ const preload = (targetSize: AssetMediaSize | 'original', preloadAssets?: AssetResponseDto[]) => { for (const preloadAsset of preloadAssets || []) { if (preloadAsset.type === AssetTypeEnum.Image) { - let img = new Image(); - img.src = getAssetUrl(preloadAsset.id, targetSize, preloadAsset.thumbhash); + preloadImageUrl(getAssetUrl(preloadAsset.id, targetSize, preloadAsset.thumbhash)); } } }; @@ -168,6 +168,7 @@ return () => { loader?.removeEventListener('load', onload); loader?.removeEventListener('error', onerror); + cancelImageUrl(imageLoaderUrl); }; }); @@ -213,7 +214,7 @@ <img src={assetFileUrl} alt={$getAltText(asset)} - class="absolute top-0 left-0 -z-10 object-cover h-full w-full blur-lg" + class="absolute top-0 start-0 -z-10 object-cover h-full w-full blur-lg" draggable="false" /> {/if} diff --git a/web/src/lib/components/assets/thumbnail/__test__/thumbnail.spec.ts b/web/src/lib/components/assets/thumbnail/__test__/thumbnail.spec.ts index f7447551f0..7f6a9d588e 100644 --- a/web/src/lib/components/assets/thumbnail/__test__/thumbnail.spec.ts +++ b/web/src/lib/components/assets/thumbnail/__test__/thumbnail.spec.ts @@ -1,7 +1,8 @@ import { getIntersectionObserverMock } from '$lib/__mocks__/intersection-observer.mock'; import Thumbnail from '$lib/components/assets/thumbnail/thumbnail.svelte'; +import { getTabbable } from '$lib/utils/focus-util'; import { assetFactory } from '@test-data/factories/asset-factory'; -import { fireEvent, render, screen } from '@testing-library/svelte'; +import { fireEvent, render } from '@testing-library/svelte'; vi.hoisted(() => { Object.defineProperty(globalThis, 'matchMedia', { @@ -25,56 +26,53 @@ describe('Thumbnail component', () => { vi.stubGlobal('IntersectionObserver', getIntersectionObserverMock()); vi.mock('$lib/utils/navigation', () => ({ currentUrlReplaceAssetId: vi.fn(), + isSharedLinkRoute: vi.fn().mockReturnValue(false), })); }); it('should only contain a single tabbable element (the container)', () => { const asset = assetFactory.build({ originalPath: 'image.jpg', originalMimeType: 'image/jpeg' }); - render(Thumbnail, { + const { baseElement } = render(Thumbnail, { asset, - focussed: false, selected: true, }); - const container = screen.getByTestId('container-with-tabindex'); - expect(container.getAttribute('tabindex')).toBe('0'); + const container = baseElement.querySelector('[data-thumbnail-focus-container]'); + expect(container).not.toBeNull(); + expect(container!.getAttribute('tabindex')).toBe('0'); - // This isn't capturing all tabbable elements, but should be the most likely ones. Mainly guarding against - // inserting extra tabbable elments in future in <Thumbnail/> - let allTabbableElements = screen.queryAllByRole('link'); - allTabbableElements = allTabbableElements.concat(screen.queryAllByRole('checkbox')); - expect(allTabbableElements.length).toBeGreaterThan(0); - for (const tabbableElement of allTabbableElements) { - const testIdValue = tabbableElement.dataset.testid; - if (testIdValue === null || testIdValue !== 'container-with-tabindex') { - expect(tabbableElement.getAttribute('tabindex')).toBe('-1'); - } - } + // Guarding against inserting extra tabbable elments in future in <Thumbnail/> + const tabbables = getTabbable(container!); + expect(tabbables.length).toBe(0); }); it('handleFocus should be called on focus of container', async () => { const asset = assetFactory.build({ originalPath: 'image.jpg', originalMimeType: 'image/jpeg' }); const handleFocusSpy = vi.fn(); - render(Thumbnail, { + const { baseElement } = render(Thumbnail, { asset, handleFocus: handleFocusSpy, }); - const container = screen.getByTestId('container-with-tabindex'); - await fireEvent(container, new FocusEvent('focus')); + const container = baseElement.querySelector('[data-thumbnail-focus-container]'); + expect(container).not.toBeNull(); + await fireEvent(container as HTMLElement, new FocusEvent('focus')); expect(handleFocusSpy).toBeCalled(); }); - it('element will be focussed if not already', () => { + it('element will be focussed if not already', async () => { const asset = assetFactory.build({ originalPath: 'image.jpg', originalMimeType: 'image/jpeg' }); const handleFocusSpy = vi.fn(); - render(Thumbnail, { + const { baseElement } = render(Thumbnail, { asset, - focussed: true, handleFocus: handleFocusSpy, }); + const container = baseElement.querySelector('[data-thumbnail-focus-container]'); + expect(container).not.toBeNull(); + await fireEvent(container as HTMLElement, new FocusEvent('focus')); + expect(handleFocusSpy).toBeCalled(); }); }); diff --git a/web/src/lib/components/assets/thumbnail/image-thumbnail.svelte b/web/src/lib/components/assets/thumbnail/image-thumbnail.svelte index 55357abbc0..04493b273c 100644 --- a/web/src/lib/components/assets/thumbnail/image-thumbnail.svelte +++ b/web/src/lib/components/assets/thumbnail/image-thumbnail.svelte @@ -2,9 +2,11 @@ import { thumbhash } from '$lib/actions/thumbhash'; import BrokenAsset from '$lib/components/assets/broken-asset.svelte'; import Icon from '$lib/components/elements/icon.svelte'; + import { cancelImageUrl } from '$lib/utils/sw-messaging'; import { TUNABLES } from '$lib/utils/tunables'; import { mdiEyeOffOutline } from '@mdi/js'; import type { ClassValue } from 'svelte/elements'; + import type { ActionReturn } from 'svelte/action'; import { fade } from 'svelte/transition'; interface Props { @@ -59,11 +61,14 @@ onComplete?.(true); }; - function mount(elem: HTMLImageElement) { + function mount(elem: HTMLImageElement): ActionReturn { if (elem.complete) { loaded = true; onComplete?.(false); } + return { + destroy: () => cancelImageUrl(url), + }; } let optionalClasses = $derived( @@ -101,7 +106,7 @@ {/if} {#if hidden} - <div class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform"> + <div class="absolute start-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform"> <Icon {title} path={mdiEyeOffOutline} size="2em" class={hiddenIconClass} /> </div> {/if} diff --git a/web/src/lib/components/assets/thumbnail/thumbnail.svelte b/web/src/lib/components/assets/thumbnail/thumbnail.svelte index c21acd8f86..3a00af34ad 100644 --- a/web/src/lib/components/assets/thumbnail/thumbnail.svelte +++ b/web/src/lib/components/assets/thumbnail/thumbnail.svelte @@ -2,7 +2,7 @@ import Icon from '$lib/components/elements/icon.svelte'; import { ProjectionType } from '$lib/constants'; import { locale, playVideoThumbnailOnHover } from '$lib/stores/preferences.store'; - import { getAssetPlaybackUrl, getAssetThumbnailUrl, isSharedLink } from '$lib/utils'; + import { getAssetPlaybackUrl, getAssetThumbnailUrl } from '$lib/utils'; import { timeToSeconds } from '$lib/utils/date-time'; import { getAltText } from '$lib/utils/thumbnail-util'; import { AssetMediaSize, AssetTypeEnum, type AssetResponseDto } from '@immich/sdk'; @@ -17,15 +17,16 @@ } from '@mdi/js'; import { thumbhash } from '$lib/actions/thumbhash'; + import { authManager } from '$lib/managers/auth-manager.svelte'; import { mobileDevice } from '$lib/stores/mobile-device.svelte'; + import { focusNext } from '$lib/utils/focus-util'; import { currentUrlReplaceAssetId } from '$lib/utils/navigation'; import { TUNABLES } from '$lib/utils/tunables'; + import { onMount } from 'svelte'; import type { ClassValue } from 'svelte/elements'; import { fade } from 'svelte/transition'; import ImageThumbnail from './image-thumbnail.svelte'; import VideoThumbnail from './video-thumbnail.svelte'; - import { onMount } from 'svelte'; - import { getFocusable } from '$lib/utils/focus-util'; interface Props { asset: AssetResponseDto; @@ -34,7 +35,6 @@ thumbnailWidth?: number | undefined; thumbnailHeight?: number | undefined; selected?: boolean; - focussed?: boolean; selectionCandidate?: boolean; disabled?: boolean; disableLinkMouseOver?: boolean; @@ -57,7 +57,6 @@ thumbnailWidth = undefined, thumbnailHeight = undefined, selected = false, - focussed = false, selectionCandidate = false, disabled = false, disableLinkMouseOver = false, @@ -78,17 +77,11 @@ } = TUNABLES; let usingMobileDevice = $derived(mobileDevice.pointerCoarse); - let focussableElement: HTMLElement | undefined = $state(); + let element: HTMLElement | undefined = $state(); let mouseOver = $state(false); let loaded = $state(false); let thumbError = $state(false); - $effect(() => { - if (focussed && document.activeElement !== focussableElement) { - focussableElement?.focus(); - } - }); - let width = $derived(thumbnailSize || thumbnailWidth || 235); let height = $derived(thumbnailSize || thumbnailHeight || 235); @@ -235,31 +228,14 @@ if (evt.key === 'x') { onSelect?.(asset); } - if (document.activeElement === focussableElement && evt.key === 'Escape') { - const focusable = getFocusable(document); - const index = focusable.indexOf(focussableElement); - - let i = index + 1; - while (i !== index) { - const next = focusable[i]; - if (next.dataset.thumbnailFocusContainer !== undefined) { - if (i === focusable.length - 1) { - i = 0; - } else { - i++; - } - continue; - } - next.focus(); - break; - } + if (document.activeElement === element && evt.key === 'Escape') { + focusNext((element) => element.dataset.thumbnailFocusContainer === undefined, true); } }} onclick={handleClick} - bind:this={focussableElement} + bind:this={element} onfocus={handleFocus} data-thumbnail-focus-container - data-testid="container-with-tabindex" tabindex={0} role="link" > @@ -331,21 +307,21 @@ ></div> <!-- Favorite asset star --> - {#if !isSharedLink() && asset.isFavorite} - <div class="absolute bottom-2 left-2 z-10"> + {#if !authManager.key && asset.isFavorite} + <div class="absolute bottom-2 start-2 z-10"> <Icon path={mdiHeart} size="24" class="text-white" /> </div> {/if} - {#if !isSharedLink() && showArchiveIcon && asset.isArchived} - <div class={['absolute left-2 z-10', asset.isFavorite ? 'bottom-10' : 'bottom-2']}> + {#if !authManager.key && showArchiveIcon && asset.isArchived} + <div class={['absolute start-2 z-10', asset.isFavorite ? 'bottom-10' : 'bottom-2']}> <Icon path={mdiArchiveArrowDownOutline} size="24" class="text-white" /> </div> {/if} {#if asset.type === AssetTypeEnum.Image && asset.exifInfo?.projectionType === ProjectionType.EQUIRECTANGULAR} - <div class="absolute right-0 top-0 z-10 flex place-items-center gap-1 text-xs font-medium text-white"> - <span class="pr-2 pt-2"> + <div class="absolute end-0 top-0 z-10 flex place-items-center gap-1 text-xs font-medium text-white"> + <span class="pe-2 pt-2"> <Icon path={mdiRotate360} size="24" /> </span> </div> @@ -356,10 +332,10 @@ <div class={[ 'absolute z-10 flex place-items-center gap-1 text-xs font-medium text-white', - asset.type == AssetTypeEnum.Image && !asset.livePhotoVideoId ? 'top-0 right-0' : 'top-7 right-1', + asset.type == AssetTypeEnum.Image && !asset.livePhotoVideoId ? 'top-0 end-0' : 'top-7 end-1', ]} > - <span class="pr-2 pt-2 flex place-items-center gap-1"> + <span class="pe-2 pt-2 flex place-items-center gap-1"> <p>{asset.stack.assetCount.toLocaleString($locale)}</p> <Icon path={mdiCameraBurst} size="24" /> </span> diff --git a/web/src/lib/components/assets/thumbnail/video-thumbnail.svelte b/web/src/lib/components/assets/thumbnail/video-thumbnail.svelte index fc3cb2e951..9959c86548 100644 --- a/web/src/lib/components/assets/thumbnail/video-thumbnail.svelte +++ b/web/src/lib/components/assets/thumbnail/video-thumbnail.svelte @@ -55,7 +55,7 @@ }; </script> -<div class="absolute right-0 top-0 z-20 flex place-items-center gap-1 text-xs font-medium text-white"> +<div class="absolute end-0 top-0 z-20 flex place-items-center gap-1 text-xs font-medium text-white"> {#if showTime} <span class="pt-2"> {#if remainingSeconds < 60} @@ -69,7 +69,7 @@ {/if} <!-- svelte-ignore a11y_no_static_element_interactions --> - <span class="pr-2 pt-2" onmouseenter={onMouseEnter} onmouseleave={onMouseLeave}> + <span class="pe-2 pt-2" onmouseenter={onMouseEnter} onmouseleave={onMouseLeave}> {#if enablePlayback} {#if loading} <LoadingSpinner /> diff --git a/web/src/lib/components/elements/buttons/button.svelte b/web/src/lib/components/elements/buttons/button.svelte index 991bbaecee..ac7d9808f3 100644 --- a/web/src/lib/components/elements/buttons/button.svelte +++ b/web/src/lib/components/elements/buttons/button.svelte @@ -79,7 +79,7 @@ }; const sizeClasses: Record<Size, string> = { - tiny: 'p-0 ml-2 mr-0 align-top', + tiny: 'p-0 ms-2 me-0 align-top', icon: 'p-2.5', link: 'p-2 font-medium', sm: 'px-4 py-2 text-sm font-medium', diff --git a/web/src/lib/components/elements/buttons/skip-link.svelte b/web/src/lib/components/elements/buttons/skip-link.svelte index a1a24634c4..a21fc60caf 100644 --- a/web/src/lib/components/elements/buttons/skip-link.svelte +++ b/web/src/lib/components/elements/buttons/skip-link.svelte @@ -1,6 +1,7 @@ <script lang="ts"> import { t } from 'svelte-i18n'; import Button from './button.svelte'; + import { getTabbable } from '$lib/utils/focus-util'; interface Props { /** @@ -23,7 +24,12 @@ const moveFocus = () => { const targetEl = document.querySelector<HTMLElement>(target); - targetEl?.focus(); + if (targetEl) { + const element = getTabbable(targetEl)[0]; + if (element) { + element.focus(); + } + } }; const getBreakpoint = () => { @@ -50,7 +56,7 @@ }; </script> -<div class="absolute z-50 top-2 left-2 transition-transform {isFocused ? 'translate-y-0' : '-translate-y-10 sr-only'}"> +<div class="absolute z-50 top-2 start-2 transition-transform {isFocused ? 'translate-y-0' : '-translate-y-10 sr-only'}"> <Button size="sm" rounded="none" diff --git a/web/src/lib/components/elements/dropdown.svelte b/web/src/lib/components/elements/dropdown.svelte index 52360f04de..3966ea8310 100644 --- a/web/src/lib/components/elements/dropdown.svelte +++ b/web/src/lib/components/elements/dropdown.svelte @@ -82,10 +82,10 @@ const getAlignClass = (position: 'bottom-left' | 'bottom-right') => { switch (position) { case 'bottom-left': { - return 'left-0'; + return 'start-0'; } case 'bottom-right': { - return 'right-0'; + return 'end-0'; } default: { diff --git a/web/src/lib/components/faces-page/edit-name-input.svelte b/web/src/lib/components/faces-page/edit-name-input.svelte index ebb44c4008..a5d0fdd757 100644 --- a/web/src/lib/components/faces-page/edit-name-input.svelte +++ b/web/src/lib/components/faces-page/edit-name-input.svelte @@ -35,7 +35,7 @@ : 'rounded-lg'} bg-gray-100 p-2 dark:bg-gray-700 border border-gray-200 dark:border-immich-dark-gray" > <ImageThumbnail circle shadow url={thumbnailData} altText={person.name} widthStyle="2rem" heightStyle="2rem" /> - <form class="ml-4 flex w-full justify-between gap-16" autocomplete="off" {onsubmit}> + <form class="ms-4 flex w-full justify-between gap-16" autocomplete="off" {onsubmit}> <SearchPeople bind:searchName={name} bind:searchedPeopleLocal={suggestedPeople} diff --git a/web/src/lib/components/faces-page/face-thumbnail.svelte b/web/src/lib/components/faces-page/face-thumbnail.svelte index cc3fffe5d7..196777f0af 100644 --- a/web/src/lib/components/faces-page/face-thumbnail.svelte +++ b/web/src/lib/components/faces-page/face-thumbnail.svelte @@ -44,7 +44,7 @@ </div> <div - class="absolute left-0 top-0 h-full w-full bg-immich-primary/30 opacity-0" + class="absolute start-0 top-0 h-full w-full bg-immich-primary/30 opacity-0" class:hover:opacity-100={selectable} class:rounded-full={circle} class:rounded-lg={!circle} @@ -52,7 +52,7 @@ {#if selected} <div - class="absolute left-0 top-0 h-full w-full bg-blue-500/80" + class="absolute start-0 top-0 h-full w-full bg-blue-500/80" class:rounded-full={circle} class:rounded-lg={!circle} ></div> @@ -60,7 +60,7 @@ {#if person.name} <span - class="w-100 text-white-shadow absolute bottom-2 left-0 w-full text-ellipsis px-1 text-center font-medium text-white hover:cursor-pointer" + class="w-100 text-white-shadow absolute bottom-2 start-0 w-full text-ellipsis px-1 text-center font-medium text-white hover:cursor-pointer" > {person.name} </span> diff --git a/web/src/lib/components/faces-page/manage-people-visibility.svelte b/web/src/lib/components/faces-page/manage-people-visibility.svelte index 0c2ed7bad0..c3c75d495b 100644 --- a/web/src/lib/components/faces-page/manage-people-visibility.svelte +++ b/web/src/lib/components/faces-page/manage-people-visibility.svelte @@ -117,12 +117,12 @@ <div class="flex items-center"> <CircleIconButton title={$t('close')} icon={mdiClose} onclick={onClose} /> <div class="flex gap-2 items-center"> - <p id={titleId} class="ml-2">{$t('show_and_hide_people')}</p> + <p id={titleId} class="ms-2">{$t('show_and_hide_people')}</p> <p class="text-sm text-gray-400 dark:text-gray-600">({totalPeopleCount.toLocaleString($locale)})</p> </div> </div> <div class="flex items-center justify-end"> - <div class="flex items-center md:mr-4"> + <div class="flex items-center md:me-4"> <CircleIconButton title={$t('reset_people_visibility')} icon={mdiRestart} onclick={handleResetVisibility} /> <CircleIconButton title={toggleButton.label} icon={toggleButton.icon} onclick={handleToggleVisibility} /> </div> @@ -154,7 +154,7 @@ hiddenIconClass="text-white group-hover:text-black transition-colors" /> {#if person.name} - <span class="absolute bottom-2 left-0 w-full select-text px-1 text-center font-medium text-white"> + <span class="absolute bottom-2 start-0 w-full select-text px-1 text-center font-medium text-white"> {person.name} </span> {/if} diff --git a/web/src/lib/components/faces-page/merge-face-selector.svelte b/web/src/lib/components/faces-page/merge-face-selector.svelte index 88576d2845..c2c1c65647 100644 --- a/web/src/lib/components/faces-page/merge-face-selector.svelte +++ b/web/src/lib/components/faces-page/merge-face-selector.svelte @@ -99,7 +99,7 @@ <section transition:fly={{ y: 500, duration: 100, easing: quintOut }} - class="absolute left-0 top-0 z-[9999] h-full w-full bg-immich-bg dark:bg-immich-dark-bg" + class="absolute start-0 top-0 z-[9999] h-full w-full bg-immich-bg dark:bg-immich-dark-bg" > <ControlAppBar onClose={onBack}> {#snippet leading()} @@ -113,7 +113,7 @@ {#snippet trailing()} <Button size="sm" disabled={!hasSelection} onclick={handleMerge}> <Icon path={mdiMerge} size={18} /> - <span class="ml-2">{$t('merge')}</span></Button + <span class="ms-2">{$t('merge')}</span></Button > {/snippet} </ControlAppBar> diff --git a/web/src/lib/components/faces-page/people-card.svelte b/web/src/lib/components/faces-page/people-card.svelte index 4aff4e96f8..b740953340 100644 --- a/web/src/lib/components/faces-page/people-card.svelte +++ b/web/src/lib/components/faces-page/people-card.svelte @@ -54,7 +54,7 @@ circle /> {#if person.isFavorite} - <div class="absolute top-4 left-4"> + <div class="absolute top-4 start-4"> <Icon path={mdiHeart} size="24" class="text-white" /> </div> {/if} @@ -62,7 +62,7 @@ </a> {#if showVerticalDots} - <div class="absolute top-2 right-2"> + <div class="absolute top-2 end-2"> <ButtonContextMenu buttonClass="icon-white-drop-shadow focus:opacity-100 {showVerticalDots ? 'opacity-100' : 'opacity-0'}" color="opaque" diff --git a/web/src/lib/components/faces-page/person-side-panel.svelte b/web/src/lib/components/faces-page/person-side-panel.svelte index 73c3ea7ae5..5e8b35c74b 100644 --- a/web/src/lib/components/faces-page/person-side-panel.svelte +++ b/web/src/lib/components/faces-page/person-side-panel.svelte @@ -227,7 +227,7 @@ <div role="button" tabindex={index} - class="absolute left-0 top-0 h-[90px] w-[90px] cursor-default" + class="absolute start-0 top-0 h-[90px] w-[90px] cursor-default" onfocus={() => ($boundingBoxesArray = [peopleWithFaces[index]])} onmouseover={() => ($boundingBoxesArray = [peopleWithFaces[index]])} onmouseleave={() => ($boundingBoxesArray = [])} @@ -303,7 +303,7 @@ </p> {/if} - <div class="absolute -right-[5px] -top-[5px] h-[20px] w-[20px] rounded-full"> + <div class="absolute -end-[5px] -top-[5px] h-[20px] w-[20px] rounded-full"> {#if selectedPersonToCreate[face.id] || selectedPersonToReassign[face.id]} <CircleIconButton color="primary" @@ -311,7 +311,7 @@ title={$t('reset')} size="18" padding="1" - class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform" + class="absolute start-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform" onclick={() => handleReset(face.id)} /> {:else} @@ -321,29 +321,29 @@ title={$t('select_new_face')} size="18" padding="1" - class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform" + class="absolute start-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform" onclick={() => handleFacePicker(face)} /> {/if} </div> - <div class="absolute right-[25px] -top-[5px] h-[20px] w-[20px] rounded-full"> + <div class="absolute end-[25px] -top-[5px] h-[20px] w-[20px] rounded-full"> {#if !selectedPersonToCreate[face.id] && !selectedPersonToReassign[face.id] && !face.person} <div - class="flex place-content-center place-items-center rounded-full bg-[#d3d3d3] p-1 transition-all absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform" + class="flex place-content-center place-items-center rounded-full bg-[#d3d3d3] p-1 transition-all absolute start-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform" > <Icon color="primary" path={mdiAccountOff} ariaHidden size="18" /> </div> {/if} </div> {#if face.person != null} - <div class="absolute -right-[5px] top-[25px] h-[20px] w-[20px] rounded-full"> + <div class="absolute -end-[5px] top-[25px] h-[20px] w-[20px] rounded-full"> <CircleIconButton color="red" icon={mdiTrashCan} title={$t('delete_face')} size="18" padding="1" - class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform" + class="absolute start-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform" onclick={() => deleteAssetFace(face)} /> </div> diff --git a/web/src/lib/components/faces-page/unmerge-face-selector.svelte b/web/src/lib/components/faces-page/unmerge-face-selector.svelte index e808c98748..41c584d602 100644 --- a/web/src/lib/components/faces-page/unmerge-face-selector.svelte +++ b/web/src/lib/components/faces-page/unmerge-face-selector.svelte @@ -120,7 +120,7 @@ <section transition:fly={{ y: 500, duration: 100, easing: quintOut }} - class="absolute left-0 top-0 z-[9999] h-full w-full bg-immich-bg dark:bg-immich-dark-bg" + class="absolute start-0 top-0 z-[9999] h-full w-full bg-immich-bg dark:bg-immich-dark-bg" > <ControlAppBar {onClose}> {#snippet leading()} @@ -140,7 +140,7 @@ {:else} <LoadingSpinner /> {/if} - <span class="ml-2"> {$t('create_new_person')}</span></Button + <span class="ms-2"> {$t('create_new_person')}</span></Button > <Button size="sm" @@ -155,7 +155,7 @@ {:else} <LoadingSpinner /> {/if} - <span class="ml-2"> {$t('reassign')}</span></Button + <span class="ms-2"> {$t('reassign')}</span></Button > </div> {/snippet} diff --git a/web/src/lib/components/forms/library-import-paths-form.svelte b/web/src/lib/components/forms/library-import-paths-form.svelte index 639b81071f..64c32532ef 100644 --- a/web/src/lib/components/forms/library-import-paths-form.svelte +++ b/web/src/lib/components/forms/library-import-paths-form.svelte @@ -173,7 +173,7 @@ {/if} <form {onsubmit} autocomplete="off" class="m-4 flex flex-col gap-4"> - <table class="text-left"> + <table class="text-start"> <tbody class="block w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray"> {#each validatedPaths as validatedPath, listIndex (validatedPath.importPath)} <tr @@ -183,7 +183,7 @@ : 'bg-immich-bg dark:bg-immich-dark-gray/50' }`} > - <td class="w-1/8 text-ellipsis pl-8 text-sm"> + <td class="w-1/8 text-ellipsis ps-8 text-sm"> {#if validatedPath.isValid} <Icon path={mdiCheckCircleOutline} diff --git a/web/src/lib/components/forms/library-scan-settings-form.svelte b/web/src/lib/components/forms/library-scan-settings-form.svelte index 0ce414c10d..7a021a1c0c 100644 --- a/web/src/lib/components/forms/library-scan-settings-form.svelte +++ b/web/src/lib/components/forms/library-scan-settings-form.svelte @@ -123,7 +123,7 @@ {/if} <form {onsubmit} autocomplete="off" class="m-4 flex flex-col gap-4"> - <table class="w-full text-left"> + <table class="w-full text-start"> <tbody class="block w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray"> {#each exclusionPatterns as exclusionPattern, listIndex (exclusionPattern)} <tr diff --git a/web/src/lib/components/forms/tag-asset-form.svelte b/web/src/lib/components/forms/tag-asset-form.svelte index 9392b1538d..a7f95c03c0 100644 --- a/web/src/lib/components/forms/tag-asset-form.svelte +++ b/web/src/lib/components/forms/tag-asset-form.svelte @@ -72,7 +72,7 @@ {#if tag} <div class="flex group transition-all"> <span - class="inline-block h-min whitespace-nowrap pl-3 pr-1 group-hover:pl-3 py-1 text-center align-baseline leading-none text-gray-100 dark:text-immich-dark-gray bg-immich-primary dark:bg-immich-dark-primary rounded-tl-full rounded-bl-full hover:bg-immich-primary/80 dark:hover:bg-immich-dark-primary/80 transition-all" + class="inline-block h-min whitespace-nowrap ps-3 pe-1 group-hover:ps-3 py-1 text-center align-baseline leading-none text-gray-100 dark:text-immich-dark-gray bg-immich-primary dark:bg-immich-dark-primary roudned-s-full hover:bg-immich-primary/80 dark:hover:bg-immich-dark-primary/80 transition-all" > <p class="text-sm"> {tag.value} @@ -81,7 +81,7 @@ <button type="button" - class="text-gray-100 dark:text-immich-dark-gray bg-immich-primary/95 dark:bg-immich-dark-primary/95 rounded-tr-full rounded-br-full place-items-center place-content-center pr-2 pl-1 py-1 hover:bg-immich-primary/80 dark:hover:bg-immich-dark-primary/80 transition-all" + class="text-gray-100 dark:text-immich-dark-gray bg-immich-primary/95 dark:bg-immich-dark-primary/95 rounded-e-full place-items-center place-content-center pe-2 ps-1 py-1 hover:bg-immich-primary/80 dark:hover:bg-immich-dark-primary/80 transition-all" title="Remove tag" onclick={() => handleRemove(tagId)} > diff --git a/web/src/lib/components/layouts/AuthPageLayout.svelte b/web/src/lib/components/layouts/AuthPageLayout.svelte index a30202e468..870a741bd1 100644 --- a/web/src/lib/components/layouts/AuthPageLayout.svelte +++ b/web/src/lib/components/layouts/AuthPageLayout.svelte @@ -17,7 +17,7 @@ alt="Immich logo" /> <div - class="w-full h-[99%] absolute left-0 top-0 backdrop-blur-[200px] bg-transparent dark:bg-immich-dark-bg/20" + class="w-full h-[99%] absolute start-0 top-0 backdrop-blur-[200px] bg-transparent dark:bg-immich-dark-bg/20" ></div> </div> diff --git a/web/src/lib/components/layouts/user-page-layout.svelte b/web/src/lib/components/layouts/user-page-layout.svelte index d5f4d96ef9..d27361ea75 100644 --- a/web/src/lib/components/layouts/user-page-layout.svelte +++ b/web/src/lib/components/layouts/user-page-layout.svelte @@ -49,7 +49,7 @@ {@render header?.()} </header> -<main +<div tabindex="-1" class="relative grid h-dvh grid-cols-[theme(spacing.0)_auto] overflow-hidden bg-immich-bg max-md:pt-[var(--navbar-height-md)] pt-[var(--navbar-height)] dark:bg-immich-dark-bg sidebar:grid-cols-[theme(spacing.64)_auto]" > @@ -59,7 +59,7 @@ <SideBar /> {/if} - <section class="relative"> + <main class="relative"> {#if title || buttons} <div class="absolute flex h-16 w-full place-items-center justify-between border-b p-2 dark:border-immich-dark-gray dark:text-immich-dark-fg" @@ -79,5 +79,5 @@ <div class="{scrollbarClass} absolute {hasTitleClass} w-full overflow-y-auto" use:useActions={use}> {@render children?.()} </div> - </section> -</main> + </main> +</div> diff --git a/web/src/lib/components/memory-page/memory-viewer.svelte b/web/src/lib/components/memory-page/memory-viewer.svelte index e39a3cfa74..8d33859fef 100644 --- a/web/src/lib/components/memory-page/memory-viewer.svelte +++ b/web/src/lib/components/memory-page/memory-viewer.svelte @@ -360,8 +360,8 @@ {#each current.memory.assets as asset, index (asset.id)} <a class="relative w-full py-2" href={asHref(asset)} aria-label={$t('view')}> - <span class="absolute left-0 h-[2px] w-full bg-gray-500"></span> - <span class="absolute left-0 h-[2px] bg-white" style:width={`${toProgressPercentage(index)}%`}></span> + <span class="absolute start-0 h-[2px] w-full bg-gray-500"></span> + <span class="absolute start-0 h-[2px] bg-white" style:width={`${toProgressPercentage(index)}%`}></span> </a> {/each} @@ -380,7 +380,7 @@ {#if galleryInView} <div - class="fixed top-20 z-30 left-1/2 -translate-x-1/2 transition-opacity" + class="fixed top-20 z-30 start-1/2 -translate-x-1/2 transition-opacity" class:opacity-0={!galleryInView} class:opacity-100={galleryInView} > @@ -396,7 +396,7 @@ <!-- Viewer --> <section class="overflow-hidden pt-32 md:pt-20" bind:clientHeight={viewerHeight}> <div - class="ml-[-100%] box-border flex h-[calc(100vh_-_224px)] md:h-[calc(100vh_-_180px)] w-[300%] items-center justify-center gap-10 overflow-hidden" + class="ms-[-100%] box-border flex h-[calc(100vh_-_224px)] md:h-[calc(100vh_-_180px)] w-[300%] items-center justify-center gap-10 overflow-hidden" > <!-- PREVIOUS MEMORY --> <div class="h-1/2 w-[20vw] rounded-2xl {current.previousMemory ? 'opacity-25 hover:opacity-70' : 'opacity-0'}"> @@ -424,7 +424,7 @@ {/if} {#if current.previousMemory} - <div class="absolute bottom-4 right-4 text-left text-white"> + <div class="absolute bottom-4 end-4 text-start text-white"> <p class="text-xs font-semibold text-gray-200">{$t('previous').toUpperCase()}</p> <p class="text-xl">{$memoryLaneTitle(current.previousMemory)}</p> </div> @@ -465,7 +465,7 @@ {/key} <div - class="absolute bottom-0 right-0 p-2 transition-all flex h-full justify-between flex-col items-end gap-2" + class="absolute bottom-0 end-0 p-2 transition-all flex h-full justify-between flex-col items-end gap-2" class:opacity-0={galleryInView} class:opacity-100={!galleryInView} > @@ -521,7 +521,7 @@ </div> <!-- CONTROL BUTTONS --> {#if current.previous} - <div class="absolute top-1/2 left-0 ml-4"> + <div class="absolute top-1/2 start-0 ms-4"> <CircleIconButton title={$t('previous_memory')} icon={mdiChevronLeft} @@ -532,7 +532,7 @@ {/if} {#if current.next} - <div class="absolute top-1/2 right-0 mr-4"> + <div class="absolute top-1/2 end-0 me-4"> <CircleIconButton title={$t('next_memory')} icon={mdiChevronRight} @@ -542,9 +542,11 @@ </div> {/if} - <div class="absolute left-8 top-4 text-sm font-medium text-white"> + <div class="absolute start-8 top-4 text-sm font-medium text-white"> <p> - {fromLocalDateTime(current.memory.assets[0].localDateTime).toLocaleString(DateTime.DATE_FULL)} + {fromLocalDateTime(current.memory.assets[0].localDateTime).toLocaleString(DateTime.DATE_FULL, { + locale: $locale, + })} </p> <p> {current.asset.exifInfo?.city || ''} @@ -580,7 +582,7 @@ {/if} {#if current.nextMemory} - <div class="absolute bottom-4 left-4 text-left text-white"> + <div class="absolute bottom-4 start-4 text-start text-white"> <p class="text-xs font-semibold text-gray-200">{$t('up_next').toUpperCase()}</p> <p class="text-xl">{$memoryLaneTitle(current.nextMemory)}</p> </div> diff --git a/web/src/lib/components/onboarding-page/onboarding-theme.svelte b/web/src/lib/components/onboarding-page/onboarding-theme.svelte index 4229cf9f67..9d3345dc2d 100644 --- a/web/src/lib/components/onboarding-page/onboarding-theme.svelte +++ b/web/src/lib/components/onboarding-page/onboarding-theme.svelte @@ -1,12 +1,12 @@ <script lang="ts"> - import { mdiArrowRight, mdiThemeLightDark } from '@mdi/js'; + import { moonPath, moonViewBox, sunPath, sunViewBox } from '$lib/assets/svg-paths'; import Button from '$lib/components/elements/buttons/button.svelte'; import Icon from '$lib/components/elements/icon.svelte'; - import OnboardingCard from './onboarding-card.svelte'; - import { colorTheme } from '$lib/stores/preferences.store'; - import { moonPath, moonViewBox, sunPath, sunViewBox } from '$lib/assets/svg-paths'; import { Theme } from '$lib/constants'; + import { themeManager } from '$lib/managers/theme-manager.svelte'; + import { mdiArrowRight, mdiThemeLightDark } from '@mdi/js'; import { t } from 'svelte-i18n'; + import OnboardingCard from './onboarding-card.svelte'; interface Props { onDone: () => void; @@ -24,7 +24,7 @@ <button type="button" class="w-1/2 aspect-square bg-immich-bg rounded-3xl transition-all shadow-sm hover:shadow-xl border-[3px] border-immich-dark-primary/80 border-immich-primary dark:border dark:border-transparent" - onclick={() => ($colorTheme.value = Theme.LIGHT)} + onclick={() => themeManager.setTheme(Theme.LIGHT)} > <div class="flex flex-col place-items-center place-content-center justify-around h-full w-full text-immich-primary" @@ -36,7 +36,7 @@ <button type="button" class="w-1/2 aspect-square bg-immich-dark-bg rounded-3xl dark:border-[3px] dark:border-immich-dark-primary/80 dark:border-immich-dark-primary border border-transparent" - onclick={() => ($colorTheme.value = Theme.DARK)} + onclick={() => themeManager.setTheme(Theme.DARK)} > <div class="flex flex-col place-items-center place-content-center justify-around h-full w-full text-immich-dark-primary" diff --git a/web/src/lib/components/photos-page/actions/remove-from-shared-link.svelte b/web/src/lib/components/photos-page/actions/remove-from-shared-link.svelte index 1639a642b5..e1e803ad50 100644 --- a/web/src/lib/components/photos-page/actions/remove-from-shared-link.svelte +++ b/web/src/lib/components/photos-page/actions/remove-from-shared-link.svelte @@ -1,13 +1,13 @@ <script lang="ts"> import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte'; - import { getKey } from '$lib/utils'; + import { dialogController } from '$lib/components/shared-components/dialog/dialog'; + import { authManager } from '$lib/managers/auth-manager.svelte'; import { handleError } from '$lib/utils/handle-error'; import { removeSharedLinkAssets, type SharedLinkResponseDto } from '@immich/sdk'; import { mdiDeleteOutline } from '@mdi/js'; + import { t } from 'svelte-i18n'; import { NotificationType, notificationController } from '../../shared-components/notification/notification'; import { getAssetControlContext } from '../asset-select-control-bar.svelte'; - import { dialogController } from '$lib/components/shared-components/dialog/dialog'; - import { t } from 'svelte-i18n'; interface Props { sharedLink: SharedLinkResponseDto; @@ -34,7 +34,7 @@ assetIdsDto: { assetIds: [...getAssets()].map((asset) => asset.id), }, - key: getKey(), + key: authManager.key, }); for (const result of results) { diff --git a/web/src/lib/components/photos-page/asset-date-group.svelte b/web/src/lib/components/photos-page/asset-date-group.svelte index e45ebea1e7..b25e9d49f5 100644 --- a/web/src/lib/components/photos-page/asset-date-group.svelte +++ b/web/src/lib/components/photos-page/asset-date-group.svelte @@ -100,9 +100,6 @@ } }; - const assetOnFocusHandler = (asset: AssetResponseDto) => { - assetInteraction.focussedAssetId = asset.id; - }; function filterIntersecting<R extends { intersecting: boolean }>(intersectable: R[]) { return intersectable.filter((int) => int.intersecting); } @@ -131,7 +128,7 @@ > <!-- Date group title --> <div - class="flex z-[100] pt-[calc(1.75rem+1px)] pb-5 h-6 place-items-center text-xs font-medium text-immich-fg bg-immich-bg dark:bg-immich-dark-bg dark:text-immich-dark-fg md:text-sm" + class="flex z-[100] pt-7 pb-5 max-md:pt-5 max-md:pb-3 h-6 place-items-center text-xs font-medium text-immich-fg bg-immich-bg dark:bg-immich-dark-bg dark:text-immich-dark-fg md:text-sm" style:width={dateGroup.width + 'px'} > {#if !singleSelect && ((hoveredDateGroup === dateGroup.groupTitle && isMouseOverGroup) || assetInteraction.selectedGroup.has(dateGroup.groupTitle))} @@ -149,7 +146,7 @@ </div> {/if} - <span class="w-full truncate first-letter:capitalize ml-2.5" title={getDateLocaleString(dateGroup.date)}> + <span class="w-full truncate first-letter:capitalize ms-2.5" title={getDateLocaleString(dateGroup.date)}> {dateGroup.groupTitle} </span> </div> @@ -182,13 +179,11 @@ {showArchiveIcon} {asset} {groupIndex} - focussed={assetInteraction.isFocussedAsset(asset.id)} onClick={(asset) => onClick(assetStore, dateGroup.getAssets(), dateGroup.groupTitle, asset)} onSelect={(asset) => assetSelectHandler(assetStore, asset, dateGroup.getAssets(), dateGroup.groupTitle)} onMouseEvent={() => assetMouseEventHandler(dateGroup.groupTitle, assetSnapshot(asset))} selected={assetInteraction.hasSelectedAsset(asset.id) || dateGroup.bucket.store.albumAssets.has(asset.id)} selectionCandidate={assetInteraction.hasSelectionCandidate(asset.id)} - handleFocus={() => assetOnFocusHandler(asset)} disabled={dateGroup.bucket.store.albumAssets.has(asset.id)} thumbnailWidth={position.width} thumbnailHeight={position.height} diff --git a/web/src/lib/components/photos-page/asset-grid.svelte b/web/src/lib/components/photos-page/asset-grid.svelte index ccba4c88a8..7d496b7e3f 100644 --- a/web/src/lib/components/photos-page/asset-grid.svelte +++ b/web/src/lib/components/photos-page/asset-grid.svelte @@ -26,6 +26,7 @@ import type { UpdatePayload } from 'vite'; import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte'; import { mobileDevice } from '$lib/stores/mobile-device.svelte'; + import { focusNext } from '$lib/utils/focus-util'; interface Props { isSelectionMode?: boolean; @@ -88,7 +89,16 @@ const usingMobileDevice = $derived(mobileDevice.pointerCoarse); $effect(() => { - assetStore.rowHeight = maxMd ? 100 : 235; + const layoutOptions = maxMd + ? { + rowHeight: 100, + headerHeight: 32, + } + : { + rowHeight: 235, + headerHeight: 48, + }; + assetStore.setLayoutOptions(layoutOptions); }); const scrollTo = (top: number) => { @@ -607,34 +617,8 @@ } }; - const focusNextAsset = async () => { - if (assetInteraction.focussedAssetId === null) { - const firstAsset = assetStore.getFirstAsset(); - if (firstAsset) { - assetInteraction.focussedAssetId = firstAsset.id; - } - } else { - const focussedAsset = assetStore.getAssets().find((asset) => asset.id === assetInteraction.focussedAssetId); - if (focussedAsset) { - const nextAsset = await assetStore.getNextAsset(focussedAsset); - if (nextAsset) { - assetInteraction.focussedAssetId = nextAsset.id; - } - } - } - }; - - const focusPreviousAsset = async () => { - if (assetInteraction.focussedAssetId !== null) { - const focussedAsset = assetStore.getAssets().find((asset) => asset.id === assetInteraction.focussedAssetId); - if (focussedAsset) { - const previousAsset = await assetStore.getPreviousAsset(focussedAsset); - if (previousAsset) { - assetInteraction.focussedAssetId = previousAsset.id; - } - } - } - }; + const focusNextAsset = () => focusNext((element) => element.dataset.thumbnailFocusContainer !== undefined, true); + const focusPreviousAsset = () => focusNext((element) => element.dataset.thumbnailFocusContainer !== undefined, false); let isTrashEnabled = $derived($featureFlags.loaded && $featureFlags.trash); let isEmpty = $derived(assetStore.isInitialized && assetStore.buckets.length === 0); @@ -743,7 +727,7 @@ <!-- Right margin MUST be equal to the width of immich-scrubbable-scrollbar --> <section id="asset-grid" - class={['scrollbar-hidden h-full overflow-y-auto outline-none', { 'm-0': isEmpty }, { 'ml-0': !isEmpty }]} + class={['scrollbar-hidden h-full overflow-y-auto outline-none', { 'm-0': isEmpty }, { 'ms-0': !isEmpty }]} style:margin-right={(usingMobileDevice ? 0 : scrubberWidth) + 'px'} tabindex="-1" bind:clientHeight={assetStore.viewportHeight} diff --git a/web/src/lib/components/photos-page/memory-lane.svelte b/web/src/lib/components/photos-page/memory-lane.svelte index acf66e5dde..e94f0af2c4 100644 --- a/web/src/lib/components/photos-page/memory-lane.svelte +++ b/web/src/lib/components/photos-page/memory-lane.svelte @@ -44,9 +44,9 @@ onscroll={onScroll} > {#if canScrollLeft || canScrollRight} - <div class="sticky left-0 z-20"> + <div class="sticky start-0 z-20"> {#if canScrollLeft} - <div class="absolute left-4 top-[6rem] z-20" transition:fade={{ duration: 200 }}> + <div class="absolute start-4 top-[6rem] z-20" transition:fade={{ duration: 200 }}> <button type="button" class="rounded-full border border-gray-500 bg-gray-100 p-2 text-gray-500 opacity-50 hover:opacity-100" @@ -59,7 +59,7 @@ </div> {/if} {#if canScrollRight} - <div class="absolute right-4 top-[6rem] z-20" transition:fade={{ duration: 200 }}> + <div class="absolute end-4 top-[6rem] z-20" transition:fade={{ duration: 200 }}> <button type="button" class="rounded-full border border-gray-500 bg-gray-100 p-2 text-gray-500 opacity-50 hover:opacity-100" @@ -76,7 +76,7 @@ <div class="inline-block" use:resizeObserver={({ width }) => (innerWidth = width)}> {#each memoryStore.memories as memory (memory.id)} <a - class="memory-card relative mr-8 last:mr-0 inline-block aspect-[3/4] md:aspect-[4/3] max-md:h-[150px] xl:aspect-video h-[215px] rounded-xl" + class="memory-card relative me-2 md:me-4 last:me-0 inline-block aspect-[3/4] md:aspect-[4/3] max-md:h-[150px] xl:aspect-video h-[215px] rounded-xl" href="{AppRoute.MEMORY}?{QueryParameter.ID}={memory.assets[0].id}" > <img @@ -85,11 +85,11 @@ alt={$t('memory_lane_title', { values: { title: $getAltText(memory.assets[0]) } })} draggable="false" /> - <p class="absolute bottom-2 left-4 z-10 text-lg text-white max-md:text-sm"> + <p class="absolute bottom-2 start-4 z-10 text-lg text-white max-md:text-sm"> {$memoryLaneTitle(memory)} </p> <div - class="absolute left-0 top-0 z-0 h-full w-full rounded-xl bg-gradient-to-t from-black/40 via-transparent to-transparent transition-all hover:bg-black/20" + class="absolute start-0 top-0 z-0 h-full w-full rounded-xl bg-gradient-to-t from-black/40 via-transparent to-transparent transition-all hover:bg-black/20" ></div> </a> {/each} diff --git a/web/src/lib/components/photos-page/skeleton.svelte b/web/src/lib/components/photos-page/skeleton.svelte index dd32d68842..87ff91c511 100644 --- a/web/src/lib/components/photos-page/skeleton.svelte +++ b/web/src/lib/components/photos-page/skeleton.svelte @@ -9,12 +9,12 @@ <div class="overflow-clip" style:height={height + 'px'}> <div - class="flex z-[100] pt-[calc(1.75rem+1px)] pb-5 h-6 place-items-center text-xs font-medium text-immich-fg bg-immich-bg dark:bg-immich-dark-bg dark:text-immich-dark-fg md:text-sm" + class="flex z-[100] pt-7 pb-5 h-6 place-items-center text-xs font-medium text-immich-fg bg-immich-bg dark:bg-immich-dark-bg dark:text-immich-dark-fg md:text-sm" > {title} </div> <div - class="animate-pulse absolute h-full ml-[10px] mr-[10px]" + class="animate-pulse absolute h-full ms-[10px] me-[10px]" style:width="calc(100% - 20px)" data-skeleton="true" ></div> diff --git a/web/src/lib/components/places-page/places-card-group.svelte b/web/src/lib/components/places-page/places-card-group.svelte index 9bd863a95d..78e54e9bb4 100644 --- a/web/src/lib/components/places-page/places-card-group.svelte +++ b/web/src/lib/components/places-page/places-card-group.svelte @@ -25,7 +25,7 @@ <button type="button" onclick={() => togglePlacesGroupCollapsing(group.id)} - class="w-fit mt-2 pt-2 pr-2 mb-2 dark:text-immich-dark-fg" + class="w-fit mt-2 pt-2 pe-2 mb-2 dark:text-immich-dark-fg" aria-expanded={!isCollapsed} > <Icon @@ -34,7 +34,7 @@ class="inline-block -mt-2.5 transition-all duration-[250ms] {iconRotation}" /> <span class="font-bold text-3xl text-black dark:text-white">{group.name}</span> - <span class="ml-1.5">({$t('places_count', { values: { count: places.length } })})</span> + <span class="ms-1.5">({$t('places_count', { values: { count: places.length } })})</span> </button> <hr class="dark:border-immich-dark-gray" /> </div> diff --git a/web/src/lib/components/share-page/individual-shared-viewer.svelte b/web/src/lib/components/share-page/individual-shared-viewer.svelte index 36ccf3f7dc..b3573ff3b5 100644 --- a/web/src/lib/components/share-page/individual-shared-viewer.svelte +++ b/web/src/lib/components/share-page/individual-shared-viewer.svelte @@ -1,27 +1,27 @@ <script lang="ts"> import { goto } from '$app/navigation'; import type { Action } from '$lib/components/asset-viewer/actions/action'; + import ImmichLogoSmallLink from '$lib/components/shared-components/immich-logo-small-link.svelte'; import { AppRoute, AssetAction } from '$lib/constants'; + import { authManager } from '$lib/managers/auth-manager.svelte'; + import { AssetInteraction } from '$lib/stores/asset-interaction.svelte'; + import type { Viewport } from '$lib/stores/assets-store.svelte'; import { dragAndDropFilesStore } from '$lib/stores/drag-and-drop-files.store'; - import { getKey, handlePromiseError } from '$lib/utils'; - import { downloadArchive } from '$lib/utils/asset-utils'; + import { handlePromiseError } from '$lib/utils'; + import { cancelMultiselect, downloadArchive } from '$lib/utils/asset-utils'; import { fileUploadHandler, openFileUploadDialog } from '$lib/utils/file-uploader'; import { handleError } from '$lib/utils/handle-error'; import { addSharedLinkAssets, type SharedLinkResponseDto } from '@immich/sdk'; import { mdiArrowLeft, mdiFileImagePlusOutline, mdiFolderDownloadOutline, mdiSelectAll } from '@mdi/js'; + import { t } from 'svelte-i18n'; + import AssetViewer from '../asset-viewer/asset-viewer.svelte'; import CircleIconButton from '../elements/buttons/circle-icon-button.svelte'; import DownloadAction from '../photos-page/actions/download-action.svelte'; import RemoveFromSharedLink from '../photos-page/actions/remove-from-shared-link.svelte'; import AssetSelectControlBar from '../photos-page/asset-select-control-bar.svelte'; import ControlAppBar from '../shared-components/control-app-bar.svelte'; import GalleryViewer from '../shared-components/gallery-viewer/gallery-viewer.svelte'; - import AssetViewer from '../asset-viewer/asset-viewer.svelte'; - import { cancelMultiselect } from '$lib/utils/asset-utils'; - import ImmichLogoSmallLink from '$lib/components/shared-components/immich-logo-small-link.svelte'; import { NotificationType, notificationController } from '../shared-components/notification/notification'; - import type { Viewport } from '$lib/stores/assets-store.svelte'; - import { t } from 'svelte-i18n'; - import { AssetInteraction } from '$lib/stores/asset-interaction.svelte'; interface Props { sharedLink: SharedLinkResponseDto; @@ -57,7 +57,7 @@ assetIdsDto: { assetIds: results.filter((id) => !!id) as string[], }, - key: getKey(), + key: authManager.key, }); const added = data.filter((item) => item.success).length; diff --git a/web/src/lib/components/shared-components/change-date.svelte b/web/src/lib/components/shared-components/change-date.svelte index 13b2752f0c..ef682d9048 100644 --- a/web/src/lib/components/shared-components/change-date.svelte +++ b/web/src/lib/components/shared-components/change-date.svelte @@ -144,7 +144,7 @@ <!-- @migration-task: migrate this slot by hand, `prompt` would shadow a prop on the parent component --> <!-- @migration-task: migrate this slot by hand, `prompt` would shadow a prop on the parent component --> {#snippet promptSnippet()} - <div class="flex flex-col text-left gap-2"> + <div class="flex flex-col text-start gap-2"> <div class="flex flex-col"> <label for="datetime">{$t('date_and_time')}</label> <DateInput class="immich-form-input" id="datetime" type="datetime-local" bind:value={selectedDate} /> diff --git a/web/src/lib/components/shared-components/change-location.svelte b/web/src/lib/components/shared-components/change-location.svelte index a84838f1db..f981e85029 100644 --- a/web/src/lib/components/shared-components/change-location.svelte +++ b/web/src/lib/components/shared-components/change-location.svelte @@ -147,7 +147,7 @@ : ''}" onclick={() => handleUseSuggested(place.latitude, place.longitude)} > - <p class="ml-4 text-sm text-gray-700 dark:text-gray-100 truncate"> + <p class="ms-4 text-sm text-gray-700 dark:text-gray-100 truncate"> {getLocation(place.name, place.admin1name, place.admin2name)} </p> </button> @@ -189,7 +189,7 @@ {/await} </div> - <div class="grid sm:grid-cols-2 gap-4 text-sm text-left mt-4"> + <div class="grid sm:grid-cols-2 gap-4 text-sm text-start mt-4"> <CoordinatesInput lat={point ? point.lat : assetLat} lng={point ? point.lng : assetLng} diff --git a/web/src/lib/components/shared-components/combobox.svelte b/web/src/lib/components/shared-components/combobox.svelte index 3b70b0e859..8be5aabc6c 100644 --- a/web/src/lib/components/shared-components/combobox.svelte +++ b/web/src/lib/components/shared-components/combobox.svelte @@ -258,7 +258,7 @@ > <div> {#if isActive} - <div class="absolute inset-y-0 left-0 flex items-center pl-3"> + <div class="absolute inset-y-0 start-0 flex items-center ps-3"> <div class="dark:text-immich-dark-fg/75"> <Icon path={mdiMagnify} ariaHidden={true} /> </div> @@ -273,11 +273,11 @@ aria-expanded={isOpen} autocomplete="off" bind:this={input} - class:!pl-8={isActive} + class:!ps-8={isActive} class:!rounded-b-none={isOpen && dropdownDirection === 'bottom'} class:!rounded-t-none={isOpen && dropdownDirection === 'top'} class:cursor-pointer={!isActive} - class="immich-form-input text-sm text-left w-full !pr-12 transition-all" + class="immich-form-input text-sm w-full !pe-12 transition-all" id={inputId} onfocus={activate} oninput={onInput} @@ -325,8 +325,8 @@ /> <div - class="absolute right-0 top-0 h-full flex px-4 justify-center items-center content-between" - class:pr-2={selectedOption} + class="absolute end-0 top-0 h-full flex px-4 justify-center items-center content-between" + class:pe-2={selectedOption} class:pointer-events-none={!selectedOption} > {#if selectedOption} @@ -341,7 +341,7 @@ role="listbox" id={listboxId} transition:fly={{ duration: 250 }} - class="fixed text-left text-sm w-full overflow-y-auto bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-900 z-[10000]" + class="fixed text-start text-sm w-full overflow-y-auto bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-900 z-[10000]" class:rounded-b-xl={dropdownDirection === 'bottom'} class:rounded-t-xl={dropdownDirection === 'top'} class:shadow={dropdownDirection === 'bottom'} @@ -360,7 +360,7 @@ role="option" aria-selected={selectedIndex === 0} aria-disabled={true} - class="text-left w-full px-4 py-2 hover:bg-gray-200 dark:hover:bg-gray-700 cursor-default aria-selected:bg-gray-200 aria-selected:dark:bg-gray-700" + class="text-start w-full px-4 py-2 hover:bg-gray-200 dark:hover:bg-gray-700 cursor-default aria-selected:bg-gray-200 aria-selected:dark:bg-gray-700" id={`${listboxId}-${0}`} onclick={closeDropdown} > @@ -372,7 +372,7 @@ <li aria-selected={index === selectedIndex} bind:this={optionRefs[index]} - class="text-left w-full px-4 py-2 hover:bg-gray-200 dark:hover:bg-gray-700 transition-all cursor-pointer aria-selected:bg-gray-200 aria-selected:dark:bg-gray-700 break-words" + class="text-start w-full px-4 py-2 hover:bg-gray-200 dark:hover:bg-gray-700 transition-all cursor-pointer aria-selected:bg-gray-200 aria-selected:dark:bg-gray-700 break-words" id={`${listboxId}-${index}`} onclick={() => handleSelect(option)} role="option" diff --git a/web/src/lib/components/shared-components/context-menu/button-context-menu.svelte b/web/src/lib/components/shared-components/context-menu/button-context-menu.svelte index a3e12e4f12..593baafc7c 100644 --- a/web/src/lib/components/shared-components/context-menu/button-context-menu.svelte +++ b/web/src/lib/components/shared-components/context-menu/button-context-menu.svelte @@ -6,6 +6,7 @@ type Padding, } from '$lib/components/elements/buttons/circle-icon-button.svelte'; import ContextMenu from '$lib/components/shared-components/context-menu/context-menu.svelte'; + import { languageManager } from '$lib/managers/language-manager.svelte'; import { optionClickCallbackStore, selectedIdStore } from '$lib/stores/context-menu.store'; import { getContextMenuPositionFromBoundingRect, @@ -26,6 +27,7 @@ /** * The direction in which the context menu should open. */ + // TODO change to start vs end direction?: 'left' | 'right'; color?: Color; size?: string | undefined; @@ -62,7 +64,15 @@ const menuId = `context-menu-${id}`; const openDropdown = (event: KeyboardEvent | MouseEvent) => { - contextMenuPosition = getContextMenuPositionFromEvent(event, align); + let layoutAlign = align; + if (languageManager.rtl) { + if (align.includes('left')) { + layoutAlign = align.replace('left', 'right') as Align; + } else if (align.includes('right')) { + layoutAlign = align.replace('right', 'left') as Align; + } + } + contextMenuPosition = getContextMenuPositionFromEvent(event, layoutAlign); isOpen = true; menuContainer?.focus(); }; diff --git a/web/src/lib/components/shared-components/context-menu/context-menu.svelte b/web/src/lib/components/shared-components/context-menu/context-menu.svelte index 976f86d3e5..7d1be35944 100644 --- a/web/src/lib/components/shared-components/context-menu/context-menu.svelte +++ b/web/src/lib/components/shared-components/context-menu/context-menu.svelte @@ -1,8 +1,9 @@ <script lang="ts"> + import { clickOutside } from '$lib/actions/click-outside'; + import { languageManager } from '$lib/managers/language-manager.svelte'; + import type { Snippet } from 'svelte'; import { quintOut } from 'svelte/easing'; import { slide } from 'svelte/transition'; - import { clickOutside } from '$lib/actions/click-outside'; - import type { Snippet } from 'svelte'; interface Props { isVisible?: boolean; @@ -41,12 +42,17 @@ $effect(() => { if (menuElement) { + let layoutDirection = direction; + if (languageManager.rtl) { + layoutDirection = direction === 'left' ? 'right' : 'left'; + } + const rect = menuElement.getBoundingClientRect(); - const directionWidth = direction === 'left' ? rect.width : 0; + const directionWidth = layoutDirection === 'left' ? rect.width : 0; const menuHeight = Math.min(menuElement.clientHeight, height) || 0; - left = Math.min(window.innerWidth - rect.width, x - directionWidth); - top = Math.min(window.innerHeight - menuHeight, y); + left = Math.max(8, Math.min(window.innerWidth - rect.width, x - directionWidth)); + top = Math.max(8, Math.min(window.innerHeight - menuHeight, y)); } }); </script> @@ -66,7 +72,7 @@ aria-labelledby={ariaLabelledBy} bind:this={menuElement} class="{isVisible - ? 'max-h-dvh max-h-svh' + ? 'max-h-dvh' : 'max-h-0'} flex flex-col transition-all duration-[250ms] ease-in-out outline-none overflow-auto" role="menu" tabindex="-1" diff --git a/web/src/lib/components/shared-components/context-menu/menu-option.svelte b/web/src/lib/components/shared-components/context-menu/menu-option.svelte index b3a6d41018..b331804958 100644 --- a/web/src/lib/components/shared-components/context-menu/menu-option.svelte +++ b/web/src/lib/components/shared-components/context-menu/menu-option.svelte @@ -53,7 +53,7 @@ onclick={handleClick} onmouseover={() => ($selectedIdStore = id)} onmouseleave={() => ($selectedIdStore = undefined)} - class="w-full p-4 text-left text-sm font-medium {textColor} focus:outline-none focus:ring-2 focus:ring-inset cursor-pointer border-gray-200 flex gap-2 items-center {isActive + class="w-full p-4 text-start text-sm font-medium {textColor} focus:outline-none focus:ring-2 focus:ring-inset cursor-pointer border-gray-200 flex gap-2 items-center {isActive ? activeColor : 'bg-slate-100'}" role="menuitem" @@ -65,7 +65,7 @@ <div class="flex justify-between"> {text} {#if shortcutLabel} - <span class="text-gray-500 pl-4"> + <span class="text-gray-500 ps-4"> {shortcutLabel} </span> {/if} diff --git a/web/src/lib/components/shared-components/context-menu/right-click-context-menu.svelte b/web/src/lib/components/shared-components/context-menu/right-click-context-menu.svelte index 9b9e68b6c5..27d50f4fe5 100644 --- a/web/src/lib/components/shared-components/context-menu/right-click-context-menu.svelte +++ b/web/src/lib/components/shared-components/context-menu/right-click-context-menu.svelte @@ -38,7 +38,7 @@ const elements = document.elementsFromPoint(event.x, event.y); if (menuContainer && elements.includes(menuContainer)) { - // User right-clicked on the context menu itself, we keep the context + // User end-clicked on the context menu itself, we keep the context // menu as is return; } @@ -91,7 +91,7 @@ }, ]} > - <section class="fixed left-0 top-0 z-10 flex h-dvh w-dvw" {oncontextmenu} role="presentation"> + <section class="fixed start-0 top-0 z-10 flex h-dvh w-dvw" {oncontextmenu} role="presentation"> <ContextMenu {direction} {x} diff --git a/web/src/lib/components/shared-components/control-app-bar.svelte b/web/src/lib/components/shared-components/control-app-bar.svelte index 6b52adff10..f4bf98c72e 100644 --- a/web/src/lib/components/shared-components/control-app-bar.svelte +++ b/web/src/lib/components/shared-components/control-app-bar.svelte @@ -67,7 +67,7 @@ </script> <div in:fly={{ y: 10, duration: 200 }} class="absolute top-0 w-full z-[100] bg-transparent"> - <div + <nav id="asset-selection-app-bar" class={[ 'grid', @@ -77,8 +77,7 @@ appBarBorder, 'mx-2 my-2 place-items-center rounded-lg p-2 max-md:p-0 transition-all', tailwindClasses, - 'bg-immich-gray dark:bg-immich-dark-gray', - forceDark && 'bg-immich-dark-gray text-white', + forceDark ? 'bg-immich-dark-gray text-white' : 'bg-immich-gray dark:bg-immich-dark-gray', ]} > <div class="flex place-items-center sm:gap-6 justify-self-start dark:text-immich-dark-fg"> @@ -92,8 +91,8 @@ {@render children?.()} </div> - <div class="mr-4 flex place-items-center gap-1 justify-self-end"> + <div class="me-4 flex place-items-center gap-1 justify-self-end"> {@render trailing?.()} </div> - </div> + </nav> </div> diff --git a/web/src/lib/components/shared-components/drag-and-drop-upload-overlay.svelte b/web/src/lib/components/shared-components/drag-and-drop-upload-overlay.svelte index 2ed5d684a4..f3084eeb57 100644 --- a/web/src/lib/components/shared-components/drag-and-drop-upload-overlay.svelte +++ b/web/src/lib/components/shared-components/drag-and-drop-upload-overlay.svelte @@ -1,15 +1,15 @@ <script lang="ts"> import { page } from '$app/state'; import { shouldIgnoreEvent } from '$lib/actions/shortcut'; + import { authManager } from '$lib/managers/auth-manager.svelte'; import { dragAndDropFilesStore } from '$lib/stores/drag-and-drop-files.store'; import { fileUploadHandler } from '$lib/utils/file-uploader'; - import { isAlbumsRoute, isSharedLinkRoute } from '$lib/utils/navigation'; + import { isAlbumsRoute } from '$lib/utils/navigation'; import { t } from 'svelte-i18n'; import { fade } from 'svelte/transition'; import ImmichLogo from './immich-logo.svelte'; let albumId = $derived(isAlbumsRoute(page.route?.id) ? page.params.albumId : undefined); - let isShare = $derived(isSharedLinkRoute(page.route?.id)); let dragStartTarget: EventTarget | null = $state(null); @@ -123,7 +123,7 @@ } const filesArray: File[] = Array.from<File>(files); - if (isShare) { + if (authManager.key) { dragAndDropFilesStore.set({ isDragging: true, files: filesArray }); } else { await fileUploadHandler(filesArray, albumId); diff --git a/web/src/lib/components/shared-components/duplicates-modal.svelte b/web/src/lib/components/shared-components/duplicates-modal.svelte index 96f563989b..c9abea1377 100644 --- a/web/src/lib/components/shared-components/duplicates-modal.svelte +++ b/web/src/lib/components/shared-components/duplicates-modal.svelte @@ -12,7 +12,7 @@ <FullScreenModal title={$t('deduplication_info')} width="auto" {onClose}> <div class="text-sm dark:text-white"> <p>{$t('deduplication_info_description')}</p> - <ol class="ml-8 mt-2" style="list-style: decimal"> + <ol class="ms-8 mt-2" style="list-style: decimal"> <li>{$t('deduplication_criteria_1')}</li> <li>{$t('deduplication_criteria_2')}</li> </ol> diff --git a/web/src/lib/components/shared-components/full-screen-modal.svelte b/web/src/lib/components/shared-components/full-screen-modal.svelte index 9284283f54..1640dad692 100644 --- a/web/src/lib/components/shared-components/full-screen-modal.svelte +++ b/web/src/lib/components/shared-components/full-screen-modal.svelte @@ -77,7 +77,7 @@ role="presentation" in:fade={{ duration: 100 }} out:fade={{ duration: 100 }} - class="fixed left-0 top-0 z-[9999] flex h-dvh w-dvw place-content-center place-items-center bg-black/40" + class="fixed start-0 top-0 z-[9999] flex h-dvh w-dvw place-content-center place-items-center bg-black/40" onkeydown={(event) => { event.stopPropagation(); }} diff --git a/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte b/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte index 9d7e45db0e..09c126e0df 100644 --- a/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte +++ b/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte @@ -22,6 +22,7 @@ import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte'; import { debounce } from 'lodash-es'; import { getJustifiedLayoutFromAssets, type CommonJustifiedLayout } from '$lib/utils/layout-utils'; + import { focusNext } from '$lib/utils/focus-util'; interface Props { assets: AssetResponseDto[]; @@ -259,25 +260,8 @@ } }; - const focusNextAsset = () => { - if (assetInteraction.focussedAssetId === null && assets.length > 0) { - assetInteraction.focussedAssetId = assets[0].id; - } else if (assetInteraction.focussedAssetId !== null && assets.length > 0) { - const currentIndex = assets.findIndex((a) => a.id === assetInteraction.focussedAssetId); - if (currentIndex !== -1 && currentIndex + 1 < assets.length) { - assetInteraction.focussedAssetId = assets[currentIndex + 1].id; - } - } - }; - - const focusPreviousAsset = () => { - if (assetInteraction.focussedAssetId !== null && assets.length > 0) { - const currentIndex = assets.findIndex((a) => a.id === assetInteraction.focussedAssetId); - if (currentIndex >= 1) { - assetInteraction.focussedAssetId = assets[currentIndex - 1].id; - } - } - }; + const focusNextAsset = () => focusNext((element) => element.dataset.thumbnailFocusContainer !== undefined, true); + const focusPreviousAsset = () => focusNext((element) => element.dataset.thumbnailFocusContainer !== undefined, false); let shortcutList = $derived( (() => { @@ -417,10 +401,6 @@ } }; - const assetOnFocusHandler = (asset: AssetResponseDto) => { - assetInteraction.focussedAssetId = asset.id; - }; - let isTrashEnabled = $derived($featureFlags.loaded && $featureFlags.trash); let idsSelectedAssets = $derived(assetInteraction.selectedAssets.map(({ id }) => id)); @@ -490,12 +470,10 @@ }} onSelect={(asset) => handleSelectAssets(asset)} onMouseEvent={() => assetMouseEventHandler(asset)} - handleFocus={() => assetOnFocusHandler(asset)} {showArchiveIcon} {asset} selected={assetInteraction.hasSelectedAsset(asset.id)} selectionCandidate={assetInteraction.hasSelectionCandidate(asset.id)} - focussed={assetInteraction.isFocussedAsset(asset.id)} thumbnailWidth={layout.width} thumbnailHeight={layout.height} /> diff --git a/web/src/lib/components/shared-components/immich-logo-small-link.svelte b/web/src/lib/components/shared-components/immich-logo-small-link.svelte index 7f96d88df8..5a940fa278 100644 --- a/web/src/lib/components/shared-components/immich-logo-small-link.svelte +++ b/web/src/lib/components/shared-components/immich-logo-small-link.svelte @@ -3,6 +3,6 @@ import { mobileDevice } from '$lib/stores/mobile-device.svelte'; </script> -<a data-sveltekit-preload-data="hover" class="ml-4" href="/"> +<a data-sveltekit-preload-data="hover" class="ms-4" href="/"> <ImmichLogo class="h-[50px] max-w-none md:w-auto md:max-w-full" noText={mobileDevice.maxMd} /> </a> diff --git a/web/src/lib/components/shared-components/map/map.svelte b/web/src/lib/components/shared-components/map/map.svelte index dc594bae3f..80e7f56148 100644 --- a/web/src/lib/components/shared-components/map/map.svelte +++ b/web/src/lib/components/shared-components/map/map.svelte @@ -9,15 +9,15 @@ <script lang="ts"> import Icon from '$lib/components/elements/icon.svelte'; import { Theme } from '$lib/constants'; - import { colorTheme, mapSettings } from '$lib/stores/preferences.store'; + import { themeManager } from '$lib/managers/theme-manager.svelte'; + import { mapSettings } from '$lib/stores/preferences.store'; import { serverConfig } from '$lib/stores/server-config.store'; import { getAssetThumbnailUrl, handlePromiseError } from '$lib/utils'; import { type MapMarkerResponseDto } from '@immich/sdk'; import mapboxRtlUrl from '@mapbox/mapbox-gl-rtl-text/mapbox-gl-rtl-text.min.js?url'; import { mdiCog, mdiMap, mdiMapMarker } from '@mdi/js'; import type { Feature, GeoJsonProperties, Geometry, Point } from 'geojson'; - import { type GeoJSONSource, GlobeControl, type LngLatLike } from 'maplibre-gl'; - import maplibregl from 'maplibre-gl'; + import maplibregl, { GlobeControl, type GeoJSONSource, type LngLatLike } from 'maplibre-gl'; import { t } from 'svelte-i18n'; import { AttributionControl, @@ -68,7 +68,7 @@ let map: maplibregl.Map | undefined = $state(); let marker: maplibregl.Marker | null = null; - const theme = $derived($mapSettings.allowDarkMode ? $colorTheme.value : Theme.LIGHT); + const theme = $derived($mapSettings.allowDarkMode ? themeManager.value : Theme.LIGHT); const styleUrl = $derived(theme === Theme.DARK ? $serverConfig.mapDarkStyleUrl : $serverConfig.mapLightStyleUrl); export function addClipMapMarker(lng: number, lat: number) { @@ -249,7 +249,11 @@ > {#snippet children({ feature }: { feature: Feature<Geometry, GeoJsonProperties> })} {#if useLocationPin} - <Icon path={mdiMapMarker} size="50px" class="dark:text-immich-dark-primary text-immich-primary" /> + <Icon + path={mdiMapMarker} + size="50px" + class="dark:text-immich-dark-primary text-immich-primary -translate-y-[50%]" + /> {:else} <img src={getAssetThumbnailUrl(feature.properties?.id)} diff --git a/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte b/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte index 5bf3f0a621..5b778cf227 100644 --- a/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte +++ b/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte @@ -5,9 +5,9 @@ import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte'; import Icon from '$lib/components/elements/icon.svelte'; import { AppRoute } from '$lib/constants'; - import { preferences, user } from '$lib/stores/user.store'; + import { user } from '$lib/stores/user.store'; import { handleError } from '$lib/utils/handle-error'; - import { deleteProfileImage, updateMyPreferences, type UserAvatarColor } from '@immich/sdk'; + import { deleteProfileImage, updateMyUser, type UserAvatarColor } from '@immich/sdk'; import { mdiCog, mdiLogout, mdiPencil, mdiWrench } from '@mdi/js'; import { t } from 'svelte-i18n'; import { fade } from 'svelte/transition'; @@ -30,8 +30,7 @@ await deleteProfileImage(); } - $preferences = await updateMyPreferences({ userPreferencesUpdateDto: { avatar: { color } } }); - $user = { ...$user, profileImagePath: '', avatarColor: $preferences.avatar.color }; + $user = await updateMyUser({ userUpdateMeDto: { avatarColor: color } }); isShowSelectAvatar = false; notificationController.show({ @@ -48,7 +47,7 @@ in:fade={{ duration: 100 }} out:fade={{ duration: 100 }} id="account-info-panel" - class="absolute right-[25px] top-[75px] z-[100] w-[min(360px,100vw-50px)] rounded-3xl bg-gray-200 shadow-lg dark:border dark:border-immich-dark-gray dark:bg-immich-dark-gray" + class="absolute end-[25px] top-[75px] z-[100] w-[min(360px,100vw-50px)] rounded-3xl bg-gray-200 shadow-lg dark:border dark:border-immich-dark-gray dark:bg-immich-dark-gray" use:focusTrap > <div @@ -56,7 +55,7 @@ > <div class="relative"> <UserAvatar user={$user} size="xl" /> - <div class="absolute z-10 bottom-0 right-0 rounded-full w-6 h-6"> + <div class="absolute z-10 bottom-0 end-0 rounded-full w-6 h-6"> <CircleIconButton color="primary" icon={mdiPencil} diff --git a/web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte b/web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte index f7f9b877f3..c8009a8fd9 100644 --- a/web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte +++ b/web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte @@ -8,23 +8,25 @@ import SkipLink from '$lib/components/elements/buttons/skip-link.svelte'; import HelpAndFeedbackModal from '$lib/components/shared-components/help-and-feedback-modal.svelte'; import ImmichLogo from '$lib/components/shared-components/immich-logo.svelte'; + import NotificationPanel from '$lib/components/shared-components/navigation-bar/notification-panel.svelte'; import SearchBar from '$lib/components/shared-components/search-bar/search-bar.svelte'; import { AppRoute } from '$lib/constants'; + import { authManager } from '$lib/managers/auth-manager.svelte'; + import { mobileDevice } from '$lib/stores/mobile-device.svelte'; import { featureFlags } from '$lib/stores/server-config.store'; + import { sidebarStore } from '$lib/stores/sidebar.svelte'; import { user } from '$lib/stores/user.store'; import { userInteraction } from '$lib/stores/user.svelte'; - import { handleLogout } from '$lib/utils/auth'; - import { getAboutInfo, logout, type ServerAboutResponseDto } from '@immich/sdk'; + import { getAboutInfo, type ServerAboutResponseDto } from '@immich/sdk'; import { Button, IconButton } from '@immich/ui'; - import { mdiHelpCircleOutline, mdiMagnify, mdiMenu, mdiTrayArrowUp } from '@mdi/js'; + import { mdiBellBadge, mdiBellOutline, mdiHelpCircleOutline, mdiMagnify, mdiMenu, mdiTrayArrowUp } from '@mdi/js'; import { onMount } from 'svelte'; import { t } from 'svelte-i18n'; import { fade } from 'svelte/transition'; import ThemeButton from '../theme-button.svelte'; import UserAvatar from '../user-avatar.svelte'; import AccountInfoPanel from './account-info-panel.svelte'; - import { sidebarStore } from '$lib/stores/sidebar.svelte'; - import { mobileDevice } from '$lib/stores/mobile-device.svelte'; + import { notificationManager } from '$lib/stores/notification-manager.svelte'; interface Props { showUploadButton?: boolean; @@ -36,12 +38,9 @@ let shouldShowAccountInfo = $state(false); let shouldShowAccountInfoPanel = $state(false); let shouldShowHelpPanel = $state(false); + let shouldShowNotificationPanel = $state(false); let innerWidth: number = $state(0); - - const onLogout = async () => { - const { redirectUri } = await logout(); - await handleLogout(redirectUri); - }; + const hasUnreadNotifications = $derived(notificationManager.notifications.length > 0); let info: ServerAboutResponseDto | undefined = $state(); @@ -56,7 +55,7 @@ <HelpAndFeedbackModal onClose={() => (shouldShowHelpPanel = false)} {info} /> {/if} -<section +<nav id="dashboard-navbar" class="fixed z-[900] max-md:h-[var(--navbar-height-md)] h-[var(--navbar-height)] w-dvw text-sm" > @@ -88,8 +87,8 @@ <ImmichLogo class="max-md:h-[48px] h-[50px]" noText={!mobileDevice.isFullSidebar} /> </a> </div> - <div class="flex justify-between gap-4 lg:gap-8 pr-6"> - <div class="hidden w-full max-w-5xl flex-1 tall:pl-0 sm:block"> + <div class="flex justify-between gap-4 lg:gap-8 pe-6"> + <div class="hidden w-full max-w-5xl flex-1 tall:ps-0 sm:block"> {#if $featureFlags.search} <SearchBar grayTheme={true} /> {/if} @@ -151,6 +150,27 @@ /> </div> + <div + use:clickOutside={{ + onOutclick: () => (shouldShowNotificationPanel = false), + onEscape: () => (shouldShowNotificationPanel = false), + }} + > + <IconButton + shape="round" + color={hasUnreadNotifications ? 'primary' : 'secondary'} + variant="ghost" + size="medium" + icon={hasUnreadNotifications ? mdiBellBadge : mdiBellOutline} + onclick={() => (shouldShowNotificationPanel = !shouldShowNotificationPanel)} + aria-label={$t('notifications')} + /> + + {#if shouldShowNotificationPanel} + <NotificationPanel /> + {/if} + </div> + <div use:clickOutside={{ onOutclick: () => (shouldShowAccountInfoPanel = false), @@ -159,7 +179,7 @@ > <button type="button" - class="flex pl-2" + class="flex ps-2" onmouseover={() => (shouldShowAccountInfo = true)} onfocus={() => (shouldShowAccountInfo = true)} onblur={() => (shouldShowAccountInfo = false)} @@ -175,7 +195,7 @@ <div in:fade={{ delay: 500, duration: 150 }} out:fade={{ delay: 200, duration: 150 }} - class="absolute -bottom-12 right-5 rounded-md border bg-gray-500 p-2 text-[12px] text-gray-100 shadow-md dark:border-immich-dark-gray dark:bg-immich-dark-gray" + class="absolute -bottom-12 end-5 rounded-md border bg-gray-500 p-2 text-[12px] text-gray-100 shadow-md dark:border-immich-dark-gray dark:bg-immich-dark-gray" > <p>{$user.name}</p> <p>{$user.email}</p> @@ -183,10 +203,10 @@ {/if} {#if shouldShowAccountInfoPanel} - <AccountInfoPanel {onLogout} /> + <AccountInfoPanel onLogout={() => authManager.logout()} /> {/if} </div> </section> </div> </div> -</section> +</nav> 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 new file mode 100644 index 0000000000..0d05e2d6d7 --- /dev/null +++ b/web/src/lib/components/shared-components/navigation-bar/notification-item.svelte @@ -0,0 +1,114 @@ +<script lang="ts"> + import { NotificationLevel, NotificationType, type NotificationDto } from '@immich/sdk'; + import { IconButton, Stack, Text } from '@immich/ui'; + import { mdiBackupRestore, mdiInformationOutline, mdiMessageBadgeOutline, mdiSync } from '@mdi/js'; + import { DateTime } from 'luxon'; + + interface Props { + notification: NotificationDto; + onclick: (id: string) => void; + } + + let { notification, onclick }: Props = $props(); + + const getAlertColor = (level: NotificationLevel) => { + switch (level) { + case NotificationLevel.Error: { + return 'danger'; + } + case NotificationLevel.Warning: { + return 'warning'; + } + case NotificationLevel.Info: { + return 'primary'; + } + case NotificationLevel.Success: { + return 'success'; + } + default: { + return 'primary'; + } + } + }; + + const getIconBgColor = (level: NotificationLevel) => { + switch (level) { + case NotificationLevel.Error: { + return 'bg-red-500 dark:bg-red-300 dark:hover:bg-red-200'; + } + case NotificationLevel.Warning: { + return 'bg-amber-500 dark:bg-amber-200 dark:hover:bg-amber-200'; + } + case NotificationLevel.Info: { + return 'bg-blue-500 dark:bg-blue-200 dark:hover:bg-blue-200'; + } + case NotificationLevel.Success: { + return 'bg-green-500 dark:bg-green-200 dark:hover:bg-green-200'; + } + } + }; + + const getIconType = (type: NotificationType) => { + switch (type) { + case NotificationType.BackupFailed: { + return mdiBackupRestore; + } + case NotificationType.JobFailed: { + return mdiSync; + } + case NotificationType.SystemMessage: { + return mdiMessageBadgeOutline; + } + case NotificationType.Custom: { + return mdiInformationOutline; + } + } + }; + + const formatRelativeTime = (dateString: string): string => { + try { + const date = DateTime.fromISO(dateString); + if (!date.isValid) { + return dateString; // Return original string if parsing fails + } + // Use Luxon's toRelative with the current locale + return date.setLocale('en').toRelative() || dateString; + } catch (error) { + console.error('Error formatting relative time:', error); + return dateString; // Fallback to original string on error + } + }; +</script> + +<button + class="min-h-[80px] p-2 py-3 hover:bg-immich-primary/10 dark:hover:bg-immich-dark-primary/10 border-b border-gray-200 dark:border-immich-dark-gray w-full" + type="button" + onclick={() => onclick(notification.id)} + title={notification.createdAt} +> + <div class="grid grid-cols-[56px_1fr_32px] items-center gap-2"> + <div class="flex place-items-center place-content-center"> + <IconButton + icon={getIconType(notification.type)} + color={getAlertColor(notification.level)} + aria-label={notification.title} + shape="round" + class={getIconBgColor(notification.level)} + size="small" + ></IconButton> + </div> + + <Stack class="text-left" gap={1}> + <Text size="tiny" class="uppercase text-black dark:text-white font-semibold">{notification.title}</Text> + {#if notification.description} + <Text class="overflow-hidden text-gray-600 dark:text-gray-300">{notification.description}</Text> + {/if} + + <Text size="tiny" color="muted">{formatRelativeTime(notification.createdAt)}</Text> + </Stack> + + {#if !notification.readAt} + <div class="w-2 h-2 rounded-full bg-primary text-right justify-self-center"></div> + {/if} + </div> +</button> diff --git a/web/src/lib/components/shared-components/navigation-bar/notification-panel.svelte b/web/src/lib/components/shared-components/navigation-bar/notification-panel.svelte new file mode 100644 index 0000000000..be9fcd2a44 --- /dev/null +++ b/web/src/lib/components/shared-components/navigation-bar/notification-panel.svelte @@ -0,0 +1,82 @@ +<script lang="ts"> + import { focusTrap } from '$lib/actions/focus-trap'; + import Icon from '$lib/components/elements/icon.svelte'; + import NotificationItem from '$lib/components/shared-components/navigation-bar/notification-item.svelte'; + import { + notificationController, + NotificationType as WebNotificationType, + } from '$lib/components/shared-components/notification/notification'; + + import { notificationManager } from '$lib/stores/notification-manager.svelte'; + import { handleError } from '$lib/utils/handle-error'; + import { Button, Scrollable, Stack, Text } from '@immich/ui'; + import { mdiBellOutline, mdiCheckAll } from '@mdi/js'; + import { t } from 'svelte-i18n'; + import { fade } from 'svelte/transition'; + import { flip } from 'svelte/animate'; + + const noUnreadNotifications = $derived(notificationManager.notifications.length === 0); + + const markAsRead = async (id: string) => { + try { + await notificationManager.markAsRead(id); + } catch (error) { + handleError(error, $t('errors.failed_to_update_notification_status')); + } + }; + + const markAllAsRead = async () => { + try { + await notificationManager.markAllAsRead(); + notificationController.show({ message: $t('marked_all_as_read'), type: WebNotificationType.Info }); + } catch (error) { + handleError(error, $t('errors.failed_to_update_notification_status')); + } + }; +</script> + +<div + in:fade={{ duration: 100 }} + out:fade={{ duration: 100 }} + id="notification-panel" + class="absolute right-[25px] top-[70px] z-[100] w-[min(360px,100vw-50px)] rounded-3xl bg-gray-100 border border-gray-200 shadow-lg dark:border dark:border-immich-dark-gray dark:bg-immich-dark-gray text-light" + use:focusTrap +> + <Stack class="max-h-[500px]"> + <div class="flex justify-between items-center mt-4 mx-4"> + <Text size="medium" color="secondary" class="font-semibold">{$t('notifications')}</Text> + <div> + <Button + variant="ghost" + disabled={noUnreadNotifications} + leadingIcon={mdiCheckAll} + size="small" + color="primary" + onclick={() => markAllAsRead()}>{$t('mark_all_as_read')}</Button + > + </div> + </div> + + <hr /> + + {#if noUnreadNotifications} + <Stack + class="py-12 flex flex-col place-items-center place-content-center text-gray-700 dark:text-gray-300" + gap={1} + > + <Icon path={mdiBellOutline} size={20}></Icon> + <Text>{$t('no_notifications')}</Text> + </Stack> + {:else} + <Scrollable class="pb-6"> + <Stack gap={0}> + {#each notificationManager.notifications as notification (notification.id)} + <div animate:flip={{ duration: 400 }}> + <NotificationItem {notification} onclick={(id) => markAsRead(id)} /> + </div> + {/each} + </Stack> + </Scrollable> + {/if} + </Stack> +</div> diff --git a/web/src/lib/components/shared-components/navigation-loading-bar.svelte b/web/src/lib/components/shared-components/navigation-loading-bar.svelte index bd5f15ae4e..afdb8ee967 100644 --- a/web/src/lib/components/shared-components/navigation-loading-bar.svelte +++ b/web/src/lib/components/shared-components/navigation-loading-bar.svelte @@ -26,7 +26,7 @@ </script> {#if showing} - <div class="absolute left-0 top-0 z-[999999999] h-[3px] w-dvw bg-white"> + <div class="absolute start-0 top-0 z-[999999999] h-[3px] w-dvw bg-white"> <span class="absolute h-[3px] bg-immich-primary" style:width={`${$progress}%`}></span> </div> {/if} diff --git a/web/src/lib/components/shared-components/notification/notification-card.svelte b/web/src/lib/components/shared-components/notification/notification-card.svelte index 5054c18695..30c8622bba 100644 --- a/web/src/lib/components/shared-components/notification/notification-card.svelte +++ b/web/src/lib/components/shared-components/notification/notification-card.svelte @@ -100,7 +100,7 @@ /> </div> - <p class="whitespace-pre-wrap pl-[28px] pr-[16px] text-sm" data-testid="message"> + <p class="whitespace-pre-wrap ps-[28px] pe-[16px] text-sm" data-testid="message"> {#if isComponentNotification(notification)} <notification.component.type {...notification.component.props} /> {:else} @@ -109,7 +109,7 @@ </p> {#if notification.button} - <p class="pl-[28px] mt-2.5 text-sm"> + <p class="ps-[28px] mt-2.5 text-sm"> <button type="button" class="{buttonStyle[notification.type]} rounded px-3 pt-1.5 pb-1 transition-all duration-200" diff --git a/web/src/lib/components/shared-components/notification/notification-list.svelte b/web/src/lib/components/shared-components/notification/notification-list.svelte index c7c54be267..6e067e839a 100644 --- a/web/src/lib/components/shared-components/notification/notification-list.svelte +++ b/web/src/lib/components/shared-components/notification/notification-list.svelte @@ -11,7 +11,7 @@ <div role="status" aria-relevant="additions" aria-label={$t('notifications')}> {#if $notificationList.length > 0} - <section transition:fade={{ duration: 250 }} id="notification-list" class="fixed right-5 top-[80px] z-[99999999]"> + <section transition:fade={{ duration: 250 }} id="notification-list" class="fixed end-5 top-[80px] z-[99999999]"> {#each $notificationList as notification (notification.id)} <div animate:flip={{ duration: 250, easing: quintOut }}> <NotificationCard {notification} /> diff --git a/web/src/lib/components/shared-components/password-field.svelte b/web/src/lib/components/shared-components/password-field.svelte index 8519f84134..32879a1755 100644 --- a/web/src/lib/components/shared-components/password-field.svelte +++ b/web/src/lib/components/shared-components/password-field.svelte @@ -19,7 +19,7 @@ <div class="relative w-full"> <input {...rest} - class="immich-form-input w-full !pr-12" + class="immich-form-input w-full !pe-12" type={showPassword ? 'text' : 'password'} {required} value={password} diff --git a/web/src/lib/components/shared-components/progress-bar/progress-bar.svelte b/web/src/lib/components/shared-components/progress-bar/progress-bar.svelte index 5b663bd1a9..2b3abb9708 100644 --- a/web/src/lib/components/shared-components/progress-bar/progress-bar.svelte +++ b/web/src/lib/components/shared-components/progress-bar/progress-bar.svelte @@ -88,5 +88,5 @@ </script> {#if !hidden} - <span class="absolute left-0 h-[3px] bg-immich-primary shadow-2xl" style:width={`${$progress * 100}%`}></span> + <span class="absolute start-0 h-[3px] bg-immich-primary shadow-2xl" style:width={`${$progress * 100}%`}></span> {/if} diff --git a/web/src/lib/components/shared-components/qrcode.svelte b/web/src/lib/components/shared-components/qrcode.svelte index fa0a237577..3940975fba 100644 --- a/web/src/lib/components/shared-components/qrcode.svelte +++ b/web/src/lib/components/shared-components/qrcode.svelte @@ -1,7 +1,7 @@ <script lang="ts"> - import QRCode from 'qrcode'; - import { colorTheme } from '$lib/stores/preferences.store'; import { Theme } from '$lib/constants'; + import { themeManager } from '$lib/managers/theme-manager.svelte'; + import QRCode from 'qrcode'; import { t } from 'svelte-i18n'; type Props = { @@ -14,7 +14,7 @@ let promise = $derived( QRCode.toDataURL(value, { - color: { dark: $colorTheme.value === Theme.DARK ? '#ffffffff' : '#000000ff', light: '#00000000' }, + color: { dark: themeManager.value === Theme.DARK ? '#ffffffff' : '#000000ff', light: '#00000000' }, margin: 0, width, }), diff --git a/web/src/lib/components/shared-components/scrubber/scrubber.svelte b/web/src/lib/components/shared-components/scrubber/scrubber.svelte index f5d9c00768..eafec2da0b 100644 --- a/web/src/lib/components/shared-components/scrubber/scrubber.svelte +++ b/web/src/lib/components/shared-components/scrubber/scrubber.svelte @@ -2,7 +2,7 @@ import Icon from '$lib/components/elements/icon.svelte'; import type { AssetStore, LiteBucket } from '$lib/stores/assets-store.svelte'; import { mobileDevice } from '$lib/stores/mobile-device.svelte'; - import { getFocusable } from '$lib/utils/focus-util'; + import { getTabbable } from '$lib/utils/focus-util'; import { fromLocalDateTime, type ScrubberListener } from '$lib/utils/timeline-util'; import { mdiPlay } from '@mdi/js'; import { clamp } from 'lodash-es'; @@ -376,7 +376,7 @@ if (forward || backward) { event.preventDefault(); - const focusable = getFocusable(document); + const focusable = getTabbable(document.body); if (scrollBar) { const index = focusable.indexOf(scrollBar); if (index !== -1) { @@ -446,7 +446,7 @@ aria-valuemax={toScrollY(1)} aria-valuemin={toScrollY(0)} data-id="immich-scrubbable-scrollbar" - class="absolute right-0 z-[1] select-none bg-immich-bg hover:cursor-row-resize" + class="absolute end-0 z-[1] select-none bg-immich-bg hover:cursor-row-resize" style:padding-top={PADDING_TOP + 'px'} style:padding-bottom={PADDING_BOTTOM + 'px'} style:width @@ -464,7 +464,7 @@ class={[ { 'border-b-2': isDragging }, { 'rounded-bl-md': !isDragging }, - 'truncate opacity-85 pointer-events-none absolute right-0 z-[100] min-w-20 max-w-64 w-fit rounded-tl-md border-immich-primary bg-immich-bg py-1 px-1 text-sm font-medium shadow-[0_0_8px_rgba(0,0,0,0.25)] dark:border-immich-dark-primary dark:bg-immich-dark-gray dark:text-immich-dark-fg', + 'truncate opacity-85 pointer-events-none absolute end-0 z-[100] min-w-20 max-w-64 w-fit rounded-ss-md border-immich-primary bg-immich-bg py-1 px-1 text-sm font-medium shadow-[0_0_8px_rgba(0,0,0,0.25)] dark:border-immich-dark-primary dark:bg-immich-dark-gray dark:text-immich-dark-fg', ]} style:top="{hoverY + 2}px" > @@ -474,7 +474,7 @@ {#if usingMobileDevice && ((assetStore.scrolling && scrollHoverLabel) || isHover || isDragging)} <div id="time-label" - class="rounded-l-full w-[32px] pl-2 text-white bg-immich-primary dark:bg-gray-600 hover:cursor-pointer select-none" + class="rounded-s-full w-[32px] ps-2 text-white bg-immich-primary dark:bg-gray-600 hover:cursor-pointer select-none" style:top="{PADDING_TOP + (scrollY - 50 / 2)}px" style:height="50px" style:right="0" @@ -482,8 +482,8 @@ in:fade={{ duration: 200 }} out:fade={{ duration: 200 }} > - <Icon path={mdiPlay} size="20" class="-rotate-90 relative top-[9px] -right-[2px]" /> - <Icon path={mdiPlay} size="20" class="rotate-90 relative top-[1px] -right-[2px]" /> + <Icon path={mdiPlay} size="20" class="-rotate-90 relative top-[9px] -end-[2px]" /> + <Icon path={mdiPlay} size="20" class="rotate-90 relative top-[1px] -end-[2px]" /> {#if (assetStore.scrolling && scrollHoverLabel) || isHover || isDragging} <p transition:fade={{ duration: 200 }} @@ -500,13 +500,13 @@ <!-- Scroll Position Indicator Line --> {#if !usingMobileDevice && !isDragging} <div - class="absolute right-0 h-[2px] w-10 bg-immich-primary dark:bg-immich-dark-primary" + class="absolute end-0 h-[2px] w-10 bg-immich-primary dark:bg-immich-dark-primary" style:top="{scrollY + PADDING_TOP - 2}px" > {#if assetStore.scrolling && scrollHoverLabel && !isHover} <p transition:fade={{ duration: 200 }} - class="truncate pointer-events-none absolute right-0 bottom-0 z-[100] min-w-20 max-w-64 w-fit rounded-tl-md border-b-2 border-immich-primary bg-immich-bg/80 py-1 px-1 text-sm font-medium shadow-[0_0_8px_rgba(0,0,0,0.25)] dark:border-immich-dark-primary dark:bg-immich-dark-gray/80 dark:text-immich-dark-fg" + class="truncate pointer-events-none absolute end-0 bottom-0 z-[100] min-w-20 max-w-64 w-fit rounded-tl-md border-b-2 border-immich-primary bg-immich-bg/80 py-1 px-1 text-sm font-medium shadow-[0_0_8px_rgba(0,0,0,0.25)] dark:border-immich-dark-primary dark:bg-immich-dark-gray/80 dark:text-immich-dark-fg" > {scrollHoverLabel} </p> @@ -521,7 +521,7 @@ data-label={segments.at(0)?.dateFormatted} > {#if relativeTopOffset > 6} - <div class="absolute right-[0.75rem] h-[4px] w-[4px] rounded-full bg-gray-300"></div> + <div class="absolute end-[0.75rem] h-[4px] w-[4px] rounded-full bg-gray-300"></div> {/if} </div> <!-- Time Segment --> @@ -535,12 +535,12 @@ > {#if !usingMobileDevice} {#if segment.hasLabel} - <div class="absolute right-[1.25rem] top-[-16px] z-10 text-[12px] dark:text-immich-dark-fg font-immich-mono"> + <div class="absolute end-[1.25rem] top-[-16px] z-10 text-[12px] dark:text-immich-dark-fg font-immich-mono"> {segment.date.year} </div> {/if} {#if segment.hasDot} - <div class="absolute right-[0.75rem] bottom-0 h-[4px] w-[4px] rounded-full bg-gray-300"></div> + <div class="absolute end-[0.75rem] bottom-0 h-[4px] w-[4px] rounded-full bg-gray-300"></div> {/if} {/if} </div> diff --git a/web/src/lib/components/shared-components/search-bar/search-bar.svelte b/web/src/lib/components/shared-components/search-bar/search-bar.svelte index a98e2f7882..e01b0edf1b 100644 --- a/web/src/lib/components/shared-components/search-bar/search-bar.svelte +++ b/web/src/lib/components/shared-components/search-bar/search-bar.svelte @@ -261,15 +261,15 @@ /> </div> - <div class="absolute inset-y-0 {showClearIcon ? 'right-14' : 'right-2'} flex items-center pl-6 transition-all"> + <div class="absolute inset-y-0 {showClearIcon ? 'end-14' : 'end-2'} flex items-center ps-6 transition-all"> <CircleIconButton title={$t('show_search_options')} icon={mdiTune} onclick={onFilterClick} size="20" /> </div> {#if isFocus} <div class="absolute inset-y-0 flex items-center" - class:right-16={isFocus} - class:right-28={isFocus && value.length > 0} + class:end-16={isFocus} + class:end-28={isFocus && value.length > 0} > <p class="bg-immich-primary text-white dark:bg-immich-dark-primary/90 dark:text-black/75 rounded-full px-3 py-1 text-xs z-10" @@ -280,11 +280,11 @@ {/if} {#if showClearIcon} - <div class="absolute inset-y-0 right-0 flex items-center pr-2"> + <div class="absolute inset-y-0 end-0 flex items-center pe-2"> <CircleIconButton onclick={onClear} icon={mdiClose} title={$t('clear')} size="20" /> </div> {/if} - <div class="absolute inset-y-0 left-0 flex items-center pl-2"> + <div class="absolute inset-y-0 start-0 flex items-center ps-2"> <CircleIconButton type="submit" disabled={showFilter} diff --git a/web/src/lib/components/shared-components/search-bar/search-history-box.svelte b/web/src/lib/components/shared-components/search-bar/search-history-box.svelte index a6188e46a9..2eb0b06d4b 100644 --- a/web/src/lib/components/shared-components/search-bar/search-history-box.svelte +++ b/web/src/lib/components/shared-components/search-bar/search-history-box.svelte @@ -122,7 +122,7 @@ <!-- svelte-ignore a11y_click_events_have_key_events --> <div id={getId(index)} - class="relative flex w-full cursor-pointer gap-3 py-3 pl-5 hover:bg-gray-100 aria-selected:bg-gray-100 dark:aria-selected:bg-gray-500/30 dark:hover:bg-gray-500/30" + class="relative flex w-full cursor-pointer gap-3 py-3 ps-5 hover:bg-gray-100 aria-selected:bg-gray-100 dark:aria-selected:bg-gray-500/30 dark:hover:bg-gray-500/30" onclick={() => handleSelect(savedSearchTerm)} role="option" tabindex="-1" @@ -132,7 +132,7 @@ <Icon path={mdiMagnify} size="1.5em" ariaHidden={true} /> {savedSearchTerm} </div> - <div aria-hidden={true} class="absolute right-5 top-0 items-center justify-center py-3"> + <div aria-hidden={true} class="absolute end-5 top-0 items-center justify-center py-3"> <CircleIconButton icon={mdiClose} title={$t('remove')} diff --git a/web/src/lib/components/shared-components/search-bar/search-tags-section.svelte b/web/src/lib/components/shared-components/search-bar/search-tags-section.svelte index 6071da1460..0819022ae2 100644 --- a/web/src/lib/components/shared-components/search-bar/search-tags-section.svelte +++ b/web/src/lib/components/shared-components/search-bar/search-tags-section.svelte @@ -57,7 +57,7 @@ {#if tag} <div class="flex group transition-all"> <span - class="inline-block h-min whitespace-nowrap pl-3 pr-1 group-hover:pl-3 py-1 text-center align-baseline leading-none text-gray-100 dark:text-immich-dark-gray bg-immich-primary dark:bg-immich-dark-primary rounded-tl-full rounded-bl-full hover:bg-immich-primary/80 dark:hover:bg-immich-dark-primary/80 transition-all" + class="inline-block h-min whitespace-nowrap ps-3 pe-1 group-hover:ps-3 py-1 text-center align-baseline leading-none text-gray-100 dark:text-immich-dark-gray bg-immich-primary dark:bg-immich-dark-primary roudned-s-full hover:bg-immich-primary/80 dark:hover:bg-immich-dark-primary/80 transition-all" > <p class="text-sm"> {tag.value} @@ -66,7 +66,7 @@ <button type="button" - class="text-gray-100 dark:text-immich-dark-gray bg-immich-primary/95 dark:bg-immich-dark-primary/95 rounded-tr-full rounded-br-full place-items-center place-content-center pr-2 pl-1 py-1 hover:bg-immich-primary/80 dark:hover:bg-immich-dark-primary/80 transition-all" + class="text-gray-100 dark:text-immich-dark-gray bg-immich-primary/95 dark:bg-immich-dark-primary/95 rounded-e-full place-items-center place-content-center pe-2 ps-1 py-1 hover:bg-immich-primary/80 dark:hover:bg-immich-dark-primary/80 transition-all" title="Remove tag" onclick={() => handleRemove(tagId)} > diff --git a/web/src/lib/components/shared-components/server-about-modal.svelte b/web/src/lib/components/shared-components/server-about-modal.svelte index cf935cd314..1284bb126d 100644 --- a/web/src/lib/components/shared-components/server-about-modal.svelte +++ b/web/src/lib/components/shared-components/server-about-modal.svelte @@ -6,6 +6,7 @@ import { t } from 'svelte-i18n'; import { mdiAlert } from '@mdi/js'; import Icon from '$lib/components/elements/icon.svelte'; + import { locale } from '$lib/stores/preferences.store'; interface Props { onClose: () => void; @@ -177,16 +178,19 @@ <span class="immich-form-label pb-2 text-xs" id="version-history" - title={createdAt.toLocaleString(DateTime.DATETIME_SHORT_WITH_SECONDS)} + title={createdAt.toLocaleString(DateTime.DATETIME_SHORT_WITH_SECONDS, { locale: $locale })} > {$t('version_history_item', { values: { version: item.version, - date: createdAt.toLocaleString({ - month: 'short', - day: 'numeric', - year: 'numeric', - }), + date: createdAt.toLocaleString( + { + month: 'short', + day: 'numeric', + year: 'numeric', + }, + { locale: $locale }, + ), }, })} </span> diff --git a/web/src/lib/components/shared-components/settings/setting-accordion.svelte b/web/src/lib/components/shared-components/settings/setting-accordion.svelte index bfa379edcd..5ae41c0551 100755 --- a/web/src/lib/components/shared-components/settings/setting-accordion.svelte +++ b/web/src/lib/components/shared-components/settings/setting-accordion.svelte @@ -73,7 +73,7 @@ type="button" aria-expanded={isOpen} {onclick} - class="flex w-full place-items-center justify-between text-left" + class="flex w-full place-items-center justify-between text-start" > <div> <div class="flex gap-2 place-items-center"> @@ -110,7 +110,7 @@ </button> {#if isOpen} - <ul transition:slide={{ duration: 150 }} class="mb-2 ml-4"> + <ul transition:slide={{ duration: 150 }} class="mb-2 ms-4"> {@render children?.()} </ul> {/if} diff --git a/web/src/lib/components/shared-components/settings/setting-input-field.svelte b/web/src/lib/components/shared-components/settings/setting-input-field.svelte index 9259f59cfb..2cab6e95eb 100644 --- a/web/src/lib/components/shared-components/settings/setting-input-field.svelte +++ b/web/src/lib/components/shared-components/settings/setting-input-field.svelte @@ -1,15 +1,15 @@ <script lang="ts"> + import { SettingInputFieldType } from '$lib/constants'; + import { onMount, tick, type Snippet } from 'svelte'; + import { t } from 'svelte-i18n'; import { quintOut } from 'svelte/easing'; import type { FormEventHandler } from 'svelte/elements'; import { fly } from 'svelte/transition'; import PasswordField from '../password-field.svelte'; - import { t } from 'svelte-i18n'; - import { onMount, tick, type Snippet } from 'svelte'; - import { SettingInputFieldType } from '$lib/constants'; interface Props { inputType: SettingInputFieldType; - value: string | number; + value: string | number | undefined; min?: number; max?: number; step?: string; @@ -101,7 +101,7 @@ {#if inputType === SettingInputFieldType.COLOR} <input bind:this={input} - class="immich-form-input w-full pb-2 rounded-none mr-1" + class="immich-form-input w-full pb-2 rounded-none me-1" aria-describedby={description ? `${label}-desc` : undefined} aria-labelledby="{label}-label" id={label} @@ -147,7 +147,7 @@ name={label} autocomplete={passwordAutocomplete} {required} - password={value.toString()} + password={(value || '').toString()} onInput={(passwordValue) => (value = passwordValue)} {disabled} {title} diff --git a/web/src/lib/components/shared-components/settings/setting-select.svelte b/web/src/lib/components/shared-components/settings/setting-select.svelte index 1cfd7123ac..e13d2387fb 100644 --- a/web/src/lib/components/shared-components/settings/setting-select.svelte +++ b/web/src/lib/components/shared-components/settings/setting-select.svelte @@ -1,12 +1,12 @@ <script lang="ts"> - import { quintOut } from 'svelte/easing'; - import { fly } from 'svelte/transition'; - import { t } from 'svelte-i18n'; import Icon from '$lib/components/elements/icon.svelte'; import { mdiChevronDown } from '@mdi/js'; + import { t } from 'svelte-i18n'; + import { quintOut } from 'svelte/easing'; + import { fly } from 'svelte/transition'; interface Props { - value: string | number; + value: string | number | undefined; options: { value: string | number; text: string }[]; label?: string; desc?: string; @@ -65,12 +65,12 @@ path={mdiChevronDown} size="1.2em" ariaHidden={true} - class="pointer-events-none right-1 relative col-start-1 row-start-1 self-center justify-self-end {disabled + class="pointer-events-none end-1 relative col-start-1 row-start-1 self-center justify-self-end {disabled ? 'text-immich-bg' : 'text-immich-fg dark:text-immich-bg'}" /> <select - class="immich-form-input w-full appearance-none row-start-1 col-start-1 !pr-6" + class="immich-form-input w-full appearance-none row-start-1 col-start-1 !pe-6" {disabled} aria-describedby={desc ? `${name}-desc` : undefined} {name} diff --git a/web/src/lib/components/shared-components/settings/setting-switch.svelte b/web/src/lib/components/shared-components/settings/setting-switch.svelte index 29c1f213d3..aa165cfaaa 100644 --- a/web/src/lib/components/shared-components/settings/setting-switch.svelte +++ b/web/src/lib/components/shared-components/settings/setting-switch.svelte @@ -33,7 +33,7 @@ </script> <div class="flex place-items-center justify-between"> - <div class="mr-2"> + <div class="me-2"> <div class="flex h-[26px] place-items-center gap-1"> <label class="font-medium text-immich-primary dark:text-immich-dark-primary text-sm" for={sliderId}> {title} diff --git a/web/src/lib/components/shared-components/show-shortcuts.svelte b/web/src/lib/components/shared-components/show-shortcuts.svelte index cee7545f48..e1454b49df 100644 --- a/web/src/lib/components/shared-components/show-shortcuts.svelte +++ b/web/src/lib/components/shared-components/show-shortcuts.svelte @@ -55,7 +55,7 @@ <div class="grid grid-cols-[30%_70%] items-center gap-4 pt-4 text-sm"> <div class="flex justify-self-end"> {#each shortcut.key as key (key)} - <p class="mr-1 flex items-center justify-center justify-self-end rounded-lg bg-immich-primary/25 p-2"> + <p class="me-1 flex items-center justify-center justify-self-end rounded-lg bg-immich-primary/25 p-2"> {key} </p> {/each} @@ -74,7 +74,7 @@ <div class="grid grid-cols-[30%_70%] items-center gap-4 pt-4 text-sm"> <div class="flex justify-self-end"> {#each shortcut.key as key (key)} - <p class="mr-1 flex items-center justify-center justify-self-end rounded-lg bg-immich-primary/25 p-2"> + <p class="me-1 flex items-center justify-center justify-self-end rounded-lg bg-immich-primary/25 p-2"> {key} </p> {/each} diff --git a/web/src/lib/components/shared-components/side-bar/admin-side-bar.svelte b/web/src/lib/components/shared-components/side-bar/admin-side-bar.svelte index af08fb4ce1..b8f02202a7 100644 --- a/web/src/lib/components/shared-components/side-bar/admin-side-bar.svelte +++ b/web/src/lib/components/shared-components/side-bar/admin-side-bar.svelte @@ -7,14 +7,12 @@ import { t } from 'svelte-i18n'; </script> -<SideBarSection> - <nav aria-label={$t('primary')}> - <SideBarLink title={$t('users')} routeId={AppRoute.ADMIN_USER_MANAGEMENT} icon={mdiAccountMultipleOutline} /> - <SideBarLink title={$t('jobs')} routeId={AppRoute.ADMIN_JOBS} icon={mdiSync} /> - <SideBarLink title={$t('settings')} routeId={AppRoute.ADMIN_SETTINGS} icon={mdiCog} /> - <SideBarLink title={$t('external_libraries')} routeId={AppRoute.ADMIN_LIBRARY_MANAGEMENT} icon={mdiBookshelf} /> - <SideBarLink title={$t('server_stats')} routeId={AppRoute.ADMIN_STATS} icon={mdiServer} /> - </nav> +<SideBarSection ariaLabel={$t('primary')}> + <SideBarLink title={$t('users')} routeId={AppRoute.ADMIN_USER_MANAGEMENT} icon={mdiAccountMultipleOutline} /> + <SideBarLink title={$t('jobs')} routeId={AppRoute.ADMIN_JOBS} icon={mdiSync} /> + <SideBarLink title={$t('settings')} routeId={AppRoute.ADMIN_SETTINGS} icon={mdiCog} /> + <SideBarLink title={$t('external_libraries')} routeId={AppRoute.ADMIN_LIBRARY_MANAGEMENT} icon={mdiBookshelf} /> + <SideBarLink title={$t('server_stats')} routeId={AppRoute.ADMIN_STATS} icon={mdiServer} /> <BottomInfo /> </SideBarSection> diff --git a/web/src/lib/components/shared-components/side-bar/purchase-info.svelte b/web/src/lib/components/shared-components/side-bar/purchase-info.svelte index 67d3eaf523..d94a0c169e 100644 --- a/web/src/lib/components/shared-components/side-bar/purchase-info.svelte +++ b/web/src/lib/components/shared-components/side-bar/purchase-info.svelte @@ -78,7 +78,7 @@ <LicenseModal onClose={() => (isOpen = false)} /> {/if} -<div class="license-status pl-4 text-sm"> +<div class="license-status ps-4 text-sm"> {#if $isPurchased && $preferences.purchase.showSupportBadge} <button onclick={() => goto(`${AppRoute.USER_SETTINGS}?isOpen=user-purchase-settings`)} @@ -123,7 +123,7 @@ {#if showMessage} <dialog open - class="hidden sidebar:block w-[500px] absolute bottom-[75px] left-[255px] bg-gray-50 dark:border-gray-800 border border-gray-200 dark:bg-immich-dark-gray dark:text-white text-black rounded-3xl z-10 shadow-2xl px-8 py-6" + class="hidden sidebar:block w-[500px] absolute bottom-[75px] start-[255px] bg-gray-50 dark:border-gray-800 border border-gray-200 dark:bg-immich-dark-gray dark:text-white text-black rounded-3xl z-10 shadow-2xl px-8 py-6" transition:fade={{ duration: 150 }} onmouseover={() => (hoverMessage = true)} onmouseleave={() => (hoverMessage = false)} diff --git a/web/src/lib/components/shared-components/side-bar/recent-albums.svelte b/web/src/lib/components/shared-components/side-bar/recent-albums.svelte index 9674ed9366..d121f74fdf 100644 --- a/web/src/lib/components/shared-components/side-bar/recent-albums.svelte +++ b/web/src/lib/components/shared-components/side-bar/recent-albums.svelte @@ -27,7 +27,7 @@ <a href={'/albums/' + album.id} title={album.albumName} - class="flex w-full place-items-center justify-between gap-4 rounded-r-full py-3 transition-[padding] delay-100 duration-100 hover:cursor-pointer hover:bg-immich-gray hover:text-immich-primary dark:text-immich-dark-fg dark:hover:bg-immich-dark-gray dark:hover:text-immich-dark-primary pl-10 group-hover:sm:px-10 md:px-10" + class="flex w-full place-items-center justify-between gap-4 rounded-e-full py-3 transition-[padding] delay-100 duration-100 hover:cursor-pointer hover:bg-immich-gray hover:text-immich-primary dark:text-immich-dark-fg dark:hover:bg-immich-dark-gray dark:hover:text-immich-dark-primary ps-10 group-hover:sm:px-10 md:px-10" > <div> <div diff --git a/web/src/lib/components/shared-components/side-bar/server-status.svelte b/web/src/lib/components/shared-components/side-bar/server-status.svelte index 8ca552a1f4..500bce524e 100644 --- a/web/src/lib/components/shared-components/side-bar/server-status.svelte +++ b/web/src/lib/components/shared-components/side-bar/server-status.svelte @@ -42,7 +42,7 @@ {/if} <div - class="text-sm flex md:flex pl-5 pr-1 place-items-center place-content-center justify-between min-w-52 overflow-hidden" + class="text-sm flex md:flex ps-5 pe-1 place-items-center place-content-center justify-between min-w-52 overflow-hidden" > {#if $connected} <div class="flex gap-2 place-items-center place-content-center"> 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 f8bf89cd29..1be7356e94 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 @@ -37,7 +37,7 @@ <div class="relative"> {#if hasDropdown} - <span class="hidden md:block absolute left-1 z-50 h-full"> + <span class="hidden md:block absolute start-1 z-50 h-full"> <button type="button" aria-label={$t('recent-albums')} @@ -59,12 +59,12 @@ data-sveltekit-preload-data={preloadData ? 'hover' : 'off'} draggable="false" aria-current={isSelected ? 'page' : undefined} - class="flex w-full place-items-center gap-4 rounded-r-full py-3 transition-[padding] delay-100 duration-100 hover:cursor-pointer hover:bg-immich-gray hover:text-immich-primary dark:text-immich-dark-fg dark:hover:bg-immich-dark-gray dark:hover:text-immich-dark-primary + 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-immich-gray 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-immich-primary hover:bg-immich-primary/10 dark:bg-immich-dark-primary/10 dark:text-immich-dark-primary' : ''}" > - <div class="flex w-full place-items-center gap-4 pl-5 overflow-hidden truncate"> + <div class="flex w-full place-items-center gap-4 ps-5 overflow-hidden truncate"> <Icon path={icon} size="1.5em" class="shrink-0" flipped={flippedLogo} ariaHidden /> <span class="text-sm font-medium">{title}</span> </div> diff --git a/web/src/lib/components/shared-components/side-bar/side-bar-section.svelte b/web/src/lib/components/shared-components/side-bar/side-bar-section.svelte index 74eb7d266b..62dda02cea 100644 --- a/web/src/lib/components/shared-components/side-bar/side-bar-section.svelte +++ b/web/src/lib/components/shared-components/side-bar/side-bar-section.svelte @@ -7,10 +7,11 @@ import { onMount, type Snippet } from 'svelte'; interface Props { + ariaLabel?: string; children?: Snippet; } - let { children }: Props = $props(); + let { ariaLabel, children }: Props = $props(); const isHidden = $derived(!sidebarStore.isOpen && !mobileDevice.isFullSidebar); const isExpanded = $derived(sidebarStore.isOpen && !mobileDevice.isFullSidebar); @@ -30,12 +31,13 @@ }; </script> -<section +<nav id="sidebar" + aria-label={ariaLabel} tabindex="-1" class="immich-scrollbar relative z-10 w-0 sidebar:w-[16rem] overflow-y-auto overflow-x-hidden bg-immich-bg pt-8 transition-all duration-200 dark:bg-immich-dark-bg" class:shadow-2xl={isExpanded} - class:dark:border-r-immich-dark-gray={isExpanded} + class:dark:border-e-immich-dark-gray={isExpanded} class:border-r={isExpanded} class:w-[min(100vw,16rem)]={sidebarStore.isOpen} data-testid="sidebar-parent" @@ -43,7 +45,7 @@ use:clickOutside={{ onOutclick: closeSidebar, onEscape: closeSidebar }} use:focusTrap={{ active: isExpanded }} > - <div class="pr-6 flex flex-col gap-1 h-max min-h-full"> + <div class="pe-6 flex flex-col gap-1 h-max min-h-full"> {@render children?.()} </div> -</section> +</nav> diff --git a/web/src/lib/components/shared-components/side-bar/side-bar.svelte b/web/src/lib/components/shared-components/side-bar/side-bar.svelte index ec9c2a06da..94b064c23e 100644 --- a/web/src/lib/components/shared-components/side-bar/side-bar.svelte +++ b/web/src/lib/components/shared-components/side-bar/side-bar.svelte @@ -42,102 +42,100 @@ let isUtilitiesSelected: boolean = $state(false); </script> -<SideBarSection> - <nav aria-label={$t('primary')}> +<SideBarSection ariaLabel={$t('primary')}> + <SideBarLink + title={$t('photos')} + routeId="/(user)/photos" + bind:isSelected={isPhotosSelected} + icon={isPhotosSelected ? mdiImageMultiple : mdiImageMultipleOutline} + ></SideBarLink> + + {#if $featureFlags.search} + <SideBarLink title={$t('explore')} routeId="/(user)/explore" icon={mdiMagnify} /> + {/if} + + {#if $featureFlags.map} <SideBarLink - title={$t('photos')} - routeId="/(user)/photos" - bind:isSelected={isPhotosSelected} - icon={isPhotosSelected ? mdiImageMultiple : mdiImageMultipleOutline} + title={$t('map')} + routeId="/(user)/map" + bind:isSelected={isMapSelected} + icon={isMapSelected ? mdiMap : mdiMapOutline} + /> + {/if} + + {#if $preferences.people.enabled && $preferences.people.sidebarWeb} + <SideBarLink + title={$t('people')} + routeId="/(user)/people" + bind:isSelected={isPeopleSelected} + icon={isPeopleSelected ? mdiAccount : mdiAccountOutline} + /> + {/if} + + {#if $preferences.sharedLinks.enabled && $preferences.sharedLinks.sidebarWeb} + <SideBarLink title={$t('shared_links')} routeId="/(user)/shared-links" icon={mdiLink} /> + {/if} + + <SideBarLink + title={$t('sharing')} + routeId="/(user)/sharing" + icon={isSharingSelected ? mdiAccountMultiple : mdiAccountMultipleOutline} + bind:isSelected={isSharingSelected} + ></SideBarLink> + + <p class="text-xs p-6 dark:text-immich-dark-fg">{$t('library').toUpperCase()}</p> + + <SideBarLink + title={$t('favorites')} + routeId="/(user)/favorites" + icon={isFavoritesSelected ? mdiHeart : mdiHeartOutline} + bind:isSelected={isFavoritesSelected} + ></SideBarLink> + + <SideBarLink + title={$t('albums')} + routeId="/(user)/albums" + icon={mdiImageAlbum} + flippedLogo + bind:dropdownOpen={$recentAlbumsDropdown} + > + {#snippet dropDownContent()} + <span in:fly={{ y: -20 }} class="hidden md:block"> + <RecentAlbums /> + </span> + {/snippet} + </SideBarLink> + + {#if $preferences.tags.enabled && $preferences.tags.sidebarWeb} + <SideBarLink title={$t('tags')} routeId="/(user)/tags" icon={mdiTagMultipleOutline} flippedLogo /> + {/if} + + {#if $preferences.folders.enabled && $preferences.folders.sidebarWeb} + <SideBarLink title={$t('folders')} routeId="/(user)/folders" icon={mdiFolderOutline} flippedLogo /> + {/if} + + <SideBarLink + title={$t('utilities')} + routeId="/(user)/utilities" + bind:isSelected={isUtilitiesSelected} + icon={isUtilitiesSelected ? mdiToolbox : mdiToolboxOutline} + ></SideBarLink> + + <SideBarLink + title={$t('archive')} + routeId="/(user)/archive" + bind:isSelected={isArchiveSelected} + icon={isArchiveSelected ? mdiArchiveArrowDown : mdiArchiveArrowDownOutline} + ></SideBarLink> + + {#if $featureFlags.trash} + <SideBarLink + title={$t('trash')} + routeId="/(user)/trash" + bind:isSelected={isTrashSelected} + icon={isTrashSelected ? mdiTrashCan : mdiTrashCanOutline} ></SideBarLink> - - {#if $featureFlags.search} - <SideBarLink title={$t('explore')} routeId="/(user)/explore" icon={mdiMagnify} /> - {/if} - - {#if $featureFlags.map} - <SideBarLink - title={$t('map')} - routeId="/(user)/map" - bind:isSelected={isMapSelected} - icon={isMapSelected ? mdiMap : mdiMapOutline} - /> - {/if} - - {#if $preferences.people.enabled && $preferences.people.sidebarWeb} - <SideBarLink - title={$t('people')} - routeId="/(user)/people" - bind:isSelected={isPeopleSelected} - icon={isPeopleSelected ? mdiAccount : mdiAccountOutline} - /> - {/if} - - {#if $preferences.sharedLinks.enabled && $preferences.sharedLinks.sidebarWeb} - <SideBarLink title={$t('shared_links')} routeId="/(user)/shared-links" icon={mdiLink} /> - {/if} - - <SideBarLink - title={$t('sharing')} - routeId="/(user)/sharing" - icon={isSharingSelected ? mdiAccountMultiple : mdiAccountMultipleOutline} - bind:isSelected={isSharingSelected} - ></SideBarLink> - - <p class="text-xs p-6 dark:text-immich-dark-fg">{$t('library').toUpperCase()}</p> - - <SideBarLink - title={$t('favorites')} - routeId="/(user)/favorites" - icon={isFavoritesSelected ? mdiHeart : mdiHeartOutline} - bind:isSelected={isFavoritesSelected} - ></SideBarLink> - - <SideBarLink - title={$t('albums')} - routeId="/(user)/albums" - icon={mdiImageAlbum} - flippedLogo - bind:dropdownOpen={$recentAlbumsDropdown} - > - {#snippet dropDownContent()} - <span in:fly={{ y: -20 }} class="hidden md:block"> - <RecentAlbums /> - </span> - {/snippet} - </SideBarLink> - - {#if $preferences.tags.enabled && $preferences.tags.sidebarWeb} - <SideBarLink title={$t('tags')} routeId="/(user)/tags" icon={mdiTagMultipleOutline} flippedLogo /> - {/if} - - {#if $preferences.folders.enabled && $preferences.folders.sidebarWeb} - <SideBarLink title={$t('folders')} routeId="/(user)/folders" icon={mdiFolderOutline} flippedLogo /> - {/if} - - <SideBarLink - title={$t('utilities')} - routeId="/(user)/utilities" - bind:isSelected={isUtilitiesSelected} - icon={isUtilitiesSelected ? mdiToolbox : mdiToolboxOutline} - ></SideBarLink> - - <SideBarLink - title={$t('archive')} - routeId="/(user)/archive" - bind:isSelected={isArchiveSelected} - icon={isArchiveSelected ? mdiArchiveArrowDown : mdiArchiveArrowDownOutline} - ></SideBarLink> - - {#if $featureFlags.trash} - <SideBarLink - title={$t('trash')} - routeId="/(user)/trash" - bind:isSelected={isTrashSelected} - icon={isTrashSelected ? mdiTrashCan : mdiTrashCanOutline} - ></SideBarLink> - {/if} - </nav> + {/if} <BottomInfo /> </SideBarSection> diff --git a/web/src/lib/components/shared-components/side-bar/storage-space.svelte b/web/src/lib/components/shared-components/side-bar/storage-space.svelte index 604522c4f0..4a40cbc1e7 100644 --- a/web/src/lib/components/shared-components/side-bar/storage-space.svelte +++ b/web/src/lib/components/shared-components/side-bar/storage-space.svelte @@ -46,7 +46,7 @@ </script> <div - class="storage-status p-4 bg-gray-100 dark:bg-immich-dark-primary/10 ml-4 rounded-lg text-sm min-w-52" + class="storage-status p-4 bg-gray-100 dark:bg-immich-dark-primary/10 ms-4 rounded-lg text-sm min-w-52" title={$t('storage_usage', { values: { used: getByteUnitString(usedBytes, $locale, 3), diff --git a/web/src/lib/components/shared-components/theme-button.svelte b/web/src/lib/components/shared-components/theme-button.svelte index 446668256f..2ed59e6cf7 100644 --- a/web/src/lib/components/shared-components/theme-button.svelte +++ b/web/src/lib/components/shared-components/theme-button.svelte @@ -1,13 +1,11 @@ <script lang="ts"> import { moonPath, moonViewBox, sunPath, sunViewBox } from '$lib/assets/svg-paths'; import CircleIconButton, { type Padding } from '$lib/components/elements/buttons/circle-icon-button.svelte'; - import { Theme } from '$lib/constants'; - import { colorTheme, handleToggleTheme } from '$lib/stores/preferences.store'; + import { themeManager } from '$lib/managers/theme-manager.svelte'; import { t } from 'svelte-i18n'; - let icon = $derived($colorTheme.value === Theme.LIGHT ? moonPath : sunPath); - let viewBox = $derived($colorTheme.value === Theme.LIGHT ? moonViewBox : sunViewBox); - let isDark = $derived($colorTheme.value === Theme.DARK); + let icon = $derived(themeManager.isDark ? sunPath : moonPath); + let viewBox = $derived(themeManager.isDark ? sunViewBox : moonViewBox); interface Props { padding?: Padding; @@ -16,14 +14,14 @@ let { padding = '3' }: Props = $props(); </script> -{#if !$colorTheme.system} +{#if !themeManager.theme.system} <CircleIconButton title={$t('toggle_theme')} {icon} {viewBox} role="switch" - aria-checked={isDark ? 'true' : 'false'} - onclick={handleToggleTheme} + aria-checked={themeManager.isDark ? 'true' : 'false'} + onclick={() => themeManager.toggleTheme()} {padding} /> {/if} diff --git a/web/src/lib/components/shared-components/tree/breadcrumbs.svelte b/web/src/lib/components/shared-components/tree/breadcrumbs.svelte index 7da2215a77..a0d4d250f7 100644 --- a/web/src/lib/components/shared-components/tree/breadcrumbs.svelte +++ b/web/src/lib/components/shared-components/tree/breadcrumbs.svelte @@ -23,7 +23,7 @@ icon={mdiArrowUpLeft} title={$t('to_parent')} href={getLink(pathSegments.slice(0, -1).join('/'))} - class="mr-2" + class="me-2" padding="2" onclick={() => {}} /> diff --git a/web/src/lib/components/shared-components/tree/tree-items.svelte b/web/src/lib/components/shared-components/tree/tree-items.svelte index 3b770e47ef..0df71ca605 100644 --- a/web/src/lib/components/shared-components/tree/tree-items.svelte +++ b/web/src/lib/components/shared-components/tree/tree-items.svelte @@ -14,7 +14,7 @@ let { items, parent = '', active = '', icons, getLink, getColor = () => undefined }: Props = $props(); </script> -<ul class="list-none ml-2"> +<ul class="list-none ms-2"> <!-- eslint-disable-next-line svelte/require-each-key --> {#each Object.entries(items).sort() as [path, tree]} {@const value = normalizeTreePath(`${parent}/${path}`)} diff --git a/web/src/lib/components/shared-components/tree/tree.svelte b/web/src/lib/components/shared-components/tree/tree.svelte index ded6f70690..463871154d 100644 --- a/web/src/lib/components/shared-components/tree/tree.svelte +++ b/web/src/lib/components/shared-components/tree/tree.svelte @@ -31,7 +31,7 @@ <a href={getLink(path)} title={value} - class={`flex flex-grow place-items-center pl-2 py-1 text-sm rounded-lg hover:bg-slate-200 dark:hover:bg-slate-800 hover:font-semibold ${isTarget ? 'bg-slate-100 dark:bg-slate-700 font-semibold text-immich-primary dark:text-immich-dark-primary' : 'dark:text-gray-200'}`} + class={`flex flex-grow place-items-center ps-2 py-1 text-sm rounded-lg hover:bg-slate-200 dark:hover:bg-slate-800 hover:font-semibold ${isTarget ? 'bg-slate-100 dark:bg-slate-700 font-semibold text-immich-primary dark:text-immich-dark-primary' : 'dark:text-gray-200'}`} data-sveltekit-keepfocus > <button type="button" {onclick} class={Object.values(tree).length === 0 ? 'invisible' : ''}> @@ -45,7 +45,7 @@ size={20} /> </div> - <span class="text-nowrap overflow-hidden text-ellipsis font-mono pl-1 pt-1 whitespace-pre-wrap">{value}</span> + <span class="text-nowrap overflow-hidden text-ellipsis font-mono ps-1 pt-1 whitespace-pre-wrap">{value}</span> </a> {#if isOpen} diff --git a/web/src/lib/components/shared-components/upload-panel.svelte b/web/src/lib/components/shared-components/upload-panel.svelte index 0eb7d1655c..e7708e5c30 100644 --- a/web/src/lib/components/shared-components/upload-panel.svelte +++ b/web/src/lib/components/shared-components/upload-panel.svelte @@ -48,7 +48,7 @@ } uploadAssetsStore.reset(); }} - class="fixed bottom-6 right-16 z-[10000]" + class="fixed bottom-6 end-16 z-[10000]" > {#if showDetail} <div @@ -136,7 +136,7 @@ type="button" in:scale={{ duration: 250, easing: quartInOut }} onclick={() => (showDetail = true)} - class="absolute -left-4 -top-4 flex h-10 w-10 place-content-center place-items-center rounded-full bg-immich-primary p-5 text-xs text-gray-200" + class="absolute -start-4 -top-4 flex h-10 w-10 place-content-center place-items-center rounded-full bg-immich-primary p-5 text-xs text-gray-200" > {$remainingUploads.toLocaleString($locale)} </button> @@ -145,7 +145,7 @@ type="button" in:scale={{ duration: 250, easing: quartInOut }} onclick={() => (showDetail = true)} - class="absolute -right-4 -top-4 flex h-10 w-10 place-content-center place-items-center rounded-full bg-immich-error p-5 text-xs text-gray-200" + class="absolute -end-4 -top-4 flex h-10 w-10 place-content-center place-items-center rounded-full bg-immich-error p-5 text-xs text-gray-200" > {$stats.errors.toLocaleString($locale)} </button> 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 63209ca289..f1d8e14787 100644 --- a/web/src/lib/components/user-settings-page/app-settings.svelte +++ b/web/src/lib/components/user-settings-page/app-settings.svelte @@ -1,11 +1,12 @@ <script lang="ts"> + import { invalidateAll } from '$app/navigation'; import type { ComboBoxOption } from '$lib/components/shared-components/combobox.svelte'; import SettingCombobox from '$lib/components/shared-components/settings/setting-combobox.svelte'; import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; import { defaultLang, fallbackLocale, langs, locales } from '$lib/constants'; + import { themeManager } from '$lib/managers/theme-manager.svelte'; import { alwaysLoadOriginalFile, - colorTheme, lang, locale, loopVideo, @@ -17,7 +18,6 @@ import { onMount } from 'svelte'; import { locale as i18nLocale, t } from 'svelte-i18n'; import { fade } from 'svelte/transition'; - import { invalidateAll } from '$app/navigation'; let time = $state(new Date()); @@ -40,10 +40,6 @@ })); }; - const handleToggleColorTheme = () => { - $colorTheme.system = !$colorTheme.system; - }; - const handleToggleLocaleBrowser = () => { $locale = $locale ? undefined : fallbackLocale.code; }; @@ -96,17 +92,17 @@ <section class="my-4"> <div in:fade={{ duration: 500 }}> - <div class="ml-4 mt-4 flex flex-col gap-4"> - <div class="ml-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> + <div class="ms-4"> <SettingSwitch title={$t('theme_selection')} subtitle={$t('theme_selection_description')} - bind:checked={$colorTheme.system} - onToggle={handleToggleColorTheme} + checked={themeManager.theme.system} + onToggle={(isChecked) => themeManager.setSystem(isChecked)} /> </div> - <div class="ml-4"> + <div class="ms-4"> <SettingCombobox comboboxPlaceholder={$t('language')} selectedOption={langOptions.find(({ value }) => value === closestLanguage) || defaultLangOption} @@ -117,7 +113,7 @@ /> </div> - <div class="ml-4"> + <div class="ms-4"> <SettingSwitch title={$t('default_locale')} subtitle={$t('default_locale_description')} @@ -128,7 +124,7 @@ </SettingSwitch> </div> {#if $locale !== undefined} - <div class="ml-4"> + <div class="ms-4"> <SettingCombobox comboboxPlaceholder={$t('searching_locales')} {selectedOption} @@ -140,7 +136,7 @@ </div> {/if} - <div class="ml-4"> + <div class="ms-4"> <SettingSwitch title={$t('display_original_photos')} subtitle={$t('display_original_photos_setting_description')} @@ -148,7 +144,7 @@ onToggle={() => ($alwaysLoadOriginalFile = !$alwaysLoadOriginalFile)} /> </div> - <div class="ml-4"> + <div class="ms-4"> <SettingSwitch title={$t('video_hover_setting')} subtitle={$t('video_hover_setting_description')} @@ -156,7 +152,7 @@ onToggle={() => ($playVideoThumbnailOnHover = !$playVideoThumbnailOnHover)} /> </div> - <div class="ml-4"> + <div class="ms-4"> <SettingSwitch title={$t('loop_videos')} subtitle={$t('loop_videos_description')} @@ -165,7 +161,7 @@ /> </div> - <div class="ml-4"> + <div class="ms-4"> <SettingSwitch title={$t('permanent_deletion_warning')} subtitle={$t('permanent_deletion_warning_setting_description')} diff --git a/web/src/lib/components/user-settings-page/change-password-settings.svelte b/web/src/lib/components/user-settings-page/change-password-settings.svelte index dc8cfac42e..2735c4f13e 100644 --- a/web/src/lib/components/user-settings-page/change-password-settings.svelte +++ b/web/src/lib/components/user-settings-page/change-password-settings.svelte @@ -44,7 +44,7 @@ <section class="my-4"> <div in:fade={{ duration: 500 }}> <form autocomplete="off" {onsubmit}> - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingInputField inputType={SettingInputFieldType.PASSWORD} label={$t('password')} diff --git a/web/src/lib/components/user-settings-page/device-card.svelte b/web/src/lib/components/user-settings-page/device-card.svelte index 5b70b006be..ad0b621921 100644 --- a/web/src/lib/components/user-settings-page/device-card.svelte +++ b/web/src/lib/components/user-settings-page/device-card.svelte @@ -31,7 +31,7 @@ </script> <div class="flex w-full flex-row"> - <div class="hidden items-center justify-center pr-2 text-immich-primary dark:text-immich-dark-primary sm:flex"> + <div class="hidden items-center justify-center pe-2 text-immich-primary dark:text-immich-dark-primary sm:flex"> {#if device.deviceOS === 'Android'} <Icon path={mdiAndroid} size="40" /> {:else if device.deviceOS === 'iOS' || device.deviceOS === 'macOS'} @@ -50,7 +50,7 @@ <Icon path={mdiHelp} size="40" /> {/if} </div> - <div class="flex grow flex-row justify-between gap-1 pl-4 sm:pl-0"> + <div class="flex grow flex-row justify-between gap-1 ps-4 sm:ps-0"> <div class="flex flex-col justify-center gap-1 dark:text-white"> <span class="text-sm"> {#if device.deviceType || device.deviceOS} @@ -64,7 +64,9 @@ <span>{DateTime.fromISO(device.updatedAt, { locale: $locale }).toRelativeCalendar(options)}</span> <span class="text-xs text-gray-500 dark:text-gray-400"> - </span> <span class="text-xs text-gray-500 dark:text-gray-400"> - {DateTime.fromISO(device.updatedAt, { locale: $locale }).toLocaleString(DateTime.DATETIME_MED)} + {DateTime.fromISO(device.updatedAt, { locale: $locale }).toLocaleString(DateTime.DATETIME_MED, { + locale: $locale, + })} </span> </div> </div> diff --git a/web/src/lib/components/user-settings-page/download-settings.svelte b/web/src/lib/components/user-settings-page/download-settings.svelte index e2401a8a76..9ed7285aa0 100644 --- a/web/src/lib/components/user-settings-page/download-settings.svelte +++ b/web/src/lib/components/user-settings-page/download-settings.svelte @@ -43,7 +43,7 @@ <section class="my-4"> <div in:fade={{ duration: 500 }}> <form autocomplete="off" {onsubmit}> - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingInputField inputType={SettingInputFieldType.NUMBER} label={$t('archive_size')} diff --git a/web/src/lib/components/user-settings-page/feature-settings.svelte b/web/src/lib/components/user-settings-page/feature-settings.svelte index 4fdd8f21d7..d331e40432 100644 --- a/web/src/lib/components/user-settings-page/feature-settings.svelte +++ b/web/src/lib/components/user-settings-page/feature-settings.svelte @@ -63,14 +63,14 @@ <section class="my-4"> <div in:fade={{ duration: 500 }}> <form autocomplete="off" {onsubmit}> - <div class="ml-4 mt-4 flex flex-col"> + <div class="ms-4 mt-4 flex flex-col"> <SettingAccordion key="folders" title={$t('folders')} subtitle={$t('folders_feature_description')}> - <div class="ml-4 mt-6"> + <div class="ms-4 mt-6"> <SettingSwitch title={$t('enable')} bind:checked={foldersEnabled} /> </div> {#if foldersEnabled} - <div class="ml-4 mt-6"> + <div class="ms-4 mt-6"> <SettingSwitch title={$t('sidebar')} subtitle={$t('sidebar_display_description')} @@ -81,18 +81,18 @@ </SettingAccordion> <SettingAccordion key="memories" title={$t('time_based_memories')} subtitle={$t('photos_from_previous_years')}> - <div class="ml-4 mt-6"> + <div class="ms-4 mt-6"> <SettingSwitch title={$t('enable')} bind:checked={memoriesEnabled} /> </div> </SettingAccordion> <SettingAccordion key="people" title={$t('people')} subtitle={$t('people_feature_description')}> - <div class="ml-4 mt-6"> + <div class="ms-4 mt-6"> <SettingSwitch title={$t('enable')} bind:checked={peopleEnabled} /> </div> {#if peopleEnabled} - <div class="ml-4 mt-6"> + <div class="ms-4 mt-6"> <SettingSwitch title={$t('sidebar')} subtitle={$t('sidebar_display_description')} @@ -103,17 +103,17 @@ </SettingAccordion> <SettingAccordion key="rating" title={$t('rating')} subtitle={$t('rating_description')}> - <div class="ml-4 mt-6"> + <div class="ms-4 mt-6"> <SettingSwitch title={$t('enable')} bind:checked={ratingsEnabled} /> </div> </SettingAccordion> <SettingAccordion key="shared-links" title={$t('shared_links')} subtitle={$t('shared_links_description')}> - <div class="ml-4 mt-6"> + <div class="ms-4 mt-6"> <SettingSwitch title={$t('enable')} bind:checked={sharedLinksEnabled} /> </div> {#if sharedLinksEnabled} - <div class="ml-4 mt-6"> + <div class="ms-4 mt-6"> <SettingSwitch title={$t('sidebar')} subtitle={$t('sidebar_display_description')} @@ -124,11 +124,11 @@ </SettingAccordion> <SettingAccordion key="tags" title={$t('tags')} subtitle={$t('tag_feature_description')}> - <div class="ml-4 mt-6"> + <div class="ms-4 mt-6"> <SettingSwitch title={$t('enable')} bind:checked={tagsEnabled} /> </div> {#if tagsEnabled} - <div class="ml-4 mt-6"> + <div class="ms-4 mt-6"> <SettingSwitch title={$t('sidebar')} subtitle={$t('sidebar_display_description')} diff --git a/web/src/lib/components/user-settings-page/notifications-settings.svelte b/web/src/lib/components/user-settings-page/notifications-settings.svelte index 51a6558f43..fe4b51e20a 100644 --- a/web/src/lib/components/user-settings-page/notifications-settings.svelte +++ b/web/src/lib/components/user-settings-page/notifications-settings.svelte @@ -46,14 +46,14 @@ <section class="my-4"> <div in:fade={{ duration: 500 }}> <form autocomplete="off" {onsubmit}> - <div class="ml-4 mt-4 flex flex-col gap-4"> - <div class="ml-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> + <div class="ms-4"> <SettingSwitch title={$t('notification_toggle_setting_description')} bind:checked={emailNotificationsEnabled} /> </div> - <div class="ml-4"> + <div class="ms-4"> <SettingSwitch title={$t('album_added')} subtitle={$t('album_added_notification_setting_description')} @@ -61,7 +61,7 @@ disabled={!emailNotificationsEnabled} /> </div> - <div class="ml-4"> + <div class="ms-4"> <SettingSwitch title={$t('album_updated')} subtitle={$t('album_updated_setting_description')} diff --git a/web/src/lib/components/user-settings-page/partner-selection-modal.svelte b/web/src/lib/components/user-settings-page/partner-selection-modal.svelte index f5f76e099f..37c6580429 100644 --- a/web/src/lib/components/user-settings-page/partner-selection-modal.svelte +++ b/web/src/lib/components/user-settings-page/partner-selection-modal.svelte @@ -54,7 +54,7 @@ <UserAvatar {user} size="lg" /> {/if} - <div class="text-left"> + <div class="text-start"> <p class="text-immich-fg dark:text-immich-dark-fg"> {user.name} </p> diff --git a/web/src/lib/components/user-settings-page/partner-settings.svelte b/web/src/lib/components/user-settings-page/partner-settings.svelte index 909b9ec517..b2238b84e2 100644 --- a/web/src/lib/components/user-settings-page/partner-settings.svelte +++ b/web/src/lib/components/user-settings-page/partner-settings.svelte @@ -130,7 +130,7 @@ <div class="flex gap-4 rounded-lg pb-4 transition-all justify-between"> <div class="flex gap-4"> <UserAvatar user={partner.user} size="md" /> - <div class="text-left"> + <div class="text-start"> <p class="text-immich-fg dark:text-immich-dark-fg"> {partner.user.name} </p> diff --git a/web/src/lib/components/user-settings-page/user-api-key-list.svelte b/web/src/lib/components/user-settings-page/user-api-key-list.svelte index 53e9079bff..6dae502aca 100644 --- a/web/src/lib/components/user-settings-page/user-api-key-list.svelte +++ b/web/src/lib/components/user-settings-page/user-api-key-list.svelte @@ -126,7 +126,7 @@ </div> {#if keys.length > 0} - <table class="w-full text-left"> + <table class="w-full text-start"> <thead class="mb-4 flex h-12 w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary" > diff --git a/web/src/lib/components/user-settings-page/user-profile-settings.svelte b/web/src/lib/components/user-settings-page/user-profile-settings.svelte index c36c36d7cc..90487f532f 100644 --- a/web/src/lib/components/user-settings-page/user-profile-settings.svelte +++ b/web/src/lib/components/user-settings-page/user-profile-settings.svelte @@ -42,7 +42,7 @@ <section class="my-4"> <div in:fade={{ duration: 500 }}> <form autocomplete="off" onsubmit={preventDefault(bubble('submit'))}> - <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ms-4 mt-4 flex flex-col gap-4"> <SettingInputField inputType={SettingInputFieldType.TEXT} label={$t('user_id')} diff --git a/web/src/lib/components/user-settings-page/user-purchase-settings.svelte b/web/src/lib/components/user-settings-page/user-purchase-settings.svelte index 64dac133f2..ba8aadd73f 100644 --- a/web/src/lib/components/user-settings-page/user-purchase-settings.svelte +++ b/web/src/lib/components/user-settings-page/user-purchase-settings.svelte @@ -122,7 +122,7 @@ <!-- PRODUCT KEY INFO CARD --> {#if isServerProduct} <div - class="bg-gray-50 border border-immich-dark-primary/20 dark:bg-immich-dark-primary/15 p-6 pr-12 rounded-xl flex place-content-center gap-4" + class="bg-gray-50 border border-immich-dark-primary/20 dark:bg-immich-dark-primary/15 p-6 pe-12 rounded-xl flex place-content-center gap-4" > <Icon path={mdiKey} size="56" class="text-immich-primary dark:text-immich-dark-primary" /> @@ -152,7 +152,7 @@ {/if} {:else} <div - class="bg-gray-50 border border-immich-dark-primary/20 dark:bg-immich-dark-primary/15 p-6 pr-12 rounded-xl flex place-content-center gap-4" + class="bg-gray-50 border border-immich-dark-primary/20 dark:bg-immich-dark-primary/15 p-6 pe-12 rounded-xl flex place-content-center gap-4" > <Icon path={mdiKey} size="56" class="text-immich-primary dark:text-immich-dark-primary" /> diff --git a/web/src/lib/components/user-settings-page/user-usage-statistic.svelte b/web/src/lib/components/user-settings-page/user-usage-statistic.svelte index f7de1d8f64..ad77516d55 100644 --- a/web/src/lib/components/user-settings-page/user-usage-statistic.svelte +++ b/web/src/lib/components/user-settings-page/user-usage-statistic.svelte @@ -68,7 +68,7 @@ <section class="my-6"> <p class="text-xs dark:text-white uppercase">{$t('photos_and_videos')}</p> <div class="overflow-x-auto"> - <table class="w-full text-left mt-4"> + <table class="w-full text-start mt-4"> <thead class="mb-4 flex h-12 w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary" > @@ -92,7 +92,7 @@ <p class="text-xs dark:text-white uppercase">{$t('albums')}</p> </div> <div class="overflow-x-auto"> - <table class="w-full text-left mt-4"> + <table class="w-full text-start mt-4"> <thead class="mb-4 flex h-12 w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary" > diff --git a/web/src/lib/components/utilities-page/duplicates/duplicate-asset.svelte b/web/src/lib/components/utilities-page/duplicates/duplicate-asset.svelte index 97f44e3ec4..b8409cb0ef 100644 --- a/web/src/lib/components/utilities-page/duplicates/duplicate-asset.svelte +++ b/web/src/lib/components/utilities-page/duplicates/duplicate-asset.svelte @@ -44,14 +44,14 @@ <!-- FAVORITE ICON --> {#if asset.isFavorite} - <div class="absolute bottom-2 left-2"> + <div class="absolute bottom-2 start-2"> <Icon path={mdiHeart} size="24" class="text-white" /> </div> {/if} <!-- OVERLAY CHIP --> <div - class="absolute bottom-1 right-3 px-4 py-1 rounded-xl text-xs transition-colors {isSelected + class="absolute bottom-1 end-3 px-4 py-1 rounded-xl text-xs transition-colors {isSelected ? 'bg-green-400/90' : 'bg-red-300/90'}" > @@ -59,7 +59,7 @@ </div> <!-- EXTERNAL LIBRARY / STACK COUNT CHIP --> - <div class="absolute top-2 right-3"> + <div class="absolute top-2 end-3"> {#if isFromExternalLibrary} <div class="bg-immich-primary/90 px-2 py-1 rounded-xl text-xs text-white"> {$t('external')} @@ -68,7 +68,7 @@ {#if asset.stack?.assetCount} <div class="bg-immich-primary/90 px-2 py-1 my-0.5 rounded-xl text-xs text-white"> <div class="flex items-center justify-center"> - <div class="mr-1">{asset.stack.assetCount}</div> + <div class="me-1">{asset.stack.assetCount}</div> <Icon path={mdiImageMultipleOutline} size="18" /> </div> </div> @@ -79,7 +79,7 @@ <button type="button" onclick={() => onViewAsset(asset)} - class="absolute rounded-full top-1 left-1 text-gray-200 p-2 hover:text-white bg-black/35 hover:bg-black/50" + class="absolute rounded-full top-1 start-1 text-gray-200 p-2 hover:text-white bg-black/35 hover:bg-black/50" title={$t('view')} > <Icon ariaLabel={$t('view')} path={mdiMagnifyPlus} flipped size="18" /> diff --git a/web/src/lib/components/utilities-page/duplicates/duplicates-compare-control.svelte b/web/src/lib/components/utilities-page/duplicates/duplicates-compare-control.svelte index b7cd9aa240..c0ef70ef06 100644 --- a/web/src/lib/components/utilities-page/duplicates/duplicates-compare-control.svelte +++ b/web/src/lib/components/utilities-page/duplicates/duplicates-compare-control.svelte @@ -114,28 +114,17 @@ /> <div class="pt-4 rounded-3xl border dark:border-2 border-gray-300 dark:border-gray-700 max-w-[54rem] mx-auto mb-16"> - <div class="flex flex-wrap gap-1 place-items-center place-content-center px-4 pt-4"> - {#each assets as asset (asset.id)} - <DuplicateAsset - {asset} - {onSelectAsset} - isSelected={selectedAssetIds.has(asset.id)} - onViewAsset={(asset) => setAsset(asset)} - /> - {/each} - </div> - - <div class="flex flex-wrap gap-y-6 mt-10 mb-4 px-6 w-full place-content-end justify-between"> + <div class="flex flex-wrap gap-y-6 mb-4 px-6 w-full place-content-end justify-between"> <!-- MARK ALL BUTTONS --> <div class="flex text-xs text-black"> <button type="button" - class="px-4 py-3 flex place-items-center gap-2 rounded-tl-full rounded-bl-full dark:bg-immich-dark-primary hover:dark:bg-immich-dark-primary/90 bg-immich-primary/25 hover:bg-immich-primary/50" + class="px-4 py-3 flex place-items-center gap-2 rounded-s-full dark:bg-immich-dark-primary hover:dark:bg-immich-dark-primary/90 bg-immich-primary/25 hover:bg-immich-primary/50" onclick={onSelectAll}><Icon path={mdiCheck} size="20" />{$t('select_keep_all')}</button > <button type="button" - class="px-4 py-3 flex place-items-center gap-2 rounded-tr-full rounded-br-full dark:bg-immich-dark-primary/50 hover:dark:bg-immich-dark-primary/70 bg-immich-primary hover:bg-immich-primary/80 text-white" + class="px-4 py-3 flex place-items-center gap-2 rounded-e-full dark:bg-immich-dark-primary/50 hover:dark:bg-immich-dark-primary/70 bg-immich-primary hover:bg-immich-primary/80 text-white" onclick={onSelectNone}><Icon path={mdiTrashCanOutline} size="20" />{$t('select_trash_all')}</button > </div> @@ -143,21 +132,11 @@ <!-- CONFIRM BUTTONS --> <div class="flex text-xs text-black"> {#if trashCount === 0} - <Button - size="sm" - color="primary" - class="flex place-items-center rounded-tl-full rounded-bl-full gap-2" - onclick={handleResolve} - > + <Button size="sm" color="primary" class="flex place-items-center rounded-s-full gap-2" onclick={handleResolve}> <Icon path={mdiCheck} size="20" />{$t('keep_all')} </Button> {:else} - <Button - size="sm" - color="red" - class="flex place-items-center rounded-tl-full rounded-bl-full gap-2 py-3" - onclick={handleResolve} - > + <Button size="sm" color="red" class="flex place-items-center rounded-s-full gap-2 py-3" onclick={handleResolve}> <Icon path={mdiTrashCanOutline} size="20" />{trashCount === assets.length ? $t('trash_all') : $t('trash_count', { values: { count: trashCount } })} @@ -166,7 +145,7 @@ <Button size="sm" color="primary" - class="flex place-items-center rounded-tr-full rounded-br-full gap-2" + class="flex place-items-center rounded-e-full gap-2" onclick={handleStack} disabled={selectedAssetIds.size !== 1} > @@ -174,6 +153,17 @@ </Button> </div> </div> + + <div class="flex flex-wrap gap-1 mb-4 place-items-center place-content-center px-4 pt-4"> + {#each assets as asset (asset.id)} + <DuplicateAsset + {asset} + {onSelectAsset} + isSelected={selectedAssetIds.has(asset.id)} + onViewAsset={(asset) => setAsset(asset)} + /> + {/each} + </div> </div> {#if $showAssetViewer} diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts index 2c21d04865..f5b044cd41 100644 --- a/web/src/lib/constants.ts +++ b/web/src/lib/constants.ts @@ -268,8 +268,8 @@ export const defaultLang = { name: 'English', code: 'en', loader: () => import(' export const langs = [ { name: 'Afrikaans', code: 'af', loader: () => import('$i18n/af.json') }, - { name: 'Arabic', code: 'ar', loader: () => import('$i18n/ar.json') }, - { name: 'Azerbaijani', code: 'az', loader: () => import('$i18n/az.json') }, + { name: 'Arabic', code: 'ar', loader: () => import('$i18n/ar.json'), rtl: true }, + { name: 'Azerbaijani', code: 'az', loader: () => import('$i18n/az.json'), rtl: true }, { name: 'Belarusian', code: 'be', loader: () => import('$i18n/be.json') }, { name: 'Bulgarian', code: 'bg', loader: () => import('$i18n/bg.json') }, { name: 'Bislama', code: 'bi', loader: () => import('$i18n/bi.json') }, @@ -284,12 +284,12 @@ export const langs = [ { name: 'Spanish', code: 'es', loader: () => import('$i18n/es.json') }, { name: 'Estonian', code: 'et', loader: () => import('$i18n/et.json') }, { name: 'Basque', code: 'eu', loader: () => import('$i18n/eu.json') }, - { name: 'Persian', code: 'fa', loader: () => import('$i18n/fa.json') }, + { name: 'Persian', code: 'fa', loader: () => import('$i18n/fa.json'), rtl: true }, { name: 'Finnish', code: 'fi', loader: () => import('$i18n/fi.json') }, { name: 'Filipino', code: 'fil', loader: () => import('$i18n/fil.json') }, { name: 'French', code: 'fr', loader: () => import('$i18n/fr.json') }, { name: 'Galician', code: 'gl', loader: () => import('$i18n/gl.json') }, - { name: 'Hebrew', code: 'he', loader: () => import('$i18n/he.json') }, + { name: 'Hebrew', code: 'he', loader: () => import('$i18n/he.json'), rtl: true }, { name: 'Hindi', code: 'hi', loader: () => import('$i18n/hi.json') }, { name: 'Croatian', code: 'hr', loader: () => import('$i18n/hr.json') }, { name: 'Hungarian', code: 'hu', loader: () => import('$i18n/hu.json') }, @@ -299,7 +299,7 @@ export const langs = [ { name: 'Japanese', code: 'ja', loader: () => import('$i18n/ja.json') }, { name: 'Georgian', code: 'ka', loader: () => import('$i18n/ka.json') }, { name: 'Kazakh', code: 'kk', loader: () => import('$i18n/kk.json') }, - { name: 'Kurdish (Northern)', code: 'kmr', loader: () => import('$i18n/kmr.json') }, + { name: 'Kurdish (Northern)', code: 'kmr', loader: () => import('$i18n/kmr.json'), rtl: true }, { name: 'Kannada', code: 'kn', loader: () => import('$i18n/kn.json') }, { name: 'Korean', code: 'ko', loader: () => import('$i18n/ko.json') }, { name: 'Luxembourgish', code: 'lb', loader: () => import('$i18n/lb.json') }, @@ -335,7 +335,7 @@ export const langs = [ { name: 'Thai', code: 'th', loader: () => import('$i18n/th.json') }, { name: 'Turkish', code: 'tr', loader: () => import('$i18n/tr.json') }, { name: 'Ukrainian', code: 'uk', loader: () => import('$i18n/uk.json') }, - { name: 'Urdu', code: 'ur', loader: () => import('$i18n/ur.json') }, + { name: 'Urdu', code: 'ur', loader: () => import('$i18n/ur.json'), rtl: true }, { name: 'Vietnamese', code: 'vi', loader: () => import('$i18n/vi.json') }, { name: 'Chinese (Traditional)', diff --git a/web/src/lib/managers/activity-manager.svelte.ts b/web/src/lib/managers/activity-manager.svelte.ts new file mode 100644 index 0000000000..a527778bb1 --- /dev/null +++ b/web/src/lib/managers/activity-manager.svelte.ts @@ -0,0 +1,113 @@ +import { user } from '$lib/stores/user.store'; +import { handlePromiseError } from '$lib/utils'; +import { + createActivity, + deleteActivity, + getActivities, + getActivityStatistics, + ReactionLevel, + ReactionType, + type ActivityCreateDto, + type ActivityResponseDto, +} from '@immich/sdk'; +import { get } from 'svelte/store'; + +class ActivityManager { + #albumId = $state<string | undefined>(); + #assetId = $state<string | undefined>(); + #activities = $state<ActivityResponseDto[]>([]); + #commentCount = $state(0); + #isLiked = $state<ActivityResponseDto | null>(null); + + get activities() { + return this.#activities; + } + + get commentCount() { + return this.#commentCount; + } + + get isLiked() { + return this.#isLiked; + } + + init(albumId: string, assetId?: string) { + this.#albumId = albumId; + this.#assetId = assetId; + } + + async addActivity(dto: ActivityCreateDto) { + if (this.#albumId === undefined) { + return; + } + + const activity = await createActivity({ activityCreateDto: dto }); + this.#activities = [...this.#activities, activity]; + + if (activity.type === ReactionType.Comment) { + this.#commentCount++; + } + + handlePromiseError(this.refreshActivities(this.#albumId, this.#assetId)); + return activity; + } + + async deleteActivity(activity: ActivityResponseDto, index?: number) { + if (!this.#albumId) { + return; + } + + if (activity.type === ReactionType.Comment) { + this.#commentCount--; + } + + this.#activities = index + ? this.#activities.splice(index, 1) + : this.#activities.filter(({ id }) => id !== activity.id); + + await deleteActivity({ id: activity.id }); + handlePromiseError(this.refreshActivities(this.#albumId, this.#assetId)); + } + + async toggleLike() { + if (!this.#albumId) { + return; + } + + if (this.#isLiked) { + await this.deleteActivity(this.#isLiked); + this.#isLiked = null; + } else { + this.#isLiked = (await this.addActivity({ + albumId: this.#albumId, + assetId: this.#assetId, + type: ReactionType.Like, + }))!; + } + } + + async refreshActivities(albumId: string, assetId?: string) { + this.#activities = await getActivities({ albumId, assetId }); + + const [liked] = await getActivities({ + albumId, + assetId, + userId: get(user).id, + $type: ReactionType.Like, + level: assetId ? undefined : ReactionLevel.Album, + }); + this.#isLiked = liked ?? null; + + const { comments } = await getActivityStatistics({ albumId, assetId }); + this.#commentCount = comments; + } + + reset() { + this.#albumId = undefined; + this.#assetId = undefined; + this.#activities = []; + this.#commentCount = 0; + } +} + +export const activityManager = new ActivityManager(); diff --git a/web/src/lib/managers/auth-manager.svelte.ts b/web/src/lib/managers/auth-manager.svelte.ts new file mode 100644 index 0000000000..9e8e30b773 --- /dev/null +++ b/web/src/lib/managers/auth-manager.svelte.ts @@ -0,0 +1,37 @@ +import { goto } from '$app/navigation'; +import { page } from '$app/state'; +import { AppRoute } from '$lib/constants'; +import { eventManager } from '$lib/managers/event-manager.svelte'; +import { isSharedLinkRoute } from '$lib/utils/navigation'; +import { logout } from '@immich/sdk'; + +class AuthManager { + key = $derived(isSharedLinkRoute(page.route?.id) ? page.params.key : undefined); + + async logout() { + let redirectUri; + + try { + const response = await logout(); + if (response.redirectUri) { + redirectUri = response.redirectUri; + } + } catch (error) { + console.log('Error logging out:', error); + } + + redirectUri = redirectUri ?? AppRoute.AUTH_LOGIN; + + try { + if (redirectUri.startsWith('/')) { + await goto(redirectUri); + } else { + globalThis.location.href = redirectUri; + } + } finally { + eventManager.emit('auth.logout'); + } + } +} + +export const authManager = new AuthManager(); diff --git a/web/src/lib/stores/download-store.svelte.ts b/web/src/lib/managers/download-manager.svelte.ts similarity index 74% rename from web/src/lib/stores/download-store.svelte.ts rename to web/src/lib/managers/download-manager.svelte.ts index 8c03671e73..107f80b8dc 100644 --- a/web/src/lib/stores/download-store.svelte.ts +++ b/web/src/lib/managers/download-manager.svelte.ts @@ -5,7 +5,7 @@ export interface DownloadProgress { abort: AbortController | null; } -class DownloadStore { +class DownloadManager { assets = $state<Record<string, DownloadProgress>>({}); isDownloading = $derived(Object.keys(this.assets).length > 0); @@ -42,10 +42,4 @@ class DownloadStore { } } -export const downloadStore = new DownloadStore(); - -export const downloadManager = { - add: (key: string, total: number, abort?: AbortController) => downloadStore.add(key, total, abort), - clear: (key: string) => downloadStore.clear(key), - update: (key: string, progress: number, total?: number) => downloadStore.update(key, progress, total), -}; +export const downloadManager = new DownloadManager(); diff --git a/web/src/lib/managers/event-manager.svelte.ts b/web/src/lib/managers/event-manager.svelte.ts new file mode 100644 index 0000000000..f8e39411cf --- /dev/null +++ b/web/src/lib/managers/event-manager.svelte.ts @@ -0,0 +1,61 @@ +import type { ThemeSetting } from '$lib/managers/theme-manager.svelte'; +import type { LoginResponseDto } from '@immich/sdk'; + +type Listener<EventMap extends Record<string, unknown[]>, K extends keyof EventMap> = (...params: EventMap[K]) => void; + +class EventManager<EventMap extends Record<string, unknown[]>> { + private listeners: { + [K in keyof EventMap]?: { + listener: Listener<EventMap, K>; + once?: boolean; + }[]; + } = {}; + + on<T extends keyof EventMap>(key: T, listener: (...params: EventMap[T]) => void) { + return this.addListener(key, listener, false); + } + + once<T extends keyof EventMap>(key: T, listener: (...params: EventMap[T]) => void) { + return this.addListener(key, listener, true); + } + + off<K extends keyof EventMap>(key: K, listener: Listener<EventMap, K>) { + if (this.listeners[key]) { + this.listeners[key] = this.listeners[key].filter((item) => item.listener !== listener); + } + + return this; + } + + emit<T extends keyof EventMap>(key: T, ...params: EventMap[T]) { + if (!this.listeners[key]) { + return; + } + + for (const { listener } of this.listeners[key]) { + listener(...params); + } + + // remove one time listeners + this.listeners[key] = this.listeners[key].filter((item) => !item.once); + } + + private addListener<T extends keyof EventMap>(key: T, listener: (...params: EventMap[T]) => void, once: boolean) { + if (!this.listeners[key]) { + this.listeners[key] = []; + } + + this.listeners[key].push({ listener, once }); + + return this; + } +} + +export const eventManager = new EventManager<{ + 'app.init': []; + 'user.login': []; + 'auth.login': [LoginResponseDto]; + 'auth.logout': []; + 'language.change': [{ name: string; code: string; rtl?: boolean }]; + 'theme.change': [ThemeSetting]; +}>(); diff --git a/web/src/lib/managers/language-manager.svelte.ts b/web/src/lib/managers/language-manager.svelte.ts new file mode 100644 index 0000000000..5acae27aa3 --- /dev/null +++ b/web/src/lib/managers/language-manager.svelte.ts @@ -0,0 +1,26 @@ +import { langs } from '$lib/constants'; +import { eventManager } from '$lib/managers/event-manager.svelte'; +import { lang } from '$lib/stores/preferences.store'; + +class LanguageManager { + constructor() { + eventManager.on('app.init', () => lang.subscribe((lang) => this.setLanguage(lang))); + } + + rtl = $state(false); + + setLanguage(code: string) { + const item = langs.find((item) => item.code === code); + if (!item) { + return; + } + + this.rtl = item.rtl ?? false; + + document.body.setAttribute('dir', item.rtl ? 'rtl' : 'ltr'); + + eventManager.emit('language.change', item); + } +} + +export const languageManager = new LanguageManager(); diff --git a/web/src/lib/managers/theme-manager.svelte.ts b/web/src/lib/managers/theme-manager.svelte.ts new file mode 100644 index 0000000000..a20e5f9a98 --- /dev/null +++ b/web/src/lib/managers/theme-manager.svelte.ts @@ -0,0 +1,78 @@ +import { browser } from '$app/environment'; +import { Theme } from '$lib/constants'; +import { eventManager } from '$lib/managers/event-manager.svelte'; +import { PersistedLocalStorage } from '$lib/utils/persisted'; + +export interface ThemeSetting { + value: Theme; + system: boolean; +} + +const getDefaultTheme = () => { + if (!browser) { + return Theme.DARK; + } + + return globalThis.matchMedia('(prefers-color-scheme: dark)').matches ? Theme.DARK : Theme.LIGHT; +}; + +class ThemeManager { + #theme = new PersistedLocalStorage<ThemeSetting>( + 'color-theme', + { value: getDefaultTheme(), system: false }, + { + valid: (value): value is ThemeSetting => { + return Object.values(Theme).includes((value as ThemeSetting)?.value); + }, + }, + ); + + get theme() { + return this.#theme.current; + } + + value = $derived(this.theme.value); + + isDark = $derived(this.value === Theme.DARK); + + constructor() { + eventManager.on('app.init', () => this.#onAppInit()); + } + + setSystem(system: boolean) { + this.#update(system ? 'system' : getDefaultTheme()); + } + + setTheme(theme: Theme) { + this.#update(theme); + } + + toggleTheme() { + this.#update(this.value === Theme.DARK ? Theme.LIGHT : Theme.DARK); + } + + #onAppInit() { + globalThis.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { + if (this.theme.system) { + this.#update('system'); + } + }); + } + + #update(value: Theme | 'system') { + 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'); + } + + this.#theme.current = theme; + + eventManager.emit('theme.change', theme); + } +} + +export const themeManager = new ThemeManager(); diff --git a/web/src/lib/stores/activity.store.ts b/web/src/lib/stores/activity.store.ts deleted file mode 100644 index 897f835063..0000000000 --- a/web/src/lib/stores/activity.store.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { writable } from 'svelte/store'; - -export const numberOfComments = writable<number>(0); - -export const setNumberOfComments = (number: number) => { - numberOfComments.set(number); -}; - -export const updateNumberOfComments = (addOrRemove: 1 | -1) => { - numberOfComments.update((n) => n + addOrRemove); -}; diff --git a/web/src/lib/stores/asset-interaction.svelte.ts b/web/src/lib/stores/asset-interaction.svelte.ts index 39bf9968c9..bcf37c6e43 100644 --- a/web/src/lib/stores/asset-interaction.svelte.ts +++ b/web/src/lib/stores/asset-interaction.svelte.ts @@ -14,7 +14,6 @@ export class AssetInteraction { return this.assetSelectionCandidates.some((asset) => asset.id === assetId); } assetSelectionStart = $state<AssetResponseDto | null>(null); - focussedAssetId = $state<string | null>(null); selectionActive = $derived(this.selectedAssets.length > 0); private user = fromStore<UserAdminResponseDto | undefined>(user); @@ -73,8 +72,4 @@ export class AssetInteraction { this.assetSelectionCandidates = []; this.assetSelectionStart = null; } - - isFocussedAsset(assetId: string) { - return this.focussedAssetId === assetId; - } } diff --git a/web/src/lib/stores/asset-viewing.store.ts b/web/src/lib/stores/asset-viewing.store.ts index 689556b522..98b9e99aa2 100644 --- a/web/src/lib/stores/asset-viewing.store.ts +++ b/web/src/lib/stores/asset-viewing.store.ts @@ -1,4 +1,4 @@ -import { getKey } from '$lib/utils'; +import { authManager } from '$lib/managers/auth-manager.svelte'; import { type AssetGridRouteSearchParams } from '$lib/utils/navigation'; import { getAssetInfo, type AssetResponseDto } from '@immich/sdk'; import { readonly, writable } from 'svelte/store'; @@ -16,7 +16,7 @@ function createAssetViewingStore() { }; const setAssetId = async (id: string): Promise<AssetResponseDto> => { - const asset = await getAssetInfo({ id, key: getKey() }); + const asset = await getAssetInfo({ id, key: authManager.key }); setAsset(asset); return asset; }; diff --git a/web/src/lib/stores/assets-store.spec.ts b/web/src/lib/stores/assets-store.spec.ts index 0685103a1b..3d0292bde8 100644 --- a/web/src/lib/stores/assets-store.spec.ts +++ b/web/src/lib/stores/assets-store.spec.ts @@ -48,15 +48,15 @@ describe('AssetStore', () => { expect(plainBuckets).toEqual( expect.arrayContaining([ - expect.objectContaining({ bucketDate: '2024-03-01T00:00:00.000Z', bucketHeight: 304 }), - expect.objectContaining({ bucketDate: '2024-02-01T00:00:00.000Z', bucketHeight: 4515.333_333_333_333 }), + expect.objectContaining({ bucketDate: '2024-03-01T00:00:00.000Z', bucketHeight: 303 }), + expect.objectContaining({ bucketDate: '2024-02-01T00:00:00.000Z', bucketHeight: 4514.333_333_333_333 }), expect.objectContaining({ bucketDate: '2024-01-01T00:00:00.000Z', bucketHeight: 286 }), ]), ); }); it('calculates timeline height', () => { - expect(assetStore.timelineHeight).toBe(5105.333_333_333_333); + expect(assetStore.timelineHeight).toBe(5103.333_333_333_333); }); }); diff --git a/web/src/lib/stores/assets-store.svelte.ts b/web/src/lib/stores/assets-store.svelte.ts index f523406a31..b4b4a4ade2 100644 --- a/web/src/lib/stores/assets-store.svelte.ts +++ b/web/src/lib/stores/assets-store.svelte.ts @@ -1,5 +1,4 @@ import { locale } from '$lib/stores/preferences.store'; -import { getKey } from '$lib/utils'; import { CancellableTask } from '$lib/utils/cancellable-task'; import { getJustifiedLayoutFromAssets, @@ -21,6 +20,7 @@ import { clamp, debounce, isEqual, throttle } from 'lodash-es'; import { DateTime } from 'luxon'; import { t } from 'svelte-i18n'; +import { authManager } from '$lib/managers/auth-manager.svelte'; import { SvelteSet } from 'svelte/reactivity'; import { get, writable, type Unsubscriber } from 'svelte/store'; import { handleError } from '../utils/handle-error'; @@ -35,9 +35,7 @@ export type AssetStoreOptions = Omit<AssetApiGetTimeBucketsRequest, 'size'> & { timelineAlbumId?: string; deferInit?: boolean; }; -export type AssetStoreLayoutOptions = { - rowHeight: number; -}; + // eslint-disable-next-line @typescript-eslint/no-explicit-any function updateObject(target: any, source: any): boolean { if (!target) { @@ -110,7 +108,6 @@ export class AssetDateGroup { readonly date: DateTime; readonly dayOfMonth: number; intersetingAssets: IntersectingAsset[] = $state([]); - dodo: IntersectingAsset[] = $state([]); height = $state(0); width = $state(0); @@ -121,6 +118,7 @@ export class AssetDateGroup { left: number = $state(0); row = $state(0); col = $state(0); + deferredLayout = false; constructor(bucket: AssetBucket, index: number, date: DateTime, dayOfMonth: number) { this.index = index; @@ -195,6 +193,10 @@ export class AssetDateGroup { } layout(options: CommonLayoutOptions) { + if (!this.bucket.intersecting) { + this.deferredLayout = true; + return; + } const assets = this.intersetingAssets.map((intersetingAsset) => intersetingAsset.asset!); const geometry = getJustifiedLayoutFromAssets(assets, options); this.width = geometry.containerWidth; @@ -547,6 +549,11 @@ export type LiteBucket = { bucketDateFormattted: string; }; +type AssetStoreLayoutOptions = { + rowHeight?: number; + headerHeight?: number; + gap?: number; +}; export class AssetStore { // --- public ---- isInitialized = $state(false); @@ -596,7 +603,7 @@ export class AssetStore { #unsubscribers: Unsubscriber[] = []; #rowHeight = $state(235); - #headerHeight = $state(49); + #headerHeight = $state(48); #gap = $state(12); #options: AssetStoreOptions = AssetStore.#INIT_OPTIONS; @@ -608,36 +615,46 @@ export class AssetStore { constructor() {} - set headerHeight(value) { + setLayoutOptions({ headerHeight = 48, rowHeight = 235, gap = 12 }: AssetStoreLayoutOptions) { + let changed = false; + changed ||= this.#setHeaderHeight(headerHeight); + changed ||= this.#setGap(gap); + changed ||= this.#setRowHeight(rowHeight); + if (changed) { + this.refreshLayout(); + } + } + + #setHeaderHeight(value: number) { if (this.#headerHeight == value) { - return; + return false; } this.#headerHeight = value; - this.refreshLayout(); + return true; } get headerHeight() { return this.#headerHeight; } - set gap(value) { + #setGap(value: number) { if (this.#gap == value) { - return; + return false; } this.#gap = value; - this.refreshLayout(); + return true; } get gap() { return this.#gap; } - set rowHeight(value) { + #setRowHeight(value: number) { if (this.#rowHeight == value) { - return; + return false; } this.#rowHeight = value; - this.refreshLayout(); + return true; } get rowHeight() { @@ -815,6 +832,15 @@ export class AssetStore { } bucket.intersecting = actuallyIntersecting || preIntersecting; bucket.actuallyIntersecting = actuallyIntersecting; + if (preIntersecting || actuallyIntersecting) { + const hasDeferred = bucket.dateGroups.some((group) => group.deferredLayout); + if (hasDeferred) { + this.#updateGeometry(bucket, true); + for (const group of bucket.dateGroups) { + group.deferredLayout = false; + } + } + } } #processPendingChanges = throttle(() => { @@ -839,7 +865,7 @@ export class AssetStore { const timebuckets = await getTimeBuckets({ ...this.#options, size: TimeBucketSize.Month, - key: getKey(), + key: authManager.key, }); this.buckets = timebuckets.map((bucket) => { @@ -1047,7 +1073,7 @@ export class AssetStore { ...this.#options, timeBucket: bucketDate, size: TimeBucketSize.Month, - key: getKey(), + key: authManager.key, }, { signal }, ); @@ -1058,7 +1084,7 @@ export class AssetStore { albumId: this.#options.timelineAlbumId, timeBucket: bucketDate, size: TimeBucketSize.Month, - key: getKey(), + key: authManager.key, }, { signal }, ); diff --git a/web/src/lib/stores/folders.svelte.ts b/web/src/lib/stores/folders.svelte.ts index fb59687a38..6f3eb8b66a 100644 --- a/web/src/lib/stores/folders.svelte.ts +++ b/web/src/lib/stores/folders.svelte.ts @@ -1,3 +1,4 @@ +import { eventManager } from '$lib/managers/event-manager.svelte'; import { getAssetsByOriginalPath, getUniqueOriginalPaths, @@ -16,6 +17,10 @@ class FoldersStore { uniquePaths = $state<string[]>([]); assets = $state<AssetCache>({}); + constructor() { + eventManager.on('auth.logout', () => this.clearCache()); + } + async fetchUniquePaths() { if (this.initialized) { return; diff --git a/web/src/lib/stores/memory.store.svelte.ts b/web/src/lib/stores/memory.store.svelte.ts index 7173b43d06..9ef3de4328 100644 --- a/web/src/lib/stores/memory.store.svelte.ts +++ b/web/src/lib/stores/memory.store.svelte.ts @@ -1,3 +1,4 @@ +import { eventManager } from '$lib/managers/event-manager.svelte'; import { asLocalTimeISO } from '$lib/utils/date-time'; import { type AssetResponseDto, @@ -24,6 +25,10 @@ export type MemoryAsset = MemoryIndex & { }; class MemoryStoreSvelte { + constructor() { + eventManager.on('auth.logout', () => this.clearCache()); + } + memories = $state<MemoryResponseDto[]>([]); private initialized = false; private memoryAssets = $derived.by(() => { diff --git a/web/src/lib/stores/notification-manager.svelte.ts b/web/src/lib/stores/notification-manager.svelte.ts new file mode 100644 index 0000000000..3eba15deed --- /dev/null +++ b/web/src/lib/stores/notification-manager.svelte.ts @@ -0,0 +1,41 @@ +import { eventManager } from '$lib/managers/event-manager.svelte'; +import { handlePromiseError } from '$lib/utils'; +import { handleError } from '$lib/utils/handle-error'; +import { getNotifications, updateNotification, updateNotifications, type NotificationDto } from '@immich/sdk'; +import { t } from 'svelte-i18n'; +import { get } from 'svelte/store'; + +class NotificationStore { + notifications = $state<NotificationDto[]>([]); + + constructor() { + eventManager.on('auth.login', () => handlePromiseError(this.refresh())); + eventManager.on('auth.logout', () => this.clear()); + } + + async refresh() { + try { + this.notifications = await getNotifications({ unread: true }); + } catch (error) { + const translate = get(t); + handleError(error, translate('errors.failed_to_load_notifications')); + } + } + + markAsRead = async (id: string) => { + this.notifications = this.notifications.filter((notification) => notification.id !== id); + await updateNotification({ id, notificationUpdateDto: { readAt: new Date().toISOString() } }); + }; + + markAllAsRead = async () => { + const ids = this.notifications.map(({ id }) => id); + this.notifications = []; + await updateNotifications({ notificationUpdateAllDto: { ids, readAt: new Date().toISOString() } }); + }; + + clear = () => { + this.notifications = []; + }; +} + +export const notificationManager = new NotificationStore(); diff --git a/web/src/lib/stores/preferences.store.ts b/web/src/lib/stores/preferences.store.ts index e268e8817d..e7f38eb6d0 100644 --- a/web/src/lib/stores/preferences.store.ts +++ b/web/src/lib/stores/preferences.store.ts @@ -2,39 +2,12 @@ import { browser } from '$app/environment'; import { Theme, defaultLang } from '$lib/constants'; import { getPreferredLocale } from '$lib/utils/i18n'; import { persisted } from 'svelte-persisted-store'; -import { get } from 'svelte/store'; export interface ThemeSetting { value: Theme; system: boolean; } -export const handleToggleTheme = () => { - const theme = get(colorTheme); - theme.value = theme.value === Theme.DARK ? Theme.LIGHT : Theme.DARK; - colorTheme.set(theme); -}; - -const initTheme = (): ThemeSetting => { - if (browser && globalThis.matchMedia && !globalThis.matchMedia('(prefers-color-scheme: dark)').matches) { - return { value: Theme.LIGHT, system: false }; - } - return { value: Theme.DARK, system: false }; -}; - -const initialTheme = initTheme(); - -// The 'color-theme' key is also used by app.html to prevent FOUC on page load. -export const colorTheme = persisted<ThemeSetting>('color-theme', initialTheme, { - serializer: { - parse: (text: string): ThemeSetting => { - const parsedText: ThemeSetting = JSON.parse(text); - return Object.values(Theme).includes(parsedText.value) ? parsedText : initTheme(); - }, - stringify: (object) => JSON.stringify(object), - }, -}); - // Locale to use for formatting dates, numbers, etc. export const locale = persisted<string | undefined>('locale', undefined, { serializer: { diff --git a/web/src/lib/stores/search.svelte.ts b/web/src/lib/stores/search.svelte.ts index 7d012922ca..32f2741955 100644 --- a/web/src/lib/stores/search.svelte.ts +++ b/web/src/lib/stores/search.svelte.ts @@ -1,7 +1,13 @@ +import { eventManager } from '$lib/managers/event-manager.svelte'; + class SearchStore { savedSearchTerms = $state<string[]>([]); isSearchEnabled = $state(false); + constructor() { + eventManager.on('auth.logout', () => this.clearCache()); + } + clearCache() { this.savedSearchTerms = []; this.isSearchEnabled = false; diff --git a/web/src/lib/stores/user.store.ts b/web/src/lib/stores/user.store.ts index 5bffc08b80..0790788278 100644 --- a/web/src/lib/stores/user.store.ts +++ b/web/src/lib/stores/user.store.ts @@ -1,3 +1,4 @@ +import { eventManager } from '$lib/managers/event-manager.svelte'; import { purchaseStore } from '$lib/stores/purchase.store'; import { type UserAdminResponseDto, type UserPreferencesResponseDto } from '@immich/sdk'; import { writable } from 'svelte/store'; @@ -14,3 +15,5 @@ export const resetSavedUser = () => { preferences.set(undefined as unknown as UserPreferencesResponseDto); purchaseStore.setPurchaseStatus(false); }; + +eventManager.on('auth.logout', () => resetSavedUser()); diff --git a/web/src/lib/stores/user.svelte.ts b/web/src/lib/stores/user.svelte.ts index 71b2cdd847..94d73efb9c 100644 --- a/web/src/lib/stores/user.svelte.ts +++ b/web/src/lib/stores/user.svelte.ts @@ -1,3 +1,4 @@ +import { eventManager } from '$lib/managers/event-manager.svelte'; import type { AlbumResponseDto, ServerAboutResponseDto, @@ -19,8 +20,10 @@ const defaultUserInteraction: UserInteractions = { serverInfo: undefined, }; -export const resetUserInteraction = () => { +export const userInteraction = $state<UserInteractions>(defaultUserInteraction); + +const reset = () => { Object.assign(userInteraction, defaultUserInteraction); }; -export const userInteraction = $state<UserInteractions>(defaultUserInteraction); +eventManager.on('auth.logout', () => reset()); diff --git a/web/src/lib/stores/websocket.ts b/web/src/lib/stores/websocket.ts index d398ca52a9..6e896e8000 100644 --- a/web/src/lib/stores/websocket.ts +++ b/web/src/lib/stores/websocket.ts @@ -1,7 +1,7 @@ -import { AppRoute } from '$lib/constants'; -import { handleLogout } from '$lib/utils/auth'; +import { authManager } from '$lib/managers/auth-manager.svelte'; +import { notificationManager } from '$lib/stores/notification-manager.svelte'; import { createEventEmitter } from '$lib/utils/eventemitter'; -import type { AssetResponseDto, ServerVersionResponseDto } from '@immich/sdk'; +import { type AssetResponseDto, type NotificationDto, type ServerVersionResponseDto } from '@immich/sdk'; import { io, type Socket } from 'socket.io-client'; import { get, writable } from 'svelte/store'; import { user } from './user.store'; @@ -27,6 +27,7 @@ export interface Events { on_config_update: () => void; on_new_release: (newRelase: ReleaseEvent) => void; on_session_delete: (sessionId: string) => void; + on_notification: (notification: NotificationDto) => void; } const websocket: Socket<Events> = io({ @@ -50,7 +51,8 @@ websocket .on('disconnect', () => websocketStore.connected.set(false)) .on('on_server_version', (serverVersion) => websocketStore.serverVersion.set(serverVersion)) .on('on_new_release', (releaseVersion) => websocketStore.release.set(releaseVersion)) - .on('on_session_delete', () => handleLogout(AppRoute.AUTH_LOGIN)) + .on('on_session_delete', () => authManager.logout()) + .on('on_notification', () => notificationManager.refresh()) .on('connect_error', (e) => console.log('Websocket Connect Error', e)); export const openWebsocketConnection = () => { diff --git a/web/src/lib/utils.ts b/web/src/lib/utils.ts index 7d542a940a..b7466b5812 100644 --- a/web/src/lib/utils.ts +++ b/web/src/lib/utils.ts @@ -1,5 +1,6 @@ import { NotificationType, notificationController } from '$lib/components/shared-components/notification/notification'; import { defaultLang, langs, locales } from '$lib/constants'; +import { authManager } from '$lib/managers/auth-manager.svelte'; import { lang } from '$lib/stores/preferences.store'; import { handleError } from '$lib/utils/handle-error'; import { @@ -155,18 +156,11 @@ export const getJobName = derived(t, ($t) => { }; }); -let _key: string | undefined; let _sharedLink: SharedLinkResponseDto | undefined; -export const setKey = (key?: string) => (_key = key); -export const getKey = (): string | undefined => _key; export const setSharedLink = (sharedLink: SharedLinkResponseDto) => (_sharedLink = sharedLink); export const getSharedLink = (): SharedLinkResponseDto | undefined => _sharedLink; -export const isSharedLink = () => { - return !!_key; -}; - const createUrl = (path: string, parameters?: Record<string, unknown>) => { const searchParameters = new URLSearchParams(); for (const key in parameters) { @@ -189,7 +183,7 @@ export const getAssetOriginalUrl = (options: string | AssetUrlOptions) => { options = { id: options }; } const { id, cacheKey } = options; - return createUrl(getAssetOriginalPath(id), { key: getKey(), c: cacheKey }); + return createUrl(getAssetOriginalPath(id), { key: authManager.key, c: cacheKey }); }; export const getAssetThumbnailUrl = (options: string | (AssetUrlOptions & { size?: AssetMediaSize })) => { @@ -197,7 +191,7 @@ export const getAssetThumbnailUrl = (options: string | (AssetUrlOptions & { size options = { id: options }; } const { id, size, cacheKey } = options; - return createUrl(getAssetThumbnailPath(id), { size, key: getKey(), c: cacheKey }); + return createUrl(getAssetThumbnailPath(id), { size, key: authManager.key, c: cacheKey }); }; export const getAssetPlaybackUrl = (options: string | AssetUrlOptions) => { @@ -205,7 +199,7 @@ export const getAssetPlaybackUrl = (options: string | AssetUrlOptions) => { options = { id: options }; } const { id, cacheKey } = options; - return createUrl(getAssetPlaybackPath(id), { key: getKey(), c: cacheKey }); + return createUrl(getAssetPlaybackPath(id), { key: authManager.key, c: cacheKey }); }; export const getProfileImageUrl = (user: UserResponseDto) => diff --git a/web/src/lib/utils/album-utils.ts b/web/src/lib/utils/album-utils.ts index 74a2e3c5e9..a5b830774c 100644 --- a/web/src/lib/utils/album-utils.ts +++ b/web/src/lib/utils/album-utils.ts @@ -59,7 +59,7 @@ export const sortOptionsMetadata: AlbumSortOptionMetadata[] = [ { id: AlbumSortBy.Title, defaultOrder: SortOrder.Asc, - columnStyle: 'text-left w-8/12 sm:w-4/12 md:w-4/12 md:w-4/12 xl:w-[30%] 2xl:w-[40%]', + columnStyle: 'text-start w-8/12 sm:w-4/12 md:w-4/12 xl:w-[30%] 2xl:w-[40%]', }, { id: AlbumSortBy.ItemCount, diff --git a/web/src/lib/utils/asset-utils.ts b/web/src/lib/utils/asset-utils.ts index bd3cb416b5..254b90f08d 100644 --- a/web/src/lib/utils/asset-utils.ts +++ b/web/src/lib/utils/asset-utils.ts @@ -3,11 +3,12 @@ import FormatBoldMessage from '$lib/components/i18n/format-bold-message.svelte'; import type { InterpolationValues } from '$lib/components/i18n/format-message'; import { NotificationType, notificationController } from '$lib/components/shared-components/notification/notification'; import { AppRoute } from '$lib/constants'; +import { authManager } from '$lib/managers/auth-manager.svelte'; +import { downloadManager } from '$lib/managers/download-manager.svelte'; import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte'; import { assetsSnapshot, isSelectingAllAssets, type AssetStore } from '$lib/stores/assets-store.svelte'; -import { downloadManager } from '$lib/stores/download-store.svelte'; import { preferences } from '$lib/stores/user.store'; -import { downloadRequest, getKey, withError } from '$lib/utils'; +import { downloadRequest, withError } from '$lib/utils'; import { createAlbum } from '$lib/utils/album-utils'; import { getByteUnitString } from '$lib/utils/byte-units'; import { getFormatter } from '$lib/utils/i18n'; @@ -44,7 +45,7 @@ export const addAssetsToAlbum = async (albumId: string, assetIds: string[], show bulkIdsDto: { ids: assetIds, }, - key: getKey(), + key: authManager.key, }); const count = result.filter(({ success }) => success).length; const $t = get(t); @@ -178,7 +179,7 @@ export const downloadArchive = async (fileName: string, options: Omit<DownloadIn const $preferences = get<UserPreferencesResponseDto | undefined>(preferences); const dto = { ...options, archiveSize: $preferences?.download.archiveSize }; - const [error, downloadInfo] = await withError(() => getDownloadInfo({ downloadInfoDto: dto, key: getKey() })); + const [error, downloadInfo] = await withError(() => getDownloadInfo({ downloadInfoDto: dto, key: authManager.key })); if (error) { const $t = get(t); handleError(error, $t('errors.unable_to_download_files')); @@ -193,7 +194,7 @@ export const downloadArchive = async (fileName: string, options: Omit<DownloadIn const archive = downloadInfo.archives[index]; const suffix = downloadInfo.archives.length > 1 ? `+${index + 1}` : ''; const archiveName = fileName.replace('.zip', `${suffix}-${DateTime.now().toFormat('yyyyLLdd_HHmmss')}.zip`); - const key = getKey(); + const key = authManager.key; let downloadKey = `${archiveName} `; if (downloadInfo.archives.length > 1) { @@ -240,7 +241,7 @@ export const downloadFile = async (asset: AssetResponseDto) => { }; if (asset.livePhotoVideoId) { - const motionAsset = await getAssetInfo({ id: asset.livePhotoVideoId, key: getKey() }); + const motionAsset = await getAssetInfo({ id: asset.livePhotoVideoId, key: authManager.key }); if (!isAndroidMotionVideo(motionAsset) || get(preferences)?.download.includeEmbeddedVideos) { assets.push({ filename: motionAsset.originalFileName, @@ -252,7 +253,7 @@ export const downloadFile = async (asset: AssetResponseDto) => { for (const { filename, id } of assets) { try { - const key = getKey(); + const key = authManager.key; notificationController.show({ type: NotificationType.Info, diff --git a/web/src/lib/utils/auth.ts b/web/src/lib/utils/auth.ts index 22b92dd988..9b78c345e2 100644 --- a/web/src/lib/utils/auth.ts +++ b/web/src/lib/utils/auth.ts @@ -1,11 +1,7 @@ import { browser } from '$app/environment'; -import { goto } from '$app/navigation'; -import { foldersStore } from '$lib/stores/folders.svelte'; -import { memoryStore } from '$lib/stores/memory.store.svelte'; import { purchaseStore } from '$lib/stores/purchase.store'; -import { searchStore } from '$lib/stores/search.svelte'; -import { preferences as preferences$, resetSavedUser, user as user$ } from '$lib/stores/user.store'; -import { resetUserInteraction, userInteraction } from '$lib/stores/user.svelte'; +import { preferences as preferences$, user as user$ } from '$lib/stores/user.store'; +import { userInteraction } from '$lib/stores/user.svelte'; import { getAboutInfo, getMyPreferences, getMyUser, getStorage } from '@immich/sdk'; import { redirect } from '@sveltejs/kit'; import { DateTime } from 'luxon'; @@ -91,19 +87,3 @@ export const getAccountAge = (): number => { return Number(accountAge); }; - -export const handleLogout = async (redirectUri: string) => { - try { - if (redirectUri.startsWith('/')) { - await goto(redirectUri); - } else { - globalThis.location.href = redirectUri; - } - } finally { - resetSavedUser(); - resetUserInteraction(); - foldersStore.clearCache(); - memoryStore.clearCache(); - searchStore.clearCache(); - } -}; diff --git a/web/src/lib/utils/byte-units.ts b/web/src/lib/utils/byte-units.ts index dae44009e2..218e22f671 100644 --- a/web/src/lib/utils/byte-units.ts +++ b/web/src/lib/utils/byte-units.ts @@ -34,6 +34,7 @@ export function getBytesWithUnit(bytes: number, maxPrecision = 1): [number, Byte * * de: `1,5 KiB` * * @param bytes number of bytes + * @param locale locale to use, default is `navigator.language` * @param maxPrecision maximum number of decimal places, default is `1` * @returns localized bytes with unit as string */ diff --git a/web/src/lib/utils/file-uploader.ts b/web/src/lib/utils/file-uploader.ts index 04d4d78810..db43e92f94 100644 --- a/web/src/lib/utils/file-uploader.ts +++ b/web/src/lib/utils/file-uploader.ts @@ -1,6 +1,7 @@ +import { authManager } from '$lib/managers/auth-manager.svelte'; import { UploadState } from '$lib/models/upload-asset'; import { uploadAssetsStore } from '$lib/stores/upload'; -import { getKey, uploadRequest } from '$lib/utils'; +import { uploadRequest } from '$lib/utils'; import { addAssetsToAlbum } from '$lib/utils/asset-utils'; import { ExecutorQueue } from '$lib/utils/executor-queue'; import { @@ -134,7 +135,7 @@ async function fileUploader( } let responseData: { id: string; status: AssetMediaStatus; isTrashed?: boolean } | undefined; - const key = getKey(); + const key = authManager.key; if (crypto?.subtle?.digest && !key) { uploadAssetsStore.updateItem(deviceAssetId, { message: $t('asset_hashing') }); await tick(); diff --git a/web/src/lib/utils/focus-util.ts b/web/src/lib/utils/focus-util.ts index 8ad774f7ac..c95ed3f31d 100644 --- a/web/src/lib/utils/focus-util.ts +++ b/web/src/lib/utils/focus-util.ts @@ -1,4 +1,39 @@ -const selectors = - 'button:not([disabled], .hidden), [href]:not(.hidden), input:not([disabled], .hidden), select:not([disabled], .hidden), textarea:not([disabled], .hidden), [tabindex]:not([tabindex="-1"], .hidden)'; +import { focusable, isTabbable, tabbable, type CheckOptions, type TabbableOptions } from 'tabbable'; -export const getFocusable = (container: ParentNode) => [...container.querySelectorAll<HTMLElement>(selectors)]; +type TabbableOpts = TabbableOptions & CheckOptions; +let defaultOpts: TabbableOpts = { + includeContainer: false, +}; + +export const setDefaultTabbleOptions = (options: TabbableOpts) => { + defaultOpts = options; +}; + +export const getTabbable = (container: Element, includeContainer: boolean = false) => + tabbable(container, { ...defaultOpts, includeContainer }); + +export const focusNext = (selector: (element: HTMLElement | SVGElement) => boolean, forwardDirection: boolean) => { + const focusElements = focusable(document.body, { includeContainer: true }); + const current = document.activeElement as HTMLElement; + const index = focusElements.indexOf(current); + if (index === -1) { + for (const element of focusElements) { + if (selector(element)) { + element.focus(); + return; + } + } + focusElements[0].focus(); + return; + } + const totalElements = focusElements.length; + let i = index; + do { + i = (i + (forwardDirection ? 1 : -1) + totalElements) % totalElements; + const next = focusElements[i]; + if (isTabbable(next) && selector(next)) { + next.focus(); + break; + } + } while (i !== index); +}; diff --git a/web/src/lib/utils/persisted.ts b/web/src/lib/utils/persisted.ts new file mode 100644 index 0000000000..73eb4de5db --- /dev/null +++ b/web/src/lib/utils/persisted.ts @@ -0,0 +1,81 @@ +import { browser } from '$app/environment'; +import { createSubscriber } from 'svelte/reactivity'; + +type PersistedBaseOptions<T> = { + read: (key: string) => T | undefined; + write: (key: string, value: T) => void; +}; + +class PersistedBase<T> { + #value: T; + #subscribe: () => void; + #update = () => {}; + + #write: (value: T) => void; + + get current() { + this.#subscribe(); + return this.#value as T; + } + + set current(value: T) { + this.#write(value); + this.#update(); + this.#value = value; + } + + constructor(key: string, defaultValue: T, options: PersistedBaseOptions<T>) { + const value = options.read(key); + + this.#value = value === undefined ? defaultValue : value; + this.#write = (value: T) => options.write(key, value); + + this.#subscribe = createSubscriber((update) => { + this.#update = update; + + return () => { + this.#update = () => {}; + }; + }); + } +} + +type PersistedLocalStorageOptions<T> = { + serializer?: { + stringify(value: T): string; + parse(text: string): T; + }; + valid?: (value: T | unknown) => value is T; +}; + +export class PersistedLocalStorage<T> extends PersistedBase<T> { + constructor(key: string, defaultValue: T, options: PersistedLocalStorageOptions<T> = {}) { + const valid = options.valid || (() => true); + const serializer = options.serializer || JSON; + + super(key, defaultValue, { + read: (key: string) => { + if (!browser) { + return; + } + + const item = localStorage.getItem(key) ?? undefined; + if (item === undefined) { + return; + } + + const parsed = serializer.parse(item); + if (!valid(parsed)) { + return; + } + + return parsed; + }, + write: (key: string, value: T) => { + if (browser) { + localStorage.setItem(key, serializer.stringify(value)); + } + }, + }); + } +} diff --git a/web/src/lib/utils/sw-messaging.ts b/web/src/lib/utils/sw-messaging.ts new file mode 100644 index 0000000000..1a19d3c134 --- /dev/null +++ b/web/src/lib/utils/sw-messaging.ts @@ -0,0 +1,8 @@ +const broadcast = new BroadcastChannel('immich'); + +export function cancelImageUrl(url: string) { + broadcast.postMessage({ type: 'cancel', url }); +} +export function preloadImageUrl(url: string) { + broadcast.postMessage({ type: 'preload', url }); +} diff --git a/web/src/lib/utils/thumbnail-util.ts b/web/src/lib/utils/thumbnail-util.ts index a53691e716..f0043790ea 100644 --- a/web/src/lib/utils/thumbnail-util.ts +++ b/web/src/lib/utils/thumbnail-util.ts @@ -1,6 +1,7 @@ +import { locale } from '$lib/stores/preferences.store'; import { AssetTypeEnum, type AssetResponseDto } from '@immich/sdk'; import { t } from 'svelte-i18n'; -import { derived } from 'svelte/store'; +import { derived, get } from 'svelte/store'; import { fromLocalDateTime } from './timeline-util'; /** @@ -43,7 +44,7 @@ export const getAltText = derived(t, ($t) => { return asset.exifInfo.description; } - const date = fromLocalDateTime(asset.localDateTime).toLocaleString({ dateStyle: 'long' }); + const date = fromLocalDateTime(asset.localDateTime).toLocaleString({ dateStyle: 'long' }, { locale: get(locale) }); const hasPlace = !!asset.exifInfo?.city && !!asset.exifInfo?.country; const names = asset.people?.filter((p) => p.name).map((p) => p.name) ?? []; const peopleCount = names.length; diff --git a/web/src/lib/utils/timeline-util.ts b/web/src/lib/utils/timeline-util.ts index f40e2bc3eb..21a7d23953 100644 --- a/web/src/lib/utils/timeline-util.ts +++ b/web/src/lib/utils/timeline-util.ts @@ -1,41 +1,13 @@ -import type { AssetBucket } from '$lib/stores/assets-store.svelte'; import { locale } from '$lib/stores/preferences.store'; -import { type CommonJustifiedLayout } from '$lib/utils/layout-utils'; - -import type { AssetResponseDto } from '@immich/sdk'; import { memoize } from 'lodash-es'; import { DateTime, type LocaleOptions } from 'luxon'; import { get } from 'svelte/store'; -export type DateGroup = { - bucket: AssetBucket; - index: number; - row: number; - col: number; - date: DateTime; - groupTitle: string; - assets: AssetResponseDto[]; - assetsIntersecting: boolean[]; - height: number; - intersecting: boolean; - geometry: CommonJustifiedLayout; -}; export type ScrubberListener = ( bucketDate: string | undefined, overallScrollPercent: number, bucketScrollPercent: number, ) => void | Promise<void>; -export type ScrollTargetListener = ({ - bucket, - dateGroup, - asset, - offset, -}: { - bucket: AssetBucket; - dateGroup: DateGroup; - asset: AssetResponseDto; - offset: number; -}) => void; export const fromLocalDateTime = (localDateTime: string) => DateTime.fromISO(localDateTime, { zone: 'UTC', locale: get(locale) }); @@ -43,31 +15,6 @@ export const fromLocalDateTime = (localDateTime: string) => export const fromDateTimeOriginal = (dateTimeOriginal: string, timeZone: string) => DateTime.fromISO(dateTimeOriginal, { zone: timeZone }); -export type LayoutBox = { - aspectRatio: number; - top: number; - width: number; - height: number; - left: number; - forcedAspectRatio?: boolean; -}; - -export function findTotalOffset(element: HTMLElement, stop: HTMLElement) { - let offset = 0; - while (element.offsetParent && element !== stop) { - offset += element.offsetTop; - element = element.offsetParent as HTMLElement; - } - return offset; -} - -export const groupDateFormat: Intl.DateTimeFormatOptions = { - weekday: 'short', - month: 'short', - day: 'numeric', - year: 'numeric', -}; - export function formatGroupTitle(_date: DateTime): string { if (!_date.isValid) { return _date.toString(); @@ -87,20 +34,24 @@ export function formatGroupTitle(_date: DateTime): string { // Last week if (date >= today.minus({ days: 6 }) && date < today) { - return date.toLocaleString({ weekday: 'long' }); + return date.toLocaleString({ weekday: 'long' }, { locale: get(locale) }); } // This year if (today.hasSame(date, 'year')) { - return date.toLocaleString({ - weekday: 'short', - month: 'short', - day: 'numeric', - }); + return date.toLocaleString( + { + weekday: 'short', + month: 'short', + day: 'numeric', + }, + { locale: get(locale) }, + ); } - return getDateLocaleString(date); + return getDateLocaleString(date, { locale: get(locale) }); } + export const getDateLocaleString = (date: DateTime, opts?: LocaleOptions): string => date.toLocaleString(DateTime.DATE_MED_WITH_WEEKDAY, opts); 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 818f48fe4a..e10fbae139 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 @@ -22,9 +22,10 @@ import FavoriteAction from '$lib/components/photos-page/actions/favorite-action.svelte'; import RemoveFromAlbum from '$lib/components/photos-page/actions/remove-from-album.svelte'; import SelectAllAssets from '$lib/components/photos-page/actions/select-all-assets.svelte'; + import TagAction from '$lib/components/photos-page/actions/tag-action.svelte'; import AssetGrid from '$lib/components/photos-page/asset-grid.svelte'; - import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte'; import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte'; + import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte'; import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte'; import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte'; import CreateSharedLinkModal from '$lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte'; @@ -33,14 +34,16 @@ notificationController, } from '$lib/components/shared-components/notification/notification'; import UserAvatar from '$lib/components/shared-components/user-avatar.svelte'; - import { AppRoute, AlbumPageViewMode } from '$lib/constants'; - import { numberOfComments, setNumberOfComments, updateNumberOfComments } from '$lib/stores/activity.store'; + import { AlbumPageViewMode, AppRoute } from '$lib/constants'; + import { activityManager } from '$lib/managers/activity-manager.svelte'; + import { AssetInteraction } from '$lib/stores/asset-interaction.svelte'; import { assetViewingStore } from '$lib/stores/asset-viewing.store'; import { AssetStore } from '$lib/stores/assets-store.svelte'; import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store'; import { preferences, user } from '$lib/stores/user.store'; import { handlePromiseError } from '$lib/utils'; - import { downloadAlbum, cancelMultiselect } from '$lib/utils/asset-utils'; + import { confirmAlbumDelete } from '$lib/utils/album-utils'; + import { cancelMultiselect, downloadAlbum } from '$lib/utils/asset-utils'; import { openFileUploadDialog } from '$lib/utils/file-uploader'; import { handleError } from '$lib/utils/handle-error'; import { @@ -53,18 +56,11 @@ import { AlbumUserRole, AssetOrder, - ReactionLevel, - ReactionType, addAssetsToAlbum, addUsersToAlbum, - createActivity, - deleteActivity, deleteAlbum, - getActivities, - getActivityStatistics, getAlbumInfo, updateAlbumInfo, - type ActivityResponseDto, type AlbumUserAddDto, } from '@immich/sdk'; import { @@ -80,13 +76,10 @@ mdiPresentationPlay, mdiShareVariantOutline, } from '@mdi/js'; + import { onDestroy } from 'svelte'; + import { t } from 'svelte-i18n'; import { fly } from 'svelte/transition'; import type { PageData } from './$types'; - import { t } from 'svelte-i18n'; - import { onDestroy } from 'svelte'; - import { confirmAlbumDelete } from '$lib/utils/album-utils'; - import TagAction from '$lib/components/photos-page/actions/tag-action.svelte'; - import { AssetInteraction } from '$lib/stores/asset-interaction.svelte'; interface Props { data: PageData; @@ -103,8 +96,6 @@ let viewMode: AlbumPageViewMode = $state(AlbumPageViewMode.VIEW); let isCreatingSharedAlbum = $state(false); let isShowActivity = $state(false); - let isLiked: ActivityResponseDto | null = $state(null); - let reactions: ActivityResponseDto[] = $state([]); let albumOrder: AssetOrder | undefined = $state(data.album.order); const assetInteraction = new AssetInteraction(); @@ -154,44 +145,15 @@ const handleFavorite = async () => { try { - if (isLiked) { - const activityId = isLiked.id; - await deleteActivity({ id: activityId }); - reactions = reactions.filter((reaction) => reaction.id !== activityId); - isLiked = null; - } else { - isLiked = await createActivity({ - activityCreateDto: { albumId: album.id, type: ReactionType.Like }, - }); - reactions = [...reactions, isLiked]; - } + await activityManager.toggleLike(); } catch (error) { handleError(error, $t('errors.cant_change_asset_favorite')); } }; - const getFavorite = async () => { - if ($user) { - try { - const data = await getActivities({ - userId: $user.id, - albumId: album.id, - $type: ReactionType.Like, - level: ReactionLevel.Album, - }); - if (data.length > 0) { - isLiked = data[0]; - } - } catch (error) { - handleError(error, $t('errors.unable_to_load_liked_status')); - } - } - }; - - const getNumberOfComments = async () => { + const updateComments = async () => { try { - const { comments } = await getActivityStatistics({ albumId: album.id }); - setNumberOfComments(comments); + await activityManager.refreshActivities(album.id); } catch (error) { handleError(error, $t('errors.cant_get_number_of_comments')); } @@ -398,7 +360,7 @@ let albumId = $derived(album.id); $effect(() => { - if (!album.isActivityEnabled && $numberOfComments === 0) { + if (!album.isActivityEnabled && activityManager.commentCount === 0) { isShowActivity = false; } }); @@ -412,7 +374,16 @@ void assetStore.updateOptions({ isArchived: false, withPartners: true, timelineAlbumId: albumId }); } }); - onDestroy(() => assetStore.destroy()); + + $effect(() => { + activityManager.reset(); + activityManager.init(album.id); + }); + + onDestroy(() => { + activityManager.reset(); + assetStore.destroy(); + }); // let timelineStore = new AssetStore(); // $effect(() => void timelineStore.updateOptions({ isArchived: false, withPartners: true, timelineAlbumId: albumId })); // onDestroy(() => timelineStore.destroy()); @@ -420,7 +391,7 @@ let isOwned = $derived($user.id == album.ownerId); let showActivityStatus = $derived( - album.albumUsers.length > 0 && !$showAssetViewer && (album.isActivityEnabled || $numberOfComments > 0), + album.albumUsers.length > 0 && !$showAssetViewer && (album.isActivityEnabled || activityManager.commentCount > 0), ); let isEditor = $derived( album.albumUsers.find(({ user: { id } }) => id === $user.id)?.role === AlbumUserRole.Editor || @@ -430,8 +401,7 @@ let albumHasViewers = $derived(album.albumUsers.some(({ role }) => role === AlbumUserRole.Viewer)); $effect(() => { if (album.albumUsers.length > 0) { - handlePromiseError(getFavorite()); - handlePromiseError(getNumberOfComments()); + handlePromiseError(updateComments()); } }); const isShared = $derived(viewMode === AlbumPageViewMode.SELECT_ASSETS ? false : album.albumUsers.length > 0); @@ -708,11 +678,11 @@ </AssetGrid> {#if showActivityStatus} - <div class="absolute z-[2] bottom-0 right-0 mb-6 mr-6 justify-self-end"> + <div class="absolute z-[2] bottom-0 end-0 mb-6 me-6 justify-self-end"> <ActivityStatus disabled={!album.isActivityEnabled} - {isLiked} - numberOfComments={$numberOfComments} + isLiked={activityManager.isLiked} + numberOfComments={activityManager.commentCount} onFavorite={handleFavorite} onOpenActivityTab={handleOpenAndCloseActivityTab} /> @@ -725,7 +695,7 @@ <div transition:fly={{ duration: 150 }} id="activity-panel" - class="z-[2] w-[360px] md:w-[460px] overflow-y-auto bg-immich-bg transition-all dark:border-l dark:border-l-immich-dark-gray dark:bg-immich-dark-bg" + class="z-[2] w-[360px] md:w-[460px] overflow-y-auto bg-immich-bg transition-all dark:border-l dark:border-s-immich-dark-gray dark:bg-immich-dark-bg" translate="yes" > <ActivityViewer @@ -733,11 +703,6 @@ disabled={!album.isActivityEnabled} albumOwnerId={album.ownerId} albumId={album.id} - {isLiked} - bind:reactions - onAddComment={() => updateNumberOfComments(1)} - onDeleteComment={() => updateNumberOfComments(-1)} - onDeleteLike={() => (isLiked = null)} onClose={handleOpenAndCloseActivityTab} /> </div> diff --git a/web/src/routes/(user)/explore/+page.svelte b/web/src/routes/(user)/explore/+page.svelte index ec62d5e869..49d38d0815 100644 --- a/web/src/routes/(user)/explore/+page.svelte +++ b/web/src/routes/(user)/explore/+page.svelte @@ -48,7 +48,7 @@ <p class="mb-4 font-medium dark:text-immich-dark-fg">{$t('people')}</p> <a href={AppRoute.PEOPLE} - class="pr-4 text-sm font-medium hover:text-immich-primary dark:text-immich-dark-fg dark:hover:text-immich-dark-primary" + class="pe-4 text-sm font-medium hover:text-immich-primary dark:text-immich-dark-fg dark:hover:text-immich-dark-primary" draggable="false">{$t('view_all')}</a > </div> @@ -64,7 +64,7 @@ widthStyle="100%" /> {#if person.isFavorite} - <div class="absolute top-2 left-2"> + <div class="absolute top-2 start-2"> <Icon path={mdiHeart} size="24" class="text-white" /> </div> {/if} @@ -82,7 +82,7 @@ <p class="mb-4 font-medium dark:text-immich-dark-fg">{$t('places')}</p> <a href={AppRoute.PLACES} - class="pr-4 text-sm font-medium hover:text-immich-primary dark:text-immich-dark-fg dark:hover:text-immich-dark-primary" + class="pe-4 text-sm font-medium hover:text-immich-primary dark:text-immich-dark-fg dark:hover:text-immich-dark-primary" draggable="false">{$t('view_all')}</a > </div> diff --git a/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte index f5a4f6cd09..d1fc0bcd90 100644 --- a/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -87,7 +87,7 @@ </script> {#if assetInteraction.selectionActive} - <div class="fixed z-[910] top-0 left-0 w-full"> + <div class="fixed z-[910] top-0 start-0 w-full"> <AssetSelectControlBar assets={assetInteraction.selectedAssets} clearSelect={() => cancelMultiselect(assetInteraction)} @@ -133,7 +133,7 @@ <SideBarSection> <SkipLink target={`#${headerId}`} text={$t('skip_to_folders')} breakpoint="md" /> <section> - <div class="text-xs pl-4 mb-2 dark:text-white">{$t('explorer').toUpperCase()}</div> + <div class="text-xs ps-4 mb-2 dark:text-white">{$t('explorer').toUpperCase()}</div> <div class="h-full"> <TreeItems icons={{ default: mdiFolderOutline, active: mdiFolder }} diff --git a/web/src/routes/(user)/people/+page.svelte b/web/src/routes/(user)/people/+page.svelte index 3e1b5a774c..fd632fccce 100644 --- a/web/src/routes/(user)/people/+page.svelte +++ b/web/src/routes/(user)/people/+page.svelte @@ -458,7 +458,7 @@ <dialog open transition:fly={{ y: innerHeight, duration: 150, easing: quintOut, opacity: 0 }} - class="absolute left-0 top-0 z-[9999] h-full w-full bg-immich-bg dark:bg-immich-dark-bg" + class="absolute start-0 top-0 z-[9999] h-full w-full bg-immich-bg dark:bg-immich-dark-bg" aria-modal="true" aria-labelledby="manage-visibility-title" use:focusTrap 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 36b59e3ace..4c58f52265 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 @@ -486,7 +486,7 @@ </header> <main - class="relative h-dvh overflow-hidden bg-immich-bg tall:ml-4 md:pt-[var(--navbar-height-md)] pt-[var(--navbar-height)] dark:bg-immich-dark-bg" + class="relative h-dvh overflow-hidden bg-immich-bg tall:ms-4 md:pt-[var(--navbar-height-md)] pt-[var(--navbar-height)] dark:bg-immich-dark-bg" use:scrollMemoryClearer={{ routeStartsWith: AppRoute.PEOPLE, beforeClear: () => { @@ -542,7 +542,7 @@ heightStyle="3.375rem" /> <div - class="flex flex-col justify-center text-left px-4 text-immich-primary dark:text-immich-dark-primary" + class="flex flex-col justify-center text-start px-4 text-immich-primary dark:text-immich-dark-primary" > <p class="w-40 sm:w-72 font-medium truncate">{person.name || $t('add_a_name')}</p> <p class="text-sm text-gray-500 dark:text-immich-gray"> @@ -598,7 +598,7 @@ widthStyle="2rem" heightStyle="2rem" /> - <p class="ml-4 text-gray-700 dark:text-gray-100">{person.name}</p> + <p class="ms-4 text-gray-700 dark:text-gray-100">{person.name}</p> </button> {/each} </div> diff --git a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte index c985104e3c..9d427e1ea7 100644 --- a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -251,7 +251,7 @@ <section> {#if assetInteraction.selectionActive} - <div class="fixed z-[100] top-0 left-0 w-full"> + <div class="fixed z-[100] top-0 start-0 w-full"> <AssetSelectControlBar assets={assetInteraction.selectedAssets} clearSelect={() => cancelMultiselect(assetInteraction)} @@ -289,13 +289,13 @@ </AssetSelectControlBar> </div> {:else} - <div class="fixed z-[100] top-0 left-0 w-full"> + <div class="fixed z-[100] top-0 start-0 w-full"> <ControlAppBar onClose={() => goto(previousRoute)} backIcon={mdiArrowLeft}> <div class="-z-[1] bg-immich-bg dark:bg-immich-dark-bg" style="position:absolute;top:0;left:0;right:0;bottom:0;" ></div> - <div class="w-full flex-1 pl-4"> + <div class="w-full flex-1 ps-4"> <SearchBar grayTheme={false} value={terms?.query ?? ''} searchQuery={terms} /> </div> </ControlAppBar> @@ -313,13 +313,13 @@ <div class="flex place-content-center place-items-center text-xs"> <div class="bg-immich-primary py-2 px-4 text-white dark:text-black dark:bg-immich-dark-primary - {value === true ? 'rounded-full' : 'rounded-tl-full rounded-bl-full'}" + {value === true ? 'rounded-full' : 'roudned-s-full'}" > {getHumanReadableSearchKey(key as keyof SearchTerms)} </div> {#if value !== true} - <div class="bg-gray-300 py-2 px-4 dark:bg-gray-800 dark:text-white rounded-tr-full rounded-br-full"> + <div class="bg-gray-300 py-2 px-4 dark:bg-gray-800 dark:text-white rounded-e-full"> {#if (key === 'takenAfter' || key === 'takenBefore') && typeof value === 'string'} {getHumanReadableDate(value)} {:else if key === 'personIds' && Array.isArray(value)} @@ -349,7 +349,7 @@ > {#if searchResultAlbums.length > 0} <section> - <div class="ml-6 text-4xl font-medium text-black/70 dark:text-white/80">{$t('albums').toUpperCase()}</div> + <div class="ms-6 text-4xl font-medium text-black/70 dark:text-white/80">{$t('albums').toUpperCase()}</div> <AlbumCardGroup albums={searchResultAlbums} showDateRange showItemCount /> <div class="m-6 text-4xl font-medium text-black/70 dark:text-white/80"> diff --git a/web/src/routes/(user)/sharing/+page.svelte b/web/src/routes/(user)/sharing/+page.svelte index e3d6ac1ced..a55452b5d1 100644 --- a/web/src/routes/(user)/sharing/+page.svelte +++ b/web/src/routes/(user)/sharing/+page.svelte @@ -68,7 +68,7 @@ class="flex gap-4 rounded-lg px-5 py-4 transition-all hover:bg-gray-200 dark:hover:bg-gray-700" > <UserAvatar user={partner} size="lg" /> - <div class="text-left"> + <div class="text-start"> <p class="text-immich-fg dark:text-immich-dark-fg"> {partner.name} </p> 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 8bb43676e8..8d33a2eb6e 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 @@ -146,7 +146,7 @@ <SideBarSection> <SkipLink target={`#${headerId}`} text={$t('skip_to_tags')} breakpoint="md" /> <section> - <div class="text-xs pl-4 mb-2 dark:text-white">{$t('explorer').toUpperCase()}</div> + <div class="text-xs ps-4 mb-2 dark:text-white">{$t('explorer').toUpperCase()}</div> <div class="h-full"> <TreeItems icons={{ default: mdiTag, active: mdiTag }} diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index f3b3930e12..844037a13f 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -10,15 +10,14 @@ import NotificationList from '$lib/components/shared-components/notification/notification-list.svelte'; import UploadPanel from '$lib/components/shared-components/upload-panel.svelte'; import VersionAnnouncementBox from '$lib/components/shared-components/version-announcement-box.svelte'; - import { Theme } from '$lib/constants'; - import { colorTheme, handleToggleTheme, type ThemeSetting } from '$lib/stores/preferences.store'; + import { eventManager } from '$lib/managers/event-manager.svelte'; import { serverConfig } from '$lib/stores/server-config.store'; import { user } from '$lib/stores/user.store'; import { closeWebsocketConnection, openWebsocketConnection } from '$lib/stores/websocket'; - import { copyToClipboard, setKey } from '$lib/utils'; - import { isAssetViewerRoute, isSharedLinkRoute } from '$lib/utils/navigation'; + import { copyToClipboard } from '$lib/utils'; + import { isAssetViewerRoute } from '$lib/utils/navigation'; import { setTranslations } from '@immich/ui'; - import { onDestroy, onMount, type Snippet } from 'svelte'; + import { onMount, type Snippet } from 'svelte'; import { t } from 'svelte-i18n'; import { run } from 'svelte/legacy'; import '../app.css'; @@ -39,24 +38,6 @@ let showNavigationLoadingBar = $state(false); - const changeTheme = (theme: ThemeSetting) => { - if (theme.system) { - theme.value = globalThis.matchMedia('(prefers-color-scheme: dark)').matches ? Theme.DARK : Theme.LIGHT; - } - - if (theme.value === Theme.LIGHT) { - document.documentElement.classList.remove('dark'); - } else { - document.documentElement.classList.add('dark'); - } - }; - - const handleChangeTheme = () => { - if ($colorTheme.system) { - handleToggleTheme(); - } - }; - const getMyImmichLink = () => { return new URL(page.url.pathname + page.url.search, 'https://my.immich.app'); }; @@ -65,20 +46,11 @@ const element = document.querySelector('#stencil'); element?.remove(); // if the browser theme changes, changes the Immich theme too - globalThis.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', handleChangeTheme); }); - onDestroy(() => { - document.removeEventListener('change', handleChangeTheme); - }); - - if (isSharedLinkRoute(page.route?.id)) { - setKey(page.params.key); - } + eventManager.emit('app.init'); beforeNavigate(({ from, to }) => { - setKey(isSharedLinkRoute(to?.route.id) ? to?.params?.key : undefined); - if (isAssetViewerRoute(from) && isAssetViewerRoute(to)) { return; } @@ -88,9 +60,6 @@ afterNavigate(() => { showNavigationLoadingBar = false; }); - run(() => { - changeTheme($colorTheme); - }); run(() => { if ($user) { openWebsocketConnection(); diff --git a/web/src/routes/admin/jobs-status/+page.svelte b/web/src/routes/admin/jobs-status/+page.svelte index 21381081e0..07757614e5 100644 --- a/web/src/routes/admin/jobs-status/+page.svelte +++ b/web/src/routes/admin/jobs-status/+page.svelte @@ -107,7 +107,7 @@ > {#snippet promptSnippet()} <form {onsubmit} autocomplete="off" id="create-tag-form" class="w-full"> - <div class="flex flex-col gap-1 text-left"> + <div class="flex flex-col gap-1 text-start"> <Combobox bind:selectedOption={selectedJob} label={$t('jobs')} diff --git a/web/src/routes/admin/library-management/+page.svelte b/web/src/routes/admin/library-management/+page.svelte index 9a598101e1..23ebcfbf0b 100644 --- a/web/src/routes/admin/library-management/+page.svelte +++ b/web/src/routes/admin/library-management/+page.svelte @@ -282,7 +282,7 @@ <section class="my-4"> <div class="flex flex-col gap-2" in:fade={{ duration: 500 }}> {#if libraries.length > 0} - <table class="w-full text-left"> + <table class="w-full text-start"> <thead class="mb-4 flex h-12 w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary" > @@ -369,7 +369,7 @@ {/if} {#if editScanSettings === index} <!-- svelte-ignore node_invalid_placement_ssr --> - <div transition:slide={{ duration: 250 }} class="mb-4 ml-4 mr-4"> + <div transition:slide={{ duration: 250 }} class="mb-4 ms-4 me-4"> <LibraryScanSettingsForm {library} onSubmit={handleUpdate} diff --git a/web/src/routes/admin/repair/+page.svelte b/web/src/routes/admin/repair/+page.svelte deleted file mode 100644 index 635a140452..0000000000 --- a/web/src/routes/admin/repair/+page.svelte +++ /dev/null @@ -1,358 +0,0 @@ -<script lang="ts"> - import empty4Url from '$lib/assets/empty-4.svg'; - import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte'; - import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte'; - import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte'; - import { - NotificationType, - notificationController, - } from '$lib/components/shared-components/notification/notification'; - import { downloadManager } from '$lib/stores/download-store.svelte'; - import { locale } from '$lib/stores/preferences.store'; - import { copyToClipboard } from '$lib/utils'; - import { downloadBlob } from '$lib/utils/asset-utils'; - import { handleError } from '$lib/utils/handle-error'; - import { fixAuditFiles, getAuditFiles, getFileChecksums, type FileReportItemDto } from '@immich/sdk'; - import { Button, HStack, Text } from '@immich/ui'; - import { mdiCheckAll, mdiContentCopy, mdiDownload, mdiRefresh, mdiWrench } from '@mdi/js'; - import { t } from 'svelte-i18n'; - import type { PageData } from './$types'; - - interface Props { - data: PageData; - } - - let { data }: Props = $props(); - - interface UntrackedFile { - filename: string; - checksum: string | null; - } - - interface Match { - orphan: FileReportItemDto; - extra: UntrackedFile; - } - - const normalize = (filenames: string[]) => filenames.map((filename) => ({ filename, checksum: null })); - - let checking = $state(false); - let repairing = $state(false); - - let orphans: FileReportItemDto[] = $state(data.orphans); - let extras: UntrackedFile[] = $state(normalize(data.extras)); - let matches: Match[] = $state([]); - - const handleDownload = () => { - if (extras.length > 0) { - const blob = new Blob([extras.map(({ filename }) => filename).join('\n')], { type: 'text/plain' }); - const downloadKey = 'untracked.txt'; - downloadManager.add(downloadKey, blob.size); - downloadManager.update(downloadKey, blob.size); - downloadBlob(blob, downloadKey); - setTimeout(() => downloadManager.clear(downloadKey), 5000); - } - - if (orphans.length > 0) { - const blob = new Blob([JSON.stringify(orphans, null, 4)], { type: 'application/json' }); - const downloadKey = 'orphans.json'; - downloadManager.add(downloadKey, blob.size); - downloadManager.update(downloadKey, blob.size); - downloadBlob(blob, downloadKey); - setTimeout(() => downloadManager.clear(downloadKey), 5000); - } - }; - - const handleRepair = async () => { - if (matches.length === 0) { - return; - } - - repairing = true; - - try { - await fixAuditFiles({ - fileReportFixDto: { - items: matches.map(({ orphan, extra }) => ({ - entityId: orphan.entityId, - entityType: orphan.entityType, - pathType: orphan.pathType, - pathValue: extra.filename, - })), - }, - }); - - notificationController.show({ - type: NotificationType.Info, - message: $t('admin.repaired_items', { values: { count: matches.length } }), - }); - - matches = []; - } catch (error) { - handleError(error, $t('errors.unable_to_repair_items')); - } finally { - repairing = false; - } - }; - - const handleSplit = (match: Match) => { - matches = matches.filter((_match) => _match !== match); - orphans = [match.orphan, ...orphans]; - extras = [match.extra, ...extras]; - }; - - const handleRefresh = async () => { - matches = []; - orphans = []; - extras = []; - - try { - const report = await getAuditFiles(); - - orphans = report.orphans; - extras = normalize(report.extras); - - notificationController.show({ message: $t('refreshed'), type: NotificationType.Info }); - } catch (error) { - handleError(error, $t('errors.unable_to_load_items')); - } - }; - - const handleCheckOne = async (filename: string) => { - try { - const matched = await loadAndMatch([filename]); - if (matched) { - notificationController.show({ - message: $t('admin.repair_matched_items', { values: { count: 1 } }), - type: NotificationType.Info, - }); - } - } catch (error) { - handleError(error, $t('errors.repair_unable_to_check_items', { values: { count: 'one' } })); - } - }; - - const handleCheckAll = async () => { - checking = true; - - let count = 0; - - try { - const chunkSize = 10; - const filenames = extras.filter(({ checksum }) => !checksum).map(({ filename }) => filename); - for (let index = 0; index < filenames.length; index += chunkSize) { - count += await loadAndMatch(filenames.slice(index, index + chunkSize)); - } - } catch (error) { - handleError(error, $t('errors.repair_unable_to_check_items', { values: { count: 'other' } })); - } finally { - checking = false; - } - - notificationController.show({ - message: $t('admin.repair_matched_items', { values: { count } }), - type: NotificationType.Info, - }); - }; - - const loadAndMatch = async (filenames: string[]) => { - const items = await getFileChecksums({ - fileChecksumDto: { filenames }, - }); - - let count = 0; - - for (const { checksum, filename } of items) { - const extra = extras.find((extra) => extra.filename === filename); - if (extra) { - extra.checksum = checksum; - extras = [...extras]; - } - - const orphan = orphans.find((orphan) => orphan.checksum === checksum); - if (orphan) { - count++; - matches = [...matches, { orphan, extra: { filename, checksum } }]; - orphans = orphans.filter((_orphan) => _orphan !== orphan); - extras = extras.filter((extra) => extra.filename !== filename); - } - } - - return count; - }; -</script> - -<UserPageLayout title={data.meta.title} admin> - {#snippet buttons()} - <HStack gap={0}> - <Button - leadingIcon={mdiWrench} - onclick={() => handleRepair()} - disabled={matches.length === 0 || repairing} - size="small" - variant="ghost" - color="secondary" - > - <Text class="hidden md:block">{$t('admin.repair_all')}</Text> - </Button> - <Button - leadingIcon={mdiCheckAll} - onclick={() => handleCheckAll()} - disabled={extras.length === 0 || checking} - size="small" - variant="ghost" - color="secondary" - > - <Text class="hidden md:block">{$t('admin.check_all')}</Text> - </Button> - <Button - leadingIcon={mdiDownload} - onclick={() => handleDownload()} - disabled={extras.length + orphans.length === 0} - size="small" - variant="ghost" - color="secondary" - > - <Text class="hidden md:block">{$t('export')}</Text> - </Button> - <Button leadingIcon={mdiRefresh} onclick={() => handleRefresh()} size="small" variant="ghost" color="secondary"> - <Text class="hidden md:block">{$t('refresh')}</Text> - </Button> - </HStack> - {/snippet} - <section id="setting-content" class="flex place-content-center sm:mx-4"> - <section class="w-full pb-28 sm:w-5/6 md:w-[850px]"> - {#if matches.length + extras.length + orphans.length === 0} - <div class="w-full"> - <EmptyPlaceholder fullWidth text={$t('repair_no_results_message')} src={empty4Url} /> - </div> - {:else} - <div class="gap-2"> - <table class="table-fixed mt-5 w-full text-left"> - <thead - class="mb-4 flex w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary" - > - <tr class="flex w-full place-items-center p-2 md:p-5"> - <th class="w-full text-sm place-items-center font-medium flex justify-between" colspan="2"> - <div class="px-3"> - <p> - {$t('matches').toUpperCase()} - {matches.length > 0 ? `(${matches.length.toLocaleString($locale)})` : ''} - </p> - <p class="text-gray-600 dark:text-gray-300 mt-1">{$t('admin.these_files_matched_by_checksum')}</p> - </div> - </th> - </tr> - </thead> - <tbody - class="w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray dark:text-immich-dark-fg max-h-[500px] block overflow-x-hidden" - > - {#each matches as match (match.extra.filename)} - <tr - class="w-full h-[75px] place-items-center border-[3px] border-transparent p-2 odd:bg-immich-gray even:bg-immich-bg hover:cursor-pointer hover:border-immich-primary/75 odd:dark:bg-immich-dark-gray/75 even:dark:bg-immich-dark-gray/50 dark:hover:border-immich-dark-primary/75 md:p-5 flex justify-between" - tabindex="0" - onclick={() => handleSplit(match)} - > - <td class="text-sm text-ellipsis flex flex-col gap-1 font-mono"> - <span>{match.orphan.pathValue} =></span> - <span>{match.extra.filename}</span> - </td> - <td class="text-sm text-ellipsis d-flex font-mono"> - <span>({match.orphan.entityType}/{match.orphan.pathType})</span> - </td> - </tr> - {/each} - </tbody> - </table> - - <table class="table-fixed mt-5 w-full text-left"> - <thead - class="mb-4 flex w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary" - > - <tr class="flex w-full place-items-center p-1 md:p-5"> - <th class="w-full text-sm font-medium justify-between place-items-center flex" colspan="2"> - <div class="px-3"> - <p> - {$t('admin.offline_paths').toUpperCase()} - {orphans.length > 0 ? `(${orphans.length.toLocaleString($locale)})` : ''} - </p> - <p class="text-gray-600 dark:text-gray-300 mt-1"> - {$t('admin.offline_paths_description')} - </p> - </div> - </th> - </tr> - </thead> - <tbody - class="w-full rounded-md border dark:border-immich-dark-gray dark:text-immich-dark-fg overflow-y-auto max-h-[500px] block overflow-x-hidden" - > - {#each orphans as orphan, index (index)} - <tr - class="w-full h-[50px] place-items-center border-[3px] border-transparent odd:bg-immich-gray even:bg-immich-bg hover:cursor-pointer hover:border-immich-primary/75 odd:dark:bg-immich-dark-gray/75 even:dark:bg-immich-dark-gray/50 dark:hover:border-immich-dark-primary/75 md:p-5 flex justify-between" - tabindex="0" - title={orphan.pathValue} - > - <td onclick={() => copyToClipboard(orphan.pathValue)}> - <CircleIconButton title={$t('copy_file_path')} icon={mdiContentCopy} size="18" onclick={() => {}} /> - </td> - <td class="truncate text-sm font-mono text-left" title={orphan.pathValue}> - {orphan.pathValue} - </td> - <td class="text-sm font-mono"> - <span>({orphan.entityType})</span> - </td> - </tr> - {/each} - </tbody> - </table> - - <table class="table-fixed mt-5 w-full text-left max-h-[300px]"> - <thead - class="mb-4 flex w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary" - > - <tr class="flex w-full place-items-center p-2 md:p-5"> - <th class="w-full text-sm font-medium place-items-center flex justify-between" colspan="2"> - <div class="px-3"> - <p> - {$t('admin.untracked_files').toUpperCase()} - {extras.length > 0 ? `(${extras.length.toLocaleString($locale)})` : ''} - </p> - <p class="text-gray-600 dark:text-gray-300 mt-1"> - {$t('admin.untracked_files_description')} - </p> - </div> - </th> - </tr> - </thead> - <tbody - class="w-full rounded-md border-2 dark:border-immich-dark-gray dark:text-immich-dark-fg overflow-y-auto max-h-[500px] block overflow-x-hidden" - > - {#each extras as extra (extra.filename)} - <tr - class="flex h-[50px] w-full place-items-center border-[3px] border-transparent p-1 odd:bg-immich-gray even:bg-immich-bg hover:cursor-pointer hover:border-immich-primary/75 odd:dark:bg-immich-dark-gray/75 even:dark:bg-immich-dark-gray/50 dark:hover:border-immich-dark-primary/75 md:p-5 justify-between" - tabindex="0" - onclick={() => handleCheckOne(extra.filename)} - title={extra.filename} - > - <td onclick={() => copyToClipboard(extra.filename)}> - <CircleIconButton title={$t('copy_file_path')} icon={mdiContentCopy} size="18" onclick={() => {}} /> - </td> - <td class="w-full text-md text-ellipsis flex justify-between pr-5"> - <span class="text-ellipsis grow truncate font-mono text-sm pr-5" title={extra.filename} - >{extra.filename}</span - > - <span class="text-sm font-mono dark:text-immich-dark-primary text-immich-primary pr-5"> - {#if extra.checksum} - [sha1:{extra.checksum}] - {/if} - </span> - </td> - </tr> - {/each} - </tbody> - </table> - </div> - {/if} - </section> - </section> -</UserPageLayout> diff --git a/web/src/routes/admin/repair/+page.ts b/web/src/routes/admin/repair/+page.ts deleted file mode 100644 index 9e52abb573..0000000000 --- a/web/src/routes/admin/repair/+page.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { authenticate } from '$lib/utils/auth'; -import { getFormatter } from '$lib/utils/i18n'; -import { getAuditFiles } from '@immich/sdk'; -import type { PageLoad } from './$types'; - -export const load = (async () => { - await authenticate({ admin: true }); - const { orphans, extras } = await getAuditFiles(); - const $t = await getFormatter(); - - return { - orphans, - extras, - meta: { - title: $t('repair'), - }, - }; -}) satisfies PageLoad; diff --git a/web/src/routes/admin/system-settings/+page.svelte b/web/src/routes/admin/system-settings/+page.svelte index 6512461ee9..1ac9f0b6fd 100644 --- a/web/src/routes/admin/system-settings/+page.svelte +++ b/web/src/routes/admin/system-settings/+page.svelte @@ -1,15 +1,16 @@ <script lang="ts"> + import type { SettingsComponentProps } from '$lib/components/admin-page/settings/admin-settings'; import AdminSettings from '$lib/components/admin-page/settings/admin-settings.svelte'; import AuthSettings from '$lib/components/admin-page/settings/auth/auth-settings.svelte'; import BackupSettings from '$lib/components/admin-page/settings/backup-settings/backup-settings.svelte'; import FFmpegSettings from '$lib/components/admin-page/settings/ffmpeg/ffmpeg-settings.svelte'; import ImageSettings from '$lib/components/admin-page/settings/image/image-settings.svelte'; import JobSettings from '$lib/components/admin-page/settings/job-settings/job-settings.svelte'; - import MetadataSettings from '$lib/components/admin-page/settings/metadata-settings/metadata-settings.svelte'; import LibrarySettings from '$lib/components/admin-page/settings/library-settings/library-settings.svelte'; import LoggingSettings from '$lib/components/admin-page/settings/logging-settings/logging-settings.svelte'; import MachineLearningSettings from '$lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte'; import MapSettings from '$lib/components/admin-page/settings/map-settings/map-settings.svelte'; + import MetadataSettings from '$lib/components/admin-page/settings/metadata-settings/metadata-settings.svelte'; import NewVersionCheckSettings from '$lib/components/admin-page/settings/new-version-check-settings/new-version-check-settings.svelte'; import NotificationSettings from '$lib/components/admin-page/settings/notification-settings/notification-settings.svelte'; import ServerSettings from '$lib/components/admin-page/settings/server/server-settings.svelte'; @@ -17,15 +18,16 @@ import ThemeSettings from '$lib/components/admin-page/settings/theme/theme-settings.svelte'; import TrashSettings from '$lib/components/admin-page/settings/trash-settings/trash-settings.svelte'; import UserSettings from '$lib/components/admin-page/settings/user-settings/user-settings.svelte'; - import { Button, Text, HStack, Alert } from '@immich/ui'; + import SearchBar from '$lib/components/elements/search-bar.svelte'; import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte'; import SettingAccordionState from '$lib/components/shared-components/settings/setting-accordion-state.svelte'; import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte'; import { QueryParameter } from '$lib/constants'; - import { downloadManager } from '$lib/stores/download-store.svelte'; + import { downloadManager } from '$lib/managers/download-manager.svelte'; import { featureFlags } from '$lib/stores/server-config.store'; import { copyToClipboard } from '$lib/utils'; import { downloadBlob } from '$lib/utils/asset-utils'; + import { Alert, Button, HStack, Text } from '@immich/ui'; import { mdiAccountOutline, mdiBackupRestore, @@ -48,11 +50,9 @@ mdiUpload, mdiVideoOutline, } from '@mdi/js'; - import type { PageData } from './$types'; - import { t } from 'svelte-i18n'; import type { Component } from 'svelte'; - import type { SettingsComponentProps } from '$lib/components/admin-page/settings/admin-settings'; - import SearchBar from '$lib/components/elements/search-bar.svelte'; + import { t } from 'svelte-i18n'; + import type { PageData } from './$types'; interface Props { data: PageData; diff --git a/web/src/routes/admin/user-management/+page.svelte b/web/src/routes/admin/user-management/+page.svelte index 0ca17c4ed8..a25799588a 100644 --- a/web/src/routes/admin/user-management/+page.svelte +++ b/web/src/routes/admin/user-management/+page.svelte @@ -180,7 +180,7 @@ </ConfirmDialog> {/if} - <table class="my-5 w-full text-left"> + <table class="my-5 w-full text-start"> <thead class="mb-4 flex h-12 w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary" > diff --git a/web/src/routes/auth/change-password/+page.svelte b/web/src/routes/auth/change-password/+page.svelte index 33d354552e..aacf9a2c35 100644 --- a/web/src/routes/auth/change-password/+page.svelte +++ b/web/src/routes/auth/change-password/+page.svelte @@ -1,9 +1,8 @@ <script lang="ts"> - import { goto } from '$app/navigation'; import AuthPageLayout from '$lib/components/layouts/AuthPageLayout.svelte'; - import { AppRoute } from '$lib/constants'; - import { resetSavedUser, user } from '$lib/stores/user.store'; - import { logout, updateMyUser } from '@immich/sdk'; + import { authManager } from '$lib/managers/auth-manager.svelte'; + import { user } from '$lib/stores/user.store'; + import { updateMyUser } from '@immich/sdk'; import { Alert, Button, Field, HelperText, PasswordInput, Stack, Text } from '@immich/ui'; import { t } from 'svelte-i18n'; import type { PageData } from './$types'; @@ -25,9 +24,7 @@ } await updateMyUser({ userUpdateMeDto: { password } }); - await goto(AppRoute.AUTH_LOGIN); - resetSavedUser(); - await logout(); + await authManager.logout(); }; </script> diff --git a/web/src/routes/auth/login/+page.svelte b/web/src/routes/auth/login/+page.svelte index c3d01b3c56..fdad88e1ff 100644 --- a/web/src/routes/auth/login/+page.svelte +++ b/web/src/routes/auth/login/+page.svelte @@ -2,10 +2,11 @@ import { goto } from '$app/navigation'; import AuthPageLayout from '$lib/components/layouts/AuthPageLayout.svelte'; import { AppRoute } from '$lib/constants'; + import { eventManager } from '$lib/managers/event-manager.svelte'; import { featureFlags, serverConfig } from '$lib/stores/server-config.store'; import { oauth } from '$lib/utils'; import { getServerErrorMessage, handleError } from '$lib/utils/handle-error'; - import { login } from '@immich/sdk'; + import { login, type LoginResponseDto } from '@immich/sdk'; import { Alert, Button, Field, Input, PasswordInput, Stack } from '@immich/ui'; import { onMount } from 'svelte'; import { t } from 'svelte-i18n'; @@ -24,7 +25,11 @@ let loading = $state(false); let oauthLoading = $state(true); - const onSuccess = async () => await goto(AppRoute.PHOTOS, { invalidateAll: true }); + const onSuccess = async (user: LoginResponseDto) => { + await goto(AppRoute.PHOTOS, { invalidateAll: true }); + eventManager.emit('auth.login', user); + }; + const onFirstLogin = async () => await goto(AppRoute.AUTH_CHANGE_PASSWORD); const onOnboarding = async () => await goto(AppRoute.AUTH_ONBOARDING); @@ -36,8 +41,8 @@ if (oauth.isCallback(globalThis.location)) { try { - await oauth.login(globalThis.location); - await onSuccess(); + const user = await oauth.login(globalThis.location); + await onSuccess(user); return; } catch (error) { console.error('Error [login-form] [oauth.callback]', error); @@ -74,7 +79,7 @@ await onFirstLogin(); return; } - await onSuccess(); + await onSuccess(user); return; } catch (error) { errorMessage = getServerErrorMessage(error) || $t('errors.incorrect_email_or_password'); @@ -132,7 +137,7 @@ <div class="inline-flex w-full items-center justify-center my-4"> <hr class="my-4 h-px w-3/4 border-0 bg-gray-200 dark:bg-gray-600" /> <span - class="absolute left-1/2 -translate-x-1/2 bg-gray-50 px-3 font-medium text-gray-900 dark:bg-neutral-900 dark:text-white" + class="absolute start-1/2 -translate-x-1/2 bg-gray-50 px-3 font-medium text-gray-900 dark:bg-neutral-900 dark:text-white" > {$t('or').toUpperCase()} </span> diff --git a/web/src/service-worker/index.ts b/web/src/service-worker/index.ts new file mode 100644 index 0000000000..797f4754b6 --- /dev/null +++ b/web/src/service-worker/index.ts @@ -0,0 +1,86 @@ +/// <reference types="@sveltejs/kit" /> +/// <reference no-default-lib="true"/> +/// <reference lib="esnext" /> +/// <reference lib="webworker" /> +import { version } from '$service-worker'; + +const useCache = true; +const sw = globalThis as unknown as ServiceWorkerGlobalScope; +const pendingLoads = new Map<string, AbortController>(); + +// Create a unique cache name for this deployment +const CACHE = `cache-${version}`; + +sw.addEventListener('install', (event) => { + event.waitUntil(sw.skipWaiting()); +}); + +sw.addEventListener('activate', (event) => { + event.waitUntil(sw.clients.claim()); + // Remove previous cached data from disk + event.waitUntil(deleteOldCaches()); +}); + +sw.addEventListener('fetch', (event) => { + if (event.request.method !== 'GET') { + return; + } + const url = new URL(event.request.url); + if (/^\/api\/assets\/[a-f0-9-]+\/(original|thumbnail)/.test(url.pathname)) { + event.respondWith(immichAsset(url)); + } +}); + +async function deleteOldCaches() { + for (const key of await caches.keys()) { + if (key !== CACHE) { + await caches.delete(key); + } + } +} + +async function immichAsset(url: URL) { + const cache = await caches.open(CACHE); + let response = useCache ? await cache.match(url) : undefined; + if (response) { + return response; + } + try { + const cancelToken = new AbortController(); + const request = fetch(url, { + signal: cancelToken.signal, + }); + pendingLoads.set(url.toString(), cancelToken); + response = await request; + if (!(response instanceof Response)) { + throw new TypeError('invalid response from fetch'); + } + if (response.status === 200) { + cache.put(url, response.clone()); + } + return response; + } catch { + return Response.error(); + } finally { + pendingLoads.delete(url.toString()); + } +} + +const broadcast = new BroadcastChannel('immich'); +// eslint-disable-next-line unicorn/prefer-add-event-listener +broadcast.onmessage = (event) => { + if (!event.data) { + return; + } + const urlstring = event.data.url; + const url = new URL(urlstring, event.origin); + if (event.data.type === 'cancel') { + const pending = pendingLoads.get(url.toString()); + if (pending) { + pending.abort(); + pendingLoads.delete(url.toString()); + } + } else if (event.data.type === 'preload') { + immichAsset(url); + } +}; diff --git a/web/src/test-data/setup.ts b/web/src/test-data/setup.ts index 6709bd5b80..f2cfac3c36 100644 --- a/web/src/test-data/setup.ts +++ b/web/src/test-data/setup.ts @@ -4,3 +4,15 @@ import { init } from 'svelte-i18n'; beforeAll(async () => { await init({ fallbackLocale: 'dev' }); }); + +Object.defineProperty(globalThis, 'matchMedia', { + writable: true, + value: vi.fn().mockImplementation((query) => ({ + matches: false, + media: query, + onchange: null, + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), + })), +}); diff --git a/web/static/.well-known/security.txt b/web/static/.well-known/security.txt new file mode 100644 index 0000000000..1cd9daa56a --- /dev/null +++ b/web/static/.well-known/security.txt @@ -0,0 +1,7 @@ +# This site is running an Immich instance. +# Immich-related security problems should be reported to the Immich security team. +# Security problems related to this instance should be reported to its administration. +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